Contract

0x54B91A0D94cb471F37f949c60F7Fa7935b551D03

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

-

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

3 Internal Transactions found.

Latest 3 internal transactions

Parent Transaction Hash Block From To
6223322024-12-18 21:45:0524 days ago1734558305
0x54B91A0D...35b551D03
 Contract Creation0 S
6223322024-12-18 21:45:0524 days ago1734558305
0x54B91A0D...35b551D03
 Contract Creation0 S
4336982024-12-15 7:23:1727 days ago1734247397  Contract Creation0 S
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FluidLendingFactory

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
Yes with 10000000 runs

Other Settings:
paris EvmVersion
File 1 of 14 : main.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

import { CREATE3 } from "solmate/src/utils/CREATE3.sol";
import { SSTORE2 } from "solmate/src/utils/SSTORE2.sol";
import { Owned } from "solmate/src/auth/Owned.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";

import { IFluidLiquidity } from "../../../liquidity/interfaces/iLiquidity.sol";
import { IFluidLendingFactory, IFluidLendingFactoryAdmin } from "../interfaces/iLendingFactory.sol";
import { LiquiditySlotsLink } from "../../../libraries/liquiditySlotsLink.sol";
import { ErrorTypes } from "../errorTypes.sol";
import { Error } from "../error.sol";
import { Events } from "./events.sol";

abstract contract LendingFactoryVariables is Owned, Error, IFluidLendingFactory {
    /*//////////////////////////////////////////////////////////////
                          CONSTANTS / IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    /// @inheritdoc IFluidLendingFactory
    IFluidLiquidity public immutable LIQUIDITY;

    /// @dev address that is mapped to the chain native token
    address internal constant _NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    /*//////////////////////////////////////////////////////////////
                          STORAGE VARIABLES
    //////////////////////////////////////////////////////////////*/

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

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

    // 12 bytes empty

    // ----------------------- slot 1 ---------------------------
    /// @dev auths can update rewards related config at created fToken contracts.
    /// owner can add/remove auths.
    /// Owner is auth by default.
    mapping(address => uint256) internal _auths;

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

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

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

    /// @dev available fTokenTypes for deployment. At least EIP2612Deposits, Permit2Deposits, NativeUnderlying.
    /// Solidity creates an automatic getter only to fetch at a certain position, so explicitly define a getter that returns all.
    string[] internal _fTokenTypes;

    // ----------------------- slot 5 ---------------------------

    /// @dev fToken creation code for each fTokenType, accessed via SSTORE2.
    /// maps keccak256(abi.encode(fTokenType)) -> SSTORE2 written creation code for the fToken contract
    mapping(bytes32 => address) internal _fTokenCreationCodePointers;

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

    constructor(IFluidLiquidity liquidity_, address owner_) Owned(owner_) {
        if (owner_ == address(0)) {
            // Owned does not have a zero check for owner_
            revert FluidLendingError(ErrorTypes.LendingFactory__ZeroAddress);
        }

        LIQUIDITY = liquidity_;
    }

    /// @inheritdoc IFluidLendingFactory
    function allTokens() public view returns (address[] memory) {
        return _allTokens;
    }

    /// @inheritdoc IFluidLendingFactory
    function fTokenTypes() public view returns (string[] memory) {
        return _fTokenTypes;
    }

    /// @inheritdoc IFluidLendingFactory
    function fTokenCreationCode(string memory fTokenType_) public view returns (bytes memory) {
        address creationCodePointer_ = _fTokenCreationCodePointers[keccak256(abi.encode(fTokenType_))];
        return creationCodePointer_ == address(0) ? new bytes(0) : SSTORE2.read(creationCodePointer_);
    }
}

abstract contract LendingFactoryAdmin is LendingFactoryVariables, Events {
    /// @dev validates that an address is not the zero address
    modifier validAddress(address value_) {
        if (value_ == address(0)) {
            revert FluidLendingError(ErrorTypes.LendingFactory__ZeroAddress);
        }
        _;
    }

    /// @dev validates that msg.sender is auth or owner
    modifier onlyAuths() {
        if (!isAuth(msg.sender)) {
            revert FluidLendingError(ErrorTypes.LendingFactory__Unauthorized);
        }
        _;
    }

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

    /// @inheritdoc IFluidLendingFactoryAdmin
    function isAuth(address auth_) public view returns (bool) {
        return auth_ == owner || _auths[auth_] == 1;
    }

    /// @inheritdoc IFluidLendingFactoryAdmin
    function isDeployer(address deployer_) public view returns (bool) {
        return deployer_ == owner || _deployers[deployer_] == 1;
    }

    /// @inheritdoc IFluidLendingFactoryAdmin
    function setAuth(address auth_, bool allowed_) external onlyOwner validAddress(auth_) {
        _auths[auth_] = allowed_ ? 1 : 0;

        emit LogSetAuth(auth_, allowed_);
    }

    /// @inheritdoc IFluidLendingFactoryAdmin
    function setDeployer(address deployer_, bool allowed_) external onlyOwner validAddress(deployer_) {
        _deployers[deployer_] = allowed_ ? 1 : 0;

        emit LogSetDeployer(deployer_, allowed_);
    }

    /// @inheritdoc IFluidLendingFactoryAdmin
    function setFTokenCreationCode(string memory fTokenType_, bytes calldata creationCode_) external onlyAuths {
        uint256 length_ = _fTokenTypes.length;
        bytes32 fTokenTypeHash_ = keccak256(abi.encode(fTokenType_));

        if (creationCode_.length == 0) {
            // remove any previously stored creation code for `fTokenType_`
            delete _fTokenCreationCodePointers[keccak256(abi.encode(fTokenType_))];

            // remove key from array _fTokenTypes. _fTokenTypes is most likely an array of very few elements,
            // where setFTokenCreationCode is a rarely called method and the removal of an fTokenType is even more rare.
            // So gas cost is not really an issue here but even if it were, this should still be cheaper than having
            // an additional mapping like with an OpenZeppelin EnumerableSet
            for (uint256 i; i < length_; ++i) {
                if (keccak256(abi.encode(_fTokenTypes[i])) == fTokenTypeHash_) {
                    _fTokenTypes[i] = _fTokenTypes[length_ - 1];
                    _fTokenTypes.pop();
                    break;
                }
            }

            emit LogSetFTokenCreationCode(fTokenType_, address(0));
        } else {
            // write creation code to SSTORE2 pointer and set in mapping
            address creationCodePointer_ = SSTORE2.write(creationCode_);
            _fTokenCreationCodePointers[keccak256(abi.encode(fTokenType_))] = creationCodePointer_;

            // make sure `fTokenType_` is present in array _fTokenTypes
            bool isPresent_;
            for (uint256 i; i < length_; ++i) {
                if (keccak256(abi.encode(_fTokenTypes[i])) == fTokenTypeHash_) {
                    isPresent_ = true;
                    break;
                }
            }
            if (!isPresent_) {
                _fTokenTypes.push(fTokenType_);
            }

            emit LogSetFTokenCreationCode(fTokenType_, creationCodePointer_);
        }
    }

    /// @inheritdoc IFluidLendingFactoryAdmin
    function createToken(
        address asset_,
        string calldata fTokenType_,
        bool isNativeUnderlying_
    ) external validAddress(asset_) onlyDeployers returns (address token_) {
        address creationCodePointer_ = _fTokenCreationCodePointers[keccak256(abi.encode(fTokenType_))];
        if (creationCodePointer_ == address(0)) {
            revert FluidLendingError(ErrorTypes.LendingFactory__InvalidParams);
        }

        bytes32 salt_ = _getSalt(asset_, fTokenType_);

        if (Address.isContract(CREATE3.getDeployed(salt_))) {
            // revert if token already exists (Solmate CREATE3 does not check before deploying)
            revert FluidLendingError(ErrorTypes.LendingFactory__TokenExists);
        }

        bytes32 liquidityExchangePricesSlot_ = LiquiditySlotsLink.calculateMappingStorageSlot(
            LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT,
            // native underlying always uses the native token at Liquidity, but also supports WETH
            isNativeUnderlying_ ? _NATIVE_TOKEN_ADDRESS : asset_
        );
        if (LIQUIDITY.readFromStorage(liquidityExchangePricesSlot_) == 0) {
            // revert if fToken has not been configured at Liquidity contract yet (exchange prices config)
            revert FluidLendingError(ErrorTypes.LendingFactory__LiquidityNotConfigured);
        }

        // Use CREATE3 for deterministic deployments. Unfortunately it has 55k gas overhead
        token_ = CREATE3.deploy(
            salt_,
            abi.encodePacked(
                SSTORE2.read(creationCodePointer_), // creation code
                abi.encode(LIQUIDITY, address(this), asset_) // constructor params
            ),
            0
        );

        // Add the created token to the allTokens array
        _allTokens.push(token_);

        // Emit the TokenCreated event
        emit LogTokenCreated(token_, asset_, _allTokens.length, fTokenType_);
    }

    /*//////////////////////////////////////////////////////////////
                                INTERNAL
    //////////////////////////////////////////////////////////////*/

    /// @dev gets the CREATE3 salt for `asset_` and `fTokenType_`
    function _getSalt(address asset_, string calldata fTokenType_) internal pure returns (bytes32) {
        return keccak256(abi.encode(asset_, fTokenType_));
    }
}

