ETH Price: $1,971.21 (-2.58%)
 

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

> 10 Token Transfers found.

View more zero value Internal Transactions in Advanced View mode

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

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

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

Contract Name:
PendlePTHelper

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 10000 runs

Other Settings:
shanghai EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {IMarket} from "src/interfaces/IMarket.sol";
import {Sweepable, SafeERC20, IERC20} from "src/util/Sweepable.sol";
import {IPendleHelper} from "src/interfaces/IPendleHelper.sol";
import "bytes-utils/BytesLib.sol";

interface IPendlePT {
    function expiry() external view returns (uint256);
}
/**
 * @title Pendle PT ALE and market helper
 * @notice This contract is a generalized ALE and market helper contract for Pendle PT tokens from and to DOLA.
 * @dev Carefully prepare the router calldata from Pendle API when using it from the ALE:
 * When converting TO collateral, the receiver in Pendle API has to be set to this contract address
 * When converting FROM collateral, the receiver in Pendle API has to be set to the ALE address.
 * The Pendle Router can either SWAP DOLA for PT or MINT PT and YT as well SWAP PT for DOLA or REDEEM PT (using YT before maturity) for DOLA.
 * Do not use this contract for other routes otherwise won't work properly.
 **/

contract PendlePTHelper is Sweepable, IPendleHelper {
    using SafeERC20 for IERC20;
    using BytesLib for bytes;

    error InsufficientDOLA();
    error InsufficientPT();
    error InsufficientYT();
    error MarketNotSet(address market);
    error PendleSwapFailed();
    error NotALE();
    error InvalidRecipient();
    error InvalidSelector();
    error Slice_OutOfBounds();

    struct PT {
        address pt;
        uint96 maturity;
        address yt;
    }

    event MarketSet(
        address indexed market,
        address indexed pt,
        address indexed yt,
        uint256 maturity
    );
    event MarketRemoved(address indexed market);
    event PTOut(
        uint256 dolaIn,
        uint256 ptOut,
        address indexed from,
        address indexed to
    );
    event DolaOut(
        uint256 ptIn,
        uint256 dolaOut,
        address indexed from,
        address indexed to
    );
    event YTOut(uint256 amount, address indexed from, address indexed to);
    event YTIn(uint256 amount, address indexed from);

    IERC20 public immutable DOLA;
    address public immutable router;
    address public immutable ale;

    /// @notice Mapping of market addresses to their associated PT and YT tokens.
    mapping(address => PT) public markets;

    bytes4 public constant SWAP_PT = hex"c81f847a";
    bytes4 public constant MINT_PT = hex"d0f42385";
    bytes4 public constant SWAP_DOLA = hex"594a88cc";
    bytes4 public constant REDEEM_PT = hex"47f1de22";

    /** @dev Constructor
    @param _gov The address of Inverse Finance governance
    @param _guardian The address of the guardian
    @param _pendleRouter The address of the Pendle Router
    **/
    constructor(
        address _gov,
        address _guardian,
        address _dola,
        address _pendleRouter,
        address _ale
    ) Sweepable(_gov, _guardian) {
        DOLA = IERC20(_dola);
        router = _pendleRouter;
        ale = _ale;
    }

    modifier onlyALE() {
        if (msg.sender != ale) revert NotALE();
        _;
    }
    /**
     * @notice Convert DOLA to PT or PT and YT
     * @dev Can only be used by the ALE. Carefully review input data for Pendle API.
     * The receiver in Pendle API has to be set to this contract address.
     * If a MINT is performed, YT will be sent to the user.
     * @param amount The amount of DOLA to be converted.
     * @param data Encoded address of the market, minimum amount of PT to receive (and possibly YT), and Pendle callData.
     * @return collateralAmount The amount of PT (and possibly YT) token received.
     */
    function convertToCollateral(
        address user,
        uint256 amount,
        bytes calldata data
    ) external override onlyALE returns (uint256 collateralAmount) {
        return _convertToCollateral(user, amount, msg.sender, data);
    }

    /**
     * @notice Helper function to convert DOLA to PT or PT and YT, sending PT and YT to msg.sender (probably better using directly the Pendle Router for saving gas)
     * @dev The receiver in Pendle API has to be set to this contract address.
     * @param amount The amount of DOLA to be converted.
     * @param data Encoded address of the market, minimum amount of PT to receive (and possibly YT), and Pendle callData.
     * @return collateralAmount The amount of PT (and possibly YT) token received.
     */
    function convertToCollateral(
        uint256 amount,
        bytes calldata data
    ) external override returns (uint256 collateralAmount) {
        return _convertToCollateral(msg.sender, amount, msg.sender, data);
    }

    /**
     * @notice Convert DOLA to PT or PT and YT and deposit PT amount on behalf of recipient, sending YT to the msg.sender
     * @dev The receiver in Pendle API has to be set to this contract address.
     * @param assets Amount of DOLA to be converted and deposited
     * @param recipient The address on behalf of which the PT tokens are deposited.
     * @param data Encoded address of the market, minimum amount of PT to receive (and possibly YT), and Pendle callData.
     * @return collateralAmount The amount of collateral deposited into the market.
     */
    function convertToCollateralAndDeposit(
        uint256 assets,
        address recipient,
        bytes calldata data
    ) external override returns (uint256) {
        (address market, , ) = abi.decode(data, (address, uint256, bytes));

        // Convert DOLA to PT token
        uint256 amount = _convertToCollateral(
            msg.sender,
            assets,
            address(this),
            data
        );

        // Deposit PT into Market
        IERC20(markets[market].pt).approve(market, amount);
        IMarket(market).deposit(recipient, amount);

        return amount;
    }

    function _convertToCollateral(
        address user,
        uint256 amount,
        address recipient,
        bytes calldata data
    ) internal returns (uint256 collateralAmount) {
        (
            address market,
            uint256 minOut, // Minimum amount of PT to receive (and possibly YT)
            bytes memory callData
        ) = abi.decode(data, (address, uint256, bytes));
        _revertIfMarketNotSet(market);

        bytes4 selector = bytes4(callData.slice(0, 4));
        if (selector != SWAP_PT && selector != MINT_PT)
            revert InvalidSelector();

        DOLA.safeTransferFrom(msg.sender, address(this), amount);
        DOLA.approve(router, amount);

        _callRouter(callData);

        IERC20 pt = IERC20(markets[market].pt);
        uint256 ptBal = pt.balanceOf(address(this));

        if (ptBal < minOut) revert InsufficientPT();
        if (recipient != address(this)) pt.safeTransfer(recipient, ptBal);

        emit PTOut(amount, ptBal, msg.sender, recipient);

        if (selector == MINT_PT) {
            IERC20 yt = IERC20(markets[market].yt);
            // Send YT to user if minted
            uint256 ytBalMinted = yt.balanceOf(address(this));
            if (ytBalMinted < minOut) revert InsufficientYT();
            yt.safeTransfer(user, ytBalMinted);
            emit YTOut(ytBalMinted, msg.sender, user);
        }

        return ptBal;
    }
    /**
     * @notice Swap or Redeem PT token for DOLA (Redemption using YT if before maturity).
     * @dev Can only be used by the ALE. Carefully review input data for Pendle API.
     * The receiver in Pendle API has to be set same as the recipient (ALE)
     * If a REDEEM is performed, ensure the user has enough balance and allowance for YT if before maturity
     * @param amount The amount of PT token to be redeemed (and YT if specified).
     * @param data Encoded address of the market, minimum amount of DOLA to receive and Pendle callData.
     * @return dolaAmount The amount of DOLA redeemed.
     */
    function convertFromCollateral(
        address user,
        uint256 amount,
        bytes calldata data
    ) external override onlyALE returns (uint256 dolaAmount) {
        return _convertFromCollateral(user, amount, msg.sender, data);
    }

    /**
     * @notice Redeem Collateral for DOLA.
     * @dev The receiver in Pendle API has to be set same as the recipient.
     * If a REDEEM is performed, ensure msg.sender has enough balance and allowance for YT if before maturity.
     * @param amount The amount of PT Token to be redeemed (and YT if specified).
     * @param recipient The address to which the underlying token is transferred.
     * @param data Encoded address of the market, minimum amount of DOLA to receive for the recipient and Pendle callData.
     * @return dolaAmount The amount of DOLA redeemed.
     */
    function convertFromCollateral(
        uint256 amount,
        address recipient,
        bytes calldata data
    ) public override returns (uint256 dolaAmount) {
        return _convertFromCollateral(msg.sender, amount, recipient, data);
    }

    function _convertFromCollateral(
        address user,
        uint256 amount,
        address recipient,
        bytes calldata data
    ) internal returns (uint256 dolaAmount) {
        (
            address market,
            uint256 minOut, // Minimum amount of DOLA to receive
            bytes memory callData
        ) = abi.decode(data, (address, uint256, bytes));
        _revertIfMarketNotSet(market);

        bytes4 selector = bytes4(callData.slice(0, 4));
        if (selector != SWAP_DOLA && selector != REDEEM_PT)
            revert InvalidSelector();

        if (selector == REDEEM_PT) _handleYT(market, user, amount);

        IERC20 pt = IERC20(markets[market].pt);
        pt.safeTransferFrom(msg.sender, address(this), amount);
        pt.approve(router, amount);

        uint256 dolaBal = DOLA.balanceOf(recipient);

        _callRouter(callData);
        // Ensure recipient received at least minOut DOLA
        dolaAmount = DOLA.balanceOf(recipient) - dolaBal;
        if (dolaAmount < minOut) revert InsufficientDOLA();
        emit DolaOut(amount, dolaAmount, msg.sender, recipient);
    }

    /**
     * @notice Withdraw the collateral from the market then convert to DOLA.
     * @dev The receiver in Pendle API has to be set same as the recipient.
     * If a REDEEM is performed Before maturity, ensure msg.sender has enough balance and allowance for YT
     * @param amount The amount of PT token to be withdrawn from the market.
     * @param recipient The address to which DOLA is transferred.
     * @param permit The permit data for the Market.
     * @param data Encoded address of the market, minimum amount of DOLA to receive for the recipient and Pendle callData.
     * @return dolaAmount The amount of DOLA redeemed.
     */
    function withdrawAndConvertFromCollateral(
        uint256 amount,
        address recipient,
        Permit calldata permit,
        bytes calldata data
    ) external override returns (uint256 dolaAmount) {
        (address market, uint256 minOut, bytes memory callData) = abi.decode(
            data,
            (address, uint256, bytes)
        );
        _revertIfMarketNotSet(market);

        bytes4 selector = bytes4(callData.slice(0, 4));
        if (selector != SWAP_DOLA && selector != REDEEM_PT)
            revert InvalidSelector();

        if (selector == REDEEM_PT) _handleYT(market, msg.sender, amount);

        IMarket(market).withdrawOnBehalf(
            msg.sender,
            amount,
            permit.deadline,
            permit.v,
            permit.r,
            permit.s
        );

        IERC20 pt = IERC20(markets[market].pt);
        pt.approve(router, amount);

        uint256 dolaBal = DOLA.balanceOf(recipient);
        _callRouter(callData);

        dolaAmount = DOLA.balanceOf(recipient) - dolaBal;
        if (dolaAmount < minOut) revert InsufficientDOLA();
        emit DolaOut(amount, dolaAmount, msg.sender, recipient);
    }

    /**
     * @notice Call router.
     * @param pendleData to be called on the router.
     */
    function _callRouter(bytes memory pendleData) internal {
        (bool success, ) = router.call(pendleData);
        if (!success) revert PendleSwapFailed();
    }

    /**
     * @notice Handle YT, pulling it from the user and approving the router if before maturity
     * @param market The market address.
     * @param user The user address.
     * @param amount The amount of YT to handle.
     */
    function _handleYT(address market, address user, uint256 amount) internal {
        // Check if PT is not expired, in which case YT is not needed
        if (block.timestamp < markets[market].maturity) {
            IERC20 yt = IERC20(markets[market].yt);
            if (yt.balanceOf(user) < amount) revert InsufficientYT();
            yt.safeTransferFrom(user, address(this), amount);
            yt.approve(router, amount);
            emit YTIn(amount, user);
        }
    }

    function _revertIfMarketNotSet(address market) internal view {
        if (address(markets[market].pt) == address(0))
            revert MarketNotSet(market);
    }

    /// ADMIN FUNCTIONS

    /**
     * @notice Set the market address and its associated Pendle PT and YT addresses.
     * @dev Only callable by the governance.
     * @param marketAddress The address of the market.
     * @param ptAddress Pendle PT address
     * @param ytAddress Pendle YT address
     */
    function setMarket(
        address marketAddress,
        address ptAddress,
        address ytAddress
    ) external onlyGov {
        uint96 maturity = uint96(IPendlePT(ptAddress).expiry());
        markets[marketAddress] = PT({
            pt: ptAddress,
            maturity: maturity,
            yt: ytAddress
        });
        emit MarketSet(marketAddress, ptAddress, ytAddress, maturity);
    }

    /**
     * @notice Remove the market.
     * @dev Only callable by the governance or the guardian.
     * @param market The address of the market to be removed.
     */
    function removeMarket(address market) external onlyGuardianOrGov {
        delete markets[market];
        emit MarketRemoved(market);
    }
}

