ETH Price: $3,326.89 (-2.58%)

Token

ERC20 ***
 

Overview

Max Total Supply

0.000000394744232226 ERC20 ***

Holders

191

Market

Onchain Market Cap

$0.00

Circulating Supply Market Cap

-

Other Info

Token Contract (WITH 18 Decimals)

Balance
0.000000000078320796 ERC20 ***

Value
$0.00
0x5f3becdb2a0257d6bf6d52f851efc84547ef2ac8
Loading...
Loading
Loading...
Loading
Loading...
Loading

Click here to update the token information / general information
# Exchange Pair Price  24H Volume % Volume
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

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

Contract Name:
BridgePoolProd

Compiler Version
v0.8.9+commit.e5eed63a

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 22 : BridgePool.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

import "./interfaces/BridgeAdminInterface.sol";
import "./interfaces/BridgePoolInterface.sol";

import "../oracle/interfaces/SkinnyOptimisticOracleInterface.sol";
import "../oracle/interfaces/StoreInterface.sol";
import "../oracle/interfaces/FinderInterface.sol";
import "../oracle/implementation/Constants.sol";

import "../common/implementation/AncillaryData.sol";
import "../common/implementation/Testable.sol";
import "../common/implementation/FixedPoint.sol";
import "../common/implementation/Lockable.sol";
import "../common/implementation/MultiCaller.sol";

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Address.sol";

interface WETH9Like {
    function withdraw(uint256 wad) external;

    function deposit() external payable;
}

/**
 * @notice Contract deployed on L1 that provides methods for "Relayers" to fulfill deposit orders that originated on L2.
 * The Relayers can either post capital to fulfill the deposit (instant relay), or request that the funds are taken out
 * of a passive liquidity provider pool following a challenge period (slow relay). This contract ingests liquidity from
 * passive liquidity providers and returns them claims to withdraw their funds. Liquidity providers are incentivized
 * to post collateral by earning a fee per fulfilled deposit order.
 * @dev A "Deposit" is an order to send capital from L2 to L1, and a "Relay" is a fulfillment attempt of that order.
 */
contract BridgePool is MultiCaller, Testable, BridgePoolInterface, ERC20, Lockable {
    using SafeERC20 for IERC20;
    using FixedPoint for FixedPoint.Unsigned;
    using Address for address;

    // Token that this contract receives as LP deposits.
    IERC20 public override l1Token;

    // Track the total number of relays and uniquely identifies relays.
    uint32 public numberOfRelays;

    // Reserves that are unutilized and withdrawable.
    uint256 public liquidReserves;

    // Reserves currently utilized due to L2-L1 transactions in flight.
    int256 public utilizedReserves;

    // Reserves that are not yet utilized but are pre-allocated for a pending relay.
    uint256 public pendingReserves;

    // True If this pool houses WETH. If the withdrawn token is WETH then unwrap and send ETH when finalizing
    // withdrawal.
    bool public isWethPool;

    // Exponential decay exchange rate to accumulate fees to LPs over time.
    uint64 public lpFeeRatePerSecond;

    // Last timestamp that LP fees were updated.
    uint32 public lastLpFeeUpdate;

    // Store local instances of contract params to save gas relaying.
    uint64 public proposerBondPct;
    uint32 public optimisticOracleLiveness;

    // Store local instance of the reserve currency final fee. This is a gas optimization to not re-call the store.
    uint256 l1TokenFinalFee;

    // Cumulative undistributed LP fees. As fees accumulate, they are subtracted from this number.
    uint256 public undistributedLpFees;

    // Total bond amount held for pending relays. Bonds are released following a successful relay or after a dispute.
    uint256 public bonds;

    // Administrative contract that deployed this contract and also houses all state variables needed to relay deposits.
    BridgeAdminInterface public bridgeAdmin;

    // Store local instances of the contract instances to save gas relaying. Can be sync with the Finder at any time via
    // the syncUmaEcosystemParams() public function.
    StoreInterface public store;
    SkinnyOptimisticOracleInterface public optimisticOracle;

    // DVM price request identifier that is resolved based on the validity of a relay attempt.
    bytes32 public identifier;

    // A Relay represents an attempt to finalize a cross-chain transfer that originated on an L2 DepositBox contract.
    // The flow chart between states is as follows:
    // - Begin at Uninitialized.
    // - When relayDeposit() is called, a new relay is created with state Pending and mapped to the L2 deposit hash.
    // - If the relay is disputed, the RelayData gets deleted and the L2 deposit hash has no relay mapped to it anymore.
    // - The above statements enable state to transfer between the Uninitialized and Pending states.
    // - When settleRelay() is successfully called, the relay state gets set to Finalized and cannot change from there.
    // - It is impossible for a relay to be deleted when in Finalized state (and have its state set to Uninitialized)
    //   because the only way for settleRelay() to succeed is if the price has resolved on the OptimisticOracle.
    // - You cannot dispute an already resolved request on the OptimisticOracle. Moreover, the mapping from
    //   a relay's ancillary data hash to its deposit hash is deleted after a successful settleRelay() call.
    enum RelayState { Uninitialized, Pending, Finalized }

    // Data from L2 deposit transaction.
    struct DepositData {
        uint256 chainId;
        uint64 depositId;
        address payable l1Recipient;
        address l2Sender;
        uint256 amount;
        uint64 slowRelayFeePct;
        uint64 instantRelayFeePct;
        uint32 quoteTimestamp;
    }

    // Each L2 Deposit can have one Relay attempt at any one time. A Relay attempt is characterized by its RelayData.
    struct RelayData {
        RelayState relayState;
        address slowRelayer;
        uint32 relayId;
        uint64 realizedLpFeePct;
        uint32 priceRequestTime;
        uint256 proposerBond;
        uint256 finalFee;
    }

    // Associate deposits with pending relay data. When the mapped relay hash is empty, new relay attempts can be made
    // for this deposit. The relay data contains information necessary to pay out relayers on successful relay.
    // Relay hashes are deleted when they are disputed on the OptimisticOracle.
    mapping(bytes32 => bytes32) public relays;

    // Map hash of deposit and realized-relay fee to instant relayers. This mapping is checked at settlement time
    // to determine if there was a valid instant relayer.
    mapping(bytes32 => address) public instantRelays;

    event LiquidityAdded(uint256 amount, uint256 lpTokensMinted, address indexed liquidityProvider);
    event LiquidityRemoved(uint256 amount, uint256 lpTokensBurnt, address indexed liquidityProvider);
    event DepositRelayed(
        bytes32 indexed depositHash,
        DepositData depositData,
        RelayData relay,
        bytes32 relayAncillaryDataHash
    );
    event RelaySpedUp(bytes32 indexed depositHash, address indexed instantRelayer, RelayData relay);

    // Note: the difference between a dispute and a cancellation is that a cancellation happens in the case where
    // something changes in the OO between request and dispute that causes calls to it to fail. The most common
    // case would be an increase in final fee. However, things like whitelisting can also cause problems.
    event RelayDisputed(bytes32 indexed depositHash, bytes32 indexed relayHash, address indexed disputer);
    event RelayCanceled(bytes32 indexed depositHash, bytes32 indexed relayHash, address indexed disputer);
    event RelaySettled(bytes32 indexed depositHash, address indexed caller, RelayData relay);
    event BridgePoolAdminTransferred(address oldAdmin, address newAdmin);

    /**
     * @notice Construct the Bridge Pool.
     * @param _lpTokenName Name of the LP token to be deployed by this contract.
     * @param _lpTokenSymbol Symbol of the LP token to be deployed by this contract.
     * @param _bridgeAdmin Admin contract deployed alongside on L1. Stores global variables and has owner control.
     * @param _l1Token Address of the L1 token that this bridgePool holds. This is the token LPs deposit and is bridged.
     * @param _lpFeeRatePerSecond Interest rate payment that scales the amount of pending fees per second paid to LPs.
     * @param _isWethPool Toggles if this is the WETH pool. If it is then can accept ETH and wrap to WETH for the user.
     * @param _timer Timer used to synchronize contract time in testing. Set to 0x000... in production.
     */
    constructor(
        string memory _lpTokenName,
        string memory _lpTokenSymbol,
        address _bridgeAdmin,
        address _l1Token,
        uint64 _lpFeeRatePerSecond,
        bool _isWethPool,
        address _timer
    ) Testable(_timer) ERC20(_lpTokenName, _lpTokenSymbol) {
        require(bytes(_lpTokenName).length != 0 && bytes(_lpTokenSymbol).length != 0, "Bad LP token name or symbol");
        bridgeAdmin = BridgeAdminInterface(_bridgeAdmin);
        l1Token = IERC20(_l1Token);
        lastLpFeeUpdate = uint32(getCurrentTime());
        lpFeeRatePerSecond = _lpFeeRatePerSecond;
        isWethPool = _isWethPool;

        syncUmaEcosystemParams(); // Fetch OptimisticOracle and Store addresses and L1Token finalFee.
        syncWithBridgeAdminParams(); // Fetch ProposerBondPct OptimisticOracleLiveness, Identifier from the BridgeAdmin.
    }

    /*************************************************
     *          LIQUIDITY PROVIDER FUNCTIONS         *
     *************************************************/

    /**
     * @notice Add liquidity to the bridge pool. Pulls l1Token from the caller's wallet. The caller is sent back a
     * commensurate number of LP tokens (minted to their address) at the prevailing exchange rate.
     * @dev The caller must approve this contract to transfer `l1TokenAmount` amount of l1Token if depositing ERC20.
     * @dev The caller can deposit ETH which is auto wrapped to WETH. This can only be done if: a) this is the Weth pool
     * and b) the l1TokenAmount matches to the transaction msg.value.
     * @dev Reentrancy guard not added to this function because this indirectly calls sync() which is guarded.
     * @param l1TokenAmount Number of l1Token to add as liquidity.
     */
    function addLiquidity(uint256 l1TokenAmount) public payable nonReentrant() {
        // If this is the weth pool and the caller sends msg.value then the msg.value must match the l1TokenAmount.
        // Else, msg.value must be set to 0.
        require((isWethPool && msg.value == l1TokenAmount) || msg.value == 0, "Bad add liquidity Eth value");

        // Since `exchangeRateCurrent()` reads this contract's balance and updates contract state using it,
        // we must call it first before transferring any tokens to this contract.
        uint256 lpTokensToMint = (l1TokenAmount * 1e18) / _exchangeRateCurrent();
        _mint(msg.sender, lpTokensToMint);
        liquidReserves += l1TokenAmount;

        if (msg.value > 0 && isWethPool) WETH9Like(address(l1Token)).deposit{ value: msg.value }();
        else l1Token.safeTransferFrom(msg.sender, address(this), l1TokenAmount);

        emit LiquidityAdded(l1TokenAmount, lpTokensToMint, msg.sender);
    }

    /**
     * @notice Removes liquidity from the bridge pool. Burns lpTokenAmount LP tokens from the caller's wallet. The caller
     * is sent back a commensurate number of l1Tokens at the prevailing exchange rate.
     * @dev The caller does not need to approve the spending of LP tokens as this method directly uses the burn logic.
     * @dev Reentrancy guard not added to this function because this indirectly calls sync() which is guarded.
     * @param lpTokenAmount Number of lpTokens to redeem for underlying.
     * @param sendEth Enable the liquidity provider to remove liquidity in ETH, if this is the WETH pool.
     */
    function removeLiquidity(uint256 lpTokenAmount, bool sendEth) public nonReentrant() {
        // Can only send eth on withdrawing liquidity iff this is the WETH pool.
        require(!sendEth || isWethPool, "Cant send eth");
        uint256 l1TokensToReturn = (lpTokenAmount * _exchangeRateCurrent()) / 1e18;

        // Check that there is enough liquid reserves to withdraw the requested amount.
        require(liquidReserves >= (pendingReserves + l1TokensToReturn), "Utilization too high to remove");

        _burn(msg.sender, lpTokenAmount);
        liquidReserves -= l1TokensToReturn;

        if (sendEth) _unwrapWETHTo(payable(msg.sender), l1TokensToReturn);
        else l1Token.safeTransfer(msg.sender, l1TokensToReturn);

        emit LiquidityRemoved(l1TokensToReturn, lpTokenAmount, msg.sender);
    }

    /**************************************
     *          RELAYER FUNCTIONS         *
     **************************************/

    /**
     * @notice Called by Relayer to execute a slow + fast relay from L2 to L1, fulfilling a corresponding deposit order.
     * @dev There can only be one pending relay for a deposit. This method is effectively the relayDeposit and
     * speedUpRelay methods concatenated. This could be refactored to just call each method, but there
     * are some gas savings in combining the transfers and hash computations.
     * @dev Caller must have approved this contract to spend the total bond + amount - fees for `l1Token`.
     * @param depositData the deposit data struct containing all the user's deposit information.
     * @param realizedLpFeePct LP fee calculated off-chain considering the L1 pool liquidity at deposit time, before
     *      quoteTimestamp. The OO acts to verify the correctness of this realized fee. Cannot exceed 50%.
     */
    function relayAndSpeedUp(DepositData memory depositData, uint64 realizedLpFeePct) public nonReentrant() {
        // If no pending relay for this deposit, then associate the caller's relay attempt with it.
        uint32 priceRequestTime = uint32(getCurrentTime());

        // The realizedLPFeePct should never be greater than 0.5e18 and the slow and instant relay fees should never be
        // more than 0.25e18 each. Therefore, the sum of all fee types can never exceed 1e18 (or 100%).
        require(
            depositData.slowRelayFeePct <= 0.25e18 &&
                depositData.instantRelayFeePct <= 0.25e18 &&
                realizedLpFeePct <= 0.5e18,
            "Invalid fees"
        );

        // Check if there is a pending relay for this deposit.
        bytes32 depositHash = _getDepositHash(depositData);

        // Note: A disputed relay deletes the stored relay hash and enables this require statement to pass.
        require(relays[depositHash] == bytes32(0), "Pending relay exists");

        uint256 proposerBond = _getProposerBond(depositData.amount);

        // Save hash of new relay attempt parameters.
        // Note: The liveness for this relay can be changed in the BridgeAdmin, which means that each relay has a
        // potentially variable liveness time. This should not provide any exploit opportunities, especially because
        // the BridgeAdmin state (including the liveness value) is permissioned to the cross domained owner.
        RelayData memory relayData =
            RelayData({
                relayState: RelayState.Pending,
                slowRelayer: msg.sender,
                relayId: numberOfRelays++, // Note: Increment numberOfRelays at the same time as setting relayId to its current value.
                realizedLpFeePct: realizedLpFeePct,
                priceRequestTime: priceRequestTime,
                proposerBond: proposerBond,
                finalFee: l1TokenFinalFee
            });
        bytes32 relayHash = _getRelayHash(depositData, relayData);
        relays[depositHash] = _getRelayDataHash(relayData);

        bytes32 instantRelayHash = _getInstantRelayHash(depositHash, relayData);
        require(
            // Can only speed up a pending relay without an existing instant relay associated with it.
            instantRelays[instantRelayHash] == address(0),
            "Relay cannot be sped up"
        );

        // Sanity check that pool has enough balance to cover relay amount + proposer reward. Reward amount will be
        // paid on settlement after the OptimisticOracle price request has passed the challenge period.
        // Note: liquidReserves should always be <= balance - bonds.
        require(liquidReserves - pendingReserves >= depositData.amount, "Insufficient pool balance");

        // Compute total proposal bond and pull from caller so that the OptimisticOracle can pull it from here.
        uint256 totalBond = proposerBond + l1TokenFinalFee;

        // Pull relay amount minus fees from caller and send to the deposit l1Recipient. The total fees paid is the sum
        // of the LP fees, the relayer fees and the instant relay fee.
        uint256 feesTotal =
            _getAmountFromPct(
                relayData.realizedLpFeePct + depositData.slowRelayFeePct + depositData.instantRelayFeePct,
                depositData.amount
            );
        // If the L1 token is WETH then: a) pull WETH from instant relayer b) unwrap WETH c) send ETH to recipient.
        uint256 recipientAmount = depositData.amount - feesTotal;

        bonds += totalBond;
        pendingReserves += depositData.amount; // Book off maximum liquidity used by this relay in the pending reserves.

        instantRelays[instantRelayHash] = msg.sender;

        l1Token.safeTransferFrom(msg.sender, address(this), recipientAmount + totalBond);

        // If this is a weth pool then unwrap and send eth.
        if (isWethPool) {
            _unwrapWETHTo(depositData.l1Recipient, recipientAmount);
            // Else, this is a normal ERC20 token. Send to recipient.
        } else l1Token.safeTransfer(depositData.l1Recipient, recipientAmount);

        emit DepositRelayed(depositHash, depositData, relayData, relayHash);
        emit RelaySpedUp(depositHash, msg.sender, relayData);
    }

    /**
     * @notice Called by Disputer to dispute an ongoing relay.
     * @dev The result of this method is to always throw out the relay, providing an opportunity for another relay for
     * the same deposit. Between the disputer and proposer, whoever is incorrect loses their bond. Whoever is correct
     * gets it back + a payout.
     * @dev Caller must have approved this contract to spend the total bond + amount - fees for `l1Token`.
     * @param depositData the deposit data struct containing all the user's deposit information.
     * @param relayData RelayData logged in the disputed relay.
     */
    function disputeRelay(DepositData memory depositData, RelayData memory relayData) public nonReentrant() {
        require(relayData.priceRequestTime + optimisticOracleLiveness > getCurrentTime(), "Past liveness");
        require(relayData.relayState == RelayState.Pending, "Not disputable");
        // Validate the input data.
        bytes32 depositHash = _getDepositHash(depositData);
        _validateRelayDataHash(depositHash, relayData);

        // Submit the proposal and dispute to the OO.
        bytes32 relayHash = _getRelayHash(depositData, relayData);

        // Note: in some cases this will fail due to changes in the OO and the method will refund the relayer.
        bool success =
            _requestProposeDispute(
                relayData.slowRelayer,
                msg.sender,
                relayData.proposerBond,
                relayData.finalFee,
                _getRelayAncillaryData(relayHash)
            );

        // Drop the relay and remove the bond from the tracked bonds.
        bonds -= relayData.finalFee + relayData.proposerBond;
        pendingReserves -= depositData.amount;
        delete relays[depositHash];
        if (success) emit RelayDisputed(depositHash, _getRelayDataHash(relayData), msg.sender);
        else emit RelayCanceled(depositHash, _getRelayDataHash(relayData), msg.sender);
    }

    /**
     * @notice Called by Relayer to execute a slow relay from L2 to L1, fulfilling a corresponding deposit order.
     * @dev There can only be one pending relay for a deposit.
     * @dev Caller must have approved this contract to spend the total bond + amount - fees for `l1Token`.
     * @param depositData the deposit data struct containing all the user's deposit information.
     * @param realizedLpFeePct LP fee calculated off-chain considering the L1 pool liquidity at deposit time, before
     *      quoteTimestamp. The OO acts to verify the correctness of this realized fee. Cannot exceed 50%.
     */
    function relayDeposit(DepositData memory depositData, uint64 realizedLpFeePct) public nonReentrant() {
        // The realizedLPFeePct should never be greater than 0.5e18 and the slow and instant relay fees should never be
        // more than 0.25e18 each. Therefore, the sum of all fee types can never exceed 1e18 (or 100%).
        require(
            depositData.slowRelayFeePct <= 0.25e18 &&
                depositData.instantRelayFeePct <= 0.25e18 &&
                realizedLpFeePct <= 0.5e18,
            "Invalid fees"
        );

        // Check if there is a pending relay for this deposit.
        bytes32 depositHash = _getDepositHash(depositData);

        // Note: A disputed relay deletes the stored relay hash and enables this require statement to pass.
        require(relays[depositHash] == bytes32(0), "Pending relay exists");

        // If no pending relay for this deposit, then associate the caller's relay attempt with it.
        uint32 priceRequestTime = uint32(getCurrentTime());

        uint256 proposerBond = _getProposerBond(depositData.amount);

        // Save hash of new relay attempt parameters.
        // Note: The liveness for this relay can be changed in the BridgeAdmin, which means that each relay has a
        // potentially variable liveness time. This should not provide any exploit opportunities, especially because
        // the BridgeAdmin state (including the liveness value) is permissioned to the cross domained owner.
        RelayData memory relayData =
            RelayData({
                relayState: RelayState.Pending,
                slowRelayer: msg.sender,
                relayId: numberOfRelays++, // Note: Increment numberOfRelays at the same time as setting relayId to its current value.
                realizedLpFeePct: realizedLpFeePct,
                priceRequestTime: priceRequestTime,
                proposerBond: proposerBond,
                finalFee: l1TokenFinalFee
            });
        relays[depositHash] = _getRelayDataHash(relayData);

        bytes32 relayHash = _getRelayHash(depositData, relayData);

        // Sanity check that pool has enough balance to cover relay amount + proposer reward. Reward amount will be
        // paid on settlement after the OptimisticOracle price request has passed the challenge period.
        // Note: liquidReserves should always be <= balance - bonds.
        require(liquidReserves - pendingReserves >= depositData.amount, "Insufficient pool balance");

        // Compute total proposal bond and pull from caller so that the OptimisticOracle can pull it from here.
        uint256 totalBond = proposerBond + l1TokenFinalFee;
        pendingReserves += depositData.amount; // Book off maximum liquidity used by this relay in the pending reserves.
        bonds += totalBond;

        l1Token.safeTransferFrom(msg.sender, address(this), totalBond);
        emit DepositRelayed(depositHash, depositData, relayData, relayHash);
    }

    /**
     * @notice Instantly relay a deposit amount minus fees to the l1Recipient. Instant relayer earns a reward following
     * the pending relay challenge period.
     * @dev We assume that the caller has performed an off-chain check that the deposit data they are attempting to
     * relay is valid. If the deposit data is invalid, then the instant relayer has no recourse to receive their funds
     * back after the invalid deposit data is disputed. Moreover, no one will be able to resubmit a relay for the
     * invalid deposit data because they know it will get disputed again. On the other hand, if the deposit data is
     * valid, then even if it is falsely disputed, the instant relayer will eventually get reimbursed because someone
     * else will be incentivized to resubmit the relay to earn slow relayer rewards. Once the valid relay is finalized,
     * the instant relayer will be reimbursed. Therefore, the caller has the same responsibility as the disputer in
     * validating the relay data.
     * @dev Caller must have approved this contract to spend the deposit amount of L1 tokens to relay. There can only
     * be one instant relayer per relay attempt. You cannot speed up a relay that is past liveness.
     * @param depositData Unique set of L2 deposit data that caller is trying to instantly relay.
     * @param relayData Parameters of Relay that caller is attempting to speedup. Must hash to the stored relay hash
     * for this deposit or this method will revert.
     */
    function speedUpRelay(DepositData memory depositData, RelayData memory relayData) public nonReentrant() {
        bytes32 depositHash = _getDepositHash(depositData);
        _validateRelayDataHash(depositHash, relayData);
        bytes32 instantRelayHash = _getInstantRelayHash(depositHash, relayData);
        require(
            // Can only speed up a pending relay without an existing instant relay associated with it.
            getCurrentTime() < relayData.priceRequestTime + optimisticOracleLiveness &&
                relayData.relayState == RelayState.Pending &&
                instantRelays[instantRelayHash] == address(0),
            "Relay cannot be sped up"
        );
        instantRelays[instantRelayHash] = msg.sender;

        // Pull relay amount minus fees from caller and send to the deposit l1Recipient. The total fees paid is the sum
        // of the LP fees, the relayer fees and the instant relay fee.
        uint256 feesTotal =
            _getAmountFromPct(
                relayData.realizedLpFeePct + depositData.slowRelayFeePct + depositData.instantRelayFeePct,
                depositData.amount
            );
        // If the L1 token is WETH then: a) pull WETH from instant relayer b) unwrap WETH c) send ETH to recipient.
        uint256 recipientAmount = depositData.amount - feesTotal;
        if (isWethPool) {
            l1Token.safeTransferFrom(msg.sender, address(this), recipientAmount);
            _unwrapWETHTo(depositData.l1Recipient, recipientAmount);
            // Else, this is a normal ERC20 token. Send to recipient.
        } else l1Token.safeTransferFrom(msg.sender, depositData.l1Recipient, recipientAmount);

        emit RelaySpedUp(depositHash, msg.sender, relayData);
    }

    /**
     * @notice Reward relayers if a pending relay price request has a price available on the OptimisticOracle. Mark
     * the relay as complete.
     * @dev We use the relayData and depositData to compute the ancillary data that the relay price request is uniquely
     * associated with on the OptimisticOracle. If the price request passed in does not match the pending relay price
     * request, then this will revert.
     * @param depositData Unique set of L2 deposit data that caller is trying to settle a relay for.
     * @param relayData Parameters of Relay that caller is attempting to settle. Must hash to the stored relay hash
     * for this deposit.
     */
    function settleRelay(DepositData memory depositData, RelayData memory relayData) public nonReentrant() {
        bytes32 depositHash = _getDepositHash(depositData);
        _validateRelayDataHash(depositHash, relayData);
        require(relayData.relayState == RelayState.Pending, "Already settled");
        uint32 expirationTime = relayData.priceRequestTime + optimisticOracleLiveness;
        require(expirationTime <= getCurrentTime(), "Not settleable yet");

        // Note: this check is to give the relayer a small, but reasonable amount of time to complete the relay before
        // before it can be "stolen" by someone else. This is to ensure there is an incentive to settle relays quickly.
        require(
            msg.sender == relayData.slowRelayer || getCurrentTime() > expirationTime + 15 minutes,
            "Not slow relayer"
        );

        // Update the relay state to Finalized. This prevents any re-settling of a relay.
        relays[depositHash] = _getRelayDataHash(
            RelayData({
                relayState: RelayState.Finalized,
                slowRelayer: relayData.slowRelayer,
                relayId: relayData.relayId,
                realizedLpFeePct: relayData.realizedLpFeePct,
                priceRequestTime: relayData.priceRequestTime,
                proposerBond: relayData.proposerBond,
                finalFee: relayData.finalFee
            })
        );

        // Reward relayers and pay out l1Recipient.
        // At this point there are two possible cases:
        // - This was a slow relay: In this case, a) pay the slow relayer their reward and b) pay the l1Recipient of the
        //      amount minus the realized LP fee and the slow Relay fee. The transfer was not sped up so no instant fee.
        // - This was an instant relay: In this case, a) pay the slow relayer their reward and b) pay the instant relayer
        //      the full bridging amount, minus the realized LP fee and minus the slow relay fee. When the instant
        //      relayer called speedUpRelay they were docked this same amount, minus the instant relayer fee. As a
        //      result, they are effectively paid what they spent when speeding up the relay + the instantRelayFee.

        uint256 instantRelayerOrRecipientAmount =
            depositData.amount -
                _getAmountFromPct(relayData.realizedLpFeePct + depositData.slowRelayFeePct, depositData.amount);

        // Refund the instant relayer iff the instant relay params match the approved relay.
        bytes32 instantRelayHash = _getInstantRelayHash(depositHash, relayData);
        address instantRelayer = instantRelays[instantRelayHash];

        // If this is the WETH pool and the instant relayer is is address 0x0 (i.e the relay was not sped up) then:
        // a) withdraw WETH to ETH and b) send the ETH to the recipient.
        if (isWethPool && instantRelayer == address(0)) {
            _unwrapWETHTo(depositData.l1Recipient, instantRelayerOrRecipientAmount);
            // Else, this is a normal slow relay being finalizes where the contract sends ERC20 to the recipient OR this
            // is the finalization of an instant relay where we need to reimburse the instant relayer in WETH.
        } else
            l1Token.safeTransfer(
                instantRelayer != address(0) ? instantRelayer : depositData.l1Recipient,
                instantRelayerOrRecipientAmount
            );

        // There is a fee and a bond to pay out. The fee goes to whoever settles. The bond always goes back to the
        // slow relayer.
        // Note: for gas efficiency, we use an if so we can combine these transfers in the event that they are the same
        // address.
        uint256 slowRelayerReward = _getAmountFromPct(depositData.slowRelayFeePct, depositData.amount);
        uint256 totalBond = relayData.finalFee + relayData.proposerBond;
        if (relayData.slowRelayer == msg.sender)
            l1Token.safeTransfer(relayData.slowRelayer, slowRelayerReward + totalBond);
        else {
            l1Token.safeTransfer(relayData.slowRelayer, totalBond);
            l1Token.safeTransfer(msg.sender, slowRelayerReward);
        }

        uint256 totalReservesSent = instantRelayerOrRecipientAmount + slowRelayerReward;

        // Update reserves by amounts changed and allocated LP fees.
        pendingReserves -= depositData.amount;
        liquidReserves -= totalReservesSent;
        utilizedReserves += int256(totalReservesSent);
        bonds -= totalBond;
        _updateAccumulatedLpFees();
        _allocateLpFees(_getAmountFromPct(relayData.realizedLpFeePct, depositData.amount));

        emit RelaySettled(depositHash, msg.sender, relayData);

        // Clean up state storage and receive gas refund. This also prevents `priceDisputed()` from being able to reset
        // this newly Finalized relay state.
        delete instantRelays[instantRelayHash];
    }

    /**
     * @notice Synchronize any balance changes in this contract with the utilized & liquid reserves. This would be done
     * at the conclusion of an L2 -> L1 token transfer via the canonical token bridge.
     */
    function sync() public nonReentrant() {
        _sync();
    }

    /**
     * @notice Computes the exchange rate between LP tokens and L1Tokens. Used when adding/removing liquidity.
     * @return The updated exchange rate between LP tokens and L1 tokens.
     */
    function exchangeRateCurrent() public nonReentrant() returns (uint256) {
        return _exchangeRateCurrent();
    }

    /**
     * @notice Computes the current liquidity utilization ratio.
     * @dev Used in computing realizedLpFeePct off-chain.
     * @return The current utilization ratio.
     */
    function liquidityUtilizationCurrent() public nonReentrant() returns (uint256) {
        return _liquidityUtilizationPostRelay(0);
    }

    /**
     * @notice Computes the liquidity utilization ratio post a relay of known size.
     * @dev Used in computing realizedLpFeePct off-chain.
     * @param relayedAmount Size of the relayed deposit to factor into the utilization calculation.
     * @return The updated utilization ratio accounting for a new `relayedAmount`.
     */
    function liquidityUtilizationPostRelay(uint256 relayedAmount) public nonReentrant() returns (uint256) {
        return _liquidityUtilizationPostRelay(relayedAmount);
    }

    /**
     * @notice Return both the current utilization value and liquidity utilization post the relay.
     * @dev Used in computing realizedLpFeePct off-chain.
     * @param relayedAmount Size of the relayed deposit to factor into the utilization calculation.
     * @return utilizationCurrent The current utilization ratio.
     * @return utilizationPostRelay The updated utilization ratio accounting for a new `relayedAmount`.
     */
    function getLiquidityUtilization(uint256 relayedAmount)
        public
        nonReentrant()
        returns (uint256 utilizationCurrent, uint256 utilizationPostRelay)
    {
        return (_liquidityUtilizationPostRelay(0), _liquidityUtilizationPostRelay(relayedAmount));
    }

    /**
     * @notice Updates the address stored in this contract for the OptimisticOracle and the Store to the latest versions
     * set in the the Finder. Also pull finalFee Store these as local variables to make relay methods gas efficient.
     * @dev There is no risk of leaving this function public for anyone to call as in all cases we want the addresses
     * in this contract to map to the latest version in the Finder and store the latest final fee.
     */
    function syncUmaEcosystemParams() public nonReentrant() {
        FinderInterface finder = FinderInterface(bridgeAdmin.finder());
        optimisticOracle = SkinnyOptimisticOracleInterface(
            finder.getImplementationAddress(OracleInterfaces.SkinnyOptimisticOracle)
        );

        store = StoreInterface(finder.getImplementationAddress(OracleInterfaces.Store));
        l1TokenFinalFee = store.computeFinalFee(address(l1Token)).rawValue;
    }

    /**
     * @notice Updates the values of stored constants for the proposerBondPct, optimisticOracleLiveness and identifier
     * to that set in the bridge Admin. We store these as local variables to make the relay methods more gas efficient.
     * @dev There is no risk of leaving this function public for anyone to call as in all cases we want these values
     * in this contract to map to the latest version set in the BridgeAdmin.
     */
    function syncWithBridgeAdminParams() public nonReentrant() {
        proposerBondPct = bridgeAdmin.proposerBondPct();
        optimisticOracleLiveness = bridgeAdmin.optimisticOracleLiveness();
        identifier = bridgeAdmin.identifier();
    }

    /************************************
     *          ADMIN FUNCTIONS         *
     ************************************/

    /**
     * @notice Enable the current bridge admin to transfer admin to to a new address.
     * @param _newAdmin Admin address of the new admin.
     */
    function changeAdmin(address _newAdmin) public override nonReentrant() {
        require(msg.sender == address(bridgeAdmin));
        bridgeAdmin = BridgeAdminInterface(_newAdmin);
        emit BridgePoolAdminTransferred(msg.sender, _newAdmin);
    }

    /************************************
     *           VIEW FUNCTIONS         *
     ************************************/

    /**
     * @notice Computes the current amount of unallocated fees that have accumulated from the previous time this the
     * contract was called.
     */
    function getAccumulatedFees() public view nonReentrantView() returns (uint256) {
        return _getAccumulatedFees();
    }

    /**
     * @notice Returns ancillary data containing all relevant Relay data that voters can format into UTF8 and use to
     * determine if the relay is valid.
     * @dev Helpful method to test that ancillary data is constructed properly. We should consider removing if we don't
     * anticipate off-chain bots or users to call this method.
     * @param depositData Contains L2 deposit information used by off-chain validators to validate relay.
     * @param relayData Contains relay information used by off-chain validators to validate relay.
     * @return bytes New ancillary data that can be decoded into UTF8.
     */
    function getRelayAncillaryData(DepositData memory depositData, RelayData memory relayData)
        public
        view
        nonReentrantView()
        returns (bytes memory)
    {
        return _getRelayAncillaryData(_getRelayHash(depositData, relayData));
    }

    /**************************************
     *    INTERNAL & PRIVATE FUNCTIONS    *
     **************************************/

    function _liquidityUtilizationPostRelay(uint256 relayedAmount) internal returns (uint256) {
        _sync(); // Fetch any balance changes due to token bridging finalization and factor them in.

        // liquidityUtilizationRatio :=
        // (relayedAmount + pendingReserves + max(utilizedReserves,0)) / (liquidReserves + max(utilizedReserves,0))
        // UtilizedReserves has a dual meaning: if it's greater than zero then it represents funds pending in the bridge
        // that will flow from L2 to L1. In this case, we can use it normally in the equation. However, if it is
        // negative, then it is already counted in liquidReserves. This occurs if tokens are transferred directly to the
        // contract. In this case, ignore it as it is captured in liquid reserves and has no meaning in the numerator.
        uint256 flooredUtilizedReserves = utilizedReserves > 0 ? uint256(utilizedReserves) : 0;
        uint256 numerator = relayedAmount + pendingReserves + flooredUtilizedReserves;
        uint256 denominator = liquidReserves + flooredUtilizedReserves;

        // If the denominator equals zero, return 1e18 (max utilization).
        if (denominator == 0) return 1e18;

        // In all other cases, return the utilization ratio.
        return (numerator * 1e18) / denominator;
    }

    function _sync() internal {
        // Check if the l1Token balance of the contract is greater than the liquidReserves. If it is then the bridging
        // action from L2 -> L1 has concluded and the local accounting can be updated.
        uint256 l1TokenBalance = l1Token.balanceOf(address(this)) - bonds;
        if (l1TokenBalance > liquidReserves) {
            // utilizedReserves can go to less than zero. This will happen if the accumulated fees exceeds the current
            // outstanding utilization. In other words, if outstanding bridging transfers are 0 then utilizedReserves
            // will equal the total LP fees accumulated over all time.
            utilizedReserves -= int256(l1TokenBalance - liquidReserves);
            liquidReserves = l1TokenBalance;
        }
    }

    function _exchangeRateCurrent() internal returns (uint256) {
        if (totalSupply() == 0) return 1e18; // initial rate is 1 pre any mint action.

        // First, update fee counters and local accounting of finalized transfers from L2 -> L1.
        _updateAccumulatedLpFees(); // Accumulate all allocated fees from the last time this method was called.
        _sync(); // Fetch any balance changes due to token bridging finalization and factor them in.

        // ExchangeRate := (liquidReserves + utilizedReserves - undistributedLpFees) / lpTokenSupply
        uint256 numerator = liquidReserves - undistributedLpFees;
        if (utilizedReserves > 0) numerator += uint256(utilizedReserves);
        else numerator -= uint256(utilizedReserves * -1);
        return (numerator * 1e18) / totalSupply();
    }

    // Return UTF8-decodable ancillary data for relay price request associated with relay hash.
    function _getRelayAncillaryData(bytes32 relayHash) private pure returns (bytes memory) {
        return AncillaryData.appendKeyValueBytes32("", "relayHash", relayHash);
    }

    // Returns hash of unique relay and deposit event. This is added to the relay request's ancillary data.
    function _getRelayHash(DepositData memory depositData, RelayData memory relayData) private view returns (bytes32) {
        return keccak256(abi.encode(depositData, relayData.relayId, relayData.realizedLpFeePct, address(l1Token)));
    }

    // Return hash of relay data, which is stored in state and mapped to a deposit hash.
    function _getRelayDataHash(RelayData memory relayData) private pure returns (bytes32) {
        return keccak256(abi.encode(relayData));
    }

    // Reverts if the stored relay data hash for `depositHash` does not match `_relayData`.
    function _validateRelayDataHash(bytes32 depositHash, RelayData memory relayData) private view {
        require(
            relays[depositHash] == _getRelayDataHash(relayData),
            "Hashed relay params do not match existing relay hash for deposit"
        );
    }

    // Return hash of unique instant relay and deposit event. This is stored in state and mapped to a deposit hash.
    function _getInstantRelayHash(bytes32 depositHash, RelayData memory relayData) private pure returns (bytes32) {
        // Only include parameters that affect the "correctness" of an instant relay. For example, the realized LP fee
        // % directly affects how many tokens the instant relayer needs to send to the user, whereas the address of the
        // instant relayer does not matter for determining whether an instant relay is "correct".
        return keccak256(abi.encode(depositHash, relayData.realizedLpFeePct));
    }

    function _getAccumulatedFees() internal view returns (uint256) {
        // UnallocatedLpFees := min(undistributedLpFees*lpFeeRatePerSecond*timeFromLastInteraction,undistributedLpFees)
        // The min acts to pay out all fees in the case the equation returns more than the remaining a fees.
        uint256 possibleUnpaidFees =
            (undistributedLpFees * lpFeeRatePerSecond * (getCurrentTime() - lastLpFeeUpdate)) / (1e18);
        return possibleUnpaidFees < undistributedLpFees ? possibleUnpaidFees : undistributedLpFees;
    }

    // Update internal fee counters by adding in any accumulated fees from the last time this logic was called.
    function _updateAccumulatedLpFees() internal {
        // Calculate the unallocatedAccumulatedFees from the last time the contract was called.
        uint256 unallocatedAccumulatedFees = _getAccumulatedFees();

        // Decrement the undistributedLpFees by the amount of accumulated fees.
        undistributedLpFees = undistributedLpFees - unallocatedAccumulatedFees;

        lastLpFeeUpdate = uint32(getCurrentTime());
    }

    // Allocate fees to the LPs by incrementing counters.
    function _allocateLpFees(uint256 allocatedLpFees) internal {
        // Add to the total undistributed LP fees and the utilized reserves. Adding it to the utilized reserves acts to
        // track the fees while they are in transit.
        undistributedLpFees += allocatedLpFees;
        utilizedReserves += int256(allocatedLpFees);
    }

    function _getAmountFromPct(uint64 percent, uint256 amount) private pure returns (uint256) {
        return (percent * amount) / 1e18;
    }

    function _getProposerBond(uint256 amount) private view returns (uint256) {
        return _getAmountFromPct(proposerBondPct, amount);
    }

    function _getDepositHash(DepositData memory depositData) private view returns (bytes32) {
        return keccak256(abi.encode(depositData, address(l1Token)));
    }

    // Proposes new price of True for relay event associated with `customAncillaryData` to optimistic oracle. If anyone
    // disagrees with the relay parameters and whether they map to an L2 deposit, they can dispute with the oracle.
    function _requestProposeDispute(
        address proposer,
        address disputer,
        uint256 proposerBond,
        uint256 finalFee,
        bytes memory customAncillaryData
    ) private returns (bool) {
        uint256 totalBond = finalFee + proposerBond;
        l1Token.safeApprove(address(optimisticOracle), totalBond);
        try
            optimisticOracle.requestAndProposePriceFor(
                identifier,
                uint32(getCurrentTime()),
                customAncillaryData,
                IERC20(l1Token),
                // Set reward to 0, since we'll settle proposer reward payouts directly from this contract after a relay
                // proposal has passed the challenge period.
                0,
                // Set the Optimistic oracle proposer bond for the price request.
                proposerBond,
                // Set the Optimistic oracle liveness for the price request.
                optimisticOracleLiveness,
                proposer,
                // Canonical value representing "True"; i.e. the proposed relay is valid.
                int256(1e18)
            )
        returns (uint256 bondSpent) {
            if (bondSpent < totalBond) {
                // If the OO pulls less (due to a change in final fee), refund the proposer.
                uint256 refund = totalBond - bondSpent;
                l1Token.safeTransfer(proposer, refund);
                l1Token.safeApprove(address(optimisticOracle), 0);
                totalBond = bondSpent;
            }
        } catch {
            // If there's an error in the OO, this means something has changed to make this request undisputable.
            // To ensure the request does not go through by default, refund the proposer and return early, allowing
            // the calling method to delete the request, but with no additional recourse by the OO.
            l1Token.safeTransfer(proposer, totalBond);
            l1Token.safeApprove(address(optimisticOracle), 0);

            // Return early noting that the attempt at a proposal + dispute did not succeed.
            return false;
        }

        SkinnyOptimisticOracleInterface.Request memory request =
            SkinnyOptimisticOracleInterface.Request({
                proposer: proposer,
                disputer: address(0),
                currency: IERC20(l1Token),
                settled: false,
                proposedPrice: int256(1e18),
                resolvedPrice: 0,
                expirationTime: getCurrentTime() + optimisticOracleLiveness,
                reward: 0,
                finalFee: totalBond - proposerBond,
                bond: proposerBond,
                customLiveness: uint256(optimisticOracleLiveness)
            });

        // Note: don't pull funds until here to avoid any transfers that aren't needed.
        l1Token.safeTransferFrom(msg.sender, address(this), totalBond);
        l1Token.safeApprove(address(optimisticOracle), totalBond);
        // Dispute the request that we just sent.
        optimisticOracle.disputePriceFor(
            identifier,
            uint32(getCurrentTime()),
            customAncillaryData,
            request,
            disputer,
            address(this)
        );

        // Return true to denote that the proposal + dispute calls succeeded.
        return true;
    }

    // Unwraps ETH and does a transfer to a recipient address. If the recipient is a smart contract then sends WETH.
    function _unwrapWETHTo(address payable to, uint256 amount) internal {
        if (address(to).isContract()) {
            l1Token.safeTransfer(to, amount);
        } else {
            WETH9Like(address(l1Token)).withdraw(amount);
            to.transfer(amount);
        }
    }

    // Added to enable the BridgePool to receive ETH. used when unwrapping Weth.
    receive() external payable {}
}