/// @title Fluid LendingFactory
/// @notice creates Fluid lending protocol fTokens, which are interacting with Fluid Liquidity.
/// fTokens are ERC20 & ERC4626 compatible tokens that allow to deposit to Fluid Liquidity to earn interest.
/// Tokens are created at a deterministic address (see `computeToken()`), only executable by allow-listed auths.
/// @dev Note the deployed token starts 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 fToken creation codes for future new fToken types.
contract FluidLendingFactory is LendingFactoryVariables, LendingFactoryAdmin {
    /// @notice initialize liquidity contract address & owner
    constructor(
        IFluidLiquidity liquidity_,
        address owner_
    ) validAddress(address(liquidity_)) validAddress(owner) LendingFactoryVariables(liquidity_, owner_) {}

    /// @inheritdoc IFluidLendingFactory
    function computeToken(address asset_, string calldata fTokenType_) public view returns (address token_) {
        return CREATE3.getDeployed(_getSalt(asset_, fTokenType_));
    }
}

File 2 of 14 : Address.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

File 3 of 14 : iProxy.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

interface IProxy {
    function setAdmin(address newAdmin_) external;

    function setDummyImplementation(address newDummyImplementation_) external;

    function addImplementation(address implementation_, bytes4[] calldata sigs_) external;

    function removeImplementation(address implementation_) external;

    function getAdmin() external view returns (address);

    function getDummyImplementation() external view returns (address);

    function getImplementationSigs(address impl_) external view returns (bytes4[] memory);

    function getSigsImplementation(bytes4 sig_) external view returns (address);

    function readFromStorage(bytes32 slot_) external view returns (uint256 result_);
}

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