File 2 of 16 : IMarket.sol
pragma solidity ^0.8.13;

import {IBorrowController, IEscrow, IOracle} from "src/Market.sol";

interface IMarket {

    function borrow(uint borrowAmount) external;

    function borrowOnBehalf(
        address msgSender,
        uint dolaAmount,
        uint deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    function withdraw(uint amount) external;

    function withdrawMax() external;

    function withdrawOnBehalf(
        address msgSender,
        uint amount,
        uint deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    function deposit(uint amount) external;

    function deposit(address msgSender, uint collateralAmount) external;

    function depositAndBorrow(
        uint collateralAmount,
        uint borrowAmount
    ) external;

    function repay(address msgSender, uint amount) external;

    function liquidate(address borrower, uint liquidationAmount) external;

    function forceReplenish(address borrower, uint deficitBefore) external;

    function collateral() external returns (address);

    function debts(address user) external returns (uint);

    function recall(uint amount) external;

    function invalidateNonce() external;

    function pauseBorrows(bool paused) external;

    function setBorrowController(IBorrowController borrowController) external;

    function escrows(address user) external view returns (IEscrow);

    function predictEscrow(address user) external view returns (IEscrow);

    function getCollateralValue(address user) external view returns (uint);

    function getWithdrawalLimit(address user) external view returns (uint);

    function getCreditLimit(address user) external view returns (uint);

    function lender() external view returns (address);

    function borrowController() external view returns (address);

    function escrowImplementation() external view returns (address);

    function totalDebt() external view returns (uint);

    function borrowPaused() external view returns (bool);

    function replenishmentIncentiveBps() external view returns (uint);

    function liquidationIncentiveBps() external view returns (uint);

    function collateralFactorBps() external view returns (uint);

    function setCollateralFactorBps(uint cfBps) external;

    function setOracle(IOracle oracle) external;

    function setGov(address newGov) external;

    function setLender(address newLender) external;

    function setPauseGuardian(address newPauseGuardian) external;

    function setReplenismentIncentiveBps(uint riBps) external;

    function setLiquidationIncentiveBps(uint liBps) external;

    function setLiquidationFactorBps(uint lfBps) external;

    function setLiquidationFeeBps(uint lfeeBps) external;

    function liquidationFeeBps() external view returns (uint);

    function DOMAIN_SEPARATOR() external view returns (uint);

    function oracle() external view returns (address);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {IERC20} from "lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol";
import {SafeERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {Governable} from "src/util/Governable.sol";

/**
 * @title Sweepable and Governable contracts
 * @notice This contract can be used to sweep tokens to the gov address and add governalble functionality
 */
contract Sweepable is Governable {
    using SafeERC20 for IERC20;

    constructor(address _gov, address _guardian) Governable(_gov, _guardian) {}

    /**
     * @notice Sweeps the specified token to the gov address
     * @dev Only callable by gov
     * @param token The address of the token to be swept
     */
    function sweep(address token) external onlyGov {
        IERC20(token).safeTransfer(gov, IERC20(token).balanceOf(address(this)));
    }
}

//SPDX-License-Identifier: None
pragma solidity ^0.8.0;

interface IPendleHelper {
    struct Permit {
        uint256 deadline;
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

    function convertToCollateral(
        address user,
        uint256 amount,
        bytes calldata data
    ) external returns (uint256 collateralAmount);

    function convertToCollateral(
        uint256 amount,
        bytes calldata data
    ) external returns (uint256 collateralAmount);

    function convertToCollateralAndDeposit(
        uint256 amount,
        address recipient,
        bytes calldata data
    ) external returns (uint256 collateralAmount);

    function convertFromCollateral(
        address user,
        uint256 amount,
        bytes calldata data
    ) external returns (uint256);

    function convertFromCollateral(
        uint256 amount,
        address recipient,
        bytes calldata data
    ) external returns (uint256);

    function withdrawAndConvertFromCollateral(
        uint256 amount,
        address recipient,
        Permit calldata permit,
        bytes calldata data
    ) external returns (uint256 underlyingAmount);
}

// SPDX-License-Identifier: Unlicense
/*
 * @title Solidity Bytes Arrays Utils
 * @author Gonçalo Sá <[email protected]>
 *
 * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity.
 *      The library lets you concatenate, slice and type cast bytes arrays both in memory and storage.
 */
pragma solidity >=0.8.0 <0.9.0;


library BytesLib {
    function concat(
        bytes memory _preBytes,
        bytes memory _postBytes
    )
        internal
        pure
        returns (bytes memory)
    {
        bytes memory tempBytes;

        assembly {
            // Get a location of some free memory and store it in tempBytes as
            // Solidity does for memory variables.
            tempBytes := mload(0x40)

            // Store the length of the first bytes array at the beginning of
            // the memory for tempBytes.
            let length := mload(_preBytes)
            mstore(tempBytes, length)

            // Maintain a memory counter for the current write location in the
            // temp bytes array by adding the 32 bytes for the array length to
            // the starting location.
            let mc := add(tempBytes, 0x20)
            // Stop copying when the memory counter reaches the length of the
            // first bytes array.
            let end := add(mc, length)

            for {
                // Initialize a copy counter to the start of the _preBytes data,
                // 32 bytes into its memory.
                let cc := add(_preBytes, 0x20)
            } lt(mc, end) {
                // Increase both counters by 32 bytes each iteration.
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                // Write the _preBytes data into the tempBytes memory 32 bytes
                // at a time.
                mstore(mc, mload(cc))
            }

            // Add the length of _postBytes to the current length of tempBytes
            // and store it as the new length in the first 32 bytes of the
            // tempBytes memory.
            length := mload(_postBytes)
            mstore(tempBytes, add(length, mload(tempBytes)))

            // Move the memory counter back from a multiple of 0x20 to the
            // actual end of the _preBytes data.
            mc := end
            // Stop copying when the memory counter reaches the new combined
            // length of the arrays.
            end := add(mc, length)

            for {
                let cc := add(_postBytes, 0x20)
            } lt(mc, end) {
                mc := add(mc, 0x20)
                cc := add(cc, 0x20)
            } {
                mstore(mc, mload(cc))
            }

            // Update the free-memory pointer by padding our last write location
            // to 32 bytes: add 31 bytes to the end of tempBytes to move to the
            // next 32 byte block, then round down to the nearest multiple of
            // 32. If the sum of the length of the two arrays is zero then add
            // one before rounding down to leave a blank 32 bytes (the length block with 0).
            mstore(0x40, and(
              add(add(end, iszero(add(length, mload(_preBytes)))), 31),
              not(31) // Round down to the nearest 32 bytes.
            ))
        }

        return tempBytes;
    }

    function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal {
        assembly {
            // Read the first 32 bytes of _preBytes storage, which is the length
            // of the array. (We don't need to use the offset into the slot
            // because arrays use the entire slot.)
            let fslot := sload(_preBytes.slot)
            // Arrays of 31 bytes or less have an even value in their slot,
            // while longer arrays have an odd value. The actual length is
            // the slot divided by two for odd values, and the lowest order
            // byte divided by two for even values.
            // If the slot is even, bitwise and the slot with 255 and divide by
            // two to get the length. If the slot is odd, bitwise and the slot
            // with -1 and divide by two.
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)
            let newlength := add(slength, mlength)
            // slength can contain both the length and contents of the array
            // if length < 32 bytes so let's prepare for that
            // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
            switch add(lt(slength, 32), lt(newlength, 32))
            case 2 {
                // Since the new array still fits in the slot, we just need to
                // update the contents of the slot.
                // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length
                sstore(
                    _preBytes.slot,
                    // all the modifications to the slot are inside this
                    // next block
                    add(
                        // we can just add to the slot contents because the
                        // bytes we want to change are the LSBs
                        fslot,
                        add(
                            mul(
                                div(
                                    // load the bytes from memory
                                    mload(add(_postBytes, 0x20)),
                                    // zero all bytes to the right
                                    exp(0x100, sub(32, mlength))
                                ),
                                // and now shift left the number of bytes to
                                // leave space for the length in the slot
                                exp(0x100, sub(32, newlength))
                            ),
                            // increase length by the double of the memory
                            // bytes length
                            mul(mlength, 2)
                        )
                    )
                )
            }
            case 1 {
                // The stored value fits in the slot, but the combined value
                // will exceed it.
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // The contents of the _postBytes array start 32 bytes into
                // the structure. Our first read should obtain the `submod`
                // bytes that can fit into the unused space in the last word
                // of the stored array. To get this, we read 32 bytes starting
                // from `submod`, so the data we read overlaps with the array
                // contents by `submod` bytes. Masking the lowest-order
                // `submod` bytes allows us to add that value directly to the
                // stored value.

                let submod := sub(32, slength)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(
                    sc,
                    add(
                        and(
                            fslot,
                            0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
                        ),
                        and(mload(mc), mask)
                    )
                )

                for {
                    mc := add(mc, 0x20)
                    sc := add(sc, 1)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
            default {
                // get the keccak hash to get the contents of the array
                mstore(0x0, _preBytes.slot)
                // Start copying to the last used word of the stored array.
                let sc := add(keccak256(0x0, 0x20), div(slength, 32))

                // save new length
                sstore(_preBytes.slot, add(mul(newlength, 2), 1))

                // Copy over the first `submod` bytes of the new data as in
                // case 1 above.
                let slengthmod := mod(slength, 32)
                let mlengthmod := mod(mlength, 32)
                let submod := sub(32, slengthmod)
                let mc := add(_postBytes, submod)
                let end := add(_postBytes, mlength)
                let mask := sub(exp(0x100, submod), 1)

                sstore(sc, add(sload(sc), and(mload(mc), mask)))

                for {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } lt(mc, end) {
                    sc := add(sc, 1)
                    mc := add(mc, 0x20)
                } {
                    sstore(sc, mload(mc))
                }

                mask := exp(0x100, sub(mc, end))

                sstore(sc, mul(div(mload(mc), mask), mask))
            }
        }
    }

    function slice(
        bytes memory _bytes,
        uint256 _start,
        uint256 _length
    )
        internal
        pure
        returns (bytes memory)
    {
        require(_length + 31 >= _length, "slice_overflow");
        require(_bytes.length >= _start + _length, "slice_outOfBounds");

        bytes memory tempBytes;

        assembly {
            switch iszero(_length)
            case 0 {
                // Get a location of some free memory and store it in tempBytes as
                // Solidity does for memory variables.
                tempBytes := mload(0x40)

                // The first word of the slice result is potentially a partial
                // word read from the original array. To read it, we calculate
                // the length of that partial word and start copying that many
                // bytes into the array. The first word we copy will start with
                // data we don't care about, but the last `lengthmod` bytes will
                // land at the beginning of the contents of the new array. When
                // we're done copying, we overwrite the full first word with
                // the actual length of the slice.
                let lengthmod := and(_length, 31)

                // The multiplication in the next line is necessary
                // because when slicing multiples of 32 bytes (lengthmod == 0)
                // the following copy loop was copying the origin's length
                // and then ending prematurely not copying everything it should.
                let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
                let end := add(mc, _length)

                for {
                    // The multiplication in the next line has the same exact purpose
                    // as the one above.
                    let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
                } lt(mc, end) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    mstore(mc, mload(cc))
                }

                mstore(tempBytes, _length)

                //update free-memory pointer
                //allocating the array padded to 32 bytes like the compiler does now
                mstore(0x40, and(add(mc, 31), not(31)))
            }
            //if we want a zero-length slice let's just return a zero-length array
            default {
                tempBytes := mload(0x40)
                //zero out the 32 bytes slice we are about to return
                //we need to do it because Solidity does not garbage collect
                mstore(tempBytes, 0)

                mstore(0x40, add(tempBytes, 0x20))
            }
        }

        return tempBytes;
    }

    function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) {
        require(_bytes.length >= _start + 20, "toAddress_outOfBounds");
        address tempAddress;

        assembly {
            tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
        }

        return tempAddress;
    }

    function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) {
        require(_bytes.length >= _start + 1 , "toUint8_outOfBounds");
        uint8 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x1), _start))
        }