/**
 * @notice This is the BridgePool contract that should be deployed on live networks. It is exactly the same as the
 * regular BridgePool contract, but it overrides getCurrentTime to make the call a simply return block.timestamp with
 * no branching or storage queries. This is done to save gas.
 */
contract BridgePoolProd is BridgePool {
    constructor(
        string memory _lpTokenName,
        string memory _lpTokenSymbol,
        address _bridgeAdmin,
        address _l1Token,
        uint64 _lpFeeRatePerSecond,
        bool _isWethPool,
        address _timer
    ) BridgePool(_lpTokenName, _lpTokenSymbol, _bridgeAdmin, _l1Token, _lpFeeRatePerSecond, _isWethPool, _timer) {}

    function getCurrentTime() public view virtual override returns (uint256) {
        return block.timestamp;
    }
}

File 2 of 22 : BridgeAdminInterface.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

/**
 * @notice Helper view methods designed to be called by BridgePool contracts.
 */
interface BridgeAdminInterface {
    event SetDepositContracts(
        uint256 indexed chainId,
        address indexed l2DepositContract,
        address indexed l2MessengerContract
    );
    event SetCrossDomainAdmin(uint256 indexed chainId, address indexed newAdmin);
    event SetRelayIdentifier(bytes32 indexed identifier);
    event SetOptimisticOracleLiveness(uint32 indexed liveness);
    event SetProposerBondPct(uint64 indexed proposerBondPct);
    event WhitelistToken(uint256 chainId, address indexed l1Token, address indexed l2Token, address indexed bridgePool);
    event SetMinimumBridgingDelay(uint256 indexed chainId, uint64 newMinimumBridgingDelay);
    event DepositsEnabled(uint256 indexed chainId, address indexed l2Token, bool depositsEnabled);
    event BridgePoolsAdminTransferred(address[] bridgePools, address indexed newAdmin);

    function finder() external view returns (address);

    struct DepositUtilityContracts {
        address depositContract; // L2 deposit contract where cross-chain relays originate.
        address messengerContract; // L1 helper contract that can send a message to the L2 with the mapped network ID.
    }

    function depositContracts(uint256) external view returns (DepositUtilityContracts memory);

    struct L1TokenRelationships {
        mapping(uint256 => address) l2Tokens; // L2 Chain Id to l2Token address.
        address bridgePool;
    }

    function whitelistedTokens(address, uint256) external view returns (address l2Token, address bridgePool);

    function optimisticOracleLiveness() external view returns (uint32);

    function proposerBondPct() external view returns (uint64);

    function identifier() external view returns (bytes32);
}

File 3 of 22 : BridgePoolInterface.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

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

interface BridgePoolInterface {
    function l1Token() external view returns (IERC20);

    function changeAdmin(address newAdmin) external;
}

File 4 of 22 : SkinnyOptimisticOracleInterface.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../interfaces/OptimisticOracleInterface.sol";

/**
 * @title Interface for the gas-cost-reduced version of the OptimisticOracle.
 * @notice Differences from normal OptimisticOracle:
 * - refundOnDispute: flag is removed, by default there are no refunds on disputes.
 * - customizing request parameters: In the OptimisticOracle, parameters like `bond` and `customLiveness` can be reset
 *   after a request is already made via `requestPrice`. In the SkinnyOptimisticOracle, these parameters can only be
 *   set in `requestPrice`, which has an expanded input set.
 * - settleAndGetPrice: Replaced by `settle`, which can only be called once per settleable request. The resolved price
 *   can be fetched via the `Settle` event or the return value of `settle`.
 * - general changes to interface: Functions that interact with existing requests all require the parameters of the
 *   request to modify to be passed as input. These parameters must match with the existing request parameters or the
 *   function will revert. This change reflects the internal refactor to store hashed request parameters instead of the
 *   full request struct.
 * @dev Interface used by financial contracts to interact with the Oracle. Voters will use a different interface.
 */