/// @notice library that helps in reading / working with storage slot data of Fluid Liquidity.
/// @dev as all data for Fluid Liquidity is internal, any data must be fetched directly through manual
/// slot reading through this library or, if gas usage is less important, through the FluidLiquidityResolver.
library LiquiditySlotsLink {
    /// @dev storage slot for status at Liquidity
    uint256 internal constant LIQUIDITY_STATUS_SLOT = 1;
    /// @dev storage slot for auths mapping at Liquidity
    uint256 internal constant LIQUIDITY_AUTHS_MAPPING_SLOT = 2;
    /// @dev storage slot for guardians mapping at Liquidity
    uint256 internal constant LIQUIDITY_GUARDIANS_MAPPING_SLOT = 3;
    /// @dev storage slot for user class mapping at Liquidity
    uint256 internal constant LIQUIDITY_USER_CLASS_MAPPING_SLOT = 4;
    /// @dev storage slot for exchangePricesAndConfig mapping at Liquidity
    uint256 internal constant LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT = 5;
    /// @dev storage slot for rateData mapping at Liquidity
    uint256 internal constant LIQUIDITY_RATE_DATA_MAPPING_SLOT = 6;
    /// @dev storage slot for totalAmounts mapping at Liquidity
    uint256 internal constant LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT = 7;
    /// @dev storage slot for user supply double mapping at Liquidity
    uint256 internal constant LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT = 8;
    /// @dev storage slot for user borrow double mapping at Liquidity
    uint256 internal constant LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT = 9;
    /// @dev storage slot for listed tokens array at Liquidity
    uint256 internal constant LIQUIDITY_LISTED_TOKENS_ARRAY_SLOT = 10;

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

    // ExchangePricesAndConfig
    uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATE = 0;
    uint256 internal constant BITS_EXCHANGE_PRICES_FEE = 16;
    uint256 internal constant BITS_EXCHANGE_PRICES_UTILIZATION = 30;
    uint256 internal constant BITS_EXCHANGE_PRICES_UPDATE_THRESHOLD = 44;
    uint256 internal constant BITS_EXCHANGE_PRICES_LAST_TIMESTAMP = 58;
    uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE = 91;
    uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE = 155;
    uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_RATIO = 219;
    uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATIO = 234;

    // RateData:
    uint256 internal constant BITS_RATE_DATA_VERSION = 0;
    // RateData: V1
    uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO = 4;
    uint256 internal constant BITS_RATE_DATA_V1_UTILIZATION_AT_KINK = 20;
    uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK = 36;
    uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX = 52;
    // RateData: V2
    uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO = 4;
    uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1 = 20;
    uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1 = 36;
    uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2 = 52;
    uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2 = 68;
    uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX = 84;

    // TotalAmounts
    uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_WITH_INTEREST = 0;
    uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE = 64;
    uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST = 128;
    uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE = 192;

    // UserSupplyData
    uint256 internal constant BITS_USER_SUPPLY_MODE = 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;
    uint256 internal constant BITS_USER_SUPPLY_IS_PAUSED = 255;

    // UserBorrowData
    uint256 internal constant BITS_USER_BORROW_MODE = 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;
    uint256 internal constant BITS_USER_BORROW_IS_PAUSED = 255;

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

    /// @notice Calculating the slot ID for Liquidity 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 Liquidity 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 5 of 14 : structs.sol
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;

abstract contract Structs {
    struct AddressBool {
        address addr;
        bool value;
    }

    struct AddressUint256 {
        address addr;
        uint256 value;
    }

    /// @notice struct to set borrow rate data for version 1
    struct RateDataV1Params {
        ///
        /// @param token for rate data
        address token;
        ///
        /// @param kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
        /// utilization below kink usually means slow increase in rate, once utilization is above kink borrow rate increases fast
        uint256 kink;
        ///
        /// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100
        /// i.e. constant minimum borrow rate
        /// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)
        uint256 rateAtUtilizationZero;
        ///
        /// @param rateAtUtilizationKink borrow rate when utilization is at kink. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 7% at kink then rateAtUtilizationKink would be 700
        uint256 rateAtUtilizationKink;
        ///
        /// @param rateAtUtilizationMax borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500
        uint256 rateAtUtilizationMax;
    }

    /// @notice struct to set borrow rate data for version 2
    struct RateDataV2Params {
        ///
        /// @param token for rate data
        address token;
        ///
        /// @param kink1 first kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
        /// utilization below kink 1 usually means slow increase in rate, once utilization is above kink 1 borrow rate increases faster
        uint256 kink1;
        ///
        /// @param kink2 second kink in borrow rate. in 1e2: 100% = 10_000; 1% = 100
        /// utilization below kink 2 usually means slow / medium increase in rate, once utilization is above kink 2 borrow rate increases fast
        uint256 kink2;
        ///
        /// @param rateAtUtilizationZero desired borrow rate when utilization is zero. in 1e2: 100% = 10_000; 1% = 100
        /// i.e. constant minimum borrow rate
        /// e.g. at utilization = 0.01% rate could still be at least 4% (rateAtUtilizationZero would be 400 then)
        uint256 rateAtUtilizationZero;
        ///
        /// @param rateAtUtilizationKink1 desired borrow rate when utilization is at first kink. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 7% at first kink then rateAtUtilizationKink would be 700
        uint256 rateAtUtilizationKink1;
        ///
        /// @param rateAtUtilizationKink2 desired borrow rate when utilization is at second kink. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 7% at second kink then rateAtUtilizationKink would be 1_200
        uint256 rateAtUtilizationKink2;
        ///
        /// @param rateAtUtilizationMax desired borrow rate when utilization is maximum at 100%. in 1e2: 100% = 10_000; 1% = 100
        /// e.g. when rate should be 125% at 100% then rateAtUtilizationMax would be 12_500
        uint256 rateAtUtilizationMax;
    }

    /// @notice struct to set token config
    struct TokenConfig {
        ///
        /// @param token address
        address token;
        ///
        /// @param fee charges on borrower's interest. in 1e2: 100% = 10_000; 1% = 100
        uint256 fee;
        ///
        /// @param threshold on when to update the storage slot. in 1e2: 100% = 10_000; 1% = 100
        uint256 threshold;
    }

    /// @notice struct to set user supply & withdrawal config
    struct UserSupplyConfig {
        ///
        /// @param user address
        address user;
        ///
        /// @param token address
        address token;
        ///
        /// @param mode: 0 = without interest. 1 = with interest
        uint8 mode;
        ///
        /// @param expandPercent withdrawal limit expand percent. in 1e2: 100% = 10_000; 1% = 100
        /// Also used to calculate rate at which withdrawal limit should decrease (instant).
        uint256 expandPercent;
        ///
        /// @param expandDuration withdrawal limit expand duration in seconds.
        /// used to calculate rate together with expandPercent
        uint256 expandDuration;
        ///
        /// @param baseWithdrawalLimit base limit, below this, user can withdraw the entire amount.
        /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
        /// with interest -> raw, without interest -> normal
        uint256 baseWithdrawalLimit;
    }

    /// @notice struct to set user borrow & payback config
    struct UserBorrowConfig {
        ///
        /// @param user address
        address user;
        ///
        /// @param token address
        address token;
        ///
        /// @param mode: 0 = without interest. 1 = with interest
        uint8 mode;
        ///
        /// @param expandPercent debt limit expand percent. in 1e2: 100% = 10_000; 1% = 100
        /// Also used to calculate rate at which debt limit should decrease (instant).
        uint256 expandPercent;
        ///
        /// @param expandDuration debt limit expand duration in seconds.
        /// used to calculate rate together with expandPercent
        uint256 expandDuration;
        ///
        /// @param baseDebtCeiling base borrow limit. until here, borrow limit remains as baseDebtCeiling
        /// (user can borrow until this point at once without stepped expansion). Above this, automated limit comes in place.
        /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
        /// with interest -> raw, without interest -> normal
        uint256 baseDebtCeiling;
        ///
        /// @param maxDebtCeiling max borrow ceiling, maximum amount the user can borrow.
        /// amount in raw (to be multiplied with exchange price) or normal depends on configured mode in user config for the token:
        /// with interest -> raw, without interest -> normal
        uint256 maxDebtCeiling;
    }
}

File 6 of 14 : iLiquidity.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import { IProxy } from "../../infiniteProxy/interfaces/iProxy.sol";
import { Structs as AdminModuleStructs } from "../adminModule/structs.sol";

interface IFluidLiquidityAdmin {
    /// @notice adds/removes auths. Auths generally could be contracts which can have restricted actions defined on contract.
    ///         auths can be helpful in reducing governance overhead where it's not needed.
    /// @param authsStatus_ array of structs setting allowed status for an address.
    ///                     status true => add auth, false => remove auth
    function updateAuths(AdminModuleStructs.AddressBool[] calldata authsStatus_) external;

    /// @notice adds/removes guardians. Only callable by Governance.
    /// @param guardiansStatus_ array of structs setting allowed status for an address.
    ///                         status true => add guardian, false => remove guardian
    function updateGuardians(AdminModuleStructs.AddressBool[] calldata guardiansStatus_) external;

    /// @notice changes the revenue collector address (contract that is sent revenue). Only callable by Governance.
    /// @param revenueCollector_  new revenue collector address
    function updateRevenueCollector(address revenueCollector_) external;

    /// @notice changes current status, e.g. for pausing or unpausing all user operations. Only callable by Auths.
    /// @param newStatus_ new status
    ///        status = 2 -> pause, status = 1 -> resume.
    function changeStatus(uint256 newStatus_) external;

    /// @notice                  update tokens rate data version 1. Only callable by Auths.
    /// @param tokensRateData_   array of RateDataV1Params with rate data to set for each token
    function updateRateDataV1s(AdminModuleStructs.RateDataV1Params[] calldata tokensRateData_) external;

    /// @notice                  update tokens rate data version 2. Only callable by Auths.
    /// @param tokensRateData_   array of RateDataV2Params with rate data to set for each token
    function updateRateDataV2s(AdminModuleStructs.RateDataV2Params[] calldata tokensRateData_) external;

    /// @notice updates token configs: fee charge on borrowers interest & storage update utilization threshold.
    ///         Only callable by Auths.
    /// @param tokenConfigs_ contains token address, fee & utilization threshold
    function updateTokenConfigs(AdminModuleStructs.TokenConfig[] calldata tokenConfigs_) external;

    /// @notice updates user classes: 0 is for new protocols, 1 is for established protocols.
    ///         Only callable by Auths.
    /// @param userClasses_ struct array of uint256 value to assign for each user address
    function updateUserClasses(AdminModuleStructs.AddressUint256[] calldata userClasses_) external;

    /// @notice sets user supply configs per token basis. Eg: with interest or interest-free and automated limits.
    ///         Only callable by Auths.
    /// @param userSupplyConfigs_ struct array containing user supply config, see `UserSupplyConfig` struct for more info
    function updateUserSupplyConfigs(AdminModuleStructs.UserSupplyConfig[] memory userSupplyConfigs_) external;

    /// @notice setting user borrow configs per token basis. Eg: with interest or interest-free and automated limits.
    ///         Only callable by Auths.
    /// @param userBorrowConfigs_ struct array containing user borrow config, see `UserBorrowConfig` struct for more info
    function updateUserBorrowConfigs(AdminModuleStructs.UserBorrowConfig[] memory userBorrowConfigs_) external;

    /// @notice pause operations for a particular user in class 0 (class 1 users can't be paused by guardians).
    /// Only callable by Guardians.
    /// @param user_          address of user to pause operations for
    /// @param supplyTokens_  token addresses to pause withdrawals for
    /// @param borrowTokens_  token addresses to pause borrowings for
    function pauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;

    /// @notice unpause operations for a particular user in class 0 (class 1 users can't be paused by guardians).
    /// Only callable by Guardians.
    /// @param user_          address of user to unpause operations for
    /// @param supplyTokens_  token addresses to unpause withdrawals for
    /// @param borrowTokens_  token addresses to unpause borrowings for
    function unpauseUser(address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_) external;

    /// @notice         collects revenue for tokens to configured revenueCollector address.
    /// @param tokens_  array of tokens to collect revenue for
    /// @dev            Note that this can revert if token balance is < revenueAmount (utilization > 100%)
    function collectRevenue(address[] calldata tokens_) external;

    /// @notice gets the current updated exchange prices for n tokens and updates all prices, rates related data in storage.
    /// @param tokens_ tokens to update exchange prices for
    /// @return supplyExchangePrices_ new supply rates of overall system for each token
    /// @return borrowExchangePrices_ new borrow rates of overall system for each token
    function updateExchangePrices(
        address[] calldata tokens_
    ) external returns (uint256[] memory supplyExchangePrices_, uint256[] memory borrowExchangePrices_);
}

interface IFluidLiquidityLogic is IFluidLiquidityAdmin {
    /// @notice Single function which handles supply, withdraw, borrow & payback
    /// @param token_ address of token (0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for native)
    /// @param supplyAmount_ if +ve then supply, if -ve then withdraw, if 0 then nothing
    /// @param borrowAmount_ if +ve then borrow, if -ve then payback, if 0 then nothing
    /// @param withdrawTo_ if withdrawal then to which address
    /// @param borrowTo_ if borrow then to which address
    /// @param callbackData_ callback data passed to `liquidityCallback` method of protocol
    /// @return memVar3_ updated supplyExchangePrice
    /// @return memVar4_ updated borrowExchangePrice
    /// @dev to trigger skipping in / out transfers when in&out amounts balance themselves out (gas optimization):
    /// -   supply(+) == borrow(+), withdraw(-) == payback(-).
    /// -   `withdrawTo_` / `borrowTo_` must be msg.sender (protocol)
    /// -   `callbackData_` MUST be encoded so that "from" address is at last 20 bytes (if this optimization is desired),
    ///     also for native token operations where liquidityCallback is not triggered!
    ///     from address must come at last position if there is more data. I.e. encode like:
    ///     abi.encode(otherVar1, otherVar2, FROM_ADDRESS). Note dynamic types used with abi.encode come at the end
    ///     so if dynamic types are needed, you must use abi.encodePacked to ensure the from address is at the end.
    function operate(
        address token_,
        int256 supplyAmount_,
        int256 borrowAmount_,
        address withdrawTo_,
        address borrowTo_,
        bytes calldata callbackData_
    ) external payable returns (uint256 memVar3_, uint256 memVar4_);
}

interface IFluidLiquidity is IProxy, IFluidLiquidityLogic {}

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

abstract contract Error {
    error FluidLendingError(uint256 errorId_);
}

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

library ErrorTypes {
    /***********************************|
    |               fToken              | 
    |__________________________________*/

    /// @notice thrown when a deposit amount is too small to increase BigMath stored balance in Liquidity.
    /// precision of BigMath is 1e12, so if token holds 120_000_000_000 USDC, min amount to make a difference would be 0.1 USDC.
    /// i.e. user would send a very small deposit which mints no shares -> revert
    uint256 internal constant fToken__DepositInsignificant = 20001;

    /// @notice thrown when minimum output amount is not reached, e.g. for minimum shares minted (deposit) or
    ///         minimum assets received (redeem)
    uint256 internal constant fToken__MinAmountOut = 20002;

    /// @notice thrown when maximum amount is surpassed, e.g. for maximum shares burned (withdraw) or
    ///         maximum assets input (mint)
    uint256 internal constant fToken__MaxAmount = 20003;

    /// @notice thrown when invalid params are sent to a method, e.g. zero address
    uint256 internal constant fToken__InvalidParams = 20004;

    /// @notice thrown when an unauthorized caller is trying to execute an auth-protected method
    uint256 internal constant fToken__Unauthorized = 20005;

    /// @notice thrown when a with permit / signature method is called from msg.sender that is the owner.
    /// Should call the method without permit instead if msg.sender is the owner.
    uint256 internal constant fToken__PermitFromOwnerCall = 20006;

    /// @notice thrown when a reentrancy is detected.
    uint256 internal constant fToken__Reentrancy = 20007;

    /// @notice thrown when _tokenExchangePrice overflows type(uint64).max
    uint256 internal constant fToken__ExchangePriceOverflow = 20008;

    /// @notice thrown when msg.sender is not rebalancer
    uint256 internal constant fToken__NotRebalancer = 20009;

    /// @notice thrown when rebalance is called with msg.value > 0 for non NativeUnderlying fToken
    uint256 internal constant fToken__NotNativeUnderlying = 20010;

    /// @notice thrown when the received new liquidity exchange price is of unexpected value (< than the old one)
    uint256 internal constant fToken__LiquidityExchangePriceUnexpected = 20011;

    /***********************************|
    |     fToken Native Underlying      | 
    |__________________________________*/

    /// @notice thrown when native deposit is called but sent along `msg.value` does not cover the deposit amount
    uint256 internal constant fTokenNativeUnderlying__TransferInsufficient = 21001;

    /// @notice thrown when a liquidity callback is called for a native token operation
    uint256 internal constant fTokenNativeUnderlying__UnexpectedLiquidityCallback = 21002;

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

    /// @notice thrown when a method is called with invalid params
    uint256 internal constant LendingFactory__InvalidParams = 22001;

    /// @notice thrown when the provided input param address is zero
    uint256 internal constant LendingFactory__ZeroAddress = 22002;

    /// @notice thrown when the token already exists
    uint256 internal constant LendingFactory__TokenExists = 22003;

    /// @notice thrown when the fToken has not yet been configured at Liquidity
    uint256 internal constant LendingFactory__LiquidityNotConfigured = 22004;

    /// @notice thrown when an unauthorized caller is trying to execute an auth-protected method
    uint256 internal constant LendingFactory__Unauthorized = 22005;

    /***********************************|
    |   Lending Rewards Rate Model      | 
    |__________________________________*/

    /// @notice thrown when invalid params are given as input
    uint256 internal constant LendingRewardsRateModel__InvalidParams = 23001;

    /// @notice thrown when calculated rewards rate is exceeding the maximum rate
    uint256 internal constant LendingRewardsRateModel__MaxRate = 23002;

    /// @notice thrown when start is called by any other address other than initiator
    uint256 internal constant LendingRewardsRateModel__NotTheInitiator = 23003;

    /// @notice thrown when start is called after the rewards are already started
    uint256 internal constant LendingRewardsRateModel__AlreadyStarted = 23004;

    /// @notice thrown when the provided input param address is zero
    uint256 internal constant LendingRewardsRateModel__ZeroAddress = 23005;
}

File 9 of 14 : iLendingFactory.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;

import { IFluidLiquidity } from "../../../liquidity/interfaces/iLiquidity.sol";

interface IFluidLendingFactoryAdmin {
    /// @notice reads if a certain `auth_` address is an allowed auth or not. Owner is auth by default.
    function isAuth(address auth_) external view returns (bool);

    /// @notice              Sets an address as allowed auth or not. Only callable by owner.
    /// @param auth_         address to set auth value for
    /// @param allowed_      bool flag for whether address is allowed as auth or not
    function setAuth(address auth_, bool allowed_) external;

    /// @notice reads if a certain `deployer_` address is an allowed deployer or not. Owner is deployer by default.
    function isDeployer(address deployer_) external view returns (bool);

    /// @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 setDeployer(address deployer_, bool allowed_) external;

    /// @notice              Sets the `creationCode_` bytecode for a certain `fTokenType_`. Only callable by auths.
    /// @param fTokenType_   the fToken Type used to refer the creation code
    /// @param creationCode_ contract creation code. can be set to bytes(0) to remove a previously available `fTokenType_`
    function setFTokenCreationCode(string memory fTokenType_, bytes calldata creationCode_) external;

    /// @notice creates token for `asset_` for a lending protocol with interest. Only callable by deployers.
    /// @param  asset_              address of the asset
    /// @param  fTokenType_         type of fToken:
    /// - if it's the native token, it should use `NativeUnderlying`
    /// - otherwise it should use `fToken`
    /// - could be more types available, check `fTokenTypes()`
    /// @param  isNativeUnderlying_ flag to signal fToken type that uses native underlying at Liquidity
    /// @return token_              address of the created token
    function createToken(
        address asset_,
        string calldata fTokenType_,
        bool isNativeUnderlying_
    ) external returns (address token_);
}

interface IFluidLendingFactory is IFluidLendingFactoryAdmin {
    /// @notice list of all created tokens
    function allTokens() external view returns (address[] memory);

    /// @notice list of all fToken types that can be deployed
    function fTokenTypes() external view returns (string[] memory);

    /// @notice returns the creation code for a certain `fTokenType_`
    function fTokenCreationCode(string memory fTokenType_) external view returns (bytes memory);

    /// @notice address of the Liquidity contract.
    function LIQUIDITY() external view returns (IFluidLiquidity);

    /// @notice computes deterministic token address for `asset_` for a lending protocol
    /// @param  asset_      address of the asset
    /// @param  fTokenType_         type of fToken:
    /// - if it's the native token, it should use `NativeUnderlying`
    /// - otherwise it should use `fToken`
    /// - could be more types available, check `fTokenTypes()`
    /// @return token_      detemrinistic address of the computed token
    function computeToken(address asset_, string calldata fTokenType_) external view returns (address token_);
}

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

import { IFluidLendingFactory } from "../interfaces/iLendingFactory.sol";

abstract contract Events {
    /// @notice emitted when a new fToken is created
    event LogTokenCreated(address indexed token, address indexed asset, uint256 indexed count, string fTokenType);

    /// @notice emitted when an auth is modified by owner
    event LogSetAuth(address indexed auth, bool indexed allowed);

    /// @notice emitted when a deployer is modified by owner
    event LogSetDeployer(address indexed deployer, bool indexed allowed);

    /// @notice emitted when the creation code for an fTokenType is set
    event LogSetFTokenCreationCode(string indexed fTokenType, address indexed creationCodePointer);
}

File 11 of 14 : Owned.sol
// 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);
    }
}