        return tempUint;
    }

    function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) {
        require(_bytes.length >= _start + 2, "toUint16_outOfBounds");
        uint16 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x2), _start))
        }

        return tempUint;
    }

    function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) {
        require(_bytes.length >= _start + 4, "toUint32_outOfBounds");
        uint32 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x4), _start))
        }

        return tempUint;
    }

    function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) {
        require(_bytes.length >= _start + 8, "toUint64_outOfBounds");
        uint64 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x8), _start))
        }

        return tempUint;
    }

    function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) {
        require(_bytes.length >= _start + 12, "toUint96_outOfBounds");
        uint96 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0xc), _start))
        }

        return tempUint;
    }

    function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) {
        require(_bytes.length >= _start + 16, "toUint128_outOfBounds");
        uint128 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x10), _start))
        }

        return tempUint;
    }

    function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) {
        require(_bytes.length >= _start + 32, "toUint256_outOfBounds");
        uint256 tempUint;

        assembly {
            tempUint := mload(add(add(_bytes, 0x20), _start))
        }

        return tempUint;
    }

    function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) {
        require(_bytes.length >= _start + 32, "toBytes32_outOfBounds");
        bytes32 tempBytes32;

        assembly {
            tempBytes32 := mload(add(add(_bytes, 0x20), _start))
        }

        return tempBytes32;
    }

    function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
        bool success = true;

        assembly {
            let length := mload(_preBytes)

            // if lengths don't match the arrays are not equal
            switch eq(length, mload(_postBytes))
            case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                let cb := 1

                let mc := add(_preBytes, 0x20)
                let end := add(mc, length)

                for {
                    let cc := add(_postBytes, 0x20)
                // the next line is the loop condition:
                // while(uint256(mc < end) + cb == 2)
                } eq(add(lt(mc, end), cb), 2) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    // if any of these checks fails then arrays are not equal
                    if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                        success := 0
                        cb := 0
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }

    function equal_nonAligned(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) {
        bool success = true;

        assembly {
            let length := mload(_preBytes)

            // if lengths don't match the arrays are not equal
            switch eq(length, mload(_postBytes))
            case 1 {
                // cb is a circuit breaker in the for loop since there's
                //  no said feature for inline assembly loops
                // cb = 1 - don't breaker
                // cb = 0 - break
                let cb := 1

                let endMinusWord := add(_preBytes, length)
                let mc := add(_preBytes, 0x20)
                let cc := add(_postBytes, 0x20)

                for {
                // the next line is the loop condition:
                // while(uint256(mc < endWord) + cb == 2)
                } eq(add(lt(mc, endMinusWord), cb), 2) {
                    mc := add(mc, 0x20)
                    cc := add(cc, 0x20)
                } {
                    // if any of these checks fails then arrays are not equal
                    if iszero(eq(mload(mc), mload(cc))) {
                        // unsuccess:
                        success := 0
                        cb := 0
                    }
                }

                // Only if still successful
                // For <1 word tail bytes
                if gt(success, 0) {
                    // Get the remainder of length/32
                    // length % 32 = AND(length, 32 - 1)
                    let numTailBytes := and(length, 0x1f)
                    let mcRem := mload(mc)
                    let ccRem := mload(cc)
                    for {
                        let i := 0
                    // the next line is the loop condition:
                    // while(uint256(i < numTailBytes) + cb == 2)
                    } eq(add(lt(i, numTailBytes), cb), 2) {
                        i := add(i, 1)
                    } {
                        if iszero(eq(byte(i, mcRem), byte(i, ccRem))) {
                            // unsuccess:
                            success := 0
                            cb := 0
                        }
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }

    function equalStorage(
        bytes storage _preBytes,
        bytes memory _postBytes
    )
        internal
        view
        returns (bool)
    {
        bool success = true;

        assembly {
            // we know _preBytes_offset is 0
            let fslot := sload(_preBytes.slot)
            // Decode the length of the stored array like in concatStorage().
            let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
            let mlength := mload(_postBytes)

            // if lengths don't match the arrays are not equal
            switch eq(slength, mlength)
            case 1 {
                // slength can contain both the length and contents of the array
                // if length < 32 bytes so let's prepare for that
                // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
                if iszero(iszero(slength)) {
                    switch lt(slength, 32)
                    case 1 {
                        // blank the last byte which is the length
                        fslot := mul(div(fslot, 0x100), 0x100)

                        if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
                            // unsuccess:
                            success := 0
                        }
                    }
                    default {
                        // cb is a circuit breaker in the for loop since there's
                        //  no said feature for inline assembly loops
                        // cb = 1 - don't breaker
                        // cb = 0 - break
                        let cb := 1

                        // get the keccak hash to get the contents of the array
                        mstore(0x0, _preBytes.slot)
                        let sc := keccak256(0x0, 0x20)

                        let mc := add(_postBytes, 0x20)
                        let end := add(mc, mlength)

                        // the next line is the loop condition:
                        // while(uint256(mc < end) + cb == 2)
                        for {} eq(add(lt(mc, end), cb), 2) {
                            sc := add(sc, 1)
                            mc := add(mc, 0x20)
                        } {
                            if iszero(eq(sload(sc), mload(mc))) {
                                // unsuccess:
                                success := 0
                                cb := 0
                            }
                        }
                    }
                }
            }
            default {
                // unsuccess:
                success := 0
            }
        }

        return success;
    }
}

File 6 of 16 : Market.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "src/interfaces/IERC20.sol";

// Caution. We assume all failed transfers cause reverts and ignore the returned bool.
interface IOracle {
    function getPrice(address,uint) external returns (uint);
    function viewPrice(address,uint) external view returns (uint);
}

interface IEscrow {
    function initialize(IERC20 _token, address beneficiary) external;
    function onDeposit() external;
    function pay(address recipient, uint amount) external;
    function balance() external view returns (uint);
}

interface IDolaBorrowingRights {
    function onBorrow(address user, uint additionalDebt) external;
    function onRepay(address user, uint repaidDebt) external;
    function onForceReplenish(address user, address replenisher, uint amount, uint replenisherReward) external;
    function balanceOf(address user) external view returns (uint);
    function deficitOf(address user) external view returns (uint);
    function replenishmentPriceBps() external view returns (uint);
}

interface IBorrowController {
    function borrowAllowed(address msgSender, address borrower, uint amount) external returns (bool);
    function onRepay(uint amount) external;
}

contract Market {

    address public gov;
    address public lender;
    address public pauseGuardian;
    address public immutable escrowImplementation;
    IDolaBorrowingRights public immutable dbr;
    IBorrowController public borrowController;
    IERC20 public immutable dola = IERC20(0x865377367054516e17014CcdED1e7d814EDC9ce4);
    IERC20 public immutable collateral;
    IOracle public oracle;
    uint public collateralFactorBps;
    uint public replenishmentIncentiveBps;
    uint public liquidationIncentiveBps;
    uint public liquidationFeeBps;
    uint public liquidationFactorBps = 5000; // 50% by default
    bool immutable callOnDepositCallback;
    bool public borrowPaused;
    uint public totalDebt;
    uint256 internal immutable INITIAL_CHAIN_ID;
    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
    mapping (address => IEscrow) public escrows; // user => escrow
    mapping (address => uint) public debts; // user => debt
    mapping(address => uint256) public nonces; // user => nonce

    constructor (
        address _gov,
        address _lender,
        address _pauseGuardian,
        address _escrowImplementation,
        IDolaBorrowingRights _dbr,
        IERC20 _collateral,
        IOracle _oracle,
        uint _collateralFactorBps,
        uint _replenishmentIncentiveBps,
        uint _liquidationIncentiveBps,
        bool _callOnDepositCallback
    ) {
        require(_collateralFactorBps < 10000, "Invalid collateral factor");
        require(_liquidationIncentiveBps > 0 && _liquidationIncentiveBps < 10000, "Invalid liquidation incentive");
        require(_replenishmentIncentiveBps < 10000, "Replenishment incentive must be less than 100%");
        gov = _gov;
        lender = _lender;
        pauseGuardian = _pauseGuardian;
        escrowImplementation = _escrowImplementation;
        dbr = _dbr;
        collateral = _collateral;
        oracle = _oracle;
        collateralFactorBps = _collateralFactorBps;
        replenishmentIncentiveBps = _replenishmentIncentiveBps;
        liquidationIncentiveBps = _liquidationIncentiveBps;
        callOnDepositCallback = _callOnDepositCallback;
        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
        if(collateralFactorBps > 0){
            uint unsafeLiquidationIncentive = (10000 - collateralFactorBps) * (liquidationFeeBps + 10000) / collateralFactorBps;
            require(liquidationIncentiveBps < unsafeLiquidationIncentive,  "Liquidation param allow profitable self liquidation");
        }
    }
    
    modifier onlyGov {
        require(msg.sender == gov, "Only gov can call this function");
        _;
    }

    modifier liquidationParamChecker {
        _;
        if(collateralFactorBps > 0){
            uint unsafeLiquidationIncentive = (10000 - collateralFactorBps) * (liquidationFeeBps + 10000) / collateralFactorBps;
            require(liquidationIncentiveBps < unsafeLiquidationIncentive,  "New liquidation param allow profitable self liquidation");
        }
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes("DBR MARKET")),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /**
    @notice sets the oracle to a new oracle. Only callable by governance.
    @param _oracle The new oracle conforming to the IOracle interface.
    */
    function setOracle(IOracle _oracle) public onlyGov { oracle = _oracle; }

    /**
    @notice sets the borrow controller to a new borrow controller. Only callable by governance.
    @param _borrowController The new borrow controller conforming to the IBorrowController interface.
    */
    function setBorrowController(IBorrowController _borrowController) public onlyGov { borrowController = _borrowController; }

    /**
    @notice sets the address of governance. Only callable by governance.
    @param _gov Address of the new governance.
    */
    function setGov(address _gov) public onlyGov { gov = _gov; }

    /**
    @notice sets the lender to a new lender. The lender is allowed to recall dola from the contract. Only callable by governance.
    @param _lender Address of the new lender.
    */
    function setLender(address _lender) public onlyGov { lender = _lender; }

    /**
    @notice sets the pause guardian. The pause guardian can pause borrowing. Only callable by governance.
    @param _pauseGuardian Address of the new pauseGuardian.
    */
    function setPauseGuardian(address _pauseGuardian) public onlyGov { pauseGuardian = _pauseGuardian; }
    
    /**
    @notice sets the Collateral Factor requirement of the market as measured in basis points. 1 = 0.01%. Only callable by governance.
    @dev Collateral factor mus be set below 100%
    @param _collateralFactorBps The new collateral factor as measured in basis points. 
    */
    function setCollateralFactorBps(uint _collateralFactorBps) public onlyGov liquidationParamChecker {
        require(_collateralFactorBps < 10000, "Invalid collateral factor");
        collateralFactorBps = _collateralFactorBps;
    }
    
    /**
    @notice sets the Liquidation Factor of the market as denoted in basis points.
     The liquidation Factor denotes the maximum amount of debt that can be liquidated in basis points.
     At 5000, 50% of of a borrower's underwater debt can be liquidated. Only callable by governance.
    @dev Must be set between 1 and 10000.
    @param _liquidationFactorBps The new liquidation factor in basis points. 1 = 0.01%/
    */
    function setLiquidationFactorBps(uint _liquidationFactorBps) public onlyGov {
        require(_liquidationFactorBps > 0 && _liquidationFactorBps <= 10000, "Invalid liquidation factor");
        liquidationFactorBps = _liquidationFactorBps;
    }

    /**
    @notice sets the Replenishment Incentive of the market as denoted in basis points.
     The Replenishment Incentive is the percentage paid out to replenishers on a successful forceReplenish call, denoted in basis points.
    @dev Must be set between 1 and 10000.
    @param _replenishmentIncentiveBps The new replenishment incentive set in basis points. 1 = 0.01%
    */
    function setReplenismentIncentiveBps(uint _replenishmentIncentiveBps) public onlyGov {
        require(_replenishmentIncentiveBps < 10000, "Invalid replenishment incentive");
        replenishmentIncentiveBps = _replenishmentIncentiveBps;
    }

    /**
    @notice sets the Liquidation Incentive of the market as denoted in basis points.
     The Liquidation Incentive is the percentage paid out to liquidators of a borrower's debt when successfully liquidated.
    @dev Must be set between 0 and 10000 - liquidation fee.
    @param _liquidationIncentiveBps The new liqudation incentive set in basis points. 1 = 0.01% 
    */
    function setLiquidationIncentiveBps(uint _liquidationIncentiveBps) public onlyGov liquidationParamChecker {
        require(_liquidationIncentiveBps > 0 && _liquidationIncentiveBps + liquidationFeeBps < 10000, "Invalid liquidation incentive");
        liquidationIncentiveBps = _liquidationIncentiveBps;
    }

    /**
    @notice sets the Liquidation Fee of the market as denoted in basis points.
     The Liquidation Fee is the percentage paid out to governance of a borrower's debt when successfully liquidated.
    @dev Must be set between 0 and 10000 - liquidation factor.
    @param _liquidationFeeBps The new liquidation fee set in basis points. 1 = 0.01%
    */
    function setLiquidationFeeBps(uint _liquidationFeeBps) public onlyGov liquidationParamChecker {
        require(_liquidationFeeBps + liquidationIncentiveBps < 10000, "Invalid liquidation fee");
        liquidationFeeBps = _liquidationFeeBps;
    }

    /**
    @notice Recalls amount of DOLA to the lender.
    @param amount The amount od DOLA to recall to the the lender.
    */
    function recall(uint amount) public {
        require(msg.sender == lender, "Only lender can recall");
        dola.transfer(msg.sender, amount);
    }

    /**
    @notice Pauses or unpauses borrowing for the market. Only gov can unpause a market, while gov and pauseGuardian can pause it.
    @param _value Boolean representing the state pause state of borrows. true = paused, false = unpaused.
    */
    function pauseBorrows(bool _value) public {
        if(_value) {
            require(msg.sender == pauseGuardian || msg.sender == gov, "Only pause guardian or governance can pause");
        } else {
            require(msg.sender == gov, "Only governance can unpause");
        }
        borrowPaused = _value;
    }

    /**
    @notice Internal function for creating an escrow for users to deposit collateral in.
    @dev Uses create2 and minimal proxies to create the escrow at a deterministic address
    @param user The address of the user to create an escrow for.
    */
    function createEscrow(address user) internal returns (IEscrow instance) {
        address implementation = escrowImplementation;
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x37, user)
        }
        require(instance != IEscrow(address(0)), "ERC1167: create2 failed");
        emit CreateEscrow(user, address(instance));
    }

    /**
    @notice Internal function for getting the escrow of a user.
    @dev If the escrow doesn't exist, an escrow contract is deployed.
    @param user The address of the user owning the escrow.
    */
    function getEscrow(address user) internal returns (IEscrow) {
        if(escrows[user] != IEscrow(address(0))) return escrows[user];
        IEscrow escrow = createEscrow(user);
        escrow.initialize(collateral, user);
        escrows[user] = escrow;
        return escrow;
    }

    /**
    @notice Deposit amount of collateral into escrow
    @dev Will deposit the amount into the escrow contract.
    @param amount Amount of collateral token to deposit.
    */
    function deposit(uint amount) public {
        deposit(msg.sender, amount);
    }

    /**
    @notice Deposit and borrow in a single transaction.
    @param amountDeposit Amount of collateral token to deposit into escrow.
    @param amountBorrow Amount of DOLA to borrow.
    */
    function depositAndBorrow(uint amountDeposit, uint amountBorrow) public {
        deposit(amountDeposit);
        borrow(amountBorrow);
    }

    /**
    @notice Deposit amount of collateral into escrow on behalf of msg.sender
    @dev Will deposit the amount into the escrow contract.
    @param user User to deposit on behalf of.
    @param amount Amount of collateral token to deposit.
    */
    function deposit(address user, uint amount) public {
        IEscrow escrow = getEscrow(user);
        collateral.transferFrom(msg.sender, address(escrow), amount);
        if(callOnDepositCallback) {
            escrow.onDeposit();
        }
        emit Deposit(user, amount);
    }

    /**
    @notice View function for predicting the deterministic escrow address of a user.
    @dev Only use deposit() function for deposits and NOT the predicted escrow address unless you know what you're doing
    @param user Address of the user owning the escrow.
    */
    function predictEscrow(address user) public view returns (IEscrow predicted) {
        address implementation = escrowImplementation;
        address deployer = address(this);
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), user)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 0x55)
        }
    }

    /**
    @notice View function for getting the dollar value of the user's collateral in escrow for the market.
    @param user Address of the user.
    */
    function getCollateralValue(address user) public view returns (uint) {
        IEscrow escrow = predictEscrow(user);
        uint collateralBalance = escrow.balance();
        return collateralBalance * oracle.viewPrice(address(collateral), collateralFactorBps) / 1 ether;
    }

    /**
    @notice Internal function for getting the dollar value of the user's collateral in escrow for the market.
    @dev Updates the lowest price comparisons of the pessimistic oracle
    @param user Address of the user.
    */
    function getCollateralValueInternal(address user) internal returns (uint) {
        IEscrow escrow = predictEscrow(user);
        uint collateralBalance = escrow.balance();
        return collateralBalance * oracle.getPrice(address(collateral), collateralFactorBps) / 1 ether;
    }

    /**
    @notice View function for getting the credit limit of a user.
    @dev To calculate the available credit, subtract user debt from credit limit.
    @param user Address of the user.
    */
    function getCreditLimit(address user) public view returns (uint) {
        uint collateralValue = getCollateralValue(user);
        return collateralValue * collateralFactorBps / 10000;
    }

    /**
    @notice Internal function for getting the credit limit of a user.
    @dev To calculate the available credit, subtract user debt from credit limit. Updates the pessimistic oracle.
    @param user Address of the user.
    */
    function getCreditLimitInternal(address user) internal returns (uint) {
        uint collateralValue = getCollateralValueInternal(user);
        return collateralValue * collateralFactorBps / 10000;
    }
    /**
    @notice Internal function for getting the withdrawal limit of a user.
     The withdrawal limit is how much collateral a user can withdraw before their loan would be underwater. Updates the pessimistic oracle.
    @param user Address of the user.
    */
    function getWithdrawalLimitInternal(address user) internal returns (uint) {
        IEscrow escrow = predictEscrow(user);
        uint collateralBalance = escrow.balance();
        if(collateralBalance == 0) return 0;
        uint debt = debts[user];
        if(debt == 0) return collateralBalance;
        if(collateralFactorBps == 0) return 0;
        uint minimumCollateral = debt * 1 ether / oracle.getPrice(address(collateral), collateralFactorBps) * 10000 / collateralFactorBps;
        if(collateralBalance <= minimumCollateral) return 0;
        return collateralBalance - minimumCollateral;
    }

    /**
    @notice View function for getting the withdrawal limit of a user.
     The withdrawal limit is how much collateral a user can withdraw before their loan would be underwater.
    @param user Address of the user.
    */
    function getWithdrawalLimit(address user) public view returns (uint) {
        IEscrow escrow = predictEscrow(user);
        uint collateralBalance = escrow.balance();
        if(collateralBalance == 0) return 0;
        uint debt = debts[user];
        if(debt == 0) return collateralBalance;
        if(collateralFactorBps == 0) return 0;
        uint minimumCollateral = debt * 1 ether / oracle.viewPrice(address(collateral), collateralFactorBps) * 10000 / collateralFactorBps;
        if(collateralBalance <= minimumCollateral) return 0;
        return collateralBalance - minimumCollateral;
    }

    /**
    @notice Internal function for borrowing DOLA against collateral.
    @dev This internal function is shared between the borrow and borrowOnBehalf function
    @param borrower The address of the borrower that debt will be accrued to.
    @param to The address that will receive the borrowed DOLA
    @param amount The amount of DOLA to be borrowed
    */
    function borrowInternal(address borrower, address to, uint amount) internal {
        require(!borrowPaused, "Borrowing is paused");
        if(borrowController != IBorrowController(address(0))) {
            require(borrowController.borrowAllowed(msg.sender, borrower, amount), "Denied by borrow controller");
        }
        uint credit = getCreditLimitInternal(borrower);
        debts[borrower] += amount;
        require(credit >= debts[borrower], "Exceeded credit limit");
        totalDebt += amount;
        dbr.onBorrow(borrower, amount);
        dola.transfer(to, amount);
        emit Borrow(borrower, amount);
    }

    /**
    @notice Function for borrowing DOLA.
    @dev Will borrow to msg.sender
    @param amount The amount of DOLA to be borrowed.
    */
    function borrow(uint amount) public {
        borrowInternal(msg.sender, msg.sender, amount);
    }

    /**
    @notice Function for using a signed message to borrow on behalf of an address owning an escrow with collateral.
    @dev Signed messaged can be invalidated by incrementing the nonce. Will always borrow to the msg.sender.
    @param from The address of the user being borrowed from
    @param amount The amount to be borrowed
    @param deadline Timestamp after which the signed message will be invalid
    @param v The v param of the ECDSA signature
    @param r The r param of the ECDSA signature
    @param s The s param of the ECDSA signature
    */
    function borrowOnBehalf(address from, uint amount, uint deadline, uint8 v, bytes32 r, bytes32 s) public {
        require(deadline >= block.timestamp, "DEADLINE_EXPIRED");
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "BorrowOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)"
                                ),
                                msg.sender,
                                from,
                                amount,
                                nonces[from]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );
            require(recoveredAddress != address(0) && recoveredAddress == from, "INVALID_SIGNER");
            borrowInternal(from, msg.sender, amount);
        }
    }

    /**
    @notice Internal function for withdrawing from the escrow
    @dev The internal function is shared by the withdraw function and withdrawOnBehalf function
    @param from The address owning the escrow to withdraw from.
    @param to The address receiving the tokens
    @param amount The amount being withdrawn.
    */
    function withdrawInternal(address from, address to, uint amount) internal {
        uint limit = getWithdrawalLimitInternal(from);
        require(limit >= amount, "Insufficient withdrawal limit");
        require(dbr.deficitOf(from) == 0, "Can't withdraw with DBR deficit");
        IEscrow escrow = getEscrow(from);
        escrow.pay(to, amount);
        emit Withdraw(from, to, amount);
    }

    /**
    @notice Function for withdrawing to msg.sender.
    @param amount Amount to withdraw.
    */
    function withdraw(uint amount) public {
        withdrawInternal(msg.sender, msg.sender, amount);
    }

    /**
    @notice Function for withdrawing maximum allowed to msg.sender.
    @dev Useful for use with escrows that continously compound tokens, so there won't be dust amounts left
    @dev Dangerous to use when the user has any amount of debt!
    */
    function withdrawMax() public {
        withdrawInternal(msg.sender, msg.sender, getWithdrawalLimitInternal(msg.sender));
    }

    /**
    @notice Function for using a signed message to withdraw on behalf of an address owning an escrow with collateral.
    @dev Signed messaged can be invalidated by incrementing the nonce. Will always withdraw to the msg.sender.
    @param from The address of the user owning the escrow being withdrawn from
    @param amount The amount to be withdrawn
    @param deadline Timestamp after which the signed message will be invalid
    @param v The v param of the ECDSA signature
    @param r The r param of the ECDSA signature
    @param s The s param of the ECDSA signature
    */
    function withdrawOnBehalf(address from, uint amount, uint deadline, uint8 v, bytes32 r, bytes32 s) public {
        require(deadline >= block.timestamp, "DEADLINE_EXPIRED");
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "WithdrawOnBehalf(address caller,address from,uint256 amount,uint256 nonce,uint256 deadline)"
                                ),
                                msg.sender,
                                from,
                                amount,
                                nonces[from]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );
            require(recoveredAddress != address(0) && recoveredAddress == from, "INVALID_SIGNER");
            withdrawInternal(from, msg.sender, amount);
        }
    }

    /**
    @notice Function for using a signed message to withdraw on behalf of an address owning an escrow with collateral.
    @dev Signed messaged can be invalidated by incrementing the nonce. Will always withdraw to the msg.sender.
    @dev Useful for use with escrows that continously compound tokens, so there won't be dust amounts left
    @dev Dangerous to use when the user has any amount of debt!
    @param from The address of the user owning the escrow being withdrawn from
    @param deadline Timestamp after which the signed message will be invalid
    @param v The v param of the ECDSA signature
    @param r The r param of the ECDSA signature
    @param s The s param of the ECDSA signature
    */
    function withdrawMaxOnBehalf(address from, uint deadline, uint8 v, bytes32 r, bytes32 s) public {
        require(deadline >= block.timestamp, "DEADLINE_EXPIRED");
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "WithdrawMaxOnBehalf(address caller,address from,uint256 nonce,uint256 deadline)"
                                ),
                                msg.sender,
                                from,
                                nonces[from]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );
            require(recoveredAddress != address(0) && recoveredAddress == from, "INVALID_SIGNER");
            withdrawInternal(from, msg.sender, getWithdrawalLimitInternal(from));
        }
    }

    /**
    @notice Function for incrementing the nonce of the msg.sender, making their latest signed message unusable.
    */
    function invalidateNonce() public {
        nonces[msg.sender]++;
    }
    
    /**
    @notice Function for repaying debt on behalf of user. Debt must be repaid in DOLA.
    @dev If the user has a DBR deficit, they risk initial debt being accrued by forced replenishments.
    @param user Address of the user whose debt is being repaid
    @param amount DOLA amount to be repaid. If set to max uint debt will be repaid in full.
    */
    function repay(address user, uint amount) public {
        uint debt = debts[user];
        if(amount == type(uint).max){
            amount = debt;
        }
        require(debt >= amount, "Repayment greater than debt");
        debts[user] -= amount;
        totalDebt -= amount;
        dbr.onRepay(user, amount);
        if(address(borrowController) != address(0)){
            borrowController.onRepay(amount);
        }
        dola.transferFrom(msg.sender, address(this), amount);
        emit Repay(user, msg.sender, amount);
    }

    /**
    @notice Bundles repayment and withdrawal into a single function call.
    @param repayAmount Amount of DOLA to be repaid
    @param withdrawAmount Amount of underlying to be withdrawn from the escrow
    */
    function repayAndWithdraw(uint repayAmount, uint withdrawAmount) public {
        repay(msg.sender, repayAmount);
        withdraw(withdrawAmount);
    }

    /**
    @notice Function for forcing a user to replenish their DBR deficit at a pre-determined price.
     The replenishment will accrue additional DOLA debt.
     On a successful call, the caller will be paid a replenishment incentive.
    @dev The function will only top the user back up to 0, meaning that the user will have a DBR deficit again in the next block.
    @param user The address of the user being forced to replenish DBR
    @param amount The amount of DBR the user will be replenished.
    */
    function forceReplenish(address user, uint amount) public {
        uint deficit = dbr.deficitOf(user);
        require(deficit > 0, "No DBR deficit");
        require(deficit >= amount, "Amount > deficit");
        uint replenishmentCost = amount * dbr.replenishmentPriceBps() / 10000;
        uint replenisherReward = replenishmentCost * replenishmentIncentiveBps / 10000;
        debts[user] += replenishmentCost;
        uint collateralValue = getCollateralValueInternal(user) * (10000 - liquidationIncentiveBps - liquidationFeeBps) / 10000;
        require(collateralValue >= debts[user], "Exceeded collateral value");
        totalDebt += replenishmentCost;
        dbr.onForceReplenish(user, msg.sender, amount, replenisherReward);
        dola.transfer(msg.sender, replenisherReward);
    }
    /**
    @notice Function for forcing a user to replenish all of their DBR deficit at a pre-determined price.
     The replenishment will accrue additional DOLA debt.
     On a successful call, the caller will be paid a replenishment incentive.
    @dev The function will only top the user back up to 0, meaning that the user will have a DBR deficit again in the next block.
    @param user The address of the user being forced to replenish DBR
    */
    function forceReplenishAll(address user) public {
        uint deficit = dbr.deficitOf(user);
        forceReplenish(user, deficit);
    }

    /**
    @notice Function for liquidating a user's under water debt. Debt is under water when the value of a user's debt is above their collateral factor.
    @param user The user to be liquidated
    @param repaidDebt Th amount of user user debt to liquidate.
    */
    function liquidate(address user, uint repaidDebt) public {
        require(repaidDebt > 0, "Must repay positive debt");
        uint debt = debts[user];
        require(getCreditLimitInternal(user) < debt, "User debt is healthy");
        require(repaidDebt <= debt * liquidationFactorBps / 10000, "Exceeded liquidation factor");
        uint price = oracle.getPrice(address(collateral), collateralFactorBps);
        uint liquidatorReward = repaidDebt * 1 ether / price;
        liquidatorReward += liquidatorReward * liquidationIncentiveBps / 10000;
        debts[user] -= repaidDebt;
        totalDebt -= repaidDebt;
        dbr.onRepay(user, repaidDebt);
        if(address(borrowController) != address(0)){
            borrowController.onRepay(repaidDebt);
        }
        dola.transferFrom(msg.sender, address(this), repaidDebt);
        IEscrow escrow = predictEscrow(user);
        escrow.pay(msg.sender, liquidatorReward);
        if(liquidationFeeBps > 0) {
            uint liquidationFee = repaidDebt * 1 ether / price * liquidationFeeBps / 10000;
            uint balance = escrow.balance();
            if(balance >= liquidationFee) {
                escrow.pay(gov, liquidationFee);
            } else if(balance > 0) {
                escrow.pay(gov, balance);
            }
        }
        emit Liquidate(user, msg.sender, repaidDebt, liquidatorReward);
    }
    
    event Deposit(address indexed account, uint amount);
    event Borrow(address indexed account, uint amount);
    event Withdraw(address indexed account, address indexed to, uint amount);
    event Repay(address indexed account, address indexed repayer, uint amount);
    event Liquidate(address indexed account, address indexed liquidator, uint repaidDebt, uint liquidatorReward);
    event CreateEscrow(address indexed user, address escrow);
}