abstract contract SkinnyOptimisticOracleInterface {
    // Struct representing a price request. Note that this differs from the OptimisticOracleInterface's Request struct
    // in that refundOnDispute is removed.
    struct Request {
        address proposer; // Address of the proposer.
        address disputer; // Address of the disputer.
        IERC20 currency; // ERC20 token used to pay rewards and fees.
        bool settled; // True if the request is settled.
        int256 proposedPrice; // Price that the proposer submitted.
        int256 resolvedPrice; // Price resolved once the request is settled.
        uint256 expirationTime; // Time at which the request auto-settles without a dispute.
        uint256 reward; // Amount of the currency to pay to the proposer on settlement.
        uint256 finalFee; // Final fee to pay to the Store upon request to the DVM.
        uint256 bond; // Bond that the proposer and disputer must pay on top of the final fee.
        uint256 customLiveness; // Custom liveness value set by the requester.
    }

    // This value must be <= the Voting contract's `ancillaryBytesLimit` value otherwise it is possible
    // that a price can be requested to this contract successfully, but cannot be disputed because the DVM refuses
    // to accept a price request made with ancillary data length over a certain size.
    uint256 public constant ancillaryBytesLimit = 8192;

    /**
     * @notice Requests a new price.
     * @param identifier price identifier being requested.
     * @param timestamp timestamp of the price being requested.
     * @param ancillaryData ancillary data representing additional args being passed with the price request.
     * @param currency ERC20 token used for payment of rewards and fees. Must be approved for use with the DVM.
     * @param reward reward offered to a successful proposer. Will be pulled from the caller. Note: this can be 0,
     *               which could make sense if the contract requests and proposes the value in the same call or
     *               provides its own reward system.
     * @param bond custom proposal bond to set for request. If set to 0, defaults to the final fee.
     * @param customLiveness custom proposal liveness to set for request.
     * @return totalBond default bond + final fee that the proposer and disputer will be required to pay.
     */
    function requestPrice(
        bytes32 identifier,
        uint32 timestamp,
        bytes memory ancillaryData,
        IERC20 currency,
        uint256 reward,
        uint256 bond,
        uint256 customLiveness
    ) external virtual returns (uint256 totalBond);

    /**
     * @notice Proposes a price value on another address' behalf. Note: this address will receive any rewards that come
     * from this proposal. However, any bonds are pulled from the caller.
     * @param requester sender of the initial price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @param request price request parameters whose hash must match the request that the caller wants to
     * propose a price for.
     * @param proposer address to set as the proposer.
     * @param proposedPrice price being proposed.
     * @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to
     * the proposer once settled if the proposal is correct.
     */
    function proposePriceFor(
        address requester,
        bytes32 identifier,
        uint32 timestamp,
        bytes memory ancillaryData,
        Request memory request,
        address proposer,
        int256 proposedPrice
    ) public virtual returns (uint256 totalBond);

    /**
     * @notice Proposes a price value where caller is the proposer.
     * @param requester sender of the initial price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @param request price request parameters whose hash must match the request that the caller wants to
     * propose a price for.
     * @param proposedPrice price being proposed.
     * @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to
     * the proposer once settled if the proposal is correct.
     */
    function proposePrice(
        address requester,
        bytes32 identifier,
        uint32 timestamp,
        bytes memory ancillaryData,
        Request memory request,
        int256 proposedPrice
    ) external virtual returns (uint256 totalBond);

    /**
     * @notice Combines logic of requestPrice and proposePrice while taking advantage of gas savings from not having to
     * overwrite Request params that a normal requestPrice() => proposePrice() flow would entail. Note: The proposer
     * will receive any rewards that come from this proposal. However, any bonds are pulled from the caller.
     * @dev The caller is the requester, but the proposer can be customized.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @param currency ERC20 token used for payment of rewards and fees. Must be approved for use with the DVM.
     * @param reward reward offered to a successful proposer. Will be pulled from the caller. Note: this can be 0,
     *               which could make sense if the contract requests and proposes the value in the same call or
     *               provides its own reward system.
     * @param bond custom proposal bond to set for request. If set to 0, defaults to the final fee.
     * @param customLiveness custom proposal liveness to set for request.
     * @param proposer address to set as the proposer.
     * @param proposedPrice price being proposed.
     * @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to
     * the proposer once settled if the proposal is correct.
     */
    function requestAndProposePriceFor(
        bytes32 identifier,
        uint32 timestamp,
        bytes memory ancillaryData,
        IERC20 currency,
        uint256 reward,
        uint256 bond,
        uint256 customLiveness,
        address proposer,
        int256 proposedPrice
    ) external virtual returns (uint256 totalBond);

    /**
     * @notice Disputes a price request with an active proposal on another address' behalf. Note: this address will
     * receive any rewards that come from this dispute. However, any bonds are pulled from the caller.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @param request price request parameters whose hash must match the request that the caller wants to
     * dispute.
     * @param disputer address to set as the disputer.
     * @param requester sender of the initial price request.
     * @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to
     * the disputer once settled if the dispute was valid (the proposal was incorrect).
     */
    function disputePriceFor(
        bytes32 identifier,
        uint32 timestamp,
        bytes memory ancillaryData,
        Request memory request,
        address disputer,
        address requester
    ) public virtual returns (uint256 totalBond);

    /**
     * @notice Disputes a price request with an active proposal where caller is the disputer.
     * @param requester sender of the initial price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @param request price request parameters whose hash must match the request that the caller wants to
     * dispute.
     * @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to
     * the disputer once settled if the dispute was valid (the proposal was incorrect).
     */
    function disputePrice(
        address requester,
        bytes32 identifier,
        uint32 timestamp,
        bytes memory ancillaryData,
        Request memory request
    ) external virtual returns (uint256 totalBond);

    /**
     * @notice Attempts to settle an outstanding price request. Will revert if it isn't settleable.
     * @param requester sender of the initial price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @param request price request parameters whose hash must match the request that the caller wants to
     * settle.
     * @return payout the amount that the "winner" (proposer or disputer) receives on settlement. This amount includes
     * the returned bonds as well as additional rewards.
     * @return resolvedPrice the price that the request settled to.
     */
    function settle(
        address requester,
        bytes32 identifier,
        uint32 timestamp,
        bytes memory ancillaryData,
        Request memory request
    ) external virtual returns (uint256 payout, int256 resolvedPrice);

    /**
     * @notice Computes the current state of a price request. See the State enum for more details.
     * @param requester sender of the initial price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @param request price request parameters.
     * @return the State.
     */
    function getState(
        address requester,
        bytes32 identifier,
        uint32 timestamp,
        bytes memory ancillaryData,
        Request memory request
    ) external virtual returns (OptimisticOracleInterface.State);

    /**
     * @notice Checks if a given request has resolved, expired or been settled (i.e the optimistic oracle has a price).
     * @param requester sender of the initial price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @param request price request parameters. The hash of these parameters must match with the request hash that is
     * associated with the price request unique ID {requester, identifier, timestamp, ancillaryData}, or this method
     * will revert.
     * @return boolean indicating true if price exists and false if not.
     */
    function hasPrice(
        address requester,
        bytes32 identifier,
        uint32 timestamp,
        bytes memory ancillaryData,
        Request memory request
    ) public virtual returns (bool);

    /**
     * @notice Generates stamped ancillary data in the format that it would be used in the case of a price dispute.
     * @param ancillaryData ancillary data of the price being requested.
     * @param requester sender of the initial price request.
     * @return the stamped ancillary bytes.
     */
    function stampAncillaryData(bytes memory ancillaryData, address requester)
        public
        pure
        virtual
        returns (bytes memory);
}

File 5 of 22 : StoreInterface.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../../common/implementation/FixedPoint.sol";

/**
 * @title Interface that allows financial contracts to pay oracle fees for their use of the system.
 */
interface StoreInterface {
    /**
     * @notice Pays Oracle fees in ETH to the store.
     * @dev To be used by contracts whose margin currency is ETH.
     */
    function payOracleFees() external payable;

    /**
     * @notice Pays oracle fees in the margin currency, erc20Address, to the store.
     * @dev To be used if the margin currency is an ERC20 token rather than ETH.
     * @param erc20Address address of the ERC20 token used to pay the fee.
     * @param amount number of tokens to transfer. An approval for at least this amount must exist.
     */
    function payOracleFeesErc20(address erc20Address, FixedPoint.Unsigned calldata amount) external;

    /**
     * @notice Computes the regular oracle fees that a contract should pay for a period.
     * @param startTime defines the beginning time from which the fee is paid.
     * @param endTime end time until which the fee is paid.
     * @param pfc "profit from corruption", or the maximum amount of margin currency that a
     * token sponsor could extract from the contract through corrupting the price feed in their favor.
     * @return regularFee amount owed for the duration from start to end time for the given pfc.
     * @return latePenalty for paying the fee after the deadline.
     */
    function computeRegularFee(
        uint256 startTime,
        uint256 endTime,
        FixedPoint.Unsigned calldata pfc
    ) external view returns (FixedPoint.Unsigned memory regularFee, FixedPoint.Unsigned memory latePenalty);

    /**
     * @notice Computes the final oracle fees that a contract should pay at settlement.
     * @param currency token used to pay the final fee.
     * @return finalFee amount due.
     */
    function computeFinalFee(address currency) external view returns (FixedPoint.Unsigned memory);
}

File 6 of 22 : FinderInterface.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

/**
 * @title Provides addresses of the live contracts implementing certain interfaces.
 * @dev Examples are the Oracle or Store interfaces.
 */
interface FinderInterface {
    /**
     * @notice Updates the address of the contract that implements `interfaceName`.
     * @param interfaceName bytes32 encoding of the interface name that is either changed or registered.
     * @param implementationAddress address of the deployed contract that implements the interface.
     */
    function changeImplementationAddress(bytes32 interfaceName, address implementationAddress) external;

    /**
     * @notice Gets the address of the contract that implements the given `interfaceName`.
     * @param interfaceName queried interface.
     * @return implementationAddress address of the deployed contract that implements the interface.
     */
    function getImplementationAddress(bytes32 interfaceName) external view returns (address);
}

File 7 of 22 : Constants.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

/**
 * @title Stores common interface names used throughout the DVM by registration in the Finder.
 */
library OracleInterfaces {
    bytes32 public constant Oracle = "Oracle";
    bytes32 public constant IdentifierWhitelist = "IdentifierWhitelist";
    bytes32 public constant Store = "Store";
    bytes32 public constant FinancialContractsAdmin = "FinancialContractsAdmin";
    bytes32 public constant Registry = "Registry";
    bytes32 public constant CollateralWhitelist = "CollateralWhitelist";
    bytes32 public constant OptimisticOracle = "OptimisticOracle";
    bytes32 public constant Bridge = "Bridge";
    bytes32 public constant GenericHandler = "GenericHandler";
    bytes32 public constant SkinnyOptimisticOracle = "SkinnyOptimisticOracle";
}

File 8 of 22 : AncillaryData.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

/**
 * @title Library for encoding and decoding ancillary data for DVM price requests.
 * @notice  We assume that on-chain ancillary data can be formatted directly from bytes to utf8 encoding via
 * web3.utils.hexToUtf8, and that clients will parse the utf8-encoded ancillary data as a comma-delimitted key-value
 * dictionary. Therefore, this library provides internal methods that aid appending to ancillary data from Solidity
 * smart contracts. More details on UMA's ancillary data guidelines below:
 * https://docs.google.com/document/d/1zhKKjgY1BupBGPPrY_WOJvui0B6DMcd-xDR8-9-SPDw/edit
 */