File 12 of 14 : Bytes32AddressLib.sol
// 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));
    }
}

File 13 of 14 : CREATE3.sol
// 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();
    }
}

File 14 of 14 : SSTORE2.sol
// 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

[{"inputs":[{"internalType":"contract IFluidLiquidity","name":"liquidity_","type":"address"},{"internalType":"address","name":"owner_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidLendingError","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"auth","type":"address"},{"indexed":true,"internalType":"bool","name":"allowed","type":"bool"}],"name":"LogSetAuth","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"deployer","type":"address"},{"indexed":true,"internalType":"bool","name":"allowed","type":"bool"}],"name":"LogSetDeployer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"string","name":"fTokenType","type":"string"},{"indexed":true,"internalType":"address","name":"creationCodePointer","type":"address"}],"name":"LogSetFTokenCreationCode","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"uint256","name":"count","type":"uint256"},{"indexed":false,"internalType":"string","name":"fTokenType","type":"string"}],"name":"LogTokenCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"LIQUIDITY","outputs":[{"internalType":"contract IFluidLiquidity","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset_","type":"address"},{"internalType":"string","name":"fTokenType_","type":"string"}],"name":"computeToken","outputs":[{"internalType":"address","name":"token_","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset_","type":"address"},{"internalType":"string","name":"fTokenType_","type":"string"},{"internalType":"bool","name":"isNativeUnderlying_","type":"bool"}],"name":"createToken","outputs":[{"internalType":"address","name":"token_","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"fTokenType_","type":"string"}],"name":"fTokenCreationCode","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fTokenTypes","outputs":[{"internalType":"string[]","name":"","type":"string[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"auth_","type":"address"}],"name":"isAuth","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"deployer_","type":"address"}],"name":"isDeployer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"auth_","type":"address"},{"internalType":"bool","name":"allowed_","type":"bool"}],"name":"setAuth","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"deployer_","type":"address"},{"internalType":"bool","name":"allowed_","type":"bool"}],"name":"setDeployer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"fTokenType_","type":"string"},{"internalType":"bytes","name":"creationCode_","type":"bytes"}],"name":"setFTokenCreationCode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b50604051620022173803806200221783398101604081905262000034916200013d565b600080546001600160a01b0319166001600160a01b03831690811782556040518492849283929091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3506001600160a01b038116620000b45760405163694bda1d60e01b81526155f260048201526024015b60405180910390fd5b506001600160a01b0390811660805282908116620000ea5760405163694bda1d60e01b81526155f26004820152602401620000ab565b6000546001600160a01b0316806200011a5760405163694bda1d60e01b81526155f26004820152602401620000ab565b505050506200017c565b6001600160a01b03811681146200013a57600080fd5b50565b600080604083850312156200015157600080fd5b82516200015e8162000124565b6020840151909250620001718162000124565b809150509250929050565b608051612071620001a660003960008181610126015281816109f50152610ada01526120716000f3fe608060405234801561001057600080fd5b50600436106100df5760003560e01c8063a34b5ee81161008c578063dd179e3b11610066578063dd179e3b146101fb578063e78e049a14610210578063f2fde38b14610223578063f60150791461023657600080fd5b8063a34b5ee8146101b5578063bb4a64d3146101c8578063c949aea6146101db57600080fd5b806350c358a4116100bd57806350c358a41461016d5780636ff97f1d146101805780638da5cb5b1461019557600080fd5b80630b44a218146100e45780632520e7ff146100f95780632861c7d114610121575b600080fd5b6100f76100f2366004611655565b610249565b005b61010c610107366004611688565b61038d565b60405190151581526020015b60405180910390f35b6101487f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610118565b61010c61017b366004611688565b6103e3565b610188610434565b60405161011891906116a3565b6000546101489073ffffffffffffffffffffffffffffffffffffffff1681565b6100f76101c3366004611655565b6104a3565b6101486101d6366004611746565b6105e2565b6101ee6101e9366004611873565b6105ff565b6040516101189190611916565b61020361069f565b6040516101189190611929565b61014861021e3660046119a9565b610778565b6100f7610231366004611688565b610c29565b6100f7610244366004611a0e565b610d1a565b60005473ffffffffffffffffffffffffffffffffffffffff1633146102cf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff8116610321576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f260048201526024016102c6565b8161032d576000610330565b60015b73ffffffffffffffffffffffffffffffffffffffff841660008181526001602052604080822060ff949094169093559151841515927f014b54fa6d2080e9aacd1c598c7689a625610d7d684dd41d10407e48aa8b120091a3505050565b6000805473ffffffffffffffffffffffffffffffffffffffff838116911614806103dd575073ffffffffffffffffffffffffffffffffffffffff8216600090815260016020819052604090912054145b92915050565b6000805473ffffffffffffffffffffffffffffffffffffffff838116911614806103dd57505073ffffffffffffffffffffffffffffffffffffffff1660009081526002602052604090205460011490565b6060600380548060200260200160405190810160405280929190818152602001828054801561049957602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff16815260019091019060200180831161046e575b5050505050905090565b60005473ffffffffffffffffffffffffffffffffffffffff163314610524576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016102c6565b8173ffffffffffffffffffffffffffffffffffffffff8116610576576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f260048201526024016102c6565b81610582576000610585565b60015b73ffffffffffffffffffffffffffffffffffffffff841660008181526002602052604080822060ff949094169093559151841515927f48cc5b4660fae22eabe5e803ee595e63572773d114bcd54ecc118c1efa8d75af91a3505050565b60006105f76105f2858585611111565b611147565b949350505050565b6060600060056000846040516020016106189190611916565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181528151602092830120835290820192909252016000205473ffffffffffffffffffffffffffffffffffffffff169050801561068857610683816112ad565b610698565b6040805160008152602081019091525b9392505050565b60606004805480602002602001604051908101604052809291908181526020016000905b8282101561076f5783829060005260206000200180546106e290611a6a565b80601f016020809104026020016040519081016040528092919081815260200182805461070e90611a6a565b801561075b5780601f106107305761010080835404028352916020019161075b565b820191906000526020600020905b81548152906001019060200180831161073e57829003601f168201915b5050505050815260200190600101906106c3565b50505050905090565b60008473ffffffffffffffffffffffffffffffffffffffff81166107cc576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f260048201526024016102c6565b6107d5336103e3565b61080f576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f560048201526024016102c6565b6000600560008787604051602001610828929190611b06565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181528151602092830120835290820192909252016000205473ffffffffffffffffffffffffffffffffffffffff169050806108bf576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f160048201526024016102c6565b60006108cc888888611111565b90506108f66108da82611147565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b15610931576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f360048201526024016102c6565b60006109c3600587610981576040805173ffffffffffffffffffffffffffffffffffffffff8d16602080830191909152818301939093528151808203830181526060909101909152805191012090565b6040805173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee602080830191909152818301939093528151808203830181526060909101909152805191012090565b6040517fb5c736e4000000000000000000000000000000000000000000000000000000008152600481018290529091507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063b5c736e490602401602060405180830381865afa158015610a51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a759190611b1a565b600003610ab2576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f460048201526024016102c6565b610b6582610abf856112ad565b6040805173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811660208301523092820192909252908d166060820152608001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052610b4f9291602001611b33565b60405160208183030381529060405260006112db565b6003805460018101825560008290527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff848116918217909255915460405193985092908c1691907f60c8487fc242a40cc8d2722cf9b3b5a14b316a50bf4ed30c9f0f1b0126728a3690610c15908d908d90611b06565b60405180910390a450505050949350505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610caa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016102c6565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b610d233361038d565b610d5d576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f560048201526024016102c6565b600454604051600090610d74908690602001611916565b60405160208183030381529060405280519060200120905083839050600003610f45576005600086604051602001610dac9190611916565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012083529082019290925201600090812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b82811015610efe578160048281548110610e3457610e34611b62565b90600052602060002001604051602001610e4e9190611b91565b6040516020818303038152906040528051906020012003610eee576004610e76600185611c69565b81548110610e8657610e86611b62565b9060005260206000200160048281548110610ea357610ea3611b62565b906000526020600020019081610eb99190611ccb565b506004805480610ecb57610ecb611e02565b600190038181906000526020600020016000610ee791906115c6565b9055610efe565b610ef781611e31565b9050610e18565b50604051600090610f10908790611e69565b604051908190038120907f93dad940f342b3cd95007806ae0cb0c162dbbfba54d55223bc6d055c62e608e090600090a361110a565b6000610f8685858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506114ad92505050565b9050806005600088604051602001610f9e9190611916565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000805b8481101561106e57836004828154811061102057611020611b62565b9060005260206000200160405160200161103a9190611b91565b604051602081830303815290604052805190602001200361105e576001915061106e565b61106781611e31565b9050611004565b50806110b257600480546001810182556000919091527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b016110b08882611e85565b505b8173ffffffffffffffffffffffffffffffffffffffff16876040516110d79190611e69565b604051908190038120907f93dad940f342b3cd95007806ae0cb0c162dbbfba54d55223bc6d055c62e608e090600090a350505b5050505050565b600083838360405160200161112893929190611f97565b6040516020818303038152906040528051906020012090509392505050565b604080518082018252601081527f67363d3d37363d34f03d5260086018f30000000000000000000000000000000060209182015290517fff00000000000000000000000000000000000000000000000000000000000000918101919091527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166021820152603581018290527f21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f60558201526000908190611223906075015b6040516020818303038152906040528051906020012090565b6040517fd69400000000000000000000000000000000000000000000000000000000000060208201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606083901b1660228201527f010000000000000000000000000000000000000000000000000000000000000060368201529091506106989060370161120a565b60606103dd8260016112d68173ffffffffffffffffffffffffffffffffffffffff84163b611c69565b611585565b6000806040518060400160405280601081526020017f67363d3d37363d34f03d5260086018f30000000000000000000000000000000081525090506000858251602084016000f5905073ffffffffffffffffffffffffffffffffffffffff81166113a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f4445504c4f594d454e545f4641494c454400000000000000000000000000000060448201526064016102c6565b6113aa86611147565b925060008173ffffffffffffffffffffffffffffffffffffffff1685876040516113d49190611e69565b60006040518083038185875af1925050503d8060008114611411576040519150601f19603f3d011682016040523d82523d6000602084013e611416565b606091505b5050905080801561143d575073ffffffffffffffffffffffffffffffffffffffff84163b15155b6114a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f494e495449414c495a4154494f4e5f4641494c4544000000000000000000000060448201526064016102c6565b5050509392505050565b600080826040516020016114c19190611fd0565b60405160208183030381529060405290506000816040516020016114e59190611ff6565b60405160208183030381529060405290508051602082016000f0925073ffffffffffffffffffffffffffffffffffffffff831661157e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f4445504c4f594d454e545f4641494c454400000000000000000000000000000060448201526064016102c6565b5050919050565b60408051603f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168101909152818152818360208301863c9392505050565b5080546115d290611a6a565b6000825580601f106115e2575050565b601f0160209004906000526020600020908101906116009190611603565b50565b5b808211156116185760008155600101611604565b5090565b803573ffffffffffffffffffffffffffffffffffffffff8116811461164057600080fd5b919050565b8035801515811461164057600080fd5b6000806040838503121561166857600080fd5b6116718361161c565b915061167f60208401611645565b90509250929050565b60006020828403121561169a57600080fd5b6106988261161c565b6020808252825182820181905260009190848201906040850190845b818110156116f157835173ffffffffffffffffffffffffffffffffffffffff16835292840192918401916001016116bf565b50909695505050505050565b60008083601f84011261170f57600080fd5b50813567ffffffffffffffff81111561172757600080fd5b60208301915083602082850101111561173f57600080fd5b9250929050565b60008060006040848603121561175b57600080fd5b6117648461161c565b9250602084013567ffffffffffffffff81111561178057600080fd5b61178c868287016116fd565b9497909650939450505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126117d957600080fd5b813567ffffffffffffffff808211156117f4576117f4611799565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561183a5761183a611799565b8160405283815286602085880101111561185357600080fd5b836020870160208301376000602085830101528094505050505092915050565b60006020828403121561188557600080fd5b813567ffffffffffffffff81111561189c57600080fd5b6105f7848285016117c8565b60005b838110156118c35781810151838201526020016118ab565b50506000910152565b600081518084526118e48160208601602086016118a8565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061069860208301846118cc565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561199c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261198a8583516118cc565b94509285019290850190600101611950565b5092979650505050505050565b600080600080606085870312156119bf57600080fd5b6119c88561161c565b9350602085013567ffffffffffffffff8111156119e457600080fd5b6119f0878288016116fd565b9094509250611a03905060408601611645565b905092959194509250565b600080600060408486031215611a2357600080fd5b833567ffffffffffffffff80821115611a3b57600080fd5b611a47878388016117c8565b94506020860135915080821115611a5d57600080fd5b5061178c868287016116fd565b600181811c90821680611a7e57607f821691505b602082108103611ab7577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b6020815260006105f7602083018486611abd565b600060208284031215611b2c57600080fd5b5051919050565b60008351611b458184602088016118a8565b835190830190611b598183602088016118a8565b01949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602080835260008454611ba581611a6a565b80848701526040600180841660008114611bc65760018114611bfe57611c2c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838a01528284151560051b8a01019550611c2c565b896000528660002060005b85811015611c245781548b8201860152908301908801611c09565b8a0184019650505b509398975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156103dd576103dd611c3a565b601f821115611cc657600081815260208120601f850160051c81016020861015611ca35750805b601f850160051c820191505b81811015611cc257828155600101611caf565b5050505b505050565b818103611cd6575050565b611ce08254611a6a565b67ffffffffffffffff811115611cf857611cf8611799565b611d0c81611d068454611a6a565b84611c7c565b6000601f821160018114611d5e5760008315611d285750848201545b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600385901b1c1916600184901b17845561110a565b6000858152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0841690600086815260209020845b83811015611db65782860154825560019586019590910190602001611d96565b5085831015611df257818501547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611e6257611e62611c3a565b5060010190565b60008251611e7b8184602087016118a8565b9190910192915050565b815167ffffffffffffffff811115611e9f57611e9f611799565b611ead81611d068454611a6a565b602080601f831160018114611f005760008415611eca5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555611cc2565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015611f4d57888601518255948401946001909101908401611f2e565b5085821015611df25793909601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f8600387901b161c19169092555050600190811b01905550565b73ffffffffffffffffffffffffffffffffffffffff84168152604060208201526000611fc7604083018486611abd565b95945050505050565b6000815260008251611fe98160018501602087016118a8565b9190910160010192915050565b7f600b5981380380925939f300000000000000000000000000000000000000000081526000825161202e81600b8501602087016118a8565b91909101600b019291505056fea2646970667358221220829aaace410b5d4c5716d8dcea71dfcc8c5c561c651a6548ba8b8d3990f3589364736f6c6343000815003300000000000000000000000052aa899454998be5b000ad077a46bbe360f4e4970000000000000000000000004f6f977acdd1177dcd81ab83074855ecb9c2d49e

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100df5760003560e01c8063a34b5ee81161008c578063dd179e3b11610066578063dd179e3b146101fb578063e78e049a14610210578063f2fde38b14610223578063f60150791461023657600080fd5b8063a34b5ee8146101b5578063bb4a64d3146101c8578063c949aea6146101db57600080fd5b806350c358a4116100bd57806350c358a41461016d5780636ff97f1d146101805780638da5cb5b1461019557600080fd5b80630b44a218146100e45780632520e7ff146100f95780632861c7d114610121575b600080fd5b6100f76100f2366004611655565b610249565b005b61010c610107366004611688565b61038d565b60405190151581526020015b60405180910390f35b6101487f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49781565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610118565b61010c61017b366004611688565b6103e3565b610188610434565b60405161011891906116a3565b6000546101489073ffffffffffffffffffffffffffffffffffffffff1681565b6100f76101c3366004611655565b6104a3565b6101486101d6366004611746565b6105e2565b6101ee6101e9366004611873565b6105ff565b6040516101189190611916565b61020361069f565b6040516101189190611929565b61014861021e3660046119a9565b610778565b6100f7610231366004611688565b610c29565b6100f7610244366004611a0e565b610d1a565b60005473ffffffffffffffffffffffffffffffffffffffff1633146102cf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064015b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff8116610321576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f260048201526024016102c6565b8161032d576000610330565b60015b73ffffffffffffffffffffffffffffffffffffffff841660008181526001602052604080822060ff949094169093559151841515927f014b54fa6d2080e9aacd1c598c7689a625610d7d684dd41d10407e48aa8b120091a3505050565b6000805473ffffffffffffffffffffffffffffffffffffffff838116911614806103dd575073ffffffffffffffffffffffffffffffffffffffff8216600090815260016020819052604090912054145b92915050565b6000805473ffffffffffffffffffffffffffffffffffffffff838116911614806103dd57505073ffffffffffffffffffffffffffffffffffffffff1660009081526002602052604090205460011490565b6060600380548060200260200160405190810160405280929190818152602001828054801561049957602002820191906000526020600020905b815473ffffffffffffffffffffffffffffffffffffffff16815260019091019060200180831161046e575b5050505050905090565b60005473ffffffffffffffffffffffffffffffffffffffff163314610524576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016102c6565b8173ffffffffffffffffffffffffffffffffffffffff8116610576576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f260048201526024016102c6565b81610582576000610585565b60015b73ffffffffffffffffffffffffffffffffffffffff841660008181526002602052604080822060ff949094169093559151841515927f48cc5b4660fae22eabe5e803ee595e63572773d114bcd54ecc118c1efa8d75af91a3505050565b60006105f76105f2858585611111565b611147565b949350505050565b6060600060056000846040516020016106189190611916565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181528151602092830120835290820192909252016000205473ffffffffffffffffffffffffffffffffffffffff169050801561068857610683816112ad565b610698565b6040805160008152602081019091525b9392505050565b60606004805480602002602001604051908101604052809291908181526020016000905b8282101561076f5783829060005260206000200180546106e290611a6a565b80601f016020809104026020016040519081016040528092919081815260200182805461070e90611a6a565b801561075b5780601f106107305761010080835404028352916020019161075b565b820191906000526020600020905b81548152906001019060200180831161073e57829003601f168201915b5050505050815260200190600101906106c3565b50505050905090565b60008473ffffffffffffffffffffffffffffffffffffffff81166107cc576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f260048201526024016102c6565b6107d5336103e3565b61080f576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f560048201526024016102c6565b6000600560008787604051602001610828929190611b06565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181528151602092830120835290820192909252016000205473ffffffffffffffffffffffffffffffffffffffff169050806108bf576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f160048201526024016102c6565b60006108cc888888611111565b90506108f66108da82611147565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b15610931576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f360048201526024016102c6565b60006109c3600587610981576040805173ffffffffffffffffffffffffffffffffffffffff8d16602080830191909152818301939093528151808203830181526060909101909152805191012090565b6040805173eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee602080830191909152818301939093528151808203830181526060909101909152805191012090565b6040517fb5c736e4000000000000000000000000000000000000000000000000000000008152600481018290529091507f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e49773ffffffffffffffffffffffffffffffffffffffff169063b5c736e490602401602060405180830381865afa158015610a51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a759190611b1a565b600003610ab2576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f460048201526024016102c6565b610b6582610abf856112ad565b6040805173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497811660208301523092820192909252908d166060820152608001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815290829052610b4f9291602001611b33565b60405160208183030381529060405260006112db565b6003805460018101825560008290527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff848116918217909255915460405193985092908c1691907f60c8487fc242a40cc8d2722cf9b3b5a14b316a50bf4ed30c9f0f1b0126728a3690610c15908d908d90611b06565b60405180910390a450505050949350505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610caa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a4544000000000000000000000000000000000000000060448201526064016102c6565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081178255604051909133917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a350565b610d233361038d565b610d5d576040517f694bda1d0000000000000000000000000000000000000000000000000000000081526155f560048201526024016102c6565b600454604051600090610d74908690602001611916565b60405160208183030381529060405280519060200120905083839050600003610f45576005600086604051602001610dac9190611916565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152918152815160209283012083529082019290925201600090812080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690555b82811015610efe578160048281548110610e3457610e34611b62565b90600052602060002001604051602001610e4e9190611b91565b6040516020818303038152906040528051906020012003610eee576004610e76600185611c69565b81548110610e8657610e86611b62565b9060005260206000200160048281548110610ea357610ea3611b62565b906000526020600020019081610eb99190611ccb565b506004805480610ecb57610ecb611e02565b600190038181906000526020600020016000610ee791906115c6565b9055610efe565b610ef781611e31565b9050610e18565b50604051600090610f10908790611e69565b604051908190038120907f93dad940f342b3cd95007806ae0cb0c162dbbfba54d55223bc6d055c62e608e090600090a361110a565b6000610f8685858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152506114ad92505050565b9050806005600088604051602001610f9e9190611916565b60405160208183030381529060405280519060200120815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000805b8481101561106e57836004828154811061102057611020611b62565b9060005260206000200160405160200161103a9190611b91565b604051602081830303815290604052805190602001200361105e576001915061106e565b61106781611e31565b9050611004565b50806110b257600480546001810182556000919091527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b016110b08882611e85565b505b8173ffffffffffffffffffffffffffffffffffffffff16876040516110d79190611e69565b604051908190038120907f93dad940f342b3cd95007806ae0cb0c162dbbfba54d55223bc6d055c62e608e090600090a350505b5050505050565b600083838360405160200161112893929190611f97565b6040516020818303038152906040528051906020012090509392505050565b604080518082018252601081527f67363d3d37363d34f03d5260086018f30000000000000000000000000000000060209182015290517fff00000000000000000000000000000000000000000000000000000000000000918101919091527fffffffffffffffffffffffffffffffffffffffff0000000000000000000000003060601b166021820152603581018290527f21c35dbe1b344a2488cf3321d6ce542f8e9f305544ff09e4993a62319a497c1f60558201526000908190611223906075015b6040516020818303038152906040528051906020012090565b6040517fd69400000000000000000000000000000000000000000000000000000000000060208201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606083901b1660228201527f010000000000000000000000000000000000000000000000000000000000000060368201529091506106989060370161120a565b60606103dd8260016112d68173ffffffffffffffffffffffffffffffffffffffff84163b611c69565b611585565b6000806040518060400160405280601081526020017f67363d3d37363d34f03d5260086018f30000000000000000000000000000000081525090506000858251602084016000f5905073ffffffffffffffffffffffffffffffffffffffff81166113a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f4445504c4f594d454e545f4641494c454400000000000000000000000000000060448201526064016102c6565b6113aa86611147565b925060008173ffffffffffffffffffffffffffffffffffffffff1685876040516113d49190611e69565b60006040518083038185875af1925050503d8060008114611411576040519150601f19603f3d011682016040523d82523d6000602084013e611416565b606091505b5050905080801561143d575073ffffffffffffffffffffffffffffffffffffffff84163b15155b6114a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f494e495449414c495a4154494f4e5f4641494c4544000000000000000000000060448201526064016102c6565b5050509392505050565b600080826040516020016114c19190611fd0565b60405160208183030381529060405290506000816040516020016114e59190611ff6565b60405160208183030381529060405290508051602082016000f0925073ffffffffffffffffffffffffffffffffffffffff831661157e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f4445504c4f594d454e545f4641494c454400000000000000000000000000000060448201526064016102c6565b5050919050565b60408051603f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168101909152818152818360208301863c9392505050565b5080546115d290611a6a565b6000825580601f106115e2575050565b601f0160209004906000526020600020908101906116009190611603565b50565b5b808211156116185760008155600101611604565b5090565b803573ffffffffffffffffffffffffffffffffffffffff8116811461164057600080fd5b919050565b8035801515811461164057600080fd5b6000806040838503121561166857600080fd5b6116718361161c565b915061167f60208401611645565b90509250929050565b60006020828403121561169a57600080fd5b6106988261161c565b6020808252825182820181905260009190848201906040850190845b818110156116f157835173ffffffffffffffffffffffffffffffffffffffff16835292840192918401916001016116bf565b50909695505050505050565b60008083601f84011261170f57600080fd5b50813567ffffffffffffffff81111561172757600080fd5b60208301915083602082850101111561173f57600080fd5b9250929050565b60008060006040848603121561175b57600080fd5b6117648461161c565b9250602084013567ffffffffffffffff81111561178057600080fd5b61178c868287016116fd565b9497909650939450505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f8301126117d957600080fd5b813567ffffffffffffffff808211156117f4576117f4611799565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561183a5761183a611799565b8160405283815286602085880101111561185357600080fd5b836020870160208301376000602085830101528094505050505092915050565b60006020828403121561188557600080fd5b813567ffffffffffffffff81111561189c57600080fd5b6105f7848285016117c8565b60005b838110156118c35781810151838201526020016118ab565b50506000910152565b600081518084526118e48160208601602086016118a8565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60208152600061069860208301846118cc565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b8281101561199c577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc088860301845261198a8583516118cc565b94509285019290850190600101611950565b5092979650505050505050565b600080600080606085870312156119bf57600080fd5b6119c88561161c565b9350602085013567ffffffffffffffff8111156119e457600080fd5b6119f0878288016116fd565b9094509250611a03905060408601611645565b905092959194509250565b600080600060408486031215611a2357600080fd5b833567ffffffffffffffff80821115611a3b57600080fd5b611a47878388016117c8565b94506020860135915080821115611a5d57600080fd5b5061178c868287016116fd565b600181811c90821680611a7e57607f821691505b602082108103611ab7577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b6020815260006105f7602083018486611abd565b600060208284031215611b2c57600080fd5b5051919050565b60008351611b458184602088016118a8565b835190830190611b598183602088016118a8565b01949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602080835260008454611ba581611a6a565b80848701526040600180841660008114611bc65760018114611bfe57611c2c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008516838a01528284151560051b8a01019550611c2c565b896000528660002060005b85811015611c245781548b8201860152908301908801611c09565b8a0184019650505b509398975050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156103dd576103dd611c3a565b601f821115611cc657600081815260208120601f850160051c81016020861015611ca35750805b601f850160051c820191505b81811015611cc257828155600101611caf565b5050505b505050565b818103611cd6575050565b611ce08254611a6a565b67ffffffffffffffff811115611cf857611cf8611799565b611d0c81611d068454611a6a565b84611c7c565b6000601f821160018114611d5e5760008315611d285750848201545b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600385901b1c1916600184901b17845561110a565b6000858152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0841690600086815260209020845b83811015611db65782860154825560019586019590910190602001611d96565b5085831015611df257818501547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203611e6257611e62611c3a565b5060010190565b60008251611e7b8184602087016118a8565b9190910192915050565b815167ffffffffffffffff811115611e9f57611e9f611799565b611ead81611d068454611a6a565b602080601f831160018114611f005760008415611eca5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555611cc2565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b82811015611f4d57888601518255948401946001909101908401611f2e565b5085821015611df25793909601517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f8600387901b161c19169092555050600190811b01905550565b73ffffffffffffffffffffffffffffffffffffffff84168152604060208201526000611fc7604083018486611abd565b95945050505050565b6000815260008251611fe98160018501602087016118a8565b9190910160010192915050565b7f600b5981380380925939f300000000000000000000000000000000000000000081526000825161202e81600b8501602087016118a8565b91909101600b019291505056fea2646970667358221220829aaace410b5d4c5716d8dcea71dfcc8c5c561c651a6548ba8b8d3990f3589364736f6c63430008150033

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

00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e4970000000000000000000000004f6f977acdd1177dcd81ab83074855ecb9c2d49e

-----Decoded View---------------
Arg [0] : liquidity_ (address): 0x52Aa899454998Be5b000Ad077a46Bbe360F4e497
Arg [1] : owner_ (address): 0x4F6F977aCDD1177DCD81aB83074855EcB9C2D49e

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000052aa899454998be5b000ad077a46bbe360f4e497
Arg [1] : 0000000000000000000000004f6f977acdd1177dcd81ab83074855ecb9c2d49e


Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.