File 7 of 16 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";

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

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";

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

    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

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

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

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

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

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

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

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

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

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

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

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

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

/**
 * @title Governable
 * @notice This contract is add Governable functionality to the contract
 */
contract Governable {
    error NotGov();
    error NotPendingGov();
    error NotGuardianOrGov();

    address public gov;
    address public pendingGov;
    address public guardian;

    event NewGov(address gov);
    event NewPendingGov(address pendingGov);
    event NewGuardian(address guardian);

    /** @dev Constructor
    @param _gov The address of Inverse Finance governance
    @param _guardian The address of the guardian
    **/
    constructor(address _gov, address _guardian) {
        gov = _gov;
        guardian = _guardian;
    }

    modifier onlyGov() {
        if (msg.sender != gov) revert NotGov();
        _;
    }

    modifier onlyGuardianOrGov() {
        if (msg.sender != guardian && msg.sender != gov)
            revert NotGuardianOrGov();
        _;
    }

    /**
     * @notice Sets the pendingGov, which can claim gov role.
     * @dev Only callable by gov
     * @param _pendingGov The address of the pendingGov
     */
    function setPendingGov(address _pendingGov) external onlyGov {
        pendingGov = _pendingGov;
        emit NewPendingGov(_pendingGov);
    }

    /**
     * @notice Claims the gov role
     * @dev Only callable by pendingGov
     */
    function claimPendingGov() external {
        if (msg.sender != pendingGov) revert NotPendingGov();
        gov = pendingGov;
        pendingGov = address(0);
        emit NewGov(gov);
    }

    /**
     * @notice Sets the guardian role
     * @dev Only callable by gov
     * @param _guardian The address of the guardian
     */
    function setGuardian(address _guardian) external onlyGov {
        guardian = _guardian;
        emit NewGuardian(_guardian);
    }
}