library AncillaryData {
    // This converts the bottom half of a bytes32 input to hex in a highly gas-optimized way.
    // Source: the brilliant implementation at https://gitter.im/ethereum/solidity?at=5840d23416207f7b0ed08c9b.
    function toUtf8Bytes32Bottom(bytes32 bytesIn) private pure returns (bytes32) {
        unchecked {
            uint256 x = uint256(bytesIn);

            // Nibble interleave
            x = x & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff;
            x = (x | (x * 2**64)) & 0x0000000000000000ffffffffffffffff0000000000000000ffffffffffffffff;
            x = (x | (x * 2**32)) & 0x00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff;
            x = (x | (x * 2**16)) & 0x0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff;
            x = (x | (x * 2**8)) & 0x00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff;
            x = (x | (x * 2**4)) & 0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f;

            // Hex encode
            uint256 h = (x & 0x0808080808080808080808080808080808080808080808080808080808080808) / 8;
            uint256 i = (x & 0x0404040404040404040404040404040404040404040404040404040404040404) / 4;
            uint256 j = (x & 0x0202020202020202020202020202020202020202020202020202020202020202) / 2;
            x = x + (h & (i | j)) * 0x27 + 0x3030303030303030303030303030303030303030303030303030303030303030;

            // Return the result.
            return bytes32(x);
        }
    }

    /**
     * @notice Returns utf8-encoded bytes32 string that can be read via web3.utils.hexToUtf8.
     * @dev Will return bytes32 in all lower case hex characters and without the leading 0x.
     * This has minor changes from the toUtf8BytesAddress to control for the size of the input.
     * @param bytesIn bytes32 to encode.
     * @return utf8 encoded bytes32.
     */
    function toUtf8Bytes(bytes32 bytesIn) internal pure returns (bytes memory) {
        return abi.encodePacked(toUtf8Bytes32Bottom(bytesIn >> 128), toUtf8Bytes32Bottom(bytesIn));
    }

    /**
     * @notice Returns utf8-encoded address that can be read via web3.utils.hexToUtf8.
     * Source: https://ethereum.stackexchange.com/questions/8346/convert-address-to-string/8447#8447
     * @dev Will return address in all lower case characters and without the leading 0x.
     * @param x address to encode.
     * @return utf8 encoded address bytes.
     */
    function toUtf8BytesAddress(address x) internal pure returns (bytes memory) {
        return
            abi.encodePacked(toUtf8Bytes32Bottom(bytes32(bytes20(x)) >> 128), bytes8(toUtf8Bytes32Bottom(bytes20(x))));
    }

    /**
     * @notice Converts a uint into a base-10, UTF-8 representation stored in a `string` type.
     * @dev This method is based off of this code: https://stackoverflow.com/a/65707309.
     */
    function toUtf8BytesUint(uint256 x) internal pure returns (bytes memory) {
        if (x == 0) {
            return "0";
        }
        uint256 j = x;
        uint256 len;
        while (j != 0) {
            len++;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint256 k = len;
        while (x != 0) {
            k = k - 1;
            uint8 temp = (48 + uint8(x - (x / 10) * 10));
            bytes1 b1 = bytes1(temp);
            bstr[k] = b1;
            x /= 10;
        }
        return bstr;
    }

    function appendKeyValueBytes32(
        bytes memory currentAncillaryData,
        bytes memory key,
        bytes32 value
    ) internal pure returns (bytes memory) {
        bytes memory prefix = constructPrefix(currentAncillaryData, key);
        return abi.encodePacked(currentAncillaryData, prefix, toUtf8Bytes(value));
    }

    /**
     * @notice Adds "key:value" to `currentAncillaryData` where `value` is an address that first needs to be converted
     * to utf8 bytes. For example, if `utf8(currentAncillaryData)="k1:v1"`, then this function will return
     * `utf8(k1:v1,key:value)`, and if `currentAncillaryData` is blank, then this will return `utf8(key:value)`.
     * @param currentAncillaryData This bytes data should ideally be able to be utf8-decoded, but its OK if not.
     * @param key Again, this bytes data should ideally be able to be utf8-decoded, but its OK if not.
     * @param value An address to set as the value in the key:value pair to append to `currentAncillaryData`.
     * @return Newly appended ancillary data.
     */
    function appendKeyValueAddress(
        bytes memory currentAncillaryData,
        bytes memory key,
        address value
    ) internal pure returns (bytes memory) {
        bytes memory prefix = constructPrefix(currentAncillaryData, key);
        return abi.encodePacked(currentAncillaryData, prefix, toUtf8BytesAddress(value));
    }

    /**
     * @notice Adds "key:value" to `currentAncillaryData` where `value` is a uint that first needs to be converted
     * to utf8 bytes. For example, if `utf8(currentAncillaryData)="k1:v1"`, then this function will return
     * `utf8(k1:v1,key:value)`, and if `currentAncillaryData` is blank, then this will return `utf8(key:value)`.
     * @param currentAncillaryData This bytes data should ideally be able to be utf8-decoded, but its OK if not.
     * @param key Again, this bytes data should ideally be able to be utf8-decoded, but its OK if not.
     * @param value A uint to set as the value in the key:value pair to append to `currentAncillaryData`.
     * @return Newly appended ancillary data.
     */
    function appendKeyValueUint(
        bytes memory currentAncillaryData,
        bytes memory key,
        uint256 value
    ) internal pure returns (bytes memory) {
        bytes memory prefix = constructPrefix(currentAncillaryData, key);
        return abi.encodePacked(currentAncillaryData, prefix, toUtf8BytesUint(value));
    }

    /**
     * @notice Helper method that returns the left hand side of a "key:value" pair plus the colon ":" and a leading
     * comma "," if the `currentAncillaryData` is not empty. The return value is intended to be prepended as a prefix to
     * some utf8 value that is ultimately added to a comma-delimited, key-value dictionary.
     */
    function constructPrefix(bytes memory currentAncillaryData, bytes memory key) internal pure returns (bytes memory) {
        if (currentAncillaryData.length > 0) {
            return abi.encodePacked(",", key, ":");
        } else {
            return abi.encodePacked(key, ":");
        }
    }
}

File 9 of 22 : Testable.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

import "./Timer.sol";

/**
 * @title Base class that provides time overrides, but only if being run in test mode.
 */
abstract contract Testable {
    // If the contract is being run in production, then `timerAddress` will be the 0x0 address.
    // Note: this variable should be set on construction and never modified.
    address public timerAddress;

    /**
     * @notice Constructs the Testable contract. Called by child contracts.
     * @param _timerAddress Contract that stores the current time in a testing environment.
     * Must be set to 0x0 for production environments that use live time.
     */
    constructor(address _timerAddress) {
        timerAddress = _timerAddress;
    }

    /**
     * @notice Reverts if not running in test mode.
     */
    modifier onlyIfTest {
        require(timerAddress != address(0x0));
        _;
    }

    /**
     * @notice Sets the current time.
     * @dev Will revert if not running in test mode.
     * @param time timestamp to set current Testable time to.
     */
    function setCurrentTime(uint256 time) external onlyIfTest {
        Timer(timerAddress).setCurrentTime(time);
    }

    /**
     * @notice Gets the current time. Will return the last time set in `setCurrentTime` if running in test mode.
     * Otherwise, it will return the block timestamp.
     * @return uint for the current Testable timestamp.
     */
    function getCurrentTime() public view virtual returns (uint256) {
        if (timerAddress != address(0x0)) {
            return Timer(timerAddress).getCurrentTime();
        } else {
            return block.timestamp; // solhint-disable-line not-rely-on-time
        }
    }
}

File 10 of 22 : FixedPoint.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/math/SignedSafeMath.sol";

/**
 * @title Library for fixed point arithmetic on uints
 */
library FixedPoint {
    using SafeMath for uint256;
    using SignedSafeMath for int256;

    // Supports 18 decimals. E.g., 1e18 represents "1", 5e17 represents "0.5".
    // For unsigned values:
    //   This can represent a value up to (2^256 - 1)/10^18 = ~10^59. 10^59 will be stored internally as uint256 10^77.
    uint256 private constant FP_SCALING_FACTOR = 10**18;

    // --------------------------------------- UNSIGNED -----------------------------------------------------------------------------
    struct Unsigned {
        uint256 rawValue;
    }

    /**
     * @notice Constructs an `Unsigned` from an unscaled uint, e.g., `b=5` gets stored internally as `5*(10**18)`.
     * @param a uint to convert into a FixedPoint.
     * @return the converted FixedPoint.
     */
    function fromUnscaledUint(uint256 a) internal pure returns (Unsigned memory) {
        return Unsigned(a.mul(FP_SCALING_FACTOR));
    }

    /**
     * @notice Whether `a` is equal to `b`.
     * @param a a FixedPoint.
     * @param b a uint256.
     * @return True if equal, or False.
     */
    function isEqual(Unsigned memory a, uint256 b) internal pure returns (bool) {
        return a.rawValue == fromUnscaledUint(b).rawValue;
    }

    /**
     * @notice Whether `a` is equal to `b`.
     * @param a a FixedPoint.
     * @param b a FixedPoint.
     * @return True if equal, or False.
     */
    function isEqual(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
        return a.rawValue == b.rawValue;
    }

    /**
     * @notice Whether `a` is greater than `b`.
     * @param a a FixedPoint.
     * @param b a FixedPoint.
     * @return True if `a > b`, or False.
     */
    function isGreaterThan(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
        return a.rawValue > b.rawValue;
    }

    /**
     * @notice Whether `a` is greater than `b`.
     * @param a a FixedPoint.
     * @param b a uint256.
     * @return True if `a > b`, or False.
     */
    function isGreaterThan(Unsigned memory a, uint256 b) internal pure returns (bool) {
        return a.rawValue > fromUnscaledUint(b).rawValue;
    }

    /**
     * @notice Whether `a` is greater than `b`.
     * @param a a uint256.
     * @param b a FixedPoint.
     * @return True if `a > b`, or False.
     */
    function isGreaterThan(uint256 a, Unsigned memory b) internal pure returns (bool) {
        return fromUnscaledUint(a).rawValue > b.rawValue;
    }

    /**
     * @notice Whether `a` is greater than or equal to `b`.
     * @param a a FixedPoint.
     * @param b a FixedPoint.
     * @return True if `a >= b`, or False.
     */
    function isGreaterThanOrEqual(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
        return a.rawValue >= b.rawValue;
    }

    /**
     * @notice Whether `a` is greater than or equal to `b`.
     * @param a a FixedPoint.
     * @param b a uint256.
     * @return True if `a >= b`, or False.
     */
    function isGreaterThanOrEqual(Unsigned memory a, uint256 b) internal pure returns (bool) {
        return a.rawValue >= fromUnscaledUint(b).rawValue;
    }

    /**
     * @notice Whether `a` is greater than or equal to `b`.
     * @param a a uint256.
     * @param b a FixedPoint.
     * @return True if `a >= b`, or False.
     */
    function isGreaterThanOrEqual(uint256 a, Unsigned memory b) internal pure returns (bool) {
        return fromUnscaledUint(a).rawValue >= b.rawValue;
    }

    /**
     * @notice Whether `a` is less than `b`.
     * @param a a FixedPoint.
     * @param b a FixedPoint.
     * @return True if `a < b`, or False.
     */
    function isLessThan(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
        return a.rawValue < b.rawValue;
    }

    /**
     * @notice Whether `a` is less than `b`.
     * @param a a FixedPoint.
     * @param b a uint256.
     * @return True if `a < b`, or False.
     */
    function isLessThan(Unsigned memory a, uint256 b) internal pure returns (bool) {
        return a.rawValue < fromUnscaledUint(b).rawValue;
    }

    /**
     * @notice Whether `a` is less than `b`.
     * @param a a uint256.
     * @param b a FixedPoint.
     * @return True if `a < b`, or False.
     */
    function isLessThan(uint256 a, Unsigned memory b) internal pure returns (bool) {
        return fromUnscaledUint(a).rawValue < b.rawValue;
    }

    /**
     * @notice Whether `a` is less than or equal to `b`.
     * @param a a FixedPoint.
     * @param b a FixedPoint.
     * @return True if `a <= b`, or False.
     */
    function isLessThanOrEqual(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
        return a.rawValue <= b.rawValue;
    }

    /**
     * @notice Whether `a` is less than or equal to `b`.
     * @param a a FixedPoint.
     * @param b a uint256.
     * @return True if `a <= b`, or False.
     */
    function isLessThanOrEqual(Unsigned memory a, uint256 b) internal pure returns (bool) {
        return a.rawValue <= fromUnscaledUint(b).rawValue;
    }

    /**
     * @notice Whether `a` is less than or equal to `b`.
     * @param a a uint256.
     * @param b a FixedPoint.
     * @return True if `a <= b`, or False.
     */
    function isLessThanOrEqual(uint256 a, Unsigned memory b) internal pure returns (bool) {
        return fromUnscaledUint(a).rawValue <= b.rawValue;
    }

    /**
     * @notice The minimum of `a` and `b`.
     * @param a a FixedPoint.
     * @param b a FixedPoint.
     * @return the minimum of `a` and `b`.
     */
    function min(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
        return a.rawValue < b.rawValue ? a : b;
    }

    /**
     * @notice The maximum of `a` and `b`.
     * @param a a FixedPoint.
     * @param b a FixedPoint.
     * @return the maximum of `a` and `b`.
     */
    function max(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
        return a.rawValue > b.rawValue ? a : b;
    }

    /**
     * @notice Adds two `Unsigned`s, reverting on overflow.
     * @param a a FixedPoint.
     * @param b a FixedPoint.
     * @return the sum of `a` and `b`.
     */
    function add(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
        return Unsigned(a.rawValue.add(b.rawValue));
    }

    /**
     * @notice Adds an `Unsigned` to an unscaled uint, reverting on overflow.
     * @param a a FixedPoint.
     * @param b a uint256.
     * @return the sum of `a` and `b`.
     */
    function add(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
        return add(a, fromUnscaledUint(b));
    }

    /**
     * @notice Subtracts two `Unsigned`s, reverting on overflow.
     * @param a a FixedPoint.
     * @param b a FixedPoint.
     * @return the difference of `a` and `b`.
     */
    function sub(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
        return Unsigned(a.rawValue.sub(b.rawValue));
    }

    /**
     * @notice Subtracts an unscaled uint256 from an `Unsigned`, reverting on overflow.
     * @param a a FixedPoint.
     * @param b a uint256.
     * @return the difference of `a` and `b`.
     */
    function sub(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
        return sub(a, fromUnscaledUint(b));
    }

    /**
     * @notice Subtracts an `Unsigned` from an unscaled uint256, reverting on overflow.
     * @param a a uint256.
     * @param b a FixedPoint.
     * @return the difference of `a` and `b`.
     */
    function sub(uint256 a, Unsigned memory b) internal pure returns (Unsigned memory) {
        return sub(fromUnscaledUint(a), b);
    }

    /**
     * @notice Multiplies two `Unsigned`s, reverting on overflow.
     * @dev This will "floor" the product.
     * @param a a FixedPoint.
     * @param b a FixedPoint.
     * @return the product of `a` and `b`.
     */
    function mul(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
        // There are two caveats with this computation:
        // 1. Max output for the represented number is ~10^41, otherwise an intermediate value overflows. 10^41 is
        // stored internally as a uint256 ~10^59.
        // 2. Results that can't be represented exactly are truncated not rounded. E.g., 1.4 * 2e-18 = 2.8e-18, which
        // would round to 3, but this computation produces the result 2.
        // No need to use SafeMath because FP_SCALING_FACTOR != 0.
        return Unsigned(a.rawValue.mul(b.rawValue) / FP_SCALING_FACTOR);
    }

    /**
     * @notice Multiplies an `Unsigned` and an unscaled uint256, reverting on overflow.
     * @dev This will "floor" the product.
     * @param a a FixedPoint.
     * @param b a uint256.
     * @return the product of `a` and `b`.
     */
    function mul(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
        return Unsigned(a.rawValue.mul(b));
    }

    /**
     * @notice Multiplies two `Unsigned`s and "ceil's" the product, reverting on overflow.
     * @param a a FixedPoint.
     * @param b a FixedPoint.
     * @return the product of `a` and `b`.
     */
    function mulCeil(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
        uint256 mulRaw = a.rawValue.mul(b.rawValue);
        uint256 mulFloor = mulRaw / FP_SCALING_FACTOR;
        uint256 mod = mulRaw.mod(FP_SCALING_FACTOR);
        if (mod != 0) {
            return Unsigned(mulFloor.add(1));
        } else {
            return Unsigned(mulFloor);
        }
    }

    /**
     * @notice Multiplies an `Unsigned` and an unscaled uint256 and "ceil's" the product, reverting on overflow.
     * @param a a FixedPoint.
     * @param b a FixedPoint.
     * @return the product of `a` and `b`.
     */
    function mulCeil(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
        // Since b is an uint, there is no risk of truncation and we can just mul it normally
        return Unsigned(a.rawValue.mul(b));
    }

    /**
     * @notice Divides one `Unsigned` by an `Unsigned`, reverting on overflow or division by 0.
     * @dev This will "floor" the quotient.
     * @param a a FixedPoint numerator.
     * @param b a FixedPoint denominator.
     * @return the quotient of `a` divided by `b`.
     */
    function div(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
        // There are two caveats with this computation:
        // 1. Max value for the number dividend `a` represents is ~10^41, otherwise an intermediate value overflows.
        // 10^41 is stored internally as a uint256 10^59.
        // 2. Results that can't be represented exactly are truncated not rounded. E.g., 2 / 3 = 0.6 repeating, which
        // would round to 0.666666666666666667, but this computation produces the result 0.666666666666666666.
        return Unsigned(a.rawValue.mul(FP_SCALING_FACTOR).div(b.rawValue));
    }

    /**
     * @notice Divides one `Unsigned` by an unscaled uint256, reverting on overflow or division by 0.
     * @dev This will "floor" the quotient.
     * @param a a FixedPoint numerator.
     * @param b a uint256 denominator.
     * @return the quotient of `a` divided by `b`.
     */
    function div(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
        return Unsigned(a.rawValue.div(b));
    }

    /**
     * @notice Divides one unscaled uint256 by an `Unsigned`, reverting on overflow or division by 0.
     * @dev This will "floor" the quotient.
     * @param a a uint256 numerator.
     * @param b a FixedPoint denominator.
     * @return the quotient of `a` divided by `b`.
     */
    function div(uint256 a, Unsigned memory b) internal pure returns (Unsigned memory) {
        return div(fromUnscaledUint(a), b);
    }

    /**
     * @notice Divides one `Unsigned` by an `Unsigned` and "ceil's" the quotient, reverting on overflow or division by 0.
     * @param a a FixedPoint numerator.
     * @param b a FixedPoint denominator.
     * @return the quotient of `a` divided by `b`.
     */
    function divCeil(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
        uint256 aScaled = a.rawValue.mul(FP_SCALING_FACTOR);
        uint256 divFloor = aScaled.div(b.rawValue);
        uint256 mod = aScaled.mod(b.rawValue);
        if (mod != 0) {
            return Unsigned(divFloor.add(1));
        } else {
            return Unsigned(divFloor);
        }
    }

    /**
     * @notice Divides one `Unsigned` by an unscaled uint256 and "ceil's" the quotient, reverting on overflow or division by 0.
     * @param a a FixedPoint numerator.
     * @param b a uint256 denominator.
     * @return the quotient of `a` divided by `b`.
     */
    function divCeil(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
        // Because it is possible that a quotient gets truncated, we can't just call "Unsigned(a.rawValue.div(b))"
        // similarly to mulCeil with a uint256 as the second parameter. Therefore we need to convert b into an Unsigned.
        // This creates the possibility of overflow if b is very large.
        return divCeil(a, fromUnscaledUint(b));
    }

    /**
     * @notice Raises an `Unsigned` to the power of an unscaled uint256, reverting on overflow. E.g., `b=2` squares `a`.
     * @dev This will "floor" the result.
     * @param a a FixedPoint numerator.
     * @param b a uint256 denominator.
     * @return output is `a` to the power of `b`.
     */
    function pow(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory output) {
        output = fromUnscaledUint(1);
        for (uint256 i = 0; i < b; i = i.add(1)) {
            output = mul(output, a);
        }
    }

    // ------------------------------------------------- SIGNED -------------------------------------------------------------
    // Supports 18 decimals. E.g., 1e18 represents "1", 5e17 represents "0.5".
    // For signed values:
    //   This can represent a value up (or down) to +-(2^255 - 1)/10^18 = ~10^58. 10^58 will be stored internally as int256 10^76.
    int256 private constant SFP_SCALING_FACTOR = 10**18;

    struct Signed {
        int256 rawValue;
    }

    function fromSigned(Signed memory a) internal pure returns (Unsigned memory) {
        require(a.rawValue >= 0, "Negative value provided");
        return Unsigned(uint256(a.rawValue));
    }

    function fromUnsigned(Unsigned memory a) internal pure returns (Signed memory) {
        require(a.rawValue <= uint256(type(int256).max), "Unsigned too large");
        return Signed(int256(a.rawValue));
    }

    /**
     * @notice Constructs a `Signed` from an unscaled int, e.g., `b=5` gets stored internally as `5*(10**18)`.
     * @param a int to convert into a FixedPoint.Signed.
     * @return the converted FixedPoint.Signed.
     */
    function fromUnscaledInt(int256 a) internal pure returns (Signed memory) {
        return Signed(a.mul(SFP_SCALING_FACTOR));
    }

    /**
     * @notice Whether `a` is equal to `b`.
     * @param a a FixedPoint.Signed.
     * @param b a int256.
     * @return True if equal, or False.
     */
    function isEqual(Signed memory a, int256 b) internal pure returns (bool) {
        return a.rawValue == fromUnscaledInt(b).rawValue;
    }

    /**
     * @notice Whether `a` is equal to `b`.
     * @param a a FixedPoint.Signed.
     * @param b a FixedPoint.Signed.
     * @return True if equal, or False.
     */
    function isEqual(Signed memory a, Signed memory b) internal pure returns (bool) {
        return a.rawValue == b.rawValue;
    }

    /**
     * @notice Whether `a` is greater than `b`.
     * @param a a FixedPoint.Signed.
     * @param b a FixedPoint.Signed.
     * @return True if `a > b`, or False.
     */
    function isGreaterThan(Signed memory a, Signed memory b) internal pure returns (bool) {
        return a.rawValue > b.rawValue;
    }

    /**
     * @notice Whether `a` is greater than `b`.
     * @param a a FixedPoint.Signed.
     * @param b an int256.
     * @return True if `a > b`, or False.
     */
    function isGreaterThan(Signed memory a, int256 b) internal pure returns (bool) {
        return a.rawValue > fromUnscaledInt(b).rawValue;
    }

    /**
     * @notice Whether `a` is greater than `b`.
     * @param a an int256.
     * @param b a FixedPoint.Signed.
     * @return True if `a > b`, or False.
     */
    function isGreaterThan(int256 a, Signed memory b) internal pure returns (bool) {
        return fromUnscaledInt(a).rawValue > b.rawValue;
    }

    /**
     * @notice Whether `a` is greater than or equal to `b`.
     * @param a a FixedPoint.Signed.
     * @param b a FixedPoint.Signed.
     * @return True if `a >= b`, or False.
     */
    function isGreaterThanOrEqual(Signed memory a, Signed memory b) internal pure returns (bool) {
        return a.rawValue >= b.rawValue;
    }

    /**
     * @notice Whether `a` is greater than or equal to `b`.
     * @param a a FixedPoint.Signed.
     * @param b an int256.
     * @return True if `a >= b`, or False.
     */
    function isGreaterThanOrEqual(Signed memory a, int256 b) internal pure returns (bool) {
        return a.rawValue >= fromUnscaledInt(b).rawValue;
    }

    /**
     * @notice Whether `a` is greater than or equal to `b`.
     * @param a an int256.
     * @param b a FixedPoint.Signed.
     * @return True if `a >= b`, or False.
     */
    function isGreaterThanOrEqual(int256 a, Signed memory b) internal pure returns (bool) {
        return fromUnscaledInt(a).rawValue >= b.rawValue;
    }

    /**
     * @notice Whether `a` is less than `b`.
     * @param a a FixedPoint.Signed.
     * @param b a FixedPoint.Signed.
     * @return True if `a < b`, or False.
     */
    function isLessThan(Signed memory a, Signed memory b) internal pure returns (bool) {
        return a.rawValue < b.rawValue;
    }

    /**
     * @notice Whether `a` is less than `b`.
     * @param a a FixedPoint.Signed.
     * @param b an int256.
     * @return True if `a < b`, or False.
     */
    function isLessThan(Signed memory a, int256 b) internal pure returns (bool) {
        return a.rawValue < fromUnscaledInt(b).rawValue;
    }

    /**
     * @notice Whether `a` is less than `b`.
     * @param a an int256.
     * @param b a FixedPoint.Signed.
     * @return True if `a < b`, or False.
     */
    function isLessThan(int256 a, Signed memory b) internal pure returns (bool) {
        return fromUnscaledInt(a).rawValue < b.rawValue;
    }

    /**
     * @notice Whether `a` is less than or equal to `b`.
     * @param a a FixedPoint.Signed.
     * @param b a FixedPoint.Signed.
     * @return True if `a <= b`, or False.
     */
    function isLessThanOrEqual(Signed memory a, Signed memory b) internal pure returns (bool) {
        return a.rawValue <= b.rawValue;
    }

    /**
     * @notice Whether `a` is less than or equal to `b`.
     * @param a a FixedPoint.Signed.
     * @param b an int256.
     * @return True if `a <= b`, or False.
     */
    function isLessThanOrEqual(Signed memory a, int256 b) internal pure returns (bool) {
        return a.rawValue <= fromUnscaledInt(b).rawValue;
    }

    /**
     * @notice Whether `a` is less than or equal to `b`.
     * @param a an int256.
     * @param b a FixedPoint.Signed.
     * @return True if `a <= b`, or False.
     */
    function isLessThanOrEqual(int256 a, Signed memory b) internal pure returns (bool) {
        return fromUnscaledInt(a).rawValue <= b.rawValue;
    }

    /**
     * @notice The minimum of `a` and `b`.
     * @param a a FixedPoint.Signed.
     * @param b a FixedPoint.Signed.
     * @return the minimum of `a` and `b`.
     */
    function min(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
        return a.rawValue < b.rawValue ? a : b;
    }

    /**
     * @notice The maximum of `a` and `b`.
     * @param a a FixedPoint.Signed.
     * @param b a FixedPoint.Signed.
     * @return the maximum of `a` and `b`.
     */
    function max(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
        return a.rawValue > b.rawValue ? a : b;
    }

    /**
     * @notice Adds two `Signed`s, reverting on overflow.
     * @param a a FixedPoint.Signed.
     * @param b a FixedPoint.Signed.
     * @return the sum of `a` and `b`.
     */
    function add(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
        return Signed(a.rawValue.add(b.rawValue));
    }

    /**
     * @notice Adds an `Signed` to an unscaled int, reverting on overflow.
     * @param a a FixedPoint.Signed.
     * @param b an int256.
     * @return the sum of `a` and `b`.
     */
    function add(Signed memory a, int256 b) internal pure returns (Signed memory) {
        return add(a, fromUnscaledInt(b));
    }

    /**
     * @notice Subtracts two `Signed`s, reverting on overflow.
     * @param a a FixedPoint.Signed.
     * @param b a FixedPoint.Signed.
     * @return the difference of `a` and `b`.
     */
    function sub(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
        return Signed(a.rawValue.sub(b.rawValue));
    }

    /**
     * @notice Subtracts an unscaled int256 from an `Signed`, reverting on overflow.
     * @param a a FixedPoint.Signed.
     * @param b an int256.
     * @return the difference of `a` and `b`.
     */
    function sub(Signed memory a, int256 b) internal pure returns (Signed memory) {
        return sub(a, fromUnscaledInt(b));
    }

    /**
     * @notice Subtracts an `Signed` from an unscaled int256, reverting on overflow.
     * @param a an int256.
     * @param b a FixedPoint.Signed.
     * @return the difference of `a` and `b`.
     */
    function sub(int256 a, Signed memory b) internal pure returns (Signed memory) {
        return sub(fromUnscaledInt(a), b);
    }

    /**
     * @notice Multiplies two `Signed`s, reverting on overflow.
     * @dev This will "floor" the product.
     * @param a a FixedPoint.Signed.
     * @param b a FixedPoint.Signed.
     * @return the product of `a` and `b`.
     */
    function mul(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
        // There are two caveats with this computation:
        // 1. Max output for the represented number is ~10^41, otherwise an intermediate value overflows. 10^41 is
        // stored internally as an int256 ~10^59.
        // 2. Results that can't be represented exactly are truncated not rounded. E.g., 1.4 * 2e-18 = 2.8e-18, which
        // would round to 3, but this computation produces the result 2.
        // No need to use SafeMath because SFP_SCALING_FACTOR != 0.
        return Signed(a.rawValue.mul(b.rawValue) / SFP_SCALING_FACTOR);
    }

    /**
     * @notice Multiplies an `Signed` and an unscaled int256, reverting on overflow.
     * @dev This will "floor" the product.
     * @param a a FixedPoint.Signed.
     * @param b an int256.
     * @return the product of `a` and `b`.
     */
    function mul(Signed memory a, int256 b) internal pure returns (Signed memory) {
        return Signed(a.rawValue.mul(b));
    }

    /**
     * @notice Multiplies two `Signed`s and "ceil's" the product, reverting on overflow.
     * @param a a FixedPoint.Signed.
     * @param b a FixedPoint.Signed.
     * @return the product of `a` and `b`.
     */
    function mulAwayFromZero(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
        int256 mulRaw = a.rawValue.mul(b.rawValue);
        int256 mulTowardsZero = mulRaw / SFP_SCALING_FACTOR;
        // Manual mod because SignedSafeMath doesn't support it.
        int256 mod = mulRaw % SFP_SCALING_FACTOR;
        if (mod != 0) {
            bool isResultPositive = isLessThan(a, 0) == isLessThan(b, 0);
            int256 valueToAdd = isResultPositive ? int256(1) : int256(-1);
            return Signed(mulTowardsZero.add(valueToAdd));
        } else {
            return Signed(mulTowardsZero);
        }
    }

    /**
     * @notice Multiplies an `Signed` and an unscaled int256 and "ceil's" the product, reverting on overflow.
     * @param a a FixedPoint.Signed.
     * @param b a FixedPoint.Signed.
     * @return the product of `a` and `b`.
     */
    function mulAwayFromZero(Signed memory a, int256 b) internal pure returns (Signed memory) {
        // Since b is an int, there is no risk of truncation and we can just mul it normally
        return Signed(a.rawValue.mul(b));
    }

    /**
     * @notice Divides one `Signed` by an `Signed`, reverting on overflow or division by 0.
     * @dev This will "floor" the quotient.
     * @param a a FixedPoint numerator.
     * @param b a FixedPoint denominator.
     * @return the quotient of `a` divided by `b`.
     */
    function div(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
        // There are two caveats with this computation:
        // 1. Max value for the number dividend `a` represents is ~10^41, otherwise an intermediate value overflows.
        // 10^41 is stored internally as an int256 10^59.
        // 2. Results that can't be represented exactly are truncated not rounded. E.g., 2 / 3 = 0.6 repeating, which
        // would round to 0.666666666666666667, but this computation produces the result 0.666666666666666666.
        return Signed(a.rawValue.mul(SFP_SCALING_FACTOR).div(b.rawValue));
    }

    /**
     * @notice Divides one `Signed` by an unscaled int256, reverting on overflow or division by 0.
     * @dev This will "floor" the quotient.
     * @param a a FixedPoint numerator.
     * @param b an int256 denominator.
     * @return the quotient of `a` divided by `b`.
     */
    function div(Signed memory a, int256 b) internal pure returns (Signed memory) {
        return Signed(a.rawValue.div(b));
    }

    /**
     * @notice Divides one unscaled int256 by an `Signed`, reverting on overflow or division by 0.
     * @dev This will "floor" the quotient.
     * @param a an int256 numerator.
     * @param b a FixedPoint denominator.
     * @return the quotient of `a` divided by `b`.
     */
    function div(int256 a, Signed memory b) internal pure returns (Signed memory) {
        return div(fromUnscaledInt(a), b);
    }

    /**
     * @notice Divides one `Signed` by an `Signed` and "ceil's" the quotient, reverting on overflow or division by 0.
     * @param a a FixedPoint numerator.
     * @param b a FixedPoint denominator.
     * @return the quotient of `a` divided by `b`.
     */
    function divAwayFromZero(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
        int256 aScaled = a.rawValue.mul(SFP_SCALING_FACTOR);
        int256 divTowardsZero = aScaled.div(b.rawValue);
        // Manual mod because SignedSafeMath doesn't support it.
        int256 mod = aScaled % b.rawValue;
        if (mod != 0) {
            bool isResultPositive = isLessThan(a, 0) == isLessThan(b, 0);
            int256 valueToAdd = isResultPositive ? int256(1) : int256(-1);
            return Signed(divTowardsZero.add(valueToAdd));
        } else {
            return Signed(divTowardsZero);
        }
    }

    /**
     * @notice Divides one `Signed` by an unscaled int256 and "ceil's" the quotient, reverting on overflow or division by 0.
     * @param a a FixedPoint numerator.
     * @param b an int256 denominator.
     * @return the quotient of `a` divided by `b`.
     */
    function divAwayFromZero(Signed memory a, int256 b) internal pure returns (Signed memory) {
        // Because it is possible that a quotient gets truncated, we can't just call "Signed(a.rawValue.div(b))"
        // similarly to mulCeil with an int256 as the second parameter. Therefore we need to convert b into an Signed.
        // This creates the possibility of overflow if b is very large.
        return divAwayFromZero(a, fromUnscaledInt(b));
    }

    /**
     * @notice Raises an `Signed` to the power of an unscaled uint256, reverting on overflow. E.g., `b=2` squares `a`.
     * @dev This will "floor" the result.
     * @param a a FixedPoint.Signed.
     * @param b a uint256 (negative exponents are not allowed).
     * @return output is `a` to the power of `b`.
     */
    function pow(Signed memory a, uint256 b) internal pure returns (Signed memory output) {
        output = fromUnscaledInt(1);
        for (uint256 i = 0; i < b; i = i.add(1)) {
            output = mul(output, a);
        }
    }
}

File 11 of 22 : Lockable.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

/**
 * @title A contract that provides modifiers to prevent reentrancy to state-changing and view-only methods. This contract
 * is inspired by https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/ReentrancyGuard.sol
 * and https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol.
 */
contract Lockable {
    bool private _notEntered;

    constructor() {
        // Storing an initial non-zero value makes deployment a bit more expensive, but in exchange the refund on every
        // call to nonReentrant will be lower in amount. Since refunds are capped to a percentage of the total
        // transaction's gas, it is best to keep them low in cases like this one, to increase the likelihood of the full
        // refund coming into effect.
        _notEntered = true;
    }

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

    /**
     * @dev Designed to prevent a view-only method from being re-entered during a call to a `nonReentrant()` state-changing method.
     */
    modifier nonReentrantView() {
        _preEntranceCheck();
        _;
    }

    // Internal methods are used to avoid copying the require statement's bytecode to every `nonReentrant()` method.
    // On entry into a function, `_preEntranceCheck()` should always be called to check if the function is being
    // re-entered. Then, if the function modifies state, it should call `_postEntranceSet()`, perform its logic, and
    // then call `_postEntranceReset()`.
    // View-only methods can simply call `_preEntranceCheck()` to make sure that it is not being re-entered.
    function _preEntranceCheck() internal view {
        // On the first call to nonReentrant, _notEntered will be true
        require(_notEntered, "ReentrancyGuard: reentrant call");
    }

    function _preEntranceSet() internal {
        // Any calls to nonReentrant after this point will fail
        _notEntered = false;
    }

    function _postEntranceReset() internal {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _notEntered = true;
    }
}

File 12 of 22 : MultiCaller.sol
// This contract is taken from Uniswaps's multi call implementation (https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/base/Multicall.sol)
// and was modified to be solidity 0.8 compatible. Additionally, the method was restricted to only work with msg.value
// set to 0 to avoid any nasty attack vectors on function calls that use value sent with deposits.
pragma solidity ^0.8.0;

/// @title MultiCaller
/// @notice Enables calling multiple methods in a single call to the contract
contract MultiCaller {
    function multicall(bytes[] calldata data) external payable returns (bytes[] memory results) {
        require(msg.value == 0, "Only multicall with 0 value");
        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            (bool success, bytes memory result) = address(this).delegatecall(data[i]);

            if (!success) {
                // Next 5 lines from https://ethereum.stackexchange.com/a/83577
                if (result.length < 68) revert();
                assembly {
                    result := add(result, 0x04)
                }
                revert(abi.decode(result, (string)));
            }

            results[i] = result;
        }
    }
}

File 13 of 22 : IERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

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

File 14 of 22 : ERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

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

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

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

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

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

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

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

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

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        _approve(_msgSender(), spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
        _transfer(sender, recipient, amount);

        uint256 currentAllowance = _allowances[sender][_msgSender()];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        _approve(sender, _msgSender(), currentAllowance - amount);

        return true;
    }

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

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

        return true;
    }

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

        _beforeTokenTransfer(sender, recipient, amount);

        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        _balances[sender] = senderBalance - amount;
        _balances[recipient] += amount;

        emit Transfer(sender, recipient, amount);
    }

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

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

        _totalSupply += amount;
        _balances[account] += amount;
        emit Transfer(address(0), account, amount);
    }

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

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

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        _balances[account] = accountBalance - amount;
        _totalSupply -= amount;

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

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

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

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

File 15 of 22 : SafeERC20.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

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

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

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

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
        }
    }
}

File 16 of 22 : Address.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

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

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

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

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

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

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // 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

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

File 17 of 22 : OptimisticOracleInterface.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

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

/**
 * @title Financial contract facing Oracle interface.
 * @dev Interface used by financial contracts to interact with the Oracle. Voters will use a different interface.
 */
abstract contract OptimisticOracleInterface {
    // Struct representing the state of a price request.
    enum State {
        Invalid, // Never requested.
        Requested, // Requested, no other actions taken.
        Proposed, // Proposed, but not expired or disputed yet.
        Expired, // Proposed, not disputed, past liveness.
        Disputed, // Disputed, but no DVM price returned yet.
        Resolved, // Disputed and DVM price is available.
        Settled // Final price has been set in the contract (can get here from Expired or Resolved).
    }

    // Struct representing a price request.
    struct Request {
        address proposer; // Address of the proposer.
        address disputer; // Address of the disputer.
        IERC20 currency; // ERC20 token used to pay rewards and fees.
        bool settled; // True if the request is settled.
        bool refundOnDispute; // True if the requester should be refunded their reward on dispute.
        int256 proposedPrice; // Price that the proposer submitted.
        int256 resolvedPrice; // Price resolved once the request is settled.
        uint256 expirationTime; // Time at which the request auto-settles without a dispute.
        uint256 reward; // Amount of the currency to pay to the proposer on settlement.
        uint256 finalFee; // Final fee to pay to the Store upon request to the DVM.
        uint256 bond; // Bond that the proposer and disputer must pay on top of the final fee.
        uint256 customLiveness; // Custom liveness value set by the requester.
    }

    // This value must be <= the Voting contract's `ancillaryBytesLimit` value otherwise it is possible
    // that a price can be requested to this contract successfully, but cannot be disputed because the DVM refuses
    // to accept a price request made with ancillary data length over a certain size.
    uint256 public constant ancillaryBytesLimit = 8192;

    /**
     * @notice Requests a new price.
     * @param identifier price identifier being requested.
     * @param timestamp timestamp of the price being requested.
     * @param ancillaryData ancillary data representing additional args being passed with the price request.
     * @param currency ERC20 token used for payment of rewards and fees. Must be approved for use with the DVM.
     * @param reward reward offered to a successful proposer. Will be pulled from the caller. Note: this can be 0,
     *               which could make sense if the contract requests and proposes the value in the same call or
     *               provides its own reward system.
     * @return totalBond default bond (final fee) + final fee that the proposer and disputer will be required to pay.
     * This can be changed with a subsequent call to setBond().
     */
    function requestPrice(
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData,
        IERC20 currency,
        uint256 reward
    ) external virtual returns (uint256 totalBond);

    /**
     * @notice Set the proposal bond associated with a price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @param bond custom bond amount to set.
     * @return totalBond new bond + final fee that the proposer and disputer will be required to pay. This can be
     * changed again with a subsequent call to setBond().
     */
    function setBond(
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData,
        uint256 bond
    ) external virtual returns (uint256 totalBond);

    /**
     * @notice Sets the request to refund the reward if the proposal is disputed. This can help to "hedge" the caller
     * in the event of a dispute-caused delay. Note: in the event of a dispute, the winner still receives the other's
     * bond, so there is still profit to be made even if the reward is refunded.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     */
    function setRefundOnDispute(
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData
    ) external virtual;

    /**
     * @notice Sets a custom liveness value for the request. Liveness is the amount of time a proposal must wait before
     * being auto-resolved.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @param customLiveness new custom liveness.
     */
    function setCustomLiveness(
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData,
        uint256 customLiveness
    ) external virtual;

    /**
     * @notice Proposes a price value on another address' behalf. Note: this address will receive any rewards that come
     * from this proposal. However, any bonds are pulled from the caller.
     * @param proposer address to set as the proposer.
     * @param requester sender of the initial price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @param proposedPrice price being proposed.
     * @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to
     * the proposer once settled if the proposal is correct.
     */
    function proposePriceFor(
        address proposer,
        address requester,
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData,
        int256 proposedPrice
    ) public virtual returns (uint256 totalBond);

    /**
     * @notice Proposes a price value for an existing price request.
     * @param requester sender of the initial price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @param proposedPrice price being proposed.
     * @return totalBond the amount that's pulled from the proposer's wallet as a bond. The bond will be returned to
     * the proposer once settled if the proposal is correct.
     */
    function proposePrice(
        address requester,
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData,
        int256 proposedPrice
    ) external virtual returns (uint256 totalBond);

    /**
     * @notice Disputes a price request with an active proposal on another address' behalf. Note: this address will
     * receive any rewards that come from this dispute. However, any bonds are pulled from the caller.
     * @param disputer address to set as the disputer.
     * @param requester sender of the initial price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @return totalBond the amount that's pulled from the caller's wallet as a bond. The bond will be returned to
     * the disputer once settled if the dispute was value (the proposal was incorrect).
     */
    function disputePriceFor(
        address disputer,
        address requester,
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData
    ) public virtual returns (uint256 totalBond);

    /**
     * @notice Disputes a price value for an existing price request with an active proposal.
     * @param requester sender of the initial price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @return totalBond the amount that's pulled from the disputer's wallet as a bond. The bond will be returned to
     * the disputer once settled if the dispute was valid (the proposal was incorrect).
     */
    function disputePrice(
        address requester,
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData
    ) external virtual returns (uint256 totalBond);

    /**
     * @notice Retrieves a price that was previously requested by a caller. Reverts if the request is not settled
     * or settleable. Note: this method is not view so that this call may actually settle the price request if it
     * hasn't been settled.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @return resolved price.
     */
    function settleAndGetPrice(
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData
    ) external virtual returns (int256);

    /**
     * @notice Attempts to settle an outstanding price request. Will revert if it isn't settleable.
     * @param requester sender of the initial price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @return payout the amount that the "winner" (proposer or disputer) receives on settlement. This amount includes
     * the returned bonds as well as additional rewards.
     */
    function settle(
        address requester,
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData
    ) external virtual returns (uint256 payout);

    /**
     * @notice Gets the current data structure containing all information about a price request.
     * @param requester sender of the initial price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @return the Request data structure.
     */
    function getRequest(
        address requester,
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData
    ) public view virtual returns (Request memory);

    /**
     * @notice Returns the state of a price request.
     * @param requester sender of the initial price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @return the State enum value.
     */
    function getState(
        address requester,
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData
    ) public view virtual returns (State);

    /**
     * @notice Checks if a given request has resolved or been settled (i.e the optimistic oracle has a price).
     * @param requester sender of the initial price request.
     * @param identifier price identifier to identify the existing request.
     * @param timestamp timestamp to identify the existing request.
     * @param ancillaryData ancillary data of the price being requested.
     * @return true if price has resolved or settled, false otherwise.
     */
    function hasPrice(
        address requester,
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData
    ) public view virtual returns (bool);

    function stampAncillaryData(bytes memory ancillaryData, address requester)
        public
        view
        virtual
        returns (bytes memory);
}

File 18 of 22 : SafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

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

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

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

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

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