pragma solidity ^0.8.13;

interface IERC20 {
    function approve(address, uint) external;

    function transfer(address, uint) external returns (bool);

    function transferFrom(address, address, uint) external returns (bool);

    function balanceOf(address) external view returns (uint);

    function allowance(address from, address to) external view returns (uint);

    function symbol() external view returns (string memory);
}

interface IMintable is IERC20 {
    function mint(address, uint) external;

    function burn(uint) external;

    function addMinter(address minter) external;
}

interface IDelegateableERC20 is IERC20 {
    function delegate(address delegatee) external;

    function delegates(
        address delegator
    ) external view returns (address delegatee);
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

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

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

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

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert Errors.FailedCall();
        }
    }

    /**
     * @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 or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {Errors.FailedCall} error.
     *
     * 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.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @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`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert Errors.InsufficientBalance(address(this).balance, value);
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
     * of an unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {Errors.FailedCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
     */
    function _revert(bytes memory returndata) 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 Errors.FailedCall();
        }
    }
}

File 14 of 16 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

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

File 15 of 16 : Errors.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();
}

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

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[ERC].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solmate/=lib/solmate/src/",
    "bytes-utils/=lib/bytes-utils/contracts/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_gov","type":"address"},{"internalType":"address","name":"_guardian","type":"address"},{"internalType":"address","name":"_dola","type":"address"},{"internalType":"address","name":"_pendleRouter","type":"address"},{"internalType":"address","name":"_ale","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientDOLA","type":"error"},{"inputs":[],"name":"InsufficientPT","type":"error"},{"inputs":[],"name":"InsufficientYT","type":"error"},{"inputs":[],"name":"InvalidRecipient","type":"error"},{"inputs":[],"name":"InvalidSelector","type":"error"},{"inputs":[{"internalType":"address","name":"market","type":"address"}],"name":"MarketNotSet","type":"error"},{"inputs":[],"name":"NotALE","type":"error"},{"inputs":[],"name":"NotGov","type":"error"},{"inputs":[],"name":"NotGuardianOrGov","type":"error"},{"inputs":[],"name":"NotPendingGov","type":"error"},{"inputs":[],"name":"PendleSwapFailed","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"Slice_OutOfBounds","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"ptIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dolaOut","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"DolaOut","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"market","type":"address"}],"name":"MarketRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"market","type":"address"},{"indexed":true,"internalType":"address","name":"pt","type":"address"},{"indexed":true,"internalType":"address","name":"yt","type":"address"},{"indexed":false,"internalType":"uint256","name":"maturity","type":"uint256"}],"name":"MarketSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"gov","type":"address"}],"name":"NewGov","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"guardian","type":"address"}],"name":"NewGuardian","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"pendingGov","type":"address"}],"name":"NewPendingGov","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"dolaIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ptOut","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"PTOut","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"}],"name":"YTIn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"YTOut","type":"event"},{"inputs":[],"name":"DOLA","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MINT_PT","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REDEEM_PT","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SWAP_DOLA","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SWAP_PT","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ale","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimPendingGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"convertFromCollateral","outputs":[{"internalType":"uint256","name":"dolaAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"convertFromCollateral","outputs":[{"internalType":"uint256","name":"dolaAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"convertToCollateral","outputs":[{"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"convertToCollateral","outputs":[{"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"convertToCollateralAndDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gov","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"guardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"markets","outputs":[{"internalType":"address","name":"pt","type":"address"},{"internalType":"uint96","name":"maturity","type":"uint96"},{"internalType":"address","name":"yt","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingGov","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"market","type":"address"}],"name":"removeMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_guardian","type":"address"}],"name":"setGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"marketAddress","type":"address"},{"internalType":"address","name":"ptAddress","type":"address"},{"internalType":"address","name":"ytAddress","type":"address"}],"name":"setMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pendingGov","type":"address"}],"name":"setPendingGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"sweep","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"components":[{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct IPendleHelper.Permit","name":"permit","type":"tuple"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"withdrawAndConvertFromCollateral","outputs":[{"internalType":"uint256","name":"dolaAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

0x60e060405234801562000010575f80fd5b506040516200273538038062002735833981016040819052620000339162000093565b5f80546001600160a01b03199081166001600160a01b03978816179091556002805490911694861694909417909355908316608052821660a0521660c052620000ff565b80516001600160a01b03811681146200008e575f80fd5b919050565b5f805f805f60a08688031215620000a8575f80fd5b620000b38662000077565b9450620000c36020870162000077565b9350620000d36040870162000077565b9250620000e36060870162000077565b9150620000f36080870162000077565b90509295509295909350565b60805160a05160c0516125b0620001855f395f81816102e30152818161070c0152610e5a01525f818161047701528181610b0b0152818161130d015281816116cc01528181611d5c0152611e2201525f81816103fc01528181610bc101528181610c71015281816113c1015281816114710152818161167401526116fb01526125b05ff3fe608060405234801561000f575f80fd5b5060043610610184575f3560e01c806367f03c65116100dd578063aa7845cb11610088578063efdf0bb011610063578063efdf0bb014610457578063f0c9e4651461046a578063f887ea4014610472575f80fd5b8063aa7845cb1461041e578063c87ae33414610431578063db91323614610444575f80fd5b80638a0dac4a116100b85780638a0dac4a146103535780638e8f294b1461036657806392c592d0146103f7575f80fd5b806367f03c65146102de5780636de03cca1461030557806375dc769a1461032c575f80fd5b80633f387a921161013d57806351f13c7f1161011857806351f13c7f1461024c578063532286471461025f5780636703ed6b146102b7575f80fd5b80633f387a9214610213578063452a932014610226578063476ff53814610239575f80fd5b8063252408101161016d57806325240810146101cc578063298c07f2146101df57806333525192146101f2575f80fd5b806301681a621461018857806312d43a511461019d575b5f80fd5b61019b610196366004612128565b610499565b005b5f546101af906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6001546101af906001600160a01b031681565b61019b6101ed366004612143565b61057a565b6102056102003660046121d0565b610700565b6040519081526020016101c3565b610205610221366004612228565b610779565b6002546101af906001600160a01b031681565b610205610247366004612268565b6108b9565b61020561025a366004612228565b610d78565b6102867fc81f847a0000000000000000000000000000000000000000000000000000000081565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020016101c3565b6102867f47f1de220000000000000000000000000000000000000000000000000000000081565b6101af7f000000000000000000000000000000000000000000000000000000000000000081565b6102867f594a88cc0000000000000000000000000000000000000000000000000000000081565b6102867fd0f423850000000000000000000000000000000000000000000000000000000081565b61019b610361366004612128565b610d86565b6103c0610374366004612128565b60036020525f9081526040902080546001909101546001600160a01b03808316927401000000000000000000000000000000000000000090046bffffffffffffffffffffffff16911683565b604080516001600160a01b0394851681526bffffffffffffffffffffffff90931660208401529216918101919091526060016101c3565b6101af7f000000000000000000000000000000000000000000000000000000000000000081565b61020561042c3660046122fd565b610e36565b61020561043f3660046121d0565b610e4e565b61019b610452366004612128565b610ebe565b61019b610465366004612128565b610f86565b61019b61102f565b6101af7f000000000000000000000000000000000000000000000000000000000000000081565b5f546001600160a01b031633146104dc576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f546040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152610577916001600160a01b0390811691908416906370a0823190602401602060405180830381865afa158015610542573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105669190612345565b6001600160a01b03841691906110e8565b50565b5f546001600160a01b031633146105bd576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f826001600160a01b031663e184c9be6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105fa573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061061e9190612345565b604080516060810182526001600160a01b038681168083526bffffffffffffffffffffffff85811660208086018281528a86168789018181528e88165f818152600386528b9020995193519389167401000000000000000000000000000000000000000094909716939093029590951788559351600190970180547fffffffffffffffffffffffff00000000000000000000000000000000000000001697909616969096179094559451948552949550927fb2293e99ade43aa212a7a8677f285eb86d6fc5ca9bf2b4b3c329320681b93ba6910160405180910390a450505050565b5f336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610763576040517fef11a41000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107708585338686611161565b95945050505050565b5f8061078783850185612389565b505090505f6107993388308888611565565b6001600160a01b038381165f81815260036020526040908190205490517f095ea7b300000000000000000000000000000000000000000000000000000000815260048101929092526024820184905292935091169063095ea7b3906044016020604051808303815f875af1158015610813573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610837919061246d565b506040517f47e7ef240000000000000000000000000000000000000000000000000000000081526001600160a01b038781166004830152602482018390528316906347e7ef24906044015f604051808303815f87803b158015610898575f80fd5b505af11580156108aa573d5f803e3d5ffd5b50929998505050505050505050565b5f8080806108c985870187612389565b9250925092506108d883611a27565b5f6108e582826004611a87565b6108ee9061248c565b90507fffffffff0000000000000000000000000000000000000000000000000000000081167f594a88cc000000000000000000000000000000000000000000000000000000001480159061098457507fffffffff0000000000000000000000000000000000000000000000000000000081167f47f1de220000000000000000000000000000000000000000000000000000000014155b156109bb576040517f7352d91c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fb80e21de000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821601610a0f57610a0f84338c611c00565b6001600160a01b038416633525f591338c8b35610a3260408e0160208f016124db565b604080517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b1681526001600160a01b0390951660048601526024850193909352604484019190915260ff1660648301528b0135608482015260608b013560a482015260c4015f604051808303815f87803b158015610ab3575f80fd5b505af1158015610ac5573d5f803e3d5ffd5b505050506001600160a01b038481165f90815260036020526040908190205490517f095ea7b30000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000083166004820152602481018d9052911690819063095ea7b3906044016020604051808303815f875af1158015610b62573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b86919061246d565b506040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b038b811660048301525f917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa158015610c08573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c2c9190612345565b9050610c3784611e1f565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b038c8116600483015282917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa158015610cb8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cdc9190612345565b610ce69190612528565b965084871015610d22576040517f44c6883000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518d8152602081018990526001600160a01b038d169133917ffc6fc897151cbcfef0cc39115ec79125e48fc6620bbace392ec138d93ecda44491015b60405180910390a350505050505095945050505050565b5f6107703386868686611161565b5f546001600160a01b03163314610dc9576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fb6182387b7ea948602a7e04e662a27ce251dc3dd014eacaed10dce36b41bf1a5906020015b60405180910390a150565b5f610e443385338686611565565b90505b9392505050565b5f336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610eb1576040517fef11a41000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107708585338686611565565b6002546001600160a01b03163314801590610ee357505f546001600160a01b03163314155b15610f1a576040517fe6000c8500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381165f8181526003602052604080822082815560010180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055517f59d7b1e52008dc342c9421dadfc773114b914a65682a4e4b53cf60a970df0d779190a250565b5f546001600160a01b03163314610fc9576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527ff74ae56780e3765c0c0897ef57fb50a10a237584f419631812daf040913e1c9f90602001610e2b565b6001546001600160a01b03163314611073576040517f7c04d72b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180545f80546001600160a01b0383167fffffffffffffffffffffffff000000000000000000000000000000000000000091821681179092559091169091556040519081527f639717155292ce2c3e699929a8b65d14a637640f75ab5b6d165a4e735d82a4559060200160405180910390a1565b6040516001600160a01b0383811660248301526044820183905261115c91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050611ed5565b505050565b5f80808061117185870187612389565b92509250925061118083611a27565b5f61118d82826004611a87565b6111969061248c565b90507fffffffff0000000000000000000000000000000000000000000000000000000081167f594a88cc000000000000000000000000000000000000000000000000000000001480159061122c57507fffffffff0000000000000000000000000000000000000000000000000000000081167f47f1de220000000000000000000000000000000000000000000000000000000014155b15611263576040517f7352d91c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fb80e21de000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008216016112b7576112b7848b8b611c00565b6001600160a01b038085165f90815260036020526040902054166112dd8133308d611f4f565b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018c905282169063095ea7b3906044016020604051808303815f875af1158015611362573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611386919061246d565b506040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b038a811660048301525f917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa158015611408573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061142c9190612345565b905061143784611e1f565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b038b8116600483015282917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa1580156114b8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114dc9190612345565b6114e69190612528565b965084871015611522576040517f44c6883000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518c8152602081018990526001600160a01b038c169133917ffc6fc897151cbcfef0cc39115ec79125e48fc6620bbace392ec138d93ecda4449101610d61565b5f80808061157585870187612389565b92509250925061158483611a27565b5f61159182826004611a87565b61159a9061248c565b90507fffffffff0000000000000000000000000000000000000000000000000000000081167fc81f847a000000000000000000000000000000000000000000000000000000001480159061163057507fffffffff0000000000000000000000000000000000000000000000000000000081167fd0f423850000000000000000000000000000000000000000000000000000000014155b15611667576040517f7352d91c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61169c6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633308c611f4f565b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152602482018b90527f0000000000000000000000000000000000000000000000000000000000000000169063095ea7b3906044016020604051808303815f875af1158015611741573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611765919061246d565b5061176f82611e1f565b6001600160a01b038481165f908152600360205260408082205490517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015292169182906370a0823190602401602060405180830381865afa1580156117dd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118019190612345565b90508481101561183d576040517f46711f9e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038a163014611861576118616001600160a01b0383168b836110e8565b604080518c8152602081018390526001600160a01b038c169133917f2e35da04afaf475d9a94f2fa2f9dbcaef35db18ef1c2e023b6fb95b6bcdc413d910160405180910390a37f2f0bdc7b000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000841601611a18576001600160a01b038681165f908152600360205260408082206001015490517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015292169182906370a0823190602401602060405180830381865afa158015611961573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119859190612345565b9050868110156119c1576040517fbe93493900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6119d56001600160a01b0383168f836110e8565b6040518181526001600160a01b038f169033907fd43e426bfd1c116e433e4748fb5d367f98a8fb4fb75298174670deb9330ffb3b9060200160405180910390a350505b9b9a5050505050505050505050565b6001600160a01b038181165f9081526003602052604090205416610577576040517f31589d090000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024015b60405180910390fd5b606081611a9581601f61253b565b1015611afd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401611a7e565b611b07828461253b565b84511015611b71576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401611a7e565b606082158015611b8f5760405191505f825260208201604052611bf7565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015611bc8578051835260209283019201611bb0565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b6001600160a01b0383165f908152600360205260409020547401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1642101561115c576001600160a01b038381165f90815260036020526040908190206001015490517f70a082310000000000000000000000000000000000000000000000000000000081528483166004820152911690829082906370a0823190602401602060405180830381865afa158015611cbb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cdf9190612345565b1015611d17576040517fbe93493900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d2c6001600160a01b038216843085611f4f565b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301526024820184905282169063095ea7b3906044016020604051808303815f875af1158015611db1573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dd5919061246d565b50826001600160a01b03167fb2156de976d9a5a3d29c4d8e10ebdd04d021086d629dfe04c1641050414b8db383604051611e1191815260200190565b60405180910390a250505050565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031682604051611e58919061254e565b5f604051808303815f865af19150503d805f8114611e91576040519150601f19603f3d011682016040523d82523d5f602084013e611e96565b606091505b5050905080611ed1576040517f0fe7fe8500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b5f611ee96001600160a01b03841683611f8e565b905080515f14158015611f0d575080806020019051810190611f0b919061246d565b155b1561115c576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401611a7e565b6040516001600160a01b038481166024830152838116604483015260648201839052611f889186918216906323b872dd90608401611115565b50505050565b6060611f9b83835f611fa4565b90505b92915050565b606081471015611fe9576040517fcf47918100000000000000000000000000000000000000000000000000000000815247600482015260248101839052604401611a7e565b5f80856001600160a01b03168486604051612004919061254e565b5f6040518083038185875af1925050503d805f811461203e576040519150601f19603f3d011682016040523d82523d5f602084013e612043565b606091505b509150915061205386838361205d565b9695505050505050565b6060826120725761206d826120d2565b610e47565b815115801561208957506001600160a01b0384163b155b156120cb576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401611a7e565b5080610e47565b8051156120e25780518082602001fd5b6040517fd6bda27500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381168114610577575f80fd5b5f60208284031215612138575f80fd5b8135610e4781612114565b5f805f60608486031215612155575f80fd5b833561216081612114565b9250602084013561217081612114565b9150604084013561218081612114565b809150509250925092565b5f8083601f84011261219b575f80fd5b50813567ffffffffffffffff8111156121b2575f80fd5b6020830191508360208285010111156121c9575f80fd5b9250929050565b5f805f80606085870312156121e3575f80fd5b84356121ee81612114565b935060208501359250604085013567ffffffffffffffff811115612210575f80fd5b61221c8782880161218b565b95989497509550505050565b5f805f806060858703121561223b575f80fd5b84359350602085013561224d81612114565b9250604085013567ffffffffffffffff811115612210575f80fd5b5f805f805f85870360e081121561227d575f80fd5b86359550602087013561228f81612114565b945060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0820112156122c0575f80fd5b5060408601925060c086013567ffffffffffffffff8111156122e0575f80fd5b6122ec8882890161218b565b969995985093965092949392505050565b5f805f6040848603121561230f575f80fd5b83359250602084013567ffffffffffffffff81111561232c575f80fd5b6123388682870161218b565b9497909650939450505050565b5f60208284031215612355575f80fd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f805f6060848603121561239b575f80fd5b83356123a681612114565b925060208401359150604084013567ffffffffffffffff808211156123c9575f80fd5b818601915086601f8301126123dc575f80fd5b8135818111156123ee576123ee61235c565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156124345761243461235c565b8160405282815289602084870101111561244c575f80fd5b826020860160208301375f6020848301015280955050505050509250925092565b5f6020828403121561247d575f80fd5b81518015158114610e47575f80fd5b5f815160208301517fffffffff00000000000000000000000000000000000000000000000000000000808216935060048310156124d35780818460040360031b1b83161693505b505050919050565b5f602082840312156124eb575f80fd5b813560ff81168114610e47575f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115611f9e57611f9e6124fb565b80820180821115611f9e57611f9e6124fb565b5f82515f5b8181101561256d5760208186018101518583015201612553565b505f92019182525091905056fea26469706673582212201f19c42be9f304cd7eca63a37f34f13c8ea2ee6a1398dee8cfc586287257029064736f6c63430008140033000000000000000000000000926df14a23be491164dcf93f4c468a50ef659d5b000000000000000000000000e3ed95e130ad9e15643f5a5f232a3dae980784cd000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4000000000000000000000000888888888889758f76e7103c6cbf23abbf58f9460000000000000000000000004df2eaa1658a220fdb415b9966a9ae7c3d16e240

Deployed Bytecode

0x608060405234801561000f575f80fd5b5060043610610184575f3560e01c806367f03c65116100dd578063aa7845cb11610088578063efdf0bb011610063578063efdf0bb014610457578063f0c9e4651461046a578063f887ea4014610472575f80fd5b8063aa7845cb1461041e578063c87ae33414610431578063db91323614610444575f80fd5b80638a0dac4a116100b85780638a0dac4a146103535780638e8f294b1461036657806392c592d0146103f7575f80fd5b806367f03c65146102de5780636de03cca1461030557806375dc769a1461032c575f80fd5b80633f387a921161013d57806351f13c7f1161011857806351f13c7f1461024c578063532286471461025f5780636703ed6b146102b7575f80fd5b80633f387a9214610213578063452a932014610226578063476ff53814610239575f80fd5b8063252408101161016d57806325240810146101cc578063298c07f2146101df57806333525192146101f2575f80fd5b806301681a621461018857806312d43a511461019d575b5f80fd5b61019b610196366004612128565b610499565b005b5f546101af906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b6001546101af906001600160a01b031681565b61019b6101ed366004612143565b61057a565b6102056102003660046121d0565b610700565b6040519081526020016101c3565b610205610221366004612228565b610779565b6002546101af906001600160a01b031681565b610205610247366004612268565b6108b9565b61020561025a366004612228565b610d78565b6102867fc81f847a0000000000000000000000000000000000000000000000000000000081565b6040517fffffffff0000000000000000000000000000000000000000000000000000000090911681526020016101c3565b6102867f47f1de220000000000000000000000000000000000000000000000000000000081565b6101af7f0000000000000000000000004df2eaa1658a220fdb415b9966a9ae7c3d16e24081565b6102867f594a88cc0000000000000000000000000000000000000000000000000000000081565b6102867fd0f423850000000000000000000000000000000000000000000000000000000081565b61019b610361366004612128565b610d86565b6103c0610374366004612128565b60036020525f9081526040902080546001909101546001600160a01b03808316927401000000000000000000000000000000000000000090046bffffffffffffffffffffffff16911683565b604080516001600160a01b0394851681526bffffffffffffffffffffffff90931660208401529216918101919091526060016101c3565b6101af7f000000000000000000000000865377367054516e17014ccded1e7d814edc9ce481565b61020561042c3660046122fd565b610e36565b61020561043f3660046121d0565b610e4e565b61019b610452366004612128565b610ebe565b61019b610465366004612128565b610f86565b61019b61102f565b6101af7f000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94681565b5f546001600160a01b031633146104dc576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f546040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152610577916001600160a01b0390811691908416906370a0823190602401602060405180830381865afa158015610542573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105669190612345565b6001600160a01b03841691906110e8565b50565b5f546001600160a01b031633146105bd576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f826001600160a01b031663e184c9be6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105fa573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061061e9190612345565b604080516060810182526001600160a01b038681168083526bffffffffffffffffffffffff85811660208086018281528a86168789018181528e88165f818152600386528b9020995193519389167401000000000000000000000000000000000000000094909716939093029590951788559351600190970180547fffffffffffffffffffffffff00000000000000000000000000000000000000001697909616969096179094559451948552949550927fb2293e99ade43aa212a7a8677f285eb86d6fc5ca9bf2b4b3c329320681b93ba6910160405180910390a450505050565b5f336001600160a01b037f0000000000000000000000004df2eaa1658a220fdb415b9966a9ae7c3d16e2401614610763576040517fef11a41000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107708585338686611161565b95945050505050565b5f8061078783850185612389565b505090505f6107993388308888611565565b6001600160a01b038381165f81815260036020526040908190205490517f095ea7b300000000000000000000000000000000000000000000000000000000815260048101929092526024820184905292935091169063095ea7b3906044016020604051808303815f875af1158015610813573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610837919061246d565b506040517f47e7ef240000000000000000000000000000000000000000000000000000000081526001600160a01b038781166004830152602482018390528316906347e7ef24906044015f604051808303815f87803b158015610898575f80fd5b505af11580156108aa573d5f803e3d5ffd5b50929998505050505050505050565b5f8080806108c985870187612389565b9250925092506108d883611a27565b5f6108e582826004611a87565b6108ee9061248c565b90507fffffffff0000000000000000000000000000000000000000000000000000000081167f594a88cc000000000000000000000000000000000000000000000000000000001480159061098457507fffffffff0000000000000000000000000000000000000000000000000000000081167f47f1de220000000000000000000000000000000000000000000000000000000014155b156109bb576040517f7352d91c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fb80e21de000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821601610a0f57610a0f84338c611c00565b6001600160a01b038416633525f591338c8b35610a3260408e0160208f016124db565b604080517fffffffff0000000000000000000000000000000000000000000000000000000060e088901b1681526001600160a01b0390951660048601526024850193909352604484019190915260ff1660648301528b0135608482015260608b013560a482015260c4015f604051808303815f87803b158015610ab3575f80fd5b505af1158015610ac5573d5f803e3d5ffd5b505050506001600160a01b038481165f90815260036020526040908190205490517f095ea7b30000000000000000000000000000000000000000000000000000000081527f000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94683166004820152602481018d9052911690819063095ea7b3906044016020604051808303815f875af1158015610b62573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b86919061246d565b506040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b038b811660048301525f917f000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4909116906370a0823190602401602060405180830381865afa158015610c08573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c2c9190612345565b9050610c3784611e1f565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b038c8116600483015282917f000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4909116906370a0823190602401602060405180830381865afa158015610cb8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cdc9190612345565b610ce69190612528565b965084871015610d22576040517f44c6883000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518d8152602081018990526001600160a01b038d169133917ffc6fc897151cbcfef0cc39115ec79125e48fc6620bbace392ec138d93ecda44491015b60405180910390a350505050505095945050505050565b5f6107703386868686611161565b5f546001600160a01b03163314610dc9576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fb6182387b7ea948602a7e04e662a27ce251dc3dd014eacaed10dce36b41bf1a5906020015b60405180910390a150565b5f610e443385338686611565565b90505b9392505050565b5f336001600160a01b037f0000000000000000000000004df2eaa1658a220fdb415b9966a9ae7c3d16e2401614610eb1576040517fef11a41000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107708585338686611565565b6002546001600160a01b03163314801590610ee357505f546001600160a01b03163314155b15610f1a576040517fe6000c8500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381165f8181526003602052604080822082815560010180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055517f59d7b1e52008dc342c9421dadfc773114b914a65682a4e4b53cf60a970df0d779190a250565b5f546001600160a01b03163314610fc9576040517fb577c1f700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527ff74ae56780e3765c0c0897ef57fb50a10a237584f419631812daf040913e1c9f90602001610e2b565b6001546001600160a01b03163314611073576040517f7c04d72b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180545f80546001600160a01b0383167fffffffffffffffffffffffff000000000000000000000000000000000000000091821681179092559091169091556040519081527f639717155292ce2c3e699929a8b65d14a637640f75ab5b6d165a4e735d82a4559060200160405180910390a1565b6040516001600160a01b0383811660248301526044820183905261115c91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050611ed5565b505050565b5f80808061117185870187612389565b92509250925061118083611a27565b5f61118d82826004611a87565b6111969061248c565b90507fffffffff0000000000000000000000000000000000000000000000000000000081167f594a88cc000000000000000000000000000000000000000000000000000000001480159061122c57507fffffffff0000000000000000000000000000000000000000000000000000000081167f47f1de220000000000000000000000000000000000000000000000000000000014155b15611263576040517f7352d91c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fb80e21de000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008216016112b7576112b7848b8b611c00565b6001600160a01b038085165f90815260036020526040902054166112dd8133308d611f4f565b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94681166004830152602482018c905282169063095ea7b3906044016020604051808303815f875af1158015611362573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611386919061246d565b506040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b038a811660048301525f917f000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4909116906370a0823190602401602060405180830381865afa158015611408573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061142c9190612345565b905061143784611e1f565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b038b8116600483015282917f000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4909116906370a0823190602401602060405180830381865afa1580156114b8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114dc9190612345565b6114e69190612528565b965084871015611522576040517f44c6883000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518c8152602081018990526001600160a01b038c169133917ffc6fc897151cbcfef0cc39115ec79125e48fc6620bbace392ec138d93ecda4449101610d61565b5f80808061157585870187612389565b92509250925061158483611a27565b5f61159182826004611a87565b61159a9061248c565b90507fffffffff0000000000000000000000000000000000000000000000000000000081167fc81f847a000000000000000000000000000000000000000000000000000000001480159061163057507fffffffff0000000000000000000000000000000000000000000000000000000081167fd0f423850000000000000000000000000000000000000000000000000000000014155b15611667576040517f7352d91c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61169c6001600160a01b037f000000000000000000000000865377367054516e17014ccded1e7d814edc9ce41633308c611f4f565b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000888888888889758f76e7103c6cbf23abbf58f94681166004830152602482018b90527f000000000000000000000000865377367054516e17014ccded1e7d814edc9ce4169063095ea7b3906044016020604051808303815f875af1158015611741573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611765919061246d565b5061176f82611e1f565b6001600160a01b038481165f908152600360205260408082205490517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015292169182906370a0823190602401602060405180830381865afa1580156117dd573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906118019190612345565b90508481101561183d576040517f46711f9e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038a163014611861576118616001600160a01b0383168b836110e8565b604080518c8152602081018390526001600160a01b038c169133917f2e35da04afaf475d9a94f2fa2f9dbcaef35db18ef1c2e023b6fb95b6bcdc413d910160405180910390a37f2f0bdc7b000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000841601611a18576001600160a01b038681165f908152600360205260408082206001015490517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015292169182906370a0823190602401602060405180830381865afa158015611961573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119859190612345565b9050868110156119c1576040517fbe93493900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6119d56001600160a01b0383168f836110e8565b6040518181526001600160a01b038f169033907fd43e426bfd1c116e433e4748fb5d367f98a8fb4fb75298174670deb9330ffb3b9060200160405180910390a350505b9b9a5050505050505050505050565b6001600160a01b038181165f9081526003602052604090205416610577576040517f31589d090000000000000000000000000000000000000000000000000000000081526001600160a01b03821660048201526024015b60405180910390fd5b606081611a9581601f61253b565b1015611afd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152606401611a7e565b611b07828461253b565b84511015611b71576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152606401611a7e565b606082158015611b8f5760405191505f825260208201604052611bf7565b6040519150601f8416801560200281840101858101878315602002848b0101015b81831015611bc8578051835260209283019201611bb0565b5050858452601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016604052505b50949350505050565b6001600160a01b0383165f908152600360205260409020547401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1642101561115c576001600160a01b038381165f90815260036020526040908190206001015490517f70a082310000000000000000000000000000000000000000000000000000000081528483166004820152911690829082906370a0823190602401602060405180830381865afa158015611cbb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cdf9190612345565b1015611d17576040517fbe93493900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d2c6001600160a01b038216843085611f4f565b6040517f095ea7b30000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000888888888889758f76e7103c6cbf23abbf58f946811660048301526024820184905282169063095ea7b3906044016020604051808303815f875af1158015611db1573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611dd5919061246d565b50826001600160a01b03167fb2156de976d9a5a3d29c4d8e10ebdd04d021086d629dfe04c1641050414b8db383604051611e1191815260200190565b60405180910390a250505050565b5f7f000000000000000000000000888888888889758f76e7103c6cbf23abbf58f9466001600160a01b031682604051611e58919061254e565b5f604051808303815f865af19150503d805f8114611e91576040519150601f19603f3d011682016040523d82523d5f602084013e611e96565b606091505b5050905080611ed1576040517f0fe7fe8500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b5f611ee96001600160a01b03841683611f8e565b905080515f14158015611f0d575080806020019051810190611f0b919061246d565b155b1561115c576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401611a7e565b6040516001600160a01b038481166024830152838116604483015260648201839052611f889186918216906323b872dd90608401611115565b50505050565b6060611f9b83835f611fa4565b90505b92915050565b606081471015611fe9576040517fcf47918100000000000000000000000000000000000000000000000000000000815247600482015260248101839052604401611a7e565b5f80856001600160a01b03168486604051612004919061254e565b5f6040518083038185875af1925050503d805f811461203e576040519150601f19603f3d011682016040523d82523d5f602084013e612043565b606091505b509150915061205386838361205d565b9695505050505050565b6060826120725761206d826120d2565b610e47565b815115801561208957506001600160a01b0384163b155b156120cb576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401611a7e565b5080610e47565b8051156120e25780518082602001fd5b6040517fd6bda27500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b0381168114610577575f80fd5b5f60208284031215612138575f80fd5b8135610e4781612114565b5f805f60608486031215612155575f80fd5b833561216081612114565b9250602084013561217081612114565b9150604084013561218081612114565b809150509250925092565b5f8083601f84011261219b575f80fd5b50813567ffffffffffffffff8111156121b2575f80fd5b6020830191508360208285010111156121c9575f80fd5b9250929050565b5f805f80606085870312156121e3575f80fd5b84356121ee81612114565b935060208501359250604085013567ffffffffffffffff811115612210575f80fd5b61221c8782880161218b565b95989497509550505050565b5f805f806060858703121561223b575f80fd5b84359350602085013561224d81612114565b9250604085013567ffffffffffffffff811115612210575f80fd5b5f805f805f85870360e081121561227d575f80fd5b86359550602087013561228f81612114565b945060807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0820112156122c0575f80fd5b5060408601925060c086013567ffffffffffffffff8111156122e0575f80fd5b6122ec8882890161218b565b969995985093965092949392505050565b5f805f6040848603121561230f575f80fd5b83359250602084013567ffffffffffffffff81111561232c575f80fd5b6123388682870161218b565b9497909650939450505050565b5f60208284031215612355575f80fd5b5051919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f805f6060848603121561239b575f80fd5b83356123a681612114565b925060208401359150604084013567ffffffffffffffff808211156123c9575f80fd5b818601915086601f8301126123dc575f80fd5b8135818111156123ee576123ee61235c565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156124345761243461235c565b8160405282815289602084870101111561244c575f80fd5b826020860160208301375f6020848301015280955050505050509250925092565b5f6020828403121561247d575f80fd5b81518015158114610e47575f80fd5b5f815160208301517fffffffff00000000000000000000000000000000000000000000000000000000808216935060048310156124d35780818460040360031b1b83161693505b505050919050565b5f602082840312156124eb575f80fd5b813560ff81168114610e47575f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115611f9e57611f9e6124fb565b80820180821115611f9e57611f9e6124fb565b5f82515f5b8181101561256d5760208186018101518583015201612553565b505f92019182525091905056fea26469706673582212201f19c42be9f304cd7eca63a37f34f13c8ea2ee6a1398dee8cfc586287257029064736f6c63430008140033

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

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.