File 19 of 22 : SignedSafeMath.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SignedSafeMath` is no longer needed starting with Solidity 0.8. The compiler
 * now has built in overflow checking.
 */
library SignedSafeMath {
    /**
     * @dev Returns the multiplication of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(int256 a, int256 b) internal pure returns (int256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two signed integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(int256 a, int256 b) internal pure returns (int256) {
        return a / b;
    }

    /**
     * @dev Returns the subtraction of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(int256 a, int256 b) internal pure returns (int256) {
        return a - b;
    }

    /**
     * @dev Returns the addition of two signed integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(int256 a, int256 b) internal pure returns (int256) {
        return a + b;
    }
}

File 20 of 22 : Timer.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;

/**
 * @title Universal store of current contract time for testing environments.
 */
contract Timer {
    uint256 private currentTime;

    constructor() {
        currentTime = block.timestamp; // solhint-disable-line not-rely-on-time
    }

    /**
     * @notice Sets the current time.
     * @dev Will revert if not running in test mode.
     * @param time timestamp to set `currentTime` to.
     */
    function setCurrentTime(uint256 time) external {
        currentTime = time;
    }

    /**
     * @notice Gets the currentTime variable set in the Timer.
     * @return uint256 for the current Testable timestamp.
     */
    function getCurrentTime() public view returns (uint256) {
        return currentTime;
    }
}

File 21 of 22 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

File 22 of 22 : Context.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

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

    function _msgData() internal view virtual returns (bytes calldata) {
        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
        return msg.data;
    }
}

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

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"string","name":"_lpTokenName","type":"string"},{"internalType":"string","name":"_lpTokenSymbol","type":"string"},{"internalType":"address","name":"_bridgeAdmin","type":"address"},{"internalType":"address","name":"_l1Token","type":"address"},{"internalType":"uint64","name":"_lpFeeRatePerSecond","type":"uint64"},{"internalType":"bool","name":"_isWethPool","type":"bool"},{"internalType":"address","name":"_timer","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"BridgePoolAdminTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"depositHash","type":"bytes32"},{"components":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint64","name":"depositId","type":"uint64"},{"internalType":"address payable","name":"l1Recipient","type":"address"},{"internalType":"address","name":"l2Sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint64","name":"slowRelayFeePct","type":"uint64"},{"internalType":"uint64","name":"instantRelayFeePct","type":"uint64"},{"internalType":"uint32","name":"quoteTimestamp","type":"uint32"}],"indexed":false,"internalType":"struct BridgePool.DepositData","name":"depositData","type":"tuple"},{"components":[{"internalType":"enum BridgePool.RelayState","name":"relayState","type":"uint8"},{"internalType":"address","name":"slowRelayer","type":"address"},{"internalType":"uint32","name":"relayId","type":"uint32"},{"internalType":"uint64","name":"realizedLpFeePct","type":"uint64"},{"internalType":"uint32","name":"priceRequestTime","type":"uint32"},{"internalType":"uint256","name":"proposerBond","type":"uint256"},{"internalType":"uint256","name":"finalFee","type":"uint256"}],"indexed":false,"internalType":"struct BridgePool.RelayData","name":"relay","type":"tuple"},{"indexed":false,"internalType":"bytes32","name":"relayAncillaryDataHash","type":"bytes32"}],"name":"DepositRelayed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokensMinted","type":"uint256"},{"indexed":true,"internalType":"address","name":"liquidityProvider","type":"address"}],"name":"LiquidityAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lpTokensBurnt","type":"uint256"},{"indexed":true,"internalType":"address","name":"liquidityProvider","type":"address"}],"name":"LiquidityRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"depositHash","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"relayHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"disputer","type":"address"}],"name":"RelayCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"depositHash","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"relayHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"disputer","type":"address"}],"name":"RelayDisputed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"depositHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"components":[{"internalType":"enum BridgePool.RelayState","name":"relayState","type":"uint8"},{"internalType":"address","name":"slowRelayer","type":"address"},{"internalType":"uint32","name":"relayId","type":"uint32"},{"internalType":"uint64","name":"realizedLpFeePct","type":"uint64"},{"internalType":"uint32","name":"priceRequestTime","type":"uint32"},{"internalType":"uint256","name":"proposerBond","type":"uint256"},{"internalType":"uint256","name":"finalFee","type":"uint256"}],"indexed":false,"internalType":"struct BridgePool.RelayData","name":"relay","type":"tuple"}],"name":"RelaySettled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"depositHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"instantRelayer","type":"address"},{"components":[{"internalType":"enum BridgePool.RelayState","name":"relayState","type":"uint8"},{"internalType":"address","name":"slowRelayer","type":"address"},{"internalType":"uint32","name":"relayId","type":"uint32"},{"internalType":"uint64","name":"realizedLpFeePct","type":"uint64"},{"internalType":"uint32","name":"priceRequestTime","type":"uint32"},{"internalType":"uint256","name":"proposerBond","type":"uint256"},{"internalType":"uint256","name":"finalFee","type":"uint256"}],"indexed":false,"internalType":"struct BridgePool.RelayData","name":"relay","type":"tuple"}],"name":"RelaySpedUp","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"uint256","name":"l1TokenAmount","type":"uint256"}],"name":"addLiquidity","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bonds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bridgeAdmin","outputs":[{"internalType":"contract BridgeAdminInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newAdmin","type":"address"}],"name":"changeAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint64","name":"depositId","type":"uint64"},{"internalType":"address payable","name":"l1Recipient","type":"address"},{"internalType":"address","name":"l2Sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint64","name":"slowRelayFeePct","type":"uint64"},{"internalType":"uint64","name":"instantRelayFeePct","type":"uint64"},{"internalType":"uint32","name":"quoteTimestamp","type":"uint32"}],"internalType":"struct BridgePool.DepositData","name":"depositData","type":"tuple"},{"components":[{"internalType":"enum BridgePool.RelayState","name":"relayState","type":"uint8"},{"internalType":"address","name":"slowRelayer","type":"address"},{"internalType":"uint32","name":"relayId","type":"uint32"},{"internalType":"uint64","name":"realizedLpFeePct","type":"uint64"},{"internalType":"uint32","name":"priceRequestTime","type":"uint32"},{"internalType":"uint256","name":"proposerBond","type":"uint256"},{"internalType":"uint256","name":"finalFee","type":"uint256"}],"internalType":"struct BridgePool.RelayData","name":"relayData","type":"tuple"}],"name":"disputeRelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exchangeRateCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAccumulatedFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"relayedAmount","type":"uint256"}],"name":"getLiquidityUtilization","outputs":[{"internalType":"uint256","name":"utilizationCurrent","type":"uint256"},{"internalType":"uint256","name":"utilizationPostRelay","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint64","name":"depositId","type":"uint64"},{"internalType":"address payable","name":"l1Recipient","type":"address"},{"internalType":"address","name":"l2Sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint64","name":"slowRelayFeePct","type":"uint64"},{"internalType":"uint64","name":"instantRelayFeePct","type":"uint64"},{"internalType":"uint32","name":"quoteTimestamp","type":"uint32"}],"internalType":"struct BridgePool.DepositData","name":"depositData","type":"tuple"},{"components":[{"internalType":"enum BridgePool.RelayState","name":"relayState","type":"uint8"},{"internalType":"address","name":"slowRelayer","type":"address"},{"internalType":"uint32","name":"relayId","type":"uint32"},{"internalType":"uint64","name":"realizedLpFeePct","type":"uint64"},{"internalType":"uint32","name":"priceRequestTime","type":"uint32"},{"internalType":"uint256","name":"proposerBond","type":"uint256"},{"internalType":"uint256","name":"finalFee","type":"uint256"}],"internalType":"struct BridgePool.RelayData","name":"relayData","type":"tuple"}],"name":"getRelayAncillaryData","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"identifier","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"instantRelays","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isWethPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"l1Token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastLpFeeUpdate","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidityUtilizationCurrent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"relayedAmount","type":"uint256"}],"name":"liquidityUtilizationPostRelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lpFeeRatePerSecond","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numberOfRelays","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optimisticOracle","outputs":[{"internalType":"contract SkinnyOptimisticOracleInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"optimisticOracleLiveness","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proposerBondPct","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint64","name":"depositId","type":"uint64"},{"internalType":"address payable","name":"l1Recipient","type":"address"},{"internalType":"address","name":"l2Sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint64","name":"slowRelayFeePct","type":"uint64"},{"internalType":"uint64","name":"instantRelayFeePct","type":"uint64"},{"internalType":"uint32","name":"quoteTimestamp","type":"uint32"}],"internalType":"struct BridgePool.DepositData","name":"depositData","type":"tuple"},{"internalType":"uint64","name":"realizedLpFeePct","type":"uint64"}],"name":"relayAndSpeedUp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint64","name":"depositId","type":"uint64"},{"internalType":"address payable","name":"l1Recipient","type":"address"},{"internalType":"address","name":"l2Sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint64","name":"slowRelayFeePct","type":"uint64"},{"internalType":"uint64","name":"instantRelayFeePct","type":"uint64"},{"internalType":"uint32","name":"quoteTimestamp","type":"uint32"}],"internalType":"struct BridgePool.DepositData","name":"depositData","type":"tuple"},{"internalType":"uint64","name":"realizedLpFeePct","type":"uint64"}],"name":"relayDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"relays","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"lpTokenAmount","type":"uint256"},{"internalType":"bool","name":"sendEth","type":"bool"}],"name":"removeLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"time","type":"uint256"}],"name":"setCurrentTime","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint64","name":"depositId","type":"uint64"},{"internalType":"address payable","name":"l1Recipient","type":"address"},{"internalType":"address","name":"l2Sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint64","name":"slowRelayFeePct","type":"uint64"},{"internalType":"uint64","name":"instantRelayFeePct","type":"uint64"},{"internalType":"uint32","name":"quoteTimestamp","type":"uint32"}],"internalType":"struct BridgePool.DepositData","name":"depositData","type":"tuple"},{"components":[{"internalType":"enum BridgePool.RelayState","name":"relayState","type":"uint8"},{"internalType":"address","name":"slowRelayer","type":"address"},{"internalType":"uint32","name":"relayId","type":"uint32"},{"internalType":"uint64","name":"realizedLpFeePct","type":"uint64"},{"internalType":"uint32","name":"priceRequestTime","type":"uint32"},{"internalType":"uint256","name":"proposerBond","type":"uint256"},{"internalType":"uint256","name":"finalFee","type":"uint256"}],"internalType":"struct BridgePool.RelayData","name":"relayData","type":"tuple"}],"name":"settleRelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint64","name":"depositId","type":"uint64"},{"internalType":"address payable","name":"l1Recipient","type":"address"},{"internalType":"address","name":"l2Sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint64","name":"slowRelayFeePct","type":"uint64"},{"internalType":"uint64","name":"instantRelayFeePct","type":"uint64"},{"internalType":"uint32","name":"quoteTimestamp","type":"uint32"}],"internalType":"struct BridgePool.DepositData","name":"depositData","type":"tuple"},{"components":[{"internalType":"enum BridgePool.RelayState","name":"relayState","type":"uint8"},{"internalType":"address","name":"slowRelayer","type":"address"},{"internalType":"uint32","name":"relayId","type":"uint32"},{"internalType":"uint64","name":"realizedLpFeePct","type":"uint64"},{"internalType":"uint32","name":"priceRequestTime","type":"uint32"},{"internalType":"uint256","name":"proposerBond","type":"uint256"},{"internalType":"uint256","name":"finalFee","type":"uint256"}],"internalType":"struct BridgePool.RelayData","name":"relayData","type":"tuple"}],"name":"speedUpRelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"store","outputs":[{"internalType":"contract StoreInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"syncUmaEcosystemParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"syncWithBridgeAdminParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"timerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"undistributedLpFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"utilizedReserves","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]

60806040523480156200001157600080fd5b506040516200521c3803806200521c833981016040819052620000349162000864565b600080546001600160a01b0319166001600160a01b038316179055865187908790879087908790879087908790879062000076906004906020850190620006ab565b5080516200008c906005906020840190620006ab565b50506006805460ff1916600117905550865115801590620000ad5750855115155b620000ff5760405162461bcd60e51b815260206004820152601b60248201527f426164204c5020746f6b656e206e616d65206f722073796d626f6c000000000060448201526064015b60405180910390fd5b600e80546001600160a01b0319166001600160a01b038781169190911790915560068054610100600160a81b03191661010092871692909202919091179055620001464290565b600a8054610100600160681b031916690100000000000000000063ffffffff9390931692909202610100600160481b031916919091176101006001600160401b038616021760ff19168315151790556200019f620001bd565b620001a962000443565b505050505050505050505050505062000a38565b620001c762000657565b620001d76006805460ff19169055565b600e5460408051632e68f21360e21b815290516000926001600160a01b03169163b9a3c84c916004808301926020929190829003018186803b1580156200021d57600080fd5b505afa15801562000232573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000258919062000931565b6040516302abf57960e61b81527f536b696e6e794f7074696d69737469634f7261636c650000000000000000000060048201529091506001600160a01b0382169063aafd5e409060240160206040518083038186803b158015620002bb57600080fd5b505afa158015620002d0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002f6919062000931565b601080546001600160a01b0319166001600160a01b039283161790556040516302abf57960e61b81526453746f726560d81b60048201529082169063aafd5e409060240160206040518083038186803b1580156200035357600080fd5b505afa15801562000368573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200038e919062000931565b600f80546001600160a01b0319166001600160a01b03928316908117909155600654604051635b97aadd60e01b8152610100909104909216600483015290635b97aadd9060240160206040518083038186803b158015620003ee57600080fd5b505afa15801562000403573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000429919062000956565b51600b5550620004416006805460ff19166001179055565b565b6200044d62000657565b6200045d6006805460ff19169055565b600e60009054906101000a90046001600160a01b03166001600160a01b031663c73a32c36040518163ffffffff1660e01b815260040160206040518083038186803b158015620004ac57600080fd5b505afa158015620004c1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620004e791906200099b565b600a600d6101000a8154816001600160401b0302191690836001600160401b03160217905550600e60009054906101000a90046001600160a01b03166001600160a01b031663173684c56040518163ffffffff1660e01b815260040160206040518083038186803b1580156200055c57600080fd5b505afa15801562000571573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620005979190620009b9565b600a60156101000a81548163ffffffff021916908363ffffffff160217905550600e60009054906101000a90046001600160a01b03166001600160a01b0316637998a1c46040518163ffffffff1660e01b815260040160206040518083038186803b1580156200060657600080fd5b505afa1580156200061b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620006419190620009e1565b601155620004416006805460ff19166001179055565b60065460ff16620004415760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401620000f6565b828054620006b990620009fb565b90600052602060002090601f016020900481019282620006dd576000855562000728565b82601f10620006f857805160ff191683800117855562000728565b8280016001018555821562000728579182015b82811115620007285782518255916020019190600101906200070b565b50620007369291506200073a565b5090565b5b808211156200073657600081556001016200073b565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171562000792576200079262000751565b604052919050565b600082601f830112620007ac57600080fd5b81516001600160401b03811115620007c857620007c862000751565b6020620007de601f8301601f1916820162000767565b8281528582848701011115620007f357600080fd5b60005b8381101562000813578581018301518282018401528201620007f6565b83811115620008255760008385840101525b5095945050505050565b80516001600160a01b03811681146200084757600080fd5b919050565b80516001600160401b03811681146200084757600080fd5b600080600080600080600060e0888a0312156200088057600080fd5b87516001600160401b03808211156200089857600080fd5b620008a68b838c016200079a565b985060208a0151915080821115620008bd57600080fd5b50620008cc8a828b016200079a565b965050620008dd604089016200082f565b9450620008ed606089016200082f565b9350620008fd608089016200084c565b925060a088015180151581146200091357600080fd5b91506200092360c089016200082f565b905092959891949750929550565b6000602082840312156200094457600080fd5b6200094f826200082f565b9392505050565b6000602082840312156200096957600080fd5b604051602081016001600160401b03811182821017156200098e576200098e62000751565b6040529151825250919050565b600060208284031215620009ae57600080fd5b6200094f826200084c565b600060208284031215620009cc57600080fd5b815163ffffffff811681146200094f57600080fd5b600060208284031215620009f457600080fd5b5051919050565b600181811c9082168062000a1057607f821691505b6020821081141562000a3257634e487b7160e01b600052602260045260246000fd5b50919050565b6147d48062000a486000396000f3fe6080604052600436106102cd5760003560e01c8063753b91bb11610175578063b5351ee2116100dc578063cefed55f11610095578063df738fc81161006f578063df738fc8146108c7578063ed4de3a3146108f4578063f1d24bab14610929578063fff6cae91461094d57600080fd5b8063cefed55f1461083d578063d412f5a41461085d578063dd62ed3e1461088157600080fd5b8063b5351ee21461078d578063bd6d894d146107a2578063bec73ade146107b7578063c01e1bd6146107d7578063c73a32c3146107fc578063cc2c929e1461082357600080fd5b8063a457c2d71161012e578063a457c2d7146106c1578063a6af2dfe146106e1578063a9059cbb14610701578063ac9650d814610721578063b208420214610741578063b454e3261461075757600080fd5b8063753b91bb146106205780637998a1c41461064057806387a515d3146106565780638f2839701461066c57806395d89b411461068c578063975057e7146106a157600080fd5b806323b872dd116102345780634464fae4116101ed5780635df45a37116101c75780635df45a371461059f57806362822d34146105b457806366db5240146105d457806370a08231146105ea57600080fd5b80634464fae4146105565780634f52fd171461056c57806351c6590a1461058c57600080fd5b806323b872dd146104b257806329cb924d146104d2578063313ce567146104e557806339509351146105015780633cc400b3146105215780633fa856c91461053657600080fd5b806318160ddd1161028657806318160ddd146103f057806319e9d894146104055780631bf71c381461041a5780631c39c38d1461043a578063223029221461047257806322f8e5661461049257600080fd5b806306fdde03146102d9578063095ea7b31461030457806311cfc159146103345780631311172514610371578063135c404e14610393578063173684c5146103b757600080fd5b366102d457005b600080fd5b3480156102e557600080fd5b506102ee610962565b6040516102fb9190613b3d565b60405180910390f35b34801561031057600080fd5b5061032461031f366004613b70565b6109f4565b60405190151581526020016102fb565b34801561034057600080fd5b50600a546103599061010090046001600160401b031681565b6040516001600160401b0390911681526020016102fb565b34801561037d57600080fd5b5061039161038c366004613cd5565b610a0b565b005b34801561039f57600080fd5b506103a9600d5481565b6040519081526020016102fb565b3480156103c357600080fd5b50600a546103db90600160a81b900463ffffffff1681565b60405163ffffffff90911681526020016102fb565b3480156103fc57600080fd5b506003546103a9565b34801561041157600080fd5b506103a9610cfd565b34801561042657600080fd5b506102ee610435366004613d0f565b610d26565b34801561044657600080fd5b5060005461045a906001600160a01b031681565b6040516001600160a01b0390911681526020016102fb565b34801561047e57600080fd5b5060105461045a906001600160a01b031681565b34801561049e57600080fd5b506103916104ad366004613dc6565b610d49565b3480156104be57600080fd5b506103246104cd366004613ddf565b610dbf565b3480156104de57600080fd5b50426103a9565b3480156104f157600080fd5b50604051601281526020016102fb565b34801561050d57600080fd5b5061032461051c366004613b70565b610e70565b34801561052d57600080fd5b50610391610ea7565b34801561054257600080fd5b50610391610551366004613d0f565b61109c565b34801561056257600080fd5b506103a9600c5481565b34801561057857600080fd5b50610391610587366004613e2e565b6114f9565b61039161059a366004613dc6565b611672565b3480156105ab57600080fd5b506103a9611819565b3480156105c057600080fd5b506103a96105cf366004613dc6565b611830565b3480156105e057600080fd5b506103a960085481565b3480156105f657600080fd5b506103a9610605366004613e53565b6001600160a01b031660009081526001602052604090205490565b34801561062c57600080fd5b5061039161063b366004613cd5565b61185a565b34801561064c57600080fd5b506103a960115481565b34801561066257600080fd5b506103a960095481565b34801561067857600080fd5b50610391610687366004613e53565b611ca9565b34801561069857600080fd5b506102ee611d30565b3480156106ad57600080fd5b50600f5461045a906001600160a01b031681565b3480156106cd57600080fd5b506103246106dc366004613b70565b611d3f565b3480156106ed57600080fd5b50600e5461045a906001600160a01b031681565b34801561070d57600080fd5b5061032461071c366004613b70565b611dda565b61073461072f366004613e70565b611de7565b6040516102fb9190613ee4565b34801561074d57600080fd5b506103a960075481565b34801561076357600080fd5b5061045a610772366004613dc6565b6013602052600090815260409020546001600160a01b031681565b34801561079957600080fd5b50610391611f8c565b3480156107ae57600080fd5b506103a96121e4565b3480156107c357600080fd5b506103916107d2366004613d0f565b6121fe565b3480156107e357600080fd5b5060065461045a9061010090046001600160a01b031681565b34801561080857600080fd5b50600a5461035990600160681b90046001600160401b031681565b34801561082f57600080fd5b50600a546103249060ff1681565b34801561084957600080fd5b50610391610858366004613d0f565b6123eb565b34801561086957600080fd5b506006546103db90600160a81b900463ffffffff1681565b34801561088d57600080fd5b506103a961089c366004613f46565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205490565b3480156108d357600080fd5b506103a96108e2366004613dc6565b60126020526000908152604090205481565b34801561090057600080fd5b5061091461090f366004613dc6565b6125d1565b604080519283526020830191909152016102fb565b34801561093557600080fd5b50600a546103db90600160481b900463ffffffff1681565b34801561095957600080fd5b50610391612608565b60606004805461097190613f74565b80601f016020809104026020016040519081016040528092919081815260200182805461099d90613f74565b80156109ea5780601f106109bf576101008083540402835291602001916109ea565b820191906000526020600020905b8154815290600101906020018083116109cd57829003601f168201915b5050505050905090565b6000610a01338484612628565b5060015b92915050565b610a1361274d565b610a1b61279f565b6703782dace9d900008260a001516001600160401b031611158015610a5557506703782dace9d900008260c001516001600160401b031611155b8015610a7257506706f05b59d3b20000816001600160401b031611155b610ab25760405162461bcd60e51b815260206004820152600c60248201526b496e76616c6964206665657360a01b60448201526064015b60405180910390fd5b6000610abd836127ab565b60008181526012602052604090205490915015610b135760405162461bcd60e51b815260206004820152601460248201527350656e64696e672072656c61792065786973747360601b6044820152606401610aa9565b60004290506000610b2785608001516127ee565b905060006040518060e0016040528060016002811115610b4957610b49613faf565b815233602082015260068054604090920191600160a81b900463ffffffff16906015610b7483613fdb565b91906101000a81548163ffffffff021916908363ffffffff16021790555063ffffffff168152602001866001600160401b031681526020018463ffffffff168152602001838152602001600b548152509050610bcf8161280e565b600085815260126020526040812091909155610beb8783612821565b90508660800151600954600754610c029190613fff565b1015610c4c5760405162461bcd60e51b8152602060048201526019602482015278496e73756666696369656e7420706f6f6c2062616c616e636560381b6044820152606401610aa9565b6000600b5484610c5c9190614016565b9050876080015160096000828254610c749190614016565b9250508190555080600d6000828254610c8d9190614016565b9091555050600654610caf9061010090046001600160a01b0316333084612875565b857fa4ca36d112520cced74325c72711f376fe4015665829d879ba21590cb8130be0898585604051610ce393929190614138565b60405180910390a2505050505050610cf96128e6565b5050565b6000610d0761274d565b610d0f61279f565b610d1960006128f5565b9050610d236128e6565b90565b6060610d3061274d565b610d42610d3d8484612821565b612983565b9392505050565b6000546001600160a01b0316610d5e57600080fd5b60005460405163117c72b360e11b8152600481018390526001600160a01b03909116906322f8e56690602401600060405180830381600087803b158015610da457600080fd5b505af1158015610db8573d6000803e3d6000fd5b5050505050565b6000610dcc8484846129c0565b6001600160a01b038416600090815260026020908152604080832033845290915290205482811015610e515760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b6064820152608401610aa9565b610e658533610e608685613fff565b612628565b506001949350505050565b3360008181526002602090815260408083206001600160a01b03871684529091528120549091610a01918590610e60908690614016565b610eaf61274d565b610eb761279f565b600e60009054906101000a90046001600160a01b03166001600160a01b031663c73a32c36040518163ffffffff1660e01b815260040160206040518083038186803b158015610f0557600080fd5b505afa158015610f19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3d9190614164565b600a600d6101000a8154816001600160401b0302191690836001600160401b03160217905550600e60009054906101000a90046001600160a01b03166001600160a01b031663173684c56040518163ffffffff1660e01b815260040160206040518083038186803b158015610fb157600080fd5b505afa158015610fc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe99190614181565b600a60156101000a81548163ffffffff021916908363ffffffff160217905550600e60009054906101000a90046001600160a01b03166001600160a01b0316637998a1c46040518163ffffffff1660e01b815260040160206040518083038186803b15801561105757600080fd5b505afa15801561106b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108f919061419e565b60115561109a6128e6565b565b6110a461274d565b6110ac61279f565b60006110b7836127ab565b90506110c38183612b98565b6001825160028111156110d8576110d8613faf565b146111175760405162461bcd60e51b815260206004820152600f60248201526e105b1c9958591e481cd95d1d1b1959608a1b6044820152606401610aa9565b6000600a60159054906101000a900463ffffffff16836080015161113b91906141b7565b9050428163ffffffff1611156111885760405162461bcd60e51b8152602060048201526012602482015271139bdd081cd95d1d1b1958589b19481e595d60721b6044820152606401610aa9565b82602001516001600160a01b0316336001600160a01b031614806111bc57506111b3816103846141b7565b63ffffffff1642115b6111fb5760405162461bcd60e51b815260206004820152601060248201526f2737ba1039b637bb903932b630bcb2b960811b6044820152606401610aa9565b6040805160e0810190915261126f90806002815260200185602001516001600160a01b03168152602001856040015163ffffffff16815260200185606001516001600160401b03168152602001856080015163ffffffff1681526020018560a0015181526020018560c0015181525061280e565b60008381526012602052604081209190915560a085015160608501516112a291611298916141df565b8660800151612c25565b85608001516112b19190613fff565b905060006112bf8486612c4d565b600081815260136020526040902054600a549192506001600160a01b03169060ff1680156112f457506001600160a01b038116155b1561130c57611307876040015184612c78565b611341565b6113416001600160a01b038216611327578760400151611329565b815b60065461010090046001600160a01b03169085612d41565b60006113558860a001518960800151612c25565b905060008760a001518860c0015161136d9190614016565b60208901519091506001600160a01b03163314156113b55760208801516113b0906113988385614016565b60065461010090046001600160a01b03169190612d41565b6113f3565b60208801516006546113d7916101009091046001600160a01b03169083612d41565b6006546113f39061010090046001600160a01b03163384612d41565b60006113ff8387614016565b90508960800151600960008282546114179190613fff565b9250508190555080600760008282546114309190613fff565b9250508190555080600860008282546114499190614201565b9250508190555081600d60008282546114629190613fff565b909155506114709050612d71565b61148a6114858a606001518c60800151612c25565b612db5565b336001600160a01b0316887fcfdda74fce9fedb259e0f0a1ab1550e19b338488ece64976a4639e7fce0293a78b6040516114c49190614242565b60405180910390a350505060009182525060136020526040902080546001600160a01b031916905550610cf991506128e69050565b61150161274d565b61150961279f565b8015806115185750600a5460ff165b6115545760405162461bcd60e51b815260206004820152600d60248201526c086c2dce840e6cadcc840cae8d609b1b6044820152606401610aa9565b6000670de0b6b3a7640000611567612de8565b6115719085614250565b61157b9190614285565b90508060095461158b9190614016565b60075410156115dc5760405162461bcd60e51b815260206004820152601e60248201527f5574696c697a6174696f6e20746f6f206869676820746f2072656d6f766500006044820152606401610aa9565b6115e63384612e88565b80600760008282546115f89190613fff565b909155505081156116125761160d3382612c78565b61162e565b60065461162e9061010090046001600160a01b03163383612d41565b604080518281526020810185905233917f0c54fc223ffd1a8f36652b5e83db4fff50f5ae151b11ceb56d5499b9f6e1fa18910160405180910390a250610cf96128e6565b61167a61274d565b61168261279f565b600a5460ff16801561169357508034145b8061169c575034155b6116e85760405162461bcd60e51b815260206004820152601b60248201527f42616420616464206c6971756964697479204574682076616c756500000000006044820152606401610aa9565b60006116f2612de8565b61170483670de0b6b3a7640000614250565b61170e9190614285565b905061171a3382612fd7565b816007600082825461172c9190614016565b909155505034158015906117425750600a5460ff165b156117b557600660019054906101000a90046001600160a01b03166001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561179757600080fd5b505af11580156117ab573d6000803e3d6000fd5b50505050506117d2565b6006546117d29061010090046001600160a01b0316333085612875565b604080518381526020810183905233917f0351f600ef1e31e5e13b4dc27bff4cbde3e9269f0ffc666629ae6cac573eb220910160405180910390a2506118166128e6565b50565b600061182361274d565b61182b6130b6565b905090565b600061183a61274d565b61184261279f565b61184b826128f5565b90506118556128e6565b919050565b61186261274d565b61186a61279f565b60004290506703782dace9d900008360a001516001600160401b0316111580156118a957506703782dace9d900008360c001516001600160401b031611155b80156118c657506706f05b59d3b20000826001600160401b031611155b6119015760405162461bcd60e51b815260206004820152600c60248201526b496e76616c6964206665657360a01b6044820152606401610aa9565b600061190c846127ab565b600081815260126020526040902054909150156119625760405162461bcd60e51b815260206004820152601460248201527350656e64696e672072656c61792065786973747360601b6044820152606401610aa9565b600061197185608001516127ee565b905060006040518060e001604052806001600281111561199357611993613faf565b815233602082015260068054604090920191600160a81b900463ffffffff169060156119be83613fdb565b91906101000a81548163ffffffff021916908363ffffffff16021790555063ffffffff168152602001866001600160401b031681526020018563ffffffff168152602001838152602001600b5481525090506000611a1c8783612821565b9050611a278261280e565b600085815260126020526040812091909155611a438584612c4d565b6000818152601360205260409020549091506001600160a01b031615611aa55760405162461bcd60e51b8152602060048201526017602482015276052656c61792063616e6e6f74206265207370656420757604c1b6044820152606401610aa9565b8760800151600954600754611aba9190613fff565b1015611b045760405162461bcd60e51b8152602060048201526019602482015278496e73756666696369656e7420706f6f6c2062616c616e636560381b6044820152606401610aa9565b6000600b5485611b149190614016565b90506000611b488a60c001518b60a001518760600151611b3491906141df565b611b3e91906141df565b8b60800151612c25565b90506000818b60800151611b5c9190613fff565b905082600d6000828254611b709190614016565b909155505060808b015160098054600090611b8c908490614016565b9091555050600084815260136020526040902080546001600160a01b03191633908117909155611bda9030611bc18685614016565b60065461010090046001600160a01b0316929190612875565b600a5460ff1615611bf857611bf38b6040015182612c78565b611c1a565b60408b0151600654611c1a916101009091046001600160a01b03169083612d41565b877fa4ca36d112520cced74325c72711f376fe4015665829d879ba21590cb8130be08c8888604051611c4e93929190614138565b60405180910390a2336001600160a01b0316887ff98cddc88bc965917007822b05056cb92bc9ddf0f8bcc61678400cb78313bb4b88604051611c909190614242565b60405180910390a3505050505050505050610cf96128e6565b611cb161274d565b611cb961279f565b600e546001600160a01b03163314611cd057600080fd5b600e80546001600160a01b0319166001600160a01b0383169081179091556040805133815260208101929092527f485a12424bd0c2c66a131c2681cb6c743b9573af3ae5f3014ef6ce7f55ab0192910160405180910390a16118166128e6565b60606005805461097190613f74565b3360009081526002602090815260408083206001600160a01b038616845290915281205482811015611dc15760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610aa9565b611dd03385610e608685613fff565b5060019392505050565b6000610a013384846129c0565b60603415611e375760405162461bcd60e51b815260206004820152601b60248201527f4f6e6c79206d756c746963616c6c207769746820302076616c756500000000006044820152606401610aa9565b816001600160401b03811115611e4f57611e4f613b9c565b604051908082528060200260200182016040528015611e8257816020015b6060815260200190600190039081611e6d5790505b50905060005b82811015611f855760008030868685818110611ea657611ea66142a7565b9050602002810190611eb891906142bd565b604051611ec692919061430a565b600060405180830381855af49150503d8060008114611f01576040519150601f19603f3d011682016040523d82523d6000602084013e611f06565b606091505b509150915081611f5257604481511015611f1f57600080fd5b60048101905080806020019051810190611f39919061431a565b60405162461bcd60e51b8152600401610aa99190613b3d565b80848481518110611f6557611f656142a7565b602002602001018190525050508080611f7d906143bb565b915050611e88565b5092915050565b611f9461274d565b611f9c61279f565b600e5460408051632e68f21360e21b815290516000926001600160a01b03169163b9a3c84c916004808301926020929190829003018186803b158015611fe157600080fd5b505afa158015611ff5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061201991906143d6565b6040516302abf57960e61b815275536b696e6e794f7074696d69737469634f7261636c6560501b60048201529091506001600160a01b0382169063aafd5e409060240160206040518083038186803b15801561207457600080fd5b505afa158015612088573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120ac91906143d6565b601080546001600160a01b0319166001600160a01b039283161790556040516302abf57960e61b81526453746f726560d81b60048201529082169063aafd5e409060240160206040518083038186803b15801561210857600080fd5b505afa15801561211c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214091906143d6565b600f80546001600160a01b0319166001600160a01b03928316908117909155600654604051635b97aadd60e01b8152610100909104909216600483015290635b97aadd9060240160206040518083038186803b15801561219f57600080fd5b505afa1580156121b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121d791906143f3565b51600b555061109a6128e6565b60006121ee61274d565b6121f661279f565b610d19612de8565b61220661274d565b61220e61279f565b6000612219836127ab565b90506122258183612b98565b60006122318284612c4d565b9050600a60159054906101000a900463ffffffff16836080015161225591906141b7565b63ffffffff164210801561227b575060018351600281111561227957612279613faf565b145b801561229c57506000818152601360205260409020546001600160a01b0316155b6122e25760405162461bcd60e51b8152602060048201526017602482015276052656c61792063616e6e6f74206265207370656420757604c1b6044820152606401610aa9565b600081815260136020526040812080546001600160a01b0319163317905560c085015160a08601516060860151612327929161231d916141df565b61129891906141df565b9050600081866080015161233b9190613fff565b600a5490915060ff1615612379576006546123669061010090046001600160a01b0316333084612875565b612374866040015182612c78565b61239d565b604086015160065461239d916101009091046001600160a01b031690339084612875565b336001600160a01b0316847ff98cddc88bc965917007822b05056cb92bc9ddf0f8bcc61678400cb78313bb4b876040516123d79190614242565b60405180910390a350505050610cf96128e6565b6123f361274d565b6123fb61279f565b42600a54608083015161241b91600160a81b900463ffffffff16906141b7565b63ffffffff161161245e5760405162461bcd60e51b815260206004820152600d60248201526c50617374206c6976656e65737360981b6044820152606401610aa9565b60018151600281111561247357612473613faf565b146124b15760405162461bcd60e51b815260206004820152600e60248201526d4e6f742064697370757461626c6560901b6044820152606401610aa9565b60006124bc836127ab565b90506124c88183612b98565b60006124d48484612821565b905060006124f98460200151338660a001518760c001516124f487612983565b613125565b90508360a001518460c0015161250f9190614016565b600d60008282546125209190613fff565b909155505060808501516009805460009061253c908490613fff565b9091555050600083815260126020526040812055801561259057336125608561280e565b60405185907f29751133c2d0a0ea7a9da312248b12ef10ac4c20f4e3ab5eebbb80d853b3836790600090a46125c6565b3361259a8561280e565b60405185907f14173c4d3bd95e34f5c8f78729323444c9272bd21051e70045b64ed48f65818390600090a45b505050610cf96128e6565b6000806125dc61274d565b6125e461279f565b6125ee60006128f5565b6125f7846128f5565b915091506126036128e6565b915091565b61261061274d565b61261861279f565b61262061344a565b61109a6128e6565b6001600160a01b03831661268a5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610aa9565b6001600160a01b0382166126eb5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610aa9565b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b60065460ff1661109a5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610aa9565b6006805460ff19169055565b6006546040516000916127d191849161010090046001600160a01b031690602001614434565b604051602081830303815290604052805190602001209050919050565b600a54600090610a0590600160681b90046001600160401b031683612c25565b6000816040516020016127d19190614242565b60008282604001518360600151600660019054906101000a90046001600160a01b0316604051602001612857949392919061445c565b60405160208183030381529060405280519060200120905092915050565b6040516001600160a01b03808516602483015283166044820152606481018290526128e09085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261350d565b50505050565b6006805460ff19166001179055565b60006128ff61344a565b60008060085413612911576000612915565b6008545b9050600081600954856129289190614016565b6129329190614016565b90506000826007546129449190614016565b90508061295d5750670de0b6b3a7640000949350505050565b8061297083670de0b6b3a7640000614250565b61297a9190614285565b95945050505050565b6060610a0560405180602001604052806000815250604051806040016040528060098152602001680e4cad8c2f290c2e6d60bb1b815250846135df565b6001600160a01b038316612a245760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610aa9565b6001600160a01b038216612a865760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610aa9565b6001600160a01b03831660009081526001602052604090205481811015612afe5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610aa9565b612b088282613fff565b6001600160a01b038086166000908152600160205260408082209390935590851681529081208054849290612b3e908490614016565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051612b8a91815260200190565b60405180910390a350505050565b612ba18161280e565b60008381526012602052604090205414610cf9576040805162461bcd60e51b81526020600482015260248101919091527f4861736865642072656c617920706172616d7320646f206e6f74206d6174636860448201527f206578697374696e672072656c6179206861736820666f72206465706f7369746064820152608401610aa9565b6000670de0b6b3a7640000612c43836001600160401b038616614250565b610d429190614285565b60008282606001516040516020016128579291909182526001600160401b0316602082015260400190565b6001600160a01b0382163b15612ca457600654610cf99061010090046001600160a01b03168383612d41565b600654604051632e1a7d4d60e01b8152600481018390526101009091046001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015612cee57600080fd5b505af1158015612d02573d6000803e3d6000fd5b50506040516001600160a01b038516925083156108fc02915083906000818181858888f19350505050158015612d3c573d6000803e3d6000fd5b505050565b6040516001600160a01b038316602482015260448101829052612d3c90849063a9059cbb60e01b906064016128a9565b6000612d7b6130b6565b905080600c54612d8b9190613fff565b600c5550600a80546cffffffff0000000000000000001916600160481b4263ffffffff1602179055565b80600c6000828254612dc79190614016565b925050819055508060086000828254612de09190614201565b909155505050565b6000612df360035490565b612e045750670de0b6b3a764000090565b612e0c612d71565b612e1461344a565b6000600c54600754612e269190613fff565b905060006008541315612e4757600854612e409082614016565b9050612e63565b600854612e56906000196144a1565b612e609082613fff565b90505b600354612e7882670de0b6b3a7640000614250565b612e829190614285565b91505090565b6001600160a01b038216612ee85760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610aa9565b6001600160a01b03821660009081526001602052604090205481811015612f5c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610aa9565b612f668282613fff565b6001600160a01b03841660009081526001602052604081209190915560038054849290612f94908490613fff565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001612740565b6001600160a01b03821661302d5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610aa9565b806003600082825461303f9190614016565b90915550506001600160a01b0382166000908152600160205260408120805483929061306c908490614016565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b600a546000908190670de0b6b3a764000090600160481b900463ffffffff16426130e09190613fff565b600a54600c546130fe9161010090046001600160401b031690614250565b6131089190614250565b6131129190614285565b9050600c54811061185557600c54612e82565b6000806131328585614016565b6010546006549192506131579161010090046001600160a01b03908116911683613625565b6010546011546001600160a01b039091169063af355d1e904286600660019054906101000a90046001600160a01b031660008b600a60159054906101000a900463ffffffff168f670de0b6b3a76400006040518a63ffffffff1660e01b81526004016131cb99989796959493929190614526565b602060405180830381600087803b1580156131e557600080fd5b505af1925050508015613215575060408051601f3d908101601f191682019092526132129181019061419e565b60015b613262576006546132359061010090046001600160a01b03168883612d41565b601054600654613258916001600160a01b03610100909204821691166000613625565b600091505061297a565b818110156132bd5760006132768284613fff565b6006549091506132959061010090046001600160a01b03168a83612d41565b6010546006546132b8916001600160a01b03610100909204821691166000613625565b819250505b5060408051610160810182526001600160a01b03898116825260006020830181905260065461010090049091169282019290925260608101829052670de0b6b3a7640000608082015260a08101829052600a5460c0820190600160a81b900463ffffffff164261332d9190614016565b8152600060208201526040016133438885613fff565b815260208101889052600a54600160a81b900463ffffffff90811660409092019190915260065491925061338c9161010090046001600160a01b03169033903090869061287516565b6010546006546133ae916001600160a01b036101009092048216911684613625565b60105460115460405163139c641960e31b81526001600160a01b0390921691639ce320c8916133e9914290899087908e903090600401614591565b602060405180830381600087803b15801561340357600080fd5b505af1158015613417573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061343b919061419e565b50600198975050505050505050565b600d546006546040516370a0823160e01b81523060048201526000929161010090046001600160a01b0316906370a082319060240160206040518083038186803b15801561349757600080fd5b505afa1580156134ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134cf919061419e565b6134d99190613fff565b9050600754811115611816576007546134f29082613fff565b60086000828254613503919061468a565b9091555050600755565b6000613562826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166137499092919063ffffffff16565b805190915015612d3c578080602001905181019061358091906146c9565b612d3c5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610aa9565b606060006135ed8585613760565b905084816135fa856137a2565b60405160200161360c939291906146e6565b6040516020818303038152906040529150509392505050565b8015806136ae5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e9060440160206040518083038186803b15801561367457600080fd5b505afa158015613688573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136ac919061419e565b155b6137195760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610aa9565b6040516001600160a01b038316602482015260448101829052612d3c90849063095ea7b360e01b906064016128a9565b606061375884846000856137e2565b949350505050565b815160609015613791578160405160200161377b9190614729565b6040516020818303038152906040529050610a05565b8160405160200161377b919061475d565b60606137b1608083901c61390a565b6137ba8361390a565b6040805160208101939093528201526060016040516020818303038152906040529050919050565b6060824710156138435760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610aa9565b843b6138915760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610aa9565b600080866001600160a01b031685876040516138ad9190614782565b60006040518083038185875af1925050503d80600081146138ea576040519150601f19603f3d011682016040523d82523d6000602084013e6138ef565b606091505b50915091506138ff828286613aac565b979650505050505050565b6000808260001c9050806fffffffffffffffffffffffffffffffff169050806801000000000000000002811777ffffffffffffffff0000000000000000ffffffffffffffff169050806401000000000281177bffffffff00000000ffffffff00000000ffffffff00000000ffffffff16905080620100000281177dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff169050806101000281177eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff1690508060100281177f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f16905060006008827f08080808080808080808080808080808080808080808080808080808080808081681613a2e57613a2e61426f565b0460047f040404040404040404040404040404040404040404040404040404040404040484160460027f020202020202020202020202020202020202020202020202020202020202020285160417166027029091017f3030303030303030303030303030303030303030303030303030303030303030019392505050565b60608315613abb575081610d42565b825115613acb5782518084602001fd5b8160405162461bcd60e51b8152600401610aa99190613b3d565b60005b83811015613b00578181015183820152602001613ae8565b838111156128e05750506000910152565b60008151808452613b29816020860160208601613ae5565b601f01601f19169290920160200192915050565b602081526000610d426020830184613b11565b6001600160a01b038116811461181657600080fd5b803561185581613b50565b60008060408385031215613b8357600080fd5b8235613b8e81613b50565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b0381118282101715613bd457613bd4613b9c565b60405290565b6001600160401b038116811461181657600080fd5b803561185581613bda565b63ffffffff8116811461181657600080fd5b803561185581613bfa565b6000610100808385031215613c2b57600080fd5b604051908101906001600160401b0382118183101715613c4d57613c4d613b9c565b816040528092508335815260208401359150613c6882613bda565b816020820152613c7a60408501613b65565b6040820152613c8b60608501613b65565b606082015260808401356080820152613ca660a08501613bef565b60a0820152613cb760c08501613bef565b60c0820152613cc860e08501613c0c565b60e0820152505092915050565b6000806101208385031215613ce957600080fd5b613cf38484613c17565b9150610100830135613d0481613bda565b809150509250929050565b6000808284036101e0811215613d2457600080fd5b613d2e8585613c17565b925060e060ff1982011215613d4257600080fd5b50613d4b613bb2565b61010084013560038110613d5e57600080fd5b8152613d6d6101208501613b65565b6020820152613d7f6101408501613c0c565b6040820152613d916101608501613bef565b6060820152613da36101808501613c0c565b60808201526101a084013560a08201526101c09093013560c08401525092909150565b600060208284031215613dd857600080fd5b5035919050565b600080600060608486031215613df457600080fd5b8335613dff81613b50565b92506020840135613e0f81613b50565b929592945050506040919091013590565b801515811461181657600080fd5b60008060408385031215613e4157600080fd5b823591506020830135613d0481613e20565b600060208284031215613e6557600080fd5b8135610d4281613b50565b60008060208385031215613e8357600080fd5b82356001600160401b0380821115613e9a57600080fd5b818501915085601f830112613eae57600080fd5b813581811115613ebd57600080fd5b8660208260051b8501011115613ed257600080fd5b60209290920196919550909350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613f3957603f19888603018452613f27858351613b11565b94509285019290850190600101613f0b565b5092979650505050505050565b60008060408385031215613f5957600080fd5b8235613f6481613b50565b91506020830135613d0481613b50565b600181811c90821680613f8857607f821691505b60208210811415613fa957634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600063ffffffff80831681811415613ff557613ff5613fc5565b6001019392505050565b60008282101561401157614011613fc5565b500390565b6000821982111561402957614029613fc5565b500190565b8051825260208101516001600160401b0380821660208501526040830151915060018060a01b03808316604086015280606085015116606086015250608083015160808501528060a08401511660a08501528060c08401511660c0850152505063ffffffff60e08201511660e08301525050565b8051600381106140c257634e487b7160e01b600052602160045260246000fd5b82526020818101516001600160a01b031690830152604080820151906140ef9084018263ffffffff169052565b50606081015161410a60608401826001600160401b03169052565b506080810151614122608084018263ffffffff169052565b5060a0818101519083015260c090810151910152565b6102008101614147828661402e565b6141556101008301856140a2565b826101e0830152949350505050565b60006020828403121561417657600080fd5b8151610d4281613bda565b60006020828403121561419357600080fd5b8151610d4281613bfa565b6000602082840312156141b057600080fd5b5051919050565b600063ffffffff8083168185168083038211156141d6576141d6613fc5565b01949350505050565b60006001600160401b038083168185168083038211156141d6576141d6613fc5565b600080821280156001600160ff1b038490038513161561422357614223613fc5565b600160ff1b839003841281161561423c5761423c613fc5565b50500190565b60e08101610a0582846140a2565b600081600019048311821515161561426a5761426a613fc5565b500290565b634e487b7160e01b600052601260045260246000fd5b6000826142a257634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b6000808335601e198436030181126142d457600080fd5b8301803591506001600160401b038211156142ee57600080fd5b60200191503681900382131561430357600080fd5b9250929050565b8183823760009101908152919050565b60006020828403121561432c57600080fd5b81516001600160401b038082111561434357600080fd5b818401915084601f83011261435757600080fd5b81518181111561436957614369613b9c565b604051601f8201601f19908116603f0116810190838211818310171561439157614391613b9c565b816040528281528760208487010111156143aa57600080fd5b6138ff836020830160208801613ae5565b60006000198214156143cf576143cf613fc5565b5060010190565b6000602082840312156143e857600080fd5b8151610d4281613b50565b60006020828403121561440557600080fd5b604051602081018181106001600160401b038211171561442757614427613b9c565b6040529151825250919050565b6101208101614443828561402e565b6001600160a01b03929092166101009190910152919050565b610160810161446b828761402e565b63ffffffff949094166101008201526001600160401b03929092166101208301526001600160a01b031661014090910152919050565b60006001600160ff1b03818413828413808216868404861116156144c7576144c7613fc5565b600160ff1b60008712828116878305891216156144e6576144e6613fc5565b6000871292508782058712848416161561450257614502613fc5565b8785058712818416161561451857614518613fc5565b505050929093029392505050565b60006101208b835263ffffffff808c16602085015281604085015261454d8285018c613b11565b6001600160a01b039a8b166060860152608085019990995260a084019790975250509290931660c083015290931660e0840152610100909201919091529392505050565b600061020088835263ffffffff881660208401528060408401526145b781840188613b11565b9150506145d06060830186516001600160a01b03169052565b60208501516001600160a01b03811660808401525060408501516001600160a01b03811660a084015250606085015180151560c084015250608085015160e083015260a0850151610100818185015260c08701519150610120828186015260e0880151925061014083818701528289015161016087015281890151610180870152808901516101a0870152505050506146756101c08301856001600160a01b03169052565b6001600160a01b0383166101e08301526138ff565b60008083128015600160ff1b8501841216156146a8576146a8613fc5565b6001600160ff1b03840183138116156146c3576146c3613fc5565b50500390565b6000602082840312156146db57600080fd5b8151610d4281613e20565b600084516146f8818460208901613ae5565b84519083019061470c818360208901613ae5565b845191019061471f818360208801613ae5565b0195945050505050565b600b60fa1b815260008251614745816001850160208701613ae5565b601d60f91b6001939091019283015250600201919050565b6000825161476f818460208701613ae5565b601d60f91b920191825250600101919050565b60008251614794818460208701613ae5565b919091019291505056fea264697066735822122000780274c57d26a2ae42dfdd378b533920fa72509ae195cb371a6f6544115ffc64736f6c6343000809003300000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000012000000000000000000000000030b44c676a05f1264d1de9cc31db5f2a945186b6000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000000015d3ef7980000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e4163726f73732057455448204c500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000009412d574554482d4c500000000000000000000000000000000000000000000000

Deployed Bytecode

0x6080604052600436106102cd5760003560e01c8063753b91bb11610175578063b5351ee2116100dc578063cefed55f11610095578063df738fc81161006f578063df738fc8146108c7578063ed4de3a3146108f4578063f1d24bab14610929578063fff6cae91461094d57600080fd5b8063cefed55f1461083d578063d412f5a41461085d578063dd62ed3e1461088157600080fd5b8063b5351ee21461078d578063bd6d894d146107a2578063bec73ade146107b7578063c01e1bd6146107d7578063c73a32c3146107fc578063cc2c929e1461082357600080fd5b8063a457c2d71161012e578063a457c2d7146106c1578063a6af2dfe146106e1578063a9059cbb14610701578063ac9650d814610721578063b208420214610741578063b454e3261461075757600080fd5b8063753b91bb146106205780637998a1c41461064057806387a515d3146106565780638f2839701461066c57806395d89b411461068c578063975057e7146106a157600080fd5b806323b872dd116102345780634464fae4116101ed5780635df45a37116101c75780635df45a371461059f57806362822d34146105b457806366db5240146105d457806370a08231146105ea57600080fd5b80634464fae4146105565780634f52fd171461056c57806351c6590a1461058c57600080fd5b806323b872dd146104b257806329cb924d146104d2578063313ce567146104e557806339509351146105015780633cc400b3146105215780633fa856c91461053657600080fd5b806318160ddd1161028657806318160ddd146103f057806319e9d894146104055780631bf71c381461041a5780631c39c38d1461043a578063223029221461047257806322f8e5661461049257600080fd5b806306fdde03146102d9578063095ea7b31461030457806311cfc159146103345780631311172514610371578063135c404e14610393578063173684c5146103b757600080fd5b366102d457005b600080fd5b3480156102e557600080fd5b506102ee610962565b6040516102fb9190613b3d565b60405180910390f35b34801561031057600080fd5b5061032461031f366004613b70565b6109f4565b60405190151581526020016102fb565b34801561034057600080fd5b50600a546103599061010090046001600160401b031681565b6040516001600160401b0390911681526020016102fb565b34801561037d57600080fd5b5061039161038c366004613cd5565b610a0b565b005b34801561039f57600080fd5b506103a9600d5481565b6040519081526020016102fb565b3480156103c357600080fd5b50600a546103db90600160a81b900463ffffffff1681565b60405163ffffffff90911681526020016102fb565b3480156103fc57600080fd5b506003546103a9565b34801561041157600080fd5b506103a9610cfd565b34801561042657600080fd5b506102ee610435366004613d0f565b610d26565b34801561044657600080fd5b5060005461045a906001600160a01b031681565b6040516001600160a01b0390911681526020016102fb565b34801561047e57600080fd5b5060105461045a906001600160a01b031681565b34801561049e57600080fd5b506103916104ad366004613dc6565b610d49565b3480156104be57600080fd5b506103246104cd366004613ddf565b610dbf565b3480156104de57600080fd5b50426103a9565b3480156104f157600080fd5b50604051601281526020016102fb565b34801561050d57600080fd5b5061032461051c366004613b70565b610e70565b34801561052d57600080fd5b50610391610ea7565b34801561054257600080fd5b50610391610551366004613d0f565b61109c565b34801561056257600080fd5b506103a9600c5481565b34801561057857600080fd5b50610391610587366004613e2e565b6114f9565b61039161059a366004613dc6565b611672565b3480156105ab57600080fd5b506103a9611819565b3480156105c057600080fd5b506103a96105cf366004613dc6565b611830565b3480156105e057600080fd5b506103a960085481565b3480156105f657600080fd5b506103a9610605366004613e53565b6001600160a01b031660009081526001602052604090205490565b34801561062c57600080fd5b5061039161063b366004613cd5565b61185a565b34801561064c57600080fd5b506103a960115481565b34801561066257600080fd5b506103a960095481565b34801561067857600080fd5b50610391610687366004613e53565b611ca9565b34801561069857600080fd5b506102ee611d30565b3480156106ad57600080fd5b50600f5461045a906001600160a01b031681565b3480156106cd57600080fd5b506103246106dc366004613b70565b611d3f565b3480156106ed57600080fd5b50600e5461045a906001600160a01b031681565b34801561070d57600080fd5b5061032461071c366004613b70565b611dda565b61073461072f366004613e70565b611de7565b6040516102fb9190613ee4565b34801561074d57600080fd5b506103a960075481565b34801561076357600080fd5b5061045a610772366004613dc6565b6013602052600090815260409020546001600160a01b031681565b34801561079957600080fd5b50610391611f8c565b3480156107ae57600080fd5b506103a96121e4565b3480156107c357600080fd5b506103916107d2366004613d0f565b6121fe565b3480156107e357600080fd5b5060065461045a9061010090046001600160a01b031681565b34801561080857600080fd5b50600a5461035990600160681b90046001600160401b031681565b34801561082f57600080fd5b50600a546103249060ff1681565b34801561084957600080fd5b50610391610858366004613d0f565b6123eb565b34801561086957600080fd5b506006546103db90600160a81b900463ffffffff1681565b34801561088d57600080fd5b506103a961089c366004613f46565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205490565b3480156108d357600080fd5b506103a96108e2366004613dc6565b60126020526000908152604090205481565b34801561090057600080fd5b5061091461090f366004613dc6565b6125d1565b604080519283526020830191909152016102fb565b34801561093557600080fd5b50600a546103db90600160481b900463ffffffff1681565b34801561095957600080fd5b50610391612608565b60606004805461097190613f74565b80601f016020809104026020016040519081016040528092919081815260200182805461099d90613f74565b80156109ea5780601f106109bf576101008083540402835291602001916109ea565b820191906000526020600020905b8154815290600101906020018083116109cd57829003601f168201915b5050505050905090565b6000610a01338484612628565b5060015b92915050565b610a1361274d565b610a1b61279f565b6703782dace9d900008260a001516001600160401b031611158015610a5557506703782dace9d900008260c001516001600160401b031611155b8015610a7257506706f05b59d3b20000816001600160401b031611155b610ab25760405162461bcd60e51b815260206004820152600c60248201526b496e76616c6964206665657360a01b60448201526064015b60405180910390fd5b6000610abd836127ab565b60008181526012602052604090205490915015610b135760405162461bcd60e51b815260206004820152601460248201527350656e64696e672072656c61792065786973747360601b6044820152606401610aa9565b60004290506000610b2785608001516127ee565b905060006040518060e0016040528060016002811115610b4957610b49613faf565b815233602082015260068054604090920191600160a81b900463ffffffff16906015610b7483613fdb565b91906101000a81548163ffffffff021916908363ffffffff16021790555063ffffffff168152602001866001600160401b031681526020018463ffffffff168152602001838152602001600b548152509050610bcf8161280e565b600085815260126020526040812091909155610beb8783612821565b90508660800151600954600754610c029190613fff565b1015610c4c5760405162461bcd60e51b8152602060048201526019602482015278496e73756666696369656e7420706f6f6c2062616c616e636560381b6044820152606401610aa9565b6000600b5484610c5c9190614016565b9050876080015160096000828254610c749190614016565b9250508190555080600d6000828254610c8d9190614016565b9091555050600654610caf9061010090046001600160a01b0316333084612875565b857fa4ca36d112520cced74325c72711f376fe4015665829d879ba21590cb8130be0898585604051610ce393929190614138565b60405180910390a2505050505050610cf96128e6565b5050565b6000610d0761274d565b610d0f61279f565b610d1960006128f5565b9050610d236128e6565b90565b6060610d3061274d565b610d42610d3d8484612821565b612983565b9392505050565b6000546001600160a01b0316610d5e57600080fd5b60005460405163117c72b360e11b8152600481018390526001600160a01b03909116906322f8e56690602401600060405180830381600087803b158015610da457600080fd5b505af1158015610db8573d6000803e3d6000fd5b5050505050565b6000610dcc8484846129c0565b6001600160a01b038416600090815260026020908152604080832033845290915290205482811015610e515760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b6064820152608401610aa9565b610e658533610e608685613fff565b612628565b506001949350505050565b3360008181526002602090815260408083206001600160a01b03871684529091528120549091610a01918590610e60908690614016565b610eaf61274d565b610eb761279f565b600e60009054906101000a90046001600160a01b03166001600160a01b031663c73a32c36040518163ffffffff1660e01b815260040160206040518083038186803b158015610f0557600080fd5b505afa158015610f19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3d9190614164565b600a600d6101000a8154816001600160401b0302191690836001600160401b03160217905550600e60009054906101000a90046001600160a01b03166001600160a01b031663173684c56040518163ffffffff1660e01b815260040160206040518083038186803b158015610fb157600080fd5b505afa158015610fc5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fe99190614181565b600a60156101000a81548163ffffffff021916908363ffffffff160217905550600e60009054906101000a90046001600160a01b03166001600160a01b0316637998a1c46040518163ffffffff1660e01b815260040160206040518083038186803b15801561105757600080fd5b505afa15801561106b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061108f919061419e565b60115561109a6128e6565b565b6110a461274d565b6110ac61279f565b60006110b7836127ab565b90506110c38183612b98565b6001825160028111156110d8576110d8613faf565b146111175760405162461bcd60e51b815260206004820152600f60248201526e105b1c9958591e481cd95d1d1b1959608a1b6044820152606401610aa9565b6000600a60159054906101000a900463ffffffff16836080015161113b91906141b7565b9050428163ffffffff1611156111885760405162461bcd60e51b8152602060048201526012602482015271139bdd081cd95d1d1b1958589b19481e595d60721b6044820152606401610aa9565b82602001516001600160a01b0316336001600160a01b031614806111bc57506111b3816103846141b7565b63ffffffff1642115b6111fb5760405162461bcd60e51b815260206004820152601060248201526f2737ba1039b637bb903932b630bcb2b960811b6044820152606401610aa9565b6040805160e0810190915261126f90806002815260200185602001516001600160a01b03168152602001856040015163ffffffff16815260200185606001516001600160401b03168152602001856080015163ffffffff1681526020018560a0015181526020018560c0015181525061280e565b60008381526012602052604081209190915560a085015160608501516112a291611298916141df565b8660800151612c25565b85608001516112b19190613fff565b905060006112bf8486612c4d565b600081815260136020526040902054600a549192506001600160a01b03169060ff1680156112f457506001600160a01b038116155b1561130c57611307876040015184612c78565b611341565b6113416001600160a01b038216611327578760400151611329565b815b60065461010090046001600160a01b03169085612d41565b60006113558860a001518960800151612c25565b905060008760a001518860c0015161136d9190614016565b60208901519091506001600160a01b03163314156113b55760208801516113b0906113988385614016565b60065461010090046001600160a01b03169190612d41565b6113f3565b60208801516006546113d7916101009091046001600160a01b03169083612d41565b6006546113f39061010090046001600160a01b03163384612d41565b60006113ff8387614016565b90508960800151600960008282546114179190613fff565b9250508190555080600760008282546114309190613fff565b9250508190555080600860008282546114499190614201565b9250508190555081600d60008282546114629190613fff565b909155506114709050612d71565b61148a6114858a606001518c60800151612c25565b612db5565b336001600160a01b0316887fcfdda74fce9fedb259e0f0a1ab1550e19b338488ece64976a4639e7fce0293a78b6040516114c49190614242565b60405180910390a350505060009182525060136020526040902080546001600160a01b031916905550610cf991506128e69050565b61150161274d565b61150961279f565b8015806115185750600a5460ff165b6115545760405162461bcd60e51b815260206004820152600d60248201526c086c2dce840e6cadcc840cae8d609b1b6044820152606401610aa9565b6000670de0b6b3a7640000611567612de8565b6115719085614250565b61157b9190614285565b90508060095461158b9190614016565b60075410156115dc5760405162461bcd60e51b815260206004820152601e60248201527f5574696c697a6174696f6e20746f6f206869676820746f2072656d6f766500006044820152606401610aa9565b6115e63384612e88565b80600760008282546115f89190613fff565b909155505081156116125761160d3382612c78565b61162e565b60065461162e9061010090046001600160a01b03163383612d41565b604080518281526020810185905233917f0c54fc223ffd1a8f36652b5e83db4fff50f5ae151b11ceb56d5499b9f6e1fa18910160405180910390a250610cf96128e6565b61167a61274d565b61168261279f565b600a5460ff16801561169357508034145b8061169c575034155b6116e85760405162461bcd60e51b815260206004820152601b60248201527f42616420616464206c6971756964697479204574682076616c756500000000006044820152606401610aa9565b60006116f2612de8565b61170483670de0b6b3a7640000614250565b61170e9190614285565b905061171a3382612fd7565b816007600082825461172c9190614016565b909155505034158015906117425750600a5460ff165b156117b557600660019054906101000a90046001600160a01b03166001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561179757600080fd5b505af11580156117ab573d6000803e3d6000fd5b50505050506117d2565b6006546117d29061010090046001600160a01b0316333085612875565b604080518381526020810183905233917f0351f600ef1e31e5e13b4dc27bff4cbde3e9269f0ffc666629ae6cac573eb220910160405180910390a2506118166128e6565b50565b600061182361274d565b61182b6130b6565b905090565b600061183a61274d565b61184261279f565b61184b826128f5565b90506118556128e6565b919050565b61186261274d565b61186a61279f565b60004290506703782dace9d900008360a001516001600160401b0316111580156118a957506703782dace9d900008360c001516001600160401b031611155b80156118c657506706f05b59d3b20000826001600160401b031611155b6119015760405162461bcd60e51b815260206004820152600c60248201526b496e76616c6964206665657360a01b6044820152606401610aa9565b600061190c846127ab565b600081815260126020526040902054909150156119625760405162461bcd60e51b815260206004820152601460248201527350656e64696e672072656c61792065786973747360601b6044820152606401610aa9565b600061197185608001516127ee565b905060006040518060e001604052806001600281111561199357611993613faf565b815233602082015260068054604090920191600160a81b900463ffffffff169060156119be83613fdb565b91906101000a81548163ffffffff021916908363ffffffff16021790555063ffffffff168152602001866001600160401b031681526020018563ffffffff168152602001838152602001600b5481525090506000611a1c8783612821565b9050611a278261280e565b600085815260126020526040812091909155611a438584612c4d565b6000818152601360205260409020549091506001600160a01b031615611aa55760405162461bcd60e51b8152602060048201526017602482015276052656c61792063616e6e6f74206265207370656420757604c1b6044820152606401610aa9565b8760800151600954600754611aba9190613fff565b1015611b045760405162461bcd60e51b8152602060048201526019602482015278496e73756666696369656e7420706f6f6c2062616c616e636560381b6044820152606401610aa9565b6000600b5485611b149190614016565b90506000611b488a60c001518b60a001518760600151611b3491906141df565b611b3e91906141df565b8b60800151612c25565b90506000818b60800151611b5c9190613fff565b905082600d6000828254611b709190614016565b909155505060808b015160098054600090611b8c908490614016565b9091555050600084815260136020526040902080546001600160a01b03191633908117909155611bda9030611bc18685614016565b60065461010090046001600160a01b0316929190612875565b600a5460ff1615611bf857611bf38b6040015182612c78565b611c1a565b60408b0151600654611c1a916101009091046001600160a01b03169083612d41565b877fa4ca36d112520cced74325c72711f376fe4015665829d879ba21590cb8130be08c8888604051611c4e93929190614138565b60405180910390a2336001600160a01b0316887ff98cddc88bc965917007822b05056cb92bc9ddf0f8bcc61678400cb78313bb4b88604051611c909190614242565b60405180910390a3505050505050505050610cf96128e6565b611cb161274d565b611cb961279f565b600e546001600160a01b03163314611cd057600080fd5b600e80546001600160a01b0319166001600160a01b0383169081179091556040805133815260208101929092527f485a12424bd0c2c66a131c2681cb6c743b9573af3ae5f3014ef6ce7f55ab0192910160405180910390a16118166128e6565b60606005805461097190613f74565b3360009081526002602090815260408083206001600160a01b038616845290915281205482811015611dc15760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610aa9565b611dd03385610e608685613fff565b5060019392505050565b6000610a013384846129c0565b60603415611e375760405162461bcd60e51b815260206004820152601b60248201527f4f6e6c79206d756c746963616c6c207769746820302076616c756500000000006044820152606401610aa9565b816001600160401b03811115611e4f57611e4f613b9c565b604051908082528060200260200182016040528015611e8257816020015b6060815260200190600190039081611e6d5790505b50905060005b82811015611f855760008030868685818110611ea657611ea66142a7565b9050602002810190611eb891906142bd565b604051611ec692919061430a565b600060405180830381855af49150503d8060008114611f01576040519150601f19603f3d011682016040523d82523d6000602084013e611f06565b606091505b509150915081611f5257604481511015611f1f57600080fd5b60048101905080806020019051810190611f39919061431a565b60405162461bcd60e51b8152600401610aa99190613b3d565b80848481518110611f6557611f656142a7565b602002602001018190525050508080611f7d906143bb565b915050611e88565b5092915050565b611f9461274d565b611f9c61279f565b600e5460408051632e68f21360e21b815290516000926001600160a01b03169163b9a3c84c916004808301926020929190829003018186803b158015611fe157600080fd5b505afa158015611ff5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061201991906143d6565b6040516302abf57960e61b815275536b696e6e794f7074696d69737469634f7261636c6560501b60048201529091506001600160a01b0382169063aafd5e409060240160206040518083038186803b15801561207457600080fd5b505afa158015612088573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120ac91906143d6565b601080546001600160a01b0319166001600160a01b039283161790556040516302abf57960e61b81526453746f726560d81b60048201529082169063aafd5e409060240160206040518083038186803b15801561210857600080fd5b505afa15801561211c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061214091906143d6565b600f80546001600160a01b0319166001600160a01b03928316908117909155600654604051635b97aadd60e01b8152610100909104909216600483015290635b97aadd9060240160206040518083038186803b15801561219f57600080fd5b505afa1580156121b3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121d791906143f3565b51600b555061109a6128e6565b60006121ee61274d565b6121f661279f565b610d19612de8565b61220661274d565b61220e61279f565b6000612219836127ab565b90506122258183612b98565b60006122318284612c4d565b9050600a60159054906101000a900463ffffffff16836080015161225591906141b7565b63ffffffff164210801561227b575060018351600281111561227957612279613faf565b145b801561229c57506000818152601360205260409020546001600160a01b0316155b6122e25760405162461bcd60e51b8152602060048201526017602482015276052656c61792063616e6e6f74206265207370656420757604c1b6044820152606401610aa9565b600081815260136020526040812080546001600160a01b0319163317905560c085015160a08601516060860151612327929161231d916141df565b61129891906141df565b9050600081866080015161233b9190613fff565b600a5490915060ff1615612379576006546123669061010090046001600160a01b0316333084612875565b612374866040015182612c78565b61239d565b604086015160065461239d916101009091046001600160a01b031690339084612875565b336001600160a01b0316847ff98cddc88bc965917007822b05056cb92bc9ddf0f8bcc61678400cb78313bb4b876040516123d79190614242565b60405180910390a350505050610cf96128e6565b6123f361274d565b6123fb61279f565b42600a54608083015161241b91600160a81b900463ffffffff16906141b7565b63ffffffff161161245e5760405162461bcd60e51b815260206004820152600d60248201526c50617374206c6976656e65737360981b6044820152606401610aa9565b60018151600281111561247357612473613faf565b146124b15760405162461bcd60e51b815260206004820152600e60248201526d4e6f742064697370757461626c6560901b6044820152606401610aa9565b60006124bc836127ab565b90506124c88183612b98565b60006124d48484612821565b905060006124f98460200151338660a001518760c001516124f487612983565b613125565b90508360a001518460c0015161250f9190614016565b600d60008282546125209190613fff565b909155505060808501516009805460009061253c908490613fff565b9091555050600083815260126020526040812055801561259057336125608561280e565b60405185907f29751133c2d0a0ea7a9da312248b12ef10ac4c20f4e3ab5eebbb80d853b3836790600090a46125c6565b3361259a8561280e565b60405185907f14173c4d3bd95e34f5c8f78729323444c9272bd21051e70045b64ed48f65818390600090a45b505050610cf96128e6565b6000806125dc61274d565b6125e461279f565b6125ee60006128f5565b6125f7846128f5565b915091506126036128e6565b915091565b61261061274d565b61261861279f565b61262061344a565b61109a6128e6565b6001600160a01b03831661268a5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610aa9565b6001600160a01b0382166126eb5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610aa9565b6001600160a01b0383811660008181526002602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b60065460ff1661109a5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610aa9565b6006805460ff19169055565b6006546040516000916127d191849161010090046001600160a01b031690602001614434565b604051602081830303815290604052805190602001209050919050565b600a54600090610a0590600160681b90046001600160401b031683612c25565b6000816040516020016127d19190614242565b60008282604001518360600151600660019054906101000a90046001600160a01b0316604051602001612857949392919061445c565b60405160208183030381529060405280519060200120905092915050565b6040516001600160a01b03808516602483015283166044820152606481018290526128e09085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261350d565b50505050565b6006805460ff19166001179055565b60006128ff61344a565b60008060085413612911576000612915565b6008545b9050600081600954856129289190614016565b6129329190614016565b90506000826007546129449190614016565b90508061295d5750670de0b6b3a7640000949350505050565b8061297083670de0b6b3a7640000614250565b61297a9190614285565b95945050505050565b6060610a0560405180602001604052806000815250604051806040016040528060098152602001680e4cad8c2f290c2e6d60bb1b815250846135df565b6001600160a01b038316612a245760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610aa9565b6001600160a01b038216612a865760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610aa9565b6001600160a01b03831660009081526001602052604090205481811015612afe5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610aa9565b612b088282613fff565b6001600160a01b038086166000908152600160205260408082209390935590851681529081208054849290612b3e908490614016565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051612b8a91815260200190565b60405180910390a350505050565b612ba18161280e565b60008381526012602052604090205414610cf9576040805162461bcd60e51b81526020600482015260248101919091527f4861736865642072656c617920706172616d7320646f206e6f74206d6174636860448201527f206578697374696e672072656c6179206861736820666f72206465706f7369746064820152608401610aa9565b6000670de0b6b3a7640000612c43836001600160401b038616614250565b610d429190614285565b60008282606001516040516020016128579291909182526001600160401b0316602082015260400190565b6001600160a01b0382163b15612ca457600654610cf99061010090046001600160a01b03168383612d41565b600654604051632e1a7d4d60e01b8152600481018390526101009091046001600160a01b031690632e1a7d4d90602401600060405180830381600087803b158015612cee57600080fd5b505af1158015612d02573d6000803e3d6000fd5b50506040516001600160a01b038516925083156108fc02915083906000818181858888f19350505050158015612d3c573d6000803e3d6000fd5b505050565b6040516001600160a01b038316602482015260448101829052612d3c90849063a9059cbb60e01b906064016128a9565b6000612d7b6130b6565b905080600c54612d8b9190613fff565b600c5550600a80546cffffffff0000000000000000001916600160481b4263ffffffff1602179055565b80600c6000828254612dc79190614016565b925050819055508060086000828254612de09190614201565b909155505050565b6000612df360035490565b612e045750670de0b6b3a764000090565b612e0c612d71565b612e1461344a565b6000600c54600754612e269190613fff565b905060006008541315612e4757600854612e409082614016565b9050612e63565b600854612e56906000196144a1565b612e609082613fff565b90505b600354612e7882670de0b6b3a7640000614250565b612e829190614285565b91505090565b6001600160a01b038216612ee85760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610aa9565b6001600160a01b03821660009081526001602052604090205481811015612f5c5760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610aa9565b612f668282613fff565b6001600160a01b03841660009081526001602052604081209190915560038054849290612f94908490613fff565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602001612740565b6001600160a01b03821661302d5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610aa9565b806003600082825461303f9190614016565b90915550506001600160a01b0382166000908152600160205260408120805483929061306c908490614016565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b600a546000908190670de0b6b3a764000090600160481b900463ffffffff16426130e09190613fff565b600a54600c546130fe9161010090046001600160401b031690614250565b6131089190614250565b6131129190614285565b9050600c54811061185557600c54612e82565b6000806131328585614016565b6010546006549192506131579161010090046001600160a01b03908116911683613625565b6010546011546001600160a01b039091169063af355d1e904286600660019054906101000a90046001600160a01b031660008b600a60159054906101000a900463ffffffff168f670de0b6b3a76400006040518a63ffffffff1660e01b81526004016131cb99989796959493929190614526565b602060405180830381600087803b1580156131e557600080fd5b505af1925050508015613215575060408051601f3d908101601f191682019092526132129181019061419e565b60015b613262576006546132359061010090046001600160a01b03168883612d41565b601054600654613258916001600160a01b03610100909204821691166000613625565b600091505061297a565b818110156132bd5760006132768284613fff565b6006549091506132959061010090046001600160a01b03168a83612d41565b6010546006546132b8916001600160a01b03610100909204821691166000613625565b819250505b5060408051610160810182526001600160a01b03898116825260006020830181905260065461010090049091169282019290925260608101829052670de0b6b3a7640000608082015260a08101829052600a5460c0820190600160a81b900463ffffffff164261332d9190614016565b8152600060208201526040016133438885613fff565b815260208101889052600a54600160a81b900463ffffffff90811660409092019190915260065491925061338c9161010090046001600160a01b03169033903090869061287516565b6010546006546133ae916001600160a01b036101009092048216911684613625565b60105460115460405163139c641960e31b81526001600160a01b0390921691639ce320c8916133e9914290899087908e903090600401614591565b602060405180830381600087803b15801561340357600080fd5b505af1158015613417573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061343b919061419e565b50600198975050505050505050565b600d546006546040516370a0823160e01b81523060048201526000929161010090046001600160a01b0316906370a082319060240160206040518083038186803b15801561349757600080fd5b505afa1580156134ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134cf919061419e565b6134d99190613fff565b9050600754811115611816576007546134f29082613fff565b60086000828254613503919061468a565b9091555050600755565b6000613562826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166137499092919063ffffffff16565b805190915015612d3c578080602001905181019061358091906146c9565b612d3c5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610aa9565b606060006135ed8585613760565b905084816135fa856137a2565b60405160200161360c939291906146e6565b6040516020818303038152906040529150509392505050565b8015806136ae5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e9060440160206040518083038186803b15801561367457600080fd5b505afa158015613688573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136ac919061419e565b155b6137195760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610aa9565b6040516001600160a01b038316602482015260448101829052612d3c90849063095ea7b360e01b906064016128a9565b606061375884846000856137e2565b949350505050565b815160609015613791578160405160200161377b9190614729565b6040516020818303038152906040529050610a05565b8160405160200161377b919061475d565b60606137b1608083901c61390a565b6137ba8361390a565b6040805160208101939093528201526060016040516020818303038152906040529050919050565b6060824710156138435760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610aa9565b843b6138915760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610aa9565b600080866001600160a01b031685876040516138ad9190614782565b60006040518083038185875af1925050503d80600081146138ea576040519150601f19603f3d011682016040523d82523d6000602084013e6138ef565b606091505b50915091506138ff828286613aac565b979650505050505050565b6000808260001c9050806fffffffffffffffffffffffffffffffff169050806801000000000000000002811777ffffffffffffffff0000000000000000ffffffffffffffff169050806401000000000281177bffffffff00000000ffffffff00000000ffffffff00000000ffffffff16905080620100000281177dffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff0000ffff169050806101000281177eff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff1690508060100281177f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f16905060006008827f08080808080808080808080808080808080808080808080808080808080808081681613a2e57613a2e61426f565b0460047f040404040404040404040404040404040404040404040404040404040404040484160460027f020202020202020202020202020202020202020202020202020202020202020285160417166027029091017f3030303030303030303030303030303030303030303030303030303030303030019392505050565b60608315613abb575081610d42565b825115613acb5782518084602001fd5b8160405162461bcd60e51b8152600401610aa99190613b3d565b60005b83811015613b00578181015183820152602001613ae8565b838111156128e05750506000910152565b60008151808452613b29816020860160208601613ae5565b601f01601f19169290920160200192915050565b602081526000610d426020830184613b11565b6001600160a01b038116811461181657600080fd5b803561185581613b50565b60008060408385031215613b8357600080fd5b8235613b8e81613b50565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b60405160e081016001600160401b0381118282101715613bd457613bd4613b9c565b60405290565b6001600160401b038116811461181657600080fd5b803561185581613bda565b63ffffffff8116811461181657600080fd5b803561185581613bfa565b6000610100808385031215613c2b57600080fd5b604051908101906001600160401b0382118183101715613c4d57613c4d613b9c565b816040528092508335815260208401359150613c6882613bda565b816020820152613c7a60408501613b65565b6040820152613c8b60608501613b65565b606082015260808401356080820152613ca660a08501613bef565b60a0820152613cb760c08501613bef565b60c0820152613cc860e08501613c0c565b60e0820152505092915050565b6000806101208385031215613ce957600080fd5b613cf38484613c17565b9150610100830135613d0481613bda565b809150509250929050565b6000808284036101e0811215613d2457600080fd5b613d2e8585613c17565b925060e060ff1982011215613d4257600080fd5b50613d4b613bb2565b61010084013560038110613d5e57600080fd5b8152613d6d6101208501613b65565b6020820152613d7f6101408501613c0c565b6040820152613d916101608501613bef565b6060820152613da36101808501613c0c565b60808201526101a084013560a08201526101c09093013560c08401525092909150565b600060208284031215613dd857600080fd5b5035919050565b600080600060608486031215613df457600080fd5b8335613dff81613b50565b92506020840135613e0f81613b50565b929592945050506040919091013590565b801515811461181657600080fd5b60008060408385031215613e4157600080fd5b823591506020830135613d0481613e20565b600060208284031215613e6557600080fd5b8135610d4281613b50565b60008060208385031215613e8357600080fd5b82356001600160401b0380821115613e9a57600080fd5b818501915085601f830112613eae57600080fd5b813581811115613ebd57600080fd5b8660208260051b8501011115613ed257600080fd5b60209290920196919550909350505050565b6000602080830181845280855180835260408601915060408160051b870101925083870160005b82811015613f3957603f19888603018452613f27858351613b11565b94509285019290850190600101613f0b565b5092979650505050505050565b60008060408385031215613f5957600080fd5b8235613f6481613b50565b91506020830135613d0481613b50565b600181811c90821680613f8857607f821691505b60208210811415613fa957634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600063ffffffff80831681811415613ff557613ff5613fc5565b6001019392505050565b60008282101561401157614011613fc5565b500390565b6000821982111561402957614029613fc5565b500190565b8051825260208101516001600160401b0380821660208501526040830151915060018060a01b03808316604086015280606085015116606086015250608083015160808501528060a08401511660a08501528060c08401511660c0850152505063ffffffff60e08201511660e08301525050565b8051600381106140c257634e487b7160e01b600052602160045260246000fd5b82526020818101516001600160a01b031690830152604080820151906140ef9084018263ffffffff169052565b50606081015161410a60608401826001600160401b03169052565b506080810151614122608084018263ffffffff169052565b5060a0818101519083015260c090810151910152565b6102008101614147828661402e565b6141556101008301856140a2565b826101e0830152949350505050565b60006020828403121561417657600080fd5b8151610d4281613bda565b60006020828403121561419357600080fd5b8151610d4281613bfa565b6000602082840312156141b057600080fd5b5051919050565b600063ffffffff8083168185168083038211156141d6576141d6613fc5565b01949350505050565b60006001600160401b038083168185168083038211156141d6576141d6613fc5565b600080821280156001600160ff1b038490038513161561422357614223613fc5565b600160ff1b839003841281161561423c5761423c613fc5565b50500190565b60e08101610a0582846140a2565b600081600019048311821515161561426a5761426a613fc5565b500290565b634e487b7160e01b600052601260045260246000fd5b6000826142a257634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052603260045260246000fd5b6000808335601e198436030181126142d457600080fd5b8301803591506001600160401b038211156142ee57600080fd5b60200191503681900382131561430357600080fd5b9250929050565b8183823760009101908152919050565b60006020828403121561432c57600080fd5b81516001600160401b038082111561434357600080fd5b818401915084601f83011261435757600080fd5b81518181111561436957614369613b9c565b604051601f8201601f19908116603f0116810190838211818310171561439157614391613b9c565b816040528281528760208487010111156143aa57600080fd5b6138ff836020830160208801613ae5565b60006000198214156143cf576143cf613fc5565b5060010190565b6000602082840312156143e857600080fd5b8151610d4281613b50565b60006020828403121561440557600080fd5b604051602081018181106001600160401b038211171561442757614427613b9c565b6040529151825250919050565b6101208101614443828561402e565b6001600160a01b03929092166101009190910152919050565b610160810161446b828761402e565b63ffffffff949094166101008201526001600160401b03929092166101208301526001600160a01b031661014090910152919050565b60006001600160ff1b03818413828413808216868404861116156144c7576144c7613fc5565b600160ff1b60008712828116878305891216156144e6576144e6613fc5565b6000871292508782058712848416161561450257614502613fc5565b8785058712818416161561451857614518613fc5565b505050929093029392505050565b60006101208b835263ffffffff808c16602085015281604085015261454d8285018c613b11565b6001600160a01b039a8b166060860152608085019990995260a084019790975250509290931660c083015290931660e0840152610100909201919091529392505050565b600061020088835263ffffffff881660208401528060408401526145b781840188613b11565b9150506145d06060830186516001600160a01b03169052565b60208501516001600160a01b03811660808401525060408501516001600160a01b03811660a084015250606085015180151560c084015250608085015160e083015260a0850151610100818185015260c08701519150610120828186015260e0880151925061014083818701528289015161016087015281890151610180870152808901516101a0870152505050506146756101c08301856001600160a01b03169052565b6001600160a01b0383166101e08301526138ff565b60008083128015600160ff1b8501841216156146a8576146a8613fc5565b6001600160ff1b03840183138116156146c3576146c3613fc5565b50500390565b6000602082840312156146db57600080fd5b8151610d4281613e20565b600084516146f8818460208901613ae5565b84519083019061470c818360208901613ae5565b845191019061471f818360208801613ae5565b0195945050505050565b600b60fa1b815260008251614745816001850160208701613ae5565b601d60f91b6001939091019283015250600201919050565b6000825161476f818460208701613ae5565b601d60f91b920191825250600101919050565b60008251614794818460208701613ae5565b919091019291505056fea264697066735822122000780274c57d26a2ae42dfdd378b533920fa72509ae195cb371a6f6544115ffc64736f6c63430008090033

Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.