Source Code
Latest 25 from a total of 2,075 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| 0x00180001 | 414184590 | 38 days ago | IN | 0 ETH | 0.00000148 | ||||
| 0x00180001 | 414184537 | 38 days ago | IN | 0 ETH | 0.00000145 | ||||
| 0x00180001 | 414177551 | 38 days ago | IN | 0 ETH | 0.00000089 | ||||
| 0x00180001 | 414164606 | 38 days ago | IN | 0 ETH | 0.00000067 | ||||
| 0x00180001 | 414163444 | 38 days ago | IN | 0 ETH | 0.00000065 | ||||
| 0x01000100 | 402567929 | 72 days ago | IN | 0 ETH | 0.00000068 | ||||
| 0x01000100 | 402566395 | 72 days ago | IN | 0 ETH | 0.00000045 | ||||
| 0x01000100 | 402566382 | 72 days ago | IN | 0 ETH | 0.00000046 | ||||
| 0x01000100 | 402565404 | 72 days ago | IN | 0 ETH | 0.00000044 | ||||
| 0x01000100 | 402565393 | 72 days ago | IN | 0 ETH | 0.00000044 | ||||
| 0x01000100 | 402564764 | 72 days ago | IN | 0 ETH | 0.00000061 | ||||
| 0x01000100 | 402564750 | 72 days ago | IN | 0 ETH | 0.0000006 | ||||
| 0x01000100 | 402562458 | 72 days ago | IN | 0 ETH | 0.00000091 | ||||
| 0x01000100 | 402562451 | 72 days ago | IN | 0 ETH | 0.00000092 | ||||
| 0x01000100 | 402562167 | 72 days ago | IN | 0 ETH | 0.00000087 | ||||
| 0x01000100 | 402562160 | 72 days ago | IN | 0 ETH | 0.00000087 | ||||
| 0x01000100 | 402562053 | 72 days ago | IN | 0 ETH | 0.00000089 | ||||
| 0x01000100 | 402562046 | 72 days ago | IN | 0 ETH | 0.00000088 | ||||
| 0x01000100 | 402561777 | 72 days ago | IN | 0 ETH | 0.00000101 | ||||
| 0x01000100 | 402561770 | 72 days ago | IN | 0 ETH | 0.00000101 | ||||
| 0x01000100 | 402561431 | 72 days ago | IN | 0 ETH | 0.00000122 | ||||
| 0x01000100 | 402561417 | 72 days ago | IN | 0 ETH | 0.00000125 | ||||
| 0x01000100 | 402561297 | 72 days ago | IN | 0 ETH | 0.00000134 | ||||
| 0x01000100 | 402561290 | 72 days ago | IN | 0 ETH | 0.00000135 | ||||
| 0x01000100 | 402561008 | 72 days ago | IN | 0 ETH | 0.00000162 |
Latest 1 internal transaction
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 139683162 | 843 days ago | Contract Creation | 0 ETH |
Cross-Chain Transactions
Loading...
Loading
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.
Contract Name:
OrderBook
Compiler Version
v0.8.18+commit.87f61d96
Optimization Enabled:
Yes with 4150 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.18;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "./interfaces/IOrderBook.sol";
import "./interfaces/ILighterV2FlashCallback.sol";
import "./interfaces/ILighterV2TransferCallback.sol";
import "./libraries/LinkedList.sol";
import "./libraries/Errors.sol";
import {IERC20Minimal} from "./interfaces/external/IERC20Minimal.sol";
/**
* @title Order Book
* @notice Contract representing an order book for trading token pairs. It manages
* the creation and interaction of orders, and tracks various parameters related
* to order management.
* @notice OrderBook can handle different types of orders and order-life-cycle management
* @notice User can swap tokens in the order book via direct call on orderBook or via router
* @dev for direct order book interaction of order-creation and token-swap, ensure the caller
* has implemented the callback interface to handle payments
*/
contract OrderBook is IOrderBook, ReentrancyGuard {
/// @dev Limits the value for size and price ticks
uint8 public constant LOG10_TICK_THRESHOLD = 38;
/// @dev Limits the total number of orders that can be created
uint32 public constant ORDER_ID_THRESHOLD = (1 << 32) - 1;
/// @dev Limits the unique number of creators, which are smart contracts
/// that call the order book and implements the callback interfaces
uint32 public constant CREATOR_ID_THRESHOLD = (1 << 31) - 1;
uint64 public constant MAX_PRICE = type(uint64).max;
// Using the LinkedListLib for order management
using LinkedListLib for LinkedList;
/// @notice The ERC20 token used as token0 in the trading pair
IERC20Minimal public immutable token0;
/// @notice The ERC20 token used as token1 in the trading pair
IERC20Minimal public immutable token1;
/// @notice The id of the order book
uint8 public immutable orderBookId;
/// @notice The minimum base token0 amount required for an order to be valid
uint64 public immutable minToken0BaseAmount;
/// @notice The minimum base token1 amount required for an order to be valid (token0Base * priceBase)
uint128 public immutable minToken1BaseAmount;
/// @notice The step size for token0 amounts
uint128 public immutable sizeTick;
/// @notice The step size for unit token0 price
uint128 public immutable priceTick;
/// @notice The multiplier used for calculating the amount1 from priceBase and amount0Base
uint128 public immutable priceMultiplier;
/// @notice The divider used for calculating the amount1 from priceBase and amount0Base
uint128 public immutable priceDivider;
/// @dev The id of the next order to be created, also used for setting ownerId and creatorId for gas efficiency
uint32 public orderIdCounter;
/// @notice The data structure used for storing the active orders
/// @dev If an ask order, book needs to store at least amount0Base * sizeTick amount of token0. If a bid order,
/// book needs to store at least amount0Base * priceMultiplier * sizeTick / priceDivider amount of token1
LinkedList private _orders;
/// @notice Mapping from address to claimable token0 balance
mapping(address => uint256) public claimableToken0Balance;
/// @notice Mapping from address to claimable token1 balance
mapping(address => uint256) public claimableToken1Balance;
/// @notice Mapping from ownerId to address
mapping(uint32 => address) public ownerIdToAddress;
/// @notice Mapping from address to ownerId
mapping(address => uint32) public addressToOwnerId;
/// @notice Mapping from address to creatorId
mapping(address => uint32) public addressToCreatorId;
/// @notice Mapping from creatorId to address
mapping(uint32 => address) public creatorIdToAddress;
/// @notice A struct containing variables used for order matching.
struct MatchOrderLocalVars {
uint32 index; // id of the maker order being matched
address makerAddress; // owner address of the maker order
uint256 filledAmount0; // Amount of token0 already filled in the taker order
uint256 filledAmount1; // Amount of token1 already filled in the taker order
uint256 amount; // Exact amount of tokens to be sent or received in a swap
uint64 makerAmount0BaseChange; // Maker order amont0Base change
uint256 swapAmount0; // Amount of token0 to be swaped with maker order
uint256 swapAmount1; // Amount of token1 to be swaped with maker order
uint64 swapAmount0Base; // Base amount of token0 to be swaped with maker order
uint128 swapAmount1Base; // Base amount of token1 to be swaped with maker order
bool atLeastOneFullSwap; // Flag indicating if taker took at least one maker order fully
bool fullTakerFill; // Flag indicating if taker order is fully filled
uint32 swapCount; // Count of swaps performed
uint32 swapCapacity; // Capacity swaps array
SwapData[] swaps; // Array of swap data
}
/// @notice A struct containing payment-related data for order and swap operations.
struct PaymentData {
bool isAsk; // Flag indicating if the taker order is an ask order
bool isPerfMode; // Flag indicating if the taker order is a performance limit order
address recipient; // Recipient address for payments
uint256 filledAmount0; // Total amount of token0 in the swaps
uint256 filledAmount1; // Total amount of token1 in the swaps
uint256 remainingLimitOrderAmount; // Amount taker needs to pay for unmatched part of their limit order
uint32 swapCount; // Count of swaps performed
SwapData[] swaps; // Array of swap data
bytes callbackData; // Additional callback data for payment.
}
/// @dev Struct that holds swap data during matching
struct SwapData {
address makerAddress; // Address of the owner of the matched order
uint256 swapAmount; // Amount of tokens matched in the order
bool isPerfMode; // Flag indicating if the order is in performance mode
}
/// @notice Contract constructor
/// @param _orderBookId The id of the order book
/// @param _token0Address The base token address
/// @param _token1Address The quote token address
/// @param _logSizeTick log10 of base token tick, size of the base token
/// should be multiples of 10**logSizeTick for limit orders
/// @param _logPriceTick log10 of price tick, price of unit base token
/// should be multiples of 10**logPriceTick for limit orders
/// @param _minToken0BaseAmount minimum token0Base amount for limit orders
/// @param _minToken1BaseAmount minimum token1Base amount (token0Base * priceBase) for limit orders
/// @dev Initializes the contract and linked lists with provided parameters
constructor(
uint8 _orderBookId,
address _token0Address,
address _token1Address,
uint8 _logSizeTick,
uint8 _logPriceTick,
uint64 _minToken0BaseAmount,
uint128 _minToken1BaseAmount
) {
token0 = IERC20Minimal(_token0Address);
token1 = IERC20Minimal(_token1Address);
orderBookId = _orderBookId;
uint8 token0Decimals = token0.decimals();
if (_logSizeTick >= LOG10_TICK_THRESHOLD || _logPriceTick >= LOG10_TICK_THRESHOLD) {
revert Errors.LighterV2CreateOrderBook_InvalidTickCombination();
}
sizeTick = uint128(10 ** _logSizeTick);
priceTick = uint128(10 ** _logPriceTick);
uint128 priceMultiplierCheck = 1;
uint128 priceDividerCheck = 1;
if (_logSizeTick + _logPriceTick >= token0Decimals) {
if (_logSizeTick + _logPriceTick - token0Decimals >= LOG10_TICK_THRESHOLD) {
revert Errors.LighterV2CreateOrderBook_InvalidTickCombination();
}
priceMultiplierCheck = uint128(10 ** (_logSizeTick + _logPriceTick - token0Decimals));
} else {
if (token0Decimals - _logSizeTick - _logPriceTick >= LOG10_TICK_THRESHOLD) {
revert Errors.LighterV2CreateOrderBook_InvalidTickCombination();
}
priceDividerCheck = uint128(10 ** (token0Decimals - _logPriceTick - _logSizeTick));
}
priceMultiplier = priceMultiplierCheck;
priceDivider = priceDividerCheck;
if (_minToken0BaseAmount == 0 || _minToken1BaseAmount == 0) {
revert Errors.LighterV2CreateOrderBook_InvalidMinAmount();
}
minToken0BaseAmount = _minToken0BaseAmount;
minToken1BaseAmount = _minToken1BaseAmount;
// Create the head node for asks linked list, this node can not be deleted
_orders.asks[0] = LimitOrder({
prev: 0,
next: 1,
perfMode_creatorId: 0,
ownerId: 1,
amount0Base: 0,
priceBase: 0
});
// Create the tail node for asks linked list, this node can not be deleted
_orders.asks[1] = LimitOrder({
prev: 0,
next: 1,
perfMode_creatorId: 0,
ownerId: 1,
amount0Base: 0,
priceBase: MAX_PRICE
});
// Create the head node for bids linked list, this node can not be deleted
_orders.bids[0] = LimitOrder({
prev: 0,
next: 1,
perfMode_creatorId: 0,
ownerId: 1,
amount0Base: 0,
priceBase: MAX_PRICE
});
// Create the tail node for bids linked list, this node can not be deleted
_orders.bids[1] = LimitOrder({
prev: 0,
next: 1,
perfMode_creatorId: 0,
ownerId: 1,
amount0Base: 0,
priceBase: 0
});
// Id 0 and 1 are used for heads and tails. Next order should start from id 2
orderIdCounter = 2;
}
/// @inheritdoc IOrderBook
function createOrder(
uint64 amount0Base,
uint64 priceBase,
bool isAsk,
address owner,
uint32 hintId,
OrderType orderType,
bytes memory callbackData
) external override nonReentrant returns (uint32 newOrderId) {
newOrderId = orderIdCounter;
// For every order type, the amount0Base needs to be at least 1
if (amount0Base == 0) {
revert Errors.LighterV2Order_AmountTooSmall();
}
// priceBase needs to be at least priceDivider
// this guarantees that any increase of amount0Base will increase amount1 by at least 1
// as priceDivider is guaranteed to be at least 1, an error is always thrown if priceBase = 0,
// which is reserved for the dummy order with id 0
if (priceBase < priceDivider) {
revert Errors.LighterV2Order_PriceTooSmall();
}
// do not allow orders with the max price, as the price is reserved for the big dummy order.
// this is required so no order is inserted after the dummy order with id 1
if (priceBase == MAX_PRICE) {
revert Errors.LighterV2Order_PriceTooBig();
}
if (orderType == OrderType.LimitOrder || orderType == OrderType.PerformanceLimitOrder) {
if (hintId >= newOrderId) {
revert Errors.LighterV2Order_InvalidHintId();
}
if ((amount0Base < minToken0BaseAmount || priceBase * amount0Base < minToken1BaseAmount)) {
revert Errors.LighterV2Order_AmountTooSmall();
}
}
LimitOrder memory newOrder;
{
if (newOrderId >= ORDER_ID_THRESHOLD) {
revert Errors.LighterV2Order_OrderIdExceedsLimit();
}
orderIdCounter = newOrderId + 1;
newOrder = LimitOrder({
perfMode_creatorId: 0, // Only set if order needs to be inserted into the order book
prev: 0, // Only set if order needs to be inserted into the order book
next: 0, // Only set if order needs to be inserted into the order book
ownerId: 0, // Only set if order needs to be inserted into the order book
amount0Base: amount0Base,
priceBase: priceBase
});
emit CreateOrder(owner, newOrderId, amount0Base, priceBase, isAsk, orderType);
}
(uint256 filledAmount0, uint256 filledAmount1, uint32 swapCount, SwapData[] memory swaps) = _matchOrder(
newOrder,
newOrderId,
owner,
isAsk
);
// Short circuit payments if Fill or Kill order is not fully filled and needs to be killed
if (orderType == OrderType.FoKOrder && newOrder.amount0Base > 0) {
revert Errors.LighterV2Order_FoKNotFilled();
}
// Computes the amount caller needs to pay for remaning part of their limit order
uint256 remainingLimitOrderAmount = 0;
if (
(orderType == OrderType.LimitOrder || orderType == OrderType.PerformanceLimitOrder) &&
newOrder.amount0Base > 0
) {
remainingLimitOrderAmount = (isAsk)
? (uint256(newOrder.amount0Base) * sizeTick)
: (uint256(newOrder.amount0Base) * newOrder.priceBase * priceMultiplier) / priceDivider;
}
// Handle token transfers between makers and takers and for remainingLimitOrderAmount
if (
filledAmount0 > 0 ||
filledAmount1 > 0 ||
orderType == OrderType.LimitOrder ||
orderType == OrderType.PerformanceLimitOrder
) {
_handlePayments(
PaymentData(
isAsk,
orderType == OrderType.PerformanceLimitOrder,
owner,
filledAmount0,
filledAmount1,
remainingLimitOrderAmount,
swapCount,
swaps,
callbackData
)
);
}
// If the order is not fully filled, set remaining value in newOrder and insert it into respective order book
if (remainingLimitOrderAmount > 0) {
// Get the ownerId if exists, otherwise set the ownerId using the from address
newOrder.ownerId = addressToOwnerId[owner];
if (newOrder.ownerId == 0) {
newOrder.ownerId = newOrderId;
addressToOwnerId[owner] = newOrder.ownerId;
ownerIdToAddress[newOrderId] = owner;
}
// creatorId can only be non-zero if msg.sender different from the owner and order is a limit order
if (msg.sender != owner) {
newOrder.perfMode_creatorId = addressToCreatorId[msg.sender];
if (newOrder.perfMode_creatorId == 0) {
// LimitOrder stores 31 bits for the creator id, only allow setting a non-zero creator id if it's below the limit
if (newOrderId >= CREATOR_ID_THRESHOLD) {
revert Errors.LighterV2Order_CreatorIdExceedsLimit();
}
newOrder.perfMode_creatorId = newOrderId;
addressToCreatorId[msg.sender] = newOrder.perfMode_creatorId;
creatorIdToAddress[newOrder.perfMode_creatorId] = msg.sender;
}
newOrder.perfMode_creatorId <<= 1;
}
if (orderType == OrderType.PerformanceLimitOrder) {
newOrder.perfMode_creatorId = newOrder.perfMode_creatorId | 1;
}
if (isAsk) {
_orders.asks[newOrderId] = newOrder;
_orders.insert(newOrderId, isAsk, hintId);
} else {
_orders.bids[newOrderId] = newOrder;
_orders.insert(newOrderId, isAsk, hintId);
}
}
}
/// @inheritdoc IOrderBook
function cancelLimitOrder(uint32 id, address owner) external override nonReentrant returns (bool) {
if (!isOrderActive(id)) {
return false;
}
LimitOrder memory order;
bool isAsk = isAskOrder(id);
if (isAsk) {
order = _orders.asks[id];
} else {
order = _orders.bids[id];
}
address _owner = ownerIdToAddress[order.ownerId];
uint32 creatorId = (order.perfMode_creatorId >> 1);
address creator = _owner;
if (creatorId != 0) {
creator = creatorIdToAddress[creatorId];
}
// only the creator or the owner can cancel the order
if ((owner != _owner) || (msg.sender != creator && msg.sender != _owner)) {
revert Errors.LighterV2Owner_CallerCannotCancel();
}
emit CancelLimitOrder(id);
if (isAsk) {
uint256 amount0 = uint256(order.amount0Base) * sizeTick;
bool success = false;
if ((order.perfMode_creatorId & 1) == 0) {
success = _sendToken(token0, _owner, amount0);
}
if (!success) {
claimableToken0Balance[_owner] += amount0;
if ((order.perfMode_creatorId & 1) == 0) {
emit ClaimableBalanceIncrease(_owner, amount0, true);
}
}
_orders.erase(id, isAsk);
} else {
uint256 amount1 = ((uint256(order.amount0Base) * order.priceBase) * priceMultiplier) / priceDivider;
bool success = false;
if ((order.perfMode_creatorId & 1) == 0) {
success = _sendToken(token1, _owner, amount1);
}
if (!success) {
claimableToken1Balance[_owner] += amount1;
if ((order.perfMode_creatorId & 1) == 0) {
emit ClaimableBalanceIncrease(_owner, amount1, false);
}
}
_orders.erase(id, isAsk);
}
return true;
}
/// @inheritdoc IOrderBook
function swapExactSingle(
bool isAsk,
bool isExactInput,
uint256 exactAmount,
uint256 expectedAmount,
address recipient,
bytes memory callbackData
) external override nonReentrant returns (uint256, uint256) {
(uint256 filledAmount0, uint256 filledAmount1, uint32 swapCount, SwapData[] memory swaps) = _matchSwapOrder(
isAsk,
isExactInput,
exactAmount,
expectedAmount,
recipient
);
_handlePayments(
PaymentData(isAsk, false, recipient, filledAmount0, filledAmount1, 0, swapCount, swaps, callbackData)
);
emit SwapExactAmount(msg.sender, recipient, isExactInput, isAsk, filledAmount0, filledAmount1);
return (filledAmount0, filledAmount1);
}
/// @inheritdoc IOrderBook
function flashLoan(
address recipient,
uint256 amount0,
uint256 amount1,
bytes calldata callbackData
) external override nonReentrant {
uint256 orderBookToken0BalanceBeforeLoan = token0.balanceOf(address(this));
uint256 orderBookToken1BalanceBeforeLoan = token1.balanceOf(address(this));
if (amount0 > 0 && !_sendToken(token0, recipient, amount0)) {
revert Errors.LighterV2TokenTransfer_Failed();
}
if (amount1 > 0 && !_sendToken(token1, recipient, amount1)) {
revert Errors.LighterV2TokenTransfer_Failed();
}
ILighterV2FlashCallback(msg.sender).flashLoanCallback(callbackData);
if (token0.balanceOf(address(this)) < orderBookToken0BalanceBeforeLoan) {
revert Errors.LighterV2FlashLoan_InsufficentCallbackTransfer();
}
if (token1.balanceOf(address(this)) < orderBookToken1BalanceBeforeLoan) {
revert Errors.LighterV2FlashLoan_InsufficentCallbackTransfer();
}
emit FlashLoan(msg.sender, recipient, amount0, amount1);
}
/// @inheritdoc IOrderBook
function depositToken(
uint256 amountToDeposit,
bool isToken0,
bytes memory callbackData
) external override nonReentrant {
address owner = msg.sender;
IERC20Minimal token = isToken0 ? token0 : token1;
uint256 balanceBefore = token.balanceOf(address(this));
ILighterV2TransferCallback(owner).lighterV2TransferCallback(amountToDeposit, token, callbackData);
if (token.balanceOf(address(this)) < balanceBefore + amountToDeposit) {
revert Errors.LighterV2Vault_InsufficentCallbackTransfer();
}
if (isToken0) {
claimableToken0Balance[owner] += amountToDeposit;
} else {
claimableToken1Balance[owner] += amountToDeposit;
}
emit ClaimableBalanceIncrease(owner, amountToDeposit, isToken0);
}
/// @inheritdoc IOrderBook
function claimToken(uint256 amountToClaim, bool isToken0) external override nonReentrant {
address owner = msg.sender;
uint256 amount = isToken0 ? claimableToken0Balance[owner] : claimableToken1Balance[owner];
if (amountToClaim > 0 && amountToClaim <= amount) {
if (isToken0) {
claimableToken0Balance[owner] -= amountToClaim;
if (!_sendToken(token0, owner, amountToClaim)) {
revert Errors.LighterV2TokenTransfer_Failed();
}
} else {
claimableToken1Balance[owner] -= amountToClaim;
if (!_sendToken(token1, owner, amountToClaim)) {
revert Errors.LighterV2TokenTransfer_Failed();
}
}
emit ClaimableBalanceDecrease(owner, amountToClaim, isToken0);
} else {
revert Errors.LighterV2Vault_InvalidClaimAmount();
}
}
/// @dev Matches the given limit order against the available maker orders.
/// @param order The taker order to be matched
/// @param orderId The id of the taker order
/// @param isAsk Indicates whether the taker order is an ask order or not
/// @return filledAmount0 The total amount of token0 swapped in matching
/// @return filledAmount1 The total amount of token1 swapped in matching
/// @return swapCount The count of swaps performed
/// @return swaps The array that contains data of swaps performed
function _matchOrder(
LimitOrder memory order,
uint32 orderId,
address owner,
bool isAsk
) internal returns (uint256, uint256, uint32, SwapData[] memory) {
MatchOrderLocalVars memory matchOrderLocalVars;
mapping(uint32 => LimitOrder) storage makerOrders = isAsk ? _orders.bids : _orders.asks;
matchOrderLocalVars.index = makerOrders[0].next;
while (matchOrderLocalVars.index != 1 && order.amount0Base > 0) {
LimitOrder storage bestOrder = makerOrders[matchOrderLocalVars.index];
matchOrderLocalVars.makerAddress = ownerIdToAddress[bestOrder.ownerId];
(matchOrderLocalVars.swapAmount0Base, matchOrderLocalVars.swapAmount1Base) = getLimitOrderSwapAmounts(
order.amount0Base,
order.priceBase,
bestOrder.amount0Base,
bestOrder.priceBase,
isAsk
);
if (matchOrderLocalVars.swapAmount0Base == 0 || matchOrderLocalVars.swapAmount1Base == 0) break;
matchOrderLocalVars.swapAmount0 = uint256(matchOrderLocalVars.swapAmount0Base) * sizeTick;
matchOrderLocalVars.swapAmount1 =
(uint256(matchOrderLocalVars.swapAmount1Base) * priceMultiplier) /
priceDivider;
if (isAsk) {
emit Swap(
orderId,
matchOrderLocalVars.index,
owner,
matchOrderLocalVars.makerAddress,
matchOrderLocalVars.swapAmount0,
matchOrderLocalVars.swapAmount1
);
} else {
emit Swap(
matchOrderLocalVars.index,
orderId,
matchOrderLocalVars.makerAddress,
owner,
matchOrderLocalVars.swapAmount0,
matchOrderLocalVars.swapAmount1
);
}
matchOrderLocalVars.filledAmount0 = matchOrderLocalVars.filledAmount0 + matchOrderLocalVars.swapAmount0;
matchOrderLocalVars.filledAmount1 = matchOrderLocalVars.filledAmount1 + matchOrderLocalVars.swapAmount1;
// if there are not enough free slots in the matchOrderLocalVars.matchedOrders, increase size to accommodate
if (matchOrderLocalVars.swapCount == matchOrderLocalVars.swapCapacity) {
// initial capacity will be 4, and we'll double afterwards
uint32 newCapacity = 4;
if (matchOrderLocalVars.swapCapacity != 0) {
newCapacity = matchOrderLocalVars.swapCapacity * 2;
}
SwapData[] memory newSwaps = new SwapData[](newCapacity);
for (uint32 i = 0; i < matchOrderLocalVars.swapCapacity; i += 1) {
newSwaps[i] = matchOrderLocalVars.swaps[i];
}
matchOrderLocalVars.swaps = newSwaps;
matchOrderLocalVars.swapCapacity = newCapacity;
}
matchOrderLocalVars.swaps[matchOrderLocalVars.swapCount++] = SwapData({
makerAddress: matchOrderLocalVars.makerAddress,
isPerfMode: (bestOrder.perfMode_creatorId & 1 == 1),
swapAmount: isAsk ? matchOrderLocalVars.swapAmount0 : matchOrderLocalVars.swapAmount1
});
order.amount0Base = order.amount0Base - matchOrderLocalVars.swapAmount0Base;
if (bestOrder.amount0Base == matchOrderLocalVars.swapAmount0Base) {
// Remove the best bid from the order book if it is fully filled
matchOrderLocalVars.atLeastOneFullSwap = true;
bestOrder.ownerId = 0;
} else {
// Update the best bid if it is partially filled
bestOrder.amount0Base = bestOrder.amount0Base - matchOrderLocalVars.swapAmount0Base;
break;
}
matchOrderLocalVars.index = bestOrder.next;
}
if (matchOrderLocalVars.atLeastOneFullSwap) {
makerOrders[matchOrderLocalVars.index].prev = 0;
makerOrders[0].next = matchOrderLocalVars.index;
}
return (
matchOrderLocalVars.filledAmount0,
matchOrderLocalVars.filledAmount1,
matchOrderLocalVars.swapCount,
matchOrderLocalVars.swaps
);
}
/// @dev Matches the given swap request (market order) against the available maker orders.
/// @param isAsk Indicates whether the swap request is an ask order or not
/// @param isExactInput Indicates whether the swapper indicated exact input or output
/// @param exactAmount The exact amount swapper wants to receive or send depending on isExactInput
/// @param thresholdAmount The minimum amount to be received or maximum amount to be sent
/// @param recipient The recipient address for swaps
/// @return filledAmount0 The total amount of token0 swapped in matching
/// @return filledAmount1 The total amount of token1 swapped in matching
/// @return swapCount The count of swaps performed
/// @return swaps The array that contains data of swaps performed
function _matchSwapOrder(
bool isAsk,
bool isExactInput,
uint256 exactAmount,
uint256 thresholdAmount,
address recipient
) internal returns (uint256, uint256, uint32, SwapData[] memory) {
MatchOrderLocalVars memory matchOrderLocalVars;
mapping(uint32 => LimitOrder) storage makerOrders = isAsk ? _orders.bids : _orders.asks;
matchOrderLocalVars.amount = exactAmount;
matchOrderLocalVars.index = makerOrders[0].next;
matchOrderLocalVars.fullTakerFill = exactAmount == 0;
while (matchOrderLocalVars.index != 1 && !matchOrderLocalVars.fullTakerFill) {
LimitOrder storage bestMatch = makerOrders[matchOrderLocalVars.index];
(
matchOrderLocalVars.swapAmount0,
matchOrderLocalVars.swapAmount1,
matchOrderLocalVars.makerAmount0BaseChange,
matchOrderLocalVars.fullTakerFill
) = (isExactInput && isAsk) || (!isExactInput && !isAsk)
? getSwapAmountsForToken0(matchOrderLocalVars.amount, isAsk, bestMatch.amount0Base, bestMatch.priceBase)
: getSwapAmountsForToken1(
matchOrderLocalVars.amount,
isAsk,
bestMatch.amount0Base,
bestMatch.priceBase
);
// If the swap amount is 0, break the loop since next orders guaranteed to have 0 as well
if (matchOrderLocalVars.swapAmount0 == 0 || matchOrderLocalVars.swapAmount1 == 0) break;
if (isAsk) {
emit Swap(
0, // emit 0 id for swap requests (market order)
matchOrderLocalVars.index,
recipient,
ownerIdToAddress[bestMatch.ownerId],
matchOrderLocalVars.swapAmount0,
matchOrderLocalVars.swapAmount1
);
} else {
emit Swap(
matchOrderLocalVars.index,
0, // emit 0 id for swap requests (market order)
ownerIdToAddress[bestMatch.ownerId],
recipient,
matchOrderLocalVars.swapAmount0,
matchOrderLocalVars.swapAmount1
);
}
matchOrderLocalVars.filledAmount0 += matchOrderLocalVars.swapAmount0;
matchOrderLocalVars.filledAmount1 += matchOrderLocalVars.swapAmount1;
// if there are not enough free slots in the matchOrderLocalVars.swaps, increase size to accommodate
if (matchOrderLocalVars.swapCount == matchOrderLocalVars.swapCapacity) {
// initial capacity will be 4, and we'll double afterwards
uint32 newCapacity = 4;
if (matchOrderLocalVars.swapCapacity != 0) {
newCapacity = matchOrderLocalVars.swapCapacity * 2;
}
SwapData[] memory newSwaps = new SwapData[](newCapacity);
for (uint32 i = 0; i < matchOrderLocalVars.swapCapacity; i += 1) {
newSwaps[i] = matchOrderLocalVars.swaps[i];
}
matchOrderLocalVars.swaps = newSwaps;
matchOrderLocalVars.swapCapacity = newCapacity;
}
matchOrderLocalVars.swaps[matchOrderLocalVars.swapCount++] = SwapData({
makerAddress: ownerIdToAddress[bestMatch.ownerId],
isPerfMode: (bestMatch.perfMode_creatorId & 1 == 1),
swapAmount: isAsk ? matchOrderLocalVars.swapAmount0 : matchOrderLocalVars.swapAmount1
});
if (bestMatch.amount0Base == matchOrderLocalVars.makerAmount0BaseChange) {
// Remove the best bid from the order book if it is fully filled
matchOrderLocalVars.atLeastOneFullSwap = true;
bestMatch.ownerId = 0;
} else {
// Update the best bid if it is partially filled
bestMatch.amount0Base -= matchOrderLocalVars.makerAmount0BaseChange;
break;
}
matchOrderLocalVars.index = bestMatch.next;
if (matchOrderLocalVars.fullTakerFill) {
// Break before updating the amount, if taker specifies exactOutput taker will receive largest
// amount of output tokens they can buy with same input needed for exactOutput. Amount can be
// negative if taker is receiving slightly more than exactOutput (depending on the ticks).
break;
}
if ((isAsk && isExactInput) || (!isAsk && !isExactInput)) {
matchOrderLocalVars.amount -= matchOrderLocalVars.swapAmount0;
} else {
matchOrderLocalVars.amount -= matchOrderLocalVars.swapAmount1;
}
}
if (matchOrderLocalVars.atLeastOneFullSwap) {
makerOrders[matchOrderLocalVars.index].prev = 0;
makerOrders[0].next = matchOrderLocalVars.index;
}
if (!matchOrderLocalVars.fullTakerFill) {
revert Errors.LighterV2Swap_NotEnoughLiquidity();
}
if (
isExactInput &&
((isAsk && matchOrderLocalVars.filledAmount1 < thresholdAmount) ||
(!isAsk && matchOrderLocalVars.filledAmount0 < thresholdAmount))
) {
revert Errors.LighterV2Swap_NotEnoughOutput();
} else if (
!isExactInput &&
((isAsk && matchOrderLocalVars.filledAmount0 > thresholdAmount) ||
(!isAsk && matchOrderLocalVars.filledAmount1 > thresholdAmount))
) {
revert Errors.LighterV2Swap_TooMuchRequested();
}
return (
matchOrderLocalVars.filledAmount0,
matchOrderLocalVars.filledAmount1,
matchOrderLocalVars.swapCount,
matchOrderLocalVars.swaps
);
}
/// @dev Handles the payment logic for a matched order.
/// @param paymentData The payment data containing information about the swaps and payments
function _handlePayments(PaymentData memory paymentData) internal {
// Determine debit and credit tokens based on the order type
IERC20Minimal debitToken = paymentData.isAsk ? token0 : token1;
IERC20Minimal creditToken = paymentData.isAsk ? token1 : token0;
uint256 debitTokenAmount = (paymentData.isAsk ? paymentData.filledAmount0 : paymentData.filledAmount1) +
paymentData.remainingLimitOrderAmount;
uint256 creditTokenAmount = paymentData.isAsk ? paymentData.filledAmount1 : paymentData.filledAmount0;
if (creditTokenAmount > 0) {
if (paymentData.isPerfMode) {
if (paymentData.isAsk) {
claimableToken1Balance[paymentData.recipient] += creditTokenAmount;
} else {
claimableToken0Balance[paymentData.recipient] += creditTokenAmount;
}
// Omit emitting ClaimableBalanceIncrease for gas savings, can be inferred from swap events
} else {
if (!_sendToken(creditToken, paymentData.recipient, creditTokenAmount)) {
revert Errors.LighterV2TokenTransfer_Failed();
}
}
}
if (paymentData.isPerfMode) {
if (paymentData.isAsk) {
if (claimableToken0Balance[msg.sender] < debitTokenAmount) {
revert Errors.LighterV2Order_InsufficientClaimableBalance();
}
claimableToken0Balance[msg.sender] -= debitTokenAmount;
} else {
if (claimableToken1Balance[msg.sender] < debitTokenAmount) {
revert Errors.LighterV2Order_InsufficientClaimableBalance();
}
claimableToken1Balance[msg.sender] -= debitTokenAmount;
}
// Omit emitting ClaimableBalanceDecrease for gas savings, can be inferred from swap and order creation events
} else {
uint256 debitTokenBalanceBeforeDebit = debitToken.balanceOf(address(this));
ILighterV2TransferCallback(msg.sender).lighterV2TransferCallback(
debitTokenAmount,
debitToken,
paymentData.callbackData
);
if (debitToken.balanceOf(address(this)) < (debitTokenBalanceBeforeDebit + debitTokenAmount)) {
revert Errors.LighterV2Order_InsufficentCallbackTransfer();
}
}
// Loop through swaps and transfer tokens to the maker order owners
for (uint32 swapIndex; swapIndex < paymentData.swapCount; ++swapIndex) {
SwapData memory swapData = paymentData.swaps[swapIndex];
if (swapData.isPerfMode) {
if (paymentData.isAsk) {
claimableToken0Balance[swapData.makerAddress] += swapData.swapAmount;
} else {
claimableToken1Balance[swapData.makerAddress] += swapData.swapAmount;
}
// omit emitting ClaimableBalanceIncrease for gas savings, can be inferred from swap events
} else {
bool success = _sendToken(debitToken, swapData.makerAddress, swapData.swapAmount);
if (!success) {
// if transfer to maker fails, mark the amount as claimable for maker
if (paymentData.isAsk) {
claimableToken0Balance[swapData.makerAddress] += swapData.swapAmount;
} else {
claimableToken1Balance[swapData.makerAddress] += swapData.swapAmount;
}
emit ClaimableBalanceIncrease(swapData.makerAddress, swapData.swapAmount, paymentData.isAsk);
}
}
}
}
/// @notice Transfer tokens from the order book to the user
/// @param tokenToTransfer The token to transfer
/// @param to The address to transfer to
/// @param amount The amount to transfer
/// @return success Whether the transfer was successful or not
function _sendToken(IERC20Minimal tokenToTransfer, address to, uint256 amount) internal returns (bool) {
uint256 orderBookBalanceBefore = tokenToTransfer.balanceOf(address(this));
bool success = false;
try tokenToTransfer.transfer(to, amount) returns (bool ret) {
success = ret;
} catch {
success = false;
}
uint256 sentAmount = success ? amount : 0;
if (tokenToTransfer.balanceOf(address(this)) + sentAmount < orderBookBalanceBefore) {
revert Errors.LighterV2Base_ContractBalanceDoesNotMatchSentAmount();
}
return success;
}
/// @inheritdoc IOrderBook
function suggestHintId(uint64 priceBase, bool isAsk) external view override returns (uint32) {
return _orders.suggestHintId(priceBase, isAsk);
}
/// @inheritdoc IOrderBook
function getLimitOrderSwapAmounts(
uint64 takerOrderAmount0Base,
uint64 takerOrderPriceBase,
uint64 makerOrderAmount0Base,
uint64 makerOrderPriceBase,
bool isTakerAsk
) public pure override returns (uint64 amount0BaseReturn, uint128 amount1BaseReturn) {
// If the takerOrder is an ask, and the makerOrder price is at least
// the takerOrder's price, then the takerOrder can be filled
// If the takerOrder is a bid, and the makerOrder price is at most
// the takerOrder's price, then the takerOrder can be filled
if (
(isTakerAsk && makerOrderPriceBase >= takerOrderPriceBase) ||
(!isTakerAsk && takerOrderPriceBase >= makerOrderPriceBase)
) {
if (takerOrderAmount0Base < makerOrderAmount0Base) {
amount0BaseReturn = takerOrderAmount0Base;
} else {
amount0BaseReturn = makerOrderAmount0Base;
}
return (amount0BaseReturn, uint128(amount0BaseReturn * makerOrderPriceBase));
}
return (0, 0);
}
/// @inheritdoc IOrderBook
function getSwapAmountsForToken0(
uint256 amount0,
bool isAsk,
uint64 makerAmount0Base,
uint64 makerPriceBase
)
public
view
override
returns (uint256 swapAmount0, uint256 swapAmount1, uint64 amount0BaseDelta, bool fullTakerFill)
{
uint256 amount0BaseToTake;
if (isAsk) {
amount0BaseToTake = amount0 / sizeTick;
} else {
amount0BaseToTake = Math.ceilDiv(amount0, sizeTick);
}
if (amount0BaseToTake > makerAmount0Base) {
amount0BaseToTake = makerAmount0Base;
fullTakerFill = false;
} else {
fullTakerFill = true;
}
amount0BaseDelta = uint64(amount0BaseToTake);
swapAmount0 = uint256(amount0BaseDelta) * sizeTick;
swapAmount1 = (uint256(amount0BaseDelta) * makerPriceBase * priceMultiplier) / priceDivider;
}
/// @inheritdoc IOrderBook
function getSwapAmountsForToken1(
uint256 amount1,
bool isAsk,
uint64 makerAmount0Base,
uint64 makerPriceBase
)
public
view
override
returns (uint256 swapAmount0, uint256 swapAmount1, uint64 amount0BaseDelta, bool fullTakerFill)
{
uint256 amount0BaseToTake = Math.mulDiv(amount1, priceDivider, makerPriceBase * priceMultiplier);
if (isAsk) {
swapAmount1 = (amount0BaseToTake * makerPriceBase * priceMultiplier) / priceDivider;
if (swapAmount1 < amount1) {
amount0BaseToTake += 1;
}
}
if (amount0BaseToTake > makerAmount0Base) {
amount0BaseToTake = makerAmount0Base;
fullTakerFill = false;
} else {
fullTakerFill = true;
}
amount0BaseDelta = uint64(amount0BaseToTake);
swapAmount1 = (uint256(amount0BaseDelta) * makerPriceBase * priceMultiplier) / priceDivider;
swapAmount0 = uint256(amount0BaseDelta) * sizeTick;
}
/// @inheritdoc IOrderBook
function getPaginatedOrders(
uint32 startOrderId,
bool isAsk,
uint32 limit
) external view override returns (OrderQueryItem memory) {
return _orders.getPaginatedOrders(startOrderId, isAsk, limit, ownerIdToAddress, sizeTick, priceTick);
}
/// @inheritdoc IOrderBook
function getLimitOrder(bool isAsk, uint32 id) external view override returns (LimitOrder memory) {
return isAsk ? _orders.asks[id] : _orders.bids[id];
}
/// @inheritdoc IOrderBook
function isOrderActive(uint32 id) public view override returns (bool) {
return _orders.asks[id].ownerId != 0 || _orders.bids[id].ownerId != 0;
}
/// @inheritdoc IOrderBook
function isAskOrder(uint32 id) public view override returns (bool) {
if (!isOrderActive(id)) {
revert Errors.LighterV2Order_OrderDoesNotExist();
}
return _orders.asks[id].ownerId > 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being 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.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @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 work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;
/// @title Minimal ERC20 interface for lighter
/// @notice Contains a subset of the full ERC20 interface that is used in lighter
interface IERC20Minimal {
/// @notice Returns the balance of the account provided
/// @param account The account to get the balance of
/// @return balance The balance of the account
function balanceOf(address account) external view returns (uint256);
/// @notice Transfers given amount of tokens from caller to the recipient
/// @param recipient The recipient of the transfer
/// @param amount The amount of the transfer
/// @return success Returns true for a successful transfer, false for unsuccessful
function transfer(address recipient, uint256 amount) external returns (bool);
/// @notice Transfers given amount of tokens from the sender to the recipient
/// @param sender The sender of the transfer
/// @param recipient The recipient of the transfer
/// @param amount The amount of the transfer
/// @return success Returns true for a successful transfer, false for unsuccessful
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/// @return decimals Returns the decimals of the token
function decimals() external returns (uint8);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.18;
/// @title Callback for IOrderBook#flashLoan
/// @notice Any contract that calls IOrderBook#flashLoan must implement this interface
interface ILighterV2FlashCallback {
/// @notice Called from `msg.sender` after transferring flashLoan to the recipient from IOrderBook#flashLoan
/// @dev In the implementation you must repay the pool the assets sent by flashLoan.
/// The caller of this method must be checked to be an order book deployed by the Factory
/// @param callbackData Data passed through by the caller via the IOrderBook#flashLoan call
function flashLoanCallback(bytes calldata callbackData) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.18;
import "./external/IERC20Minimal.sol";
/// @title Callback for IOrderBook#swapExactSingle and IOrderBook#createOrder
/// @notice Any contract that calls IOrderBook#swapExactSingle and IOrderBook#createOrder must implement this interface with one exception
/// @dev If orderType is PerformanceLimitOrder, then no need to implement this interface
/// @dev PerformanceLimitOrder handles payments with pre-deposited funds by market-makers
interface ILighterV2TransferCallback {
/// @notice Called by order book after transferring received assets from IOrderBook#swapExactInput or IOrderBook#swapExactOutput for payments
/// @dev In the implementation order creator must pay the order book the assets for the order
/// The caller of this method must be checked to be an order book deployed by the Factory
/// @param callbackData Data passed through by the caller via the IOrderBook#swapExactSingle or IOrderBook#swapExactOutput call
function lighterV2TransferCallback(
uint256 debitTokenAmount,
IERC20Minimal debitToken,
bytes calldata callbackData
) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.18;
import "../libraries/LinkedList.sol";
import "./external/IERC20Minimal.sol";
/// @title Order Book Interface
/// @notice Order book implements spot trading endpoints and storage for two assets which conform to the IERC20Minimal specification.
interface IOrderBook {
/// @notice Limit Order type.
enum OrderType {
LimitOrder, // Limit order
PerformanceLimitOrder, // Limit order that uses claimable balances
FoKOrder, // Fill or Kill limit order
IoCOrder // Immediate or Cancel limit order
}
/// @notice Struct to use for storing limit orders
struct LimitOrder {
uint32 perfMode_creatorId; // lowest bit for perfMode, remaining 31 bits for creatorId
uint32 prev; // id of the previous order in the list
uint32 next; // id of the next order in the list
uint32 ownerId; // id of the owner of the order
uint64 amount0Base; // amount0Base of the order
uint64 priceBase; // priceBase of the order
}
/// @notice Struct to use returning the paginated orders
struct OrderQueryItem {
bool isAsk; // true if the paginated orders are ask orders, false if bid orders
uint32[] ids; // order ids of returned orders
address[] owners; // owner addresses of returned orders
uint256[] amount0s; // amount0s of returned orders (amount0Base * sizeTick)
uint256[] prices; // prices of returned orders (priceBase * priceTick)
}
/// @notice Emitted when a limit order gets created
/// @param owner The address of the order owner
/// @param id The id of the order
/// @param amount0Base The amount of token0 in the limit order in terms of number of sizeTicks
/// @param priceBase The price of the token0 in terms of price ticks
/// @param isAsk Whether the order is an ask order
/// @param orderType type of the order
event CreateOrder(
address indexed owner,
uint32 indexed id,
uint64 amount0Base,
uint64 priceBase,
bool isAsk,
OrderType orderType
);
/// @notice Emitted when a limit order gets canceled
/// @param id The id of the canceled order
event CancelLimitOrder(uint32 indexed id);
/// @notice Emitted when a taker initiates a swap (market order)
/// @param sender The address that initiated the swap
/// @param recipient The address that received the tokens from the swap
/// @param isExactInput Whether the input amount is exact or output amount is exact
/// @param isAsk Whether the order is an ask order
/// @param swapAmount0 The amount of token0 that was swapped
/// @param swapAmount1 The amount of token1 that was swapped
event SwapExactAmount(
address indexed sender,
address indexed recipient,
bool isExactInput,
bool isAsk,
uint256 swapAmount0,
uint256 swapAmount1
);
/// @notice Emitted when a maker gets filled by a taker
/// @param askId The id of the ask order
/// @param bidId The id of the bid order
/// @param askOwner The address of the ask order owner
/// @param bidOwner The address of the bid order owner
/// @param amount0 The amount of token0 that was swapped
/// @param amount1 The amount of token1 that was swapped
event Swap(
uint32 indexed askId,
uint32 indexed bidId,
address askOwner,
address bidOwner,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when flashLoan is called
/// @param sender The address that initiated the flashLoan, and that received the callback
/// @param recipient The address that received the tokens from flash loan
/// @param amount0 The amount of token0 that was flash loaned
/// @param amount1 The amount of token1 that was flash loaned
event FlashLoan(address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1);
/// @notice Emitted when user claimable balance is increased due to deposit or order operations
event ClaimableBalanceIncrease(address indexed owner, uint256 amountDelta, bool isToken0);
/// @notice Emitted when user claimable balance is decreased due to withdraw or order operations
event ClaimableBalanceDecrease(address indexed owner, uint256 amountDelta, bool isToken0);
/// @notice Creates a limit order.
/// @param amount0Base The amount of token0 in the limit order in terms of number of sizeTicks.
/// amount0 is calculated by multiplying amount0Base by sizeTick.
/// @param priceBase The price of the token0 in terms of price ticks.
/// amount1 is calculated by multiplying priceBase by sizeTick and priceMultiplier and dividing by priceDivider.
/// @param isAsk Whether the order is an ask order
/// @param owner The address which will receive the funds and that can
/// cancel this order. When called by a router, it'll be populated
/// with msg.sender. Smart wallets should use msg.sender directly.
/// @param hintId Hint on where to insert the order in the order book.
/// Can be calculated with suggestHintId function, is not used for FoK and IoC orders.
/// @param orderType type of the order, if FoK or IoC remaining order will not be added for future matches.
/// @param callbackData data to be passed to callback
/// @return id The id of the order
function createOrder(
uint64 amount0Base,
uint64 priceBase,
bool isAsk,
address owner,
uint32 hintId,
OrderType orderType,
bytes memory callbackData
) external returns (uint32);
/// @notice Cancels an outstanding limit order. Refunds the remaining tokens in the order to the owner
/// @param id The id of the order to cancel
/// @param owner The address of the order sender
/// @return isCanceled Whether the order was successfully canceled or not
function cancelLimitOrder(uint32 id, address owner) external returns (bool);
/// @notice Swaps exact input or output amount of token0 or token1 for the other token
/// @param isAsk Whether the order is an ask order, if true sender pays token0 and receives token1
/// @param isExactInput Whether the input amount is exact or output amount is exact
/// @param exactAmount exact token amount to swap (can be token0 or token1 based on isAsk and isExactInput)
/// @param expectedAmount expected token amount to receive (can be token0 or token1 based on isAsk and isExactInput).
/// if isExactInput is true, then expectedAmount is the minimum amount to receive.
/// if isExactInput is false, then expectedAmount is the maximum amount to pay
/// @param recipient The address which will receive the output
/// @param callbackData data to be passed to callback
function swapExactSingle(
bool isAsk,
bool isExactInput,
uint256 exactAmount,
uint256 expectedAmount,
address recipient,
bytes memory callbackData
) external returns (uint256, uint256);
/// @notice Flash loans token0 and token1 to the recipient, sender receives the callback
/// @param recipient The address which will receive the token0 and token1
/// @param amount0 The amount of token0 to flash loan
/// @param amount1 The amount of token1 to flash loan
/// @param callbackData data to be passed to callback
function flashLoan(address recipient, uint256 amount0, uint256 amount1, bytes calldata callbackData) external;
/// @notice Deposits token0 or token1 from user to the order book and marks it as claimable
/// to be used for performance limit orders for gas efficient limit order creations.
/// @param amountToDeposit Amount to deposit
/// @param isToken0 Whether the deposit is token0 or token1
/// @param callbackData Byte data to send to callback
function depositToken(uint256 amountToDeposit, bool isToken0, bytes memory callbackData) external;
/// @notice Withdraws deposited or swapped token0 or token1 to the owner.
/// @param amountToClaim Amount to withdraw
/// @param isToken0 Whether the claimable token is token0 or token1
function claimToken(uint256 amountToClaim, bool isToken0) external;
/// @notice Finds the order id where the new order should be inserted to the right of
/// Meant to be used off-chain to find the hintId for limit order creation functions
/// @param priceBase basePrice derived from amount0Base and amount1Base
/// @param isAsk Whether the new order is an ask order
/// @return hintId The id of the order where the new order
/// should be inserted to the right of
function suggestHintId(uint64 priceBase, bool isAsk) external view returns (uint32);
/// @notice Returns the amount of token0 and token1 to traded between two limit orders
/// @param takerOrderAmount0Base The amount0Base of the taker order
/// @param takerOrderPriceBase The priceBase of the taker order
/// @param makerOrderAmount0Base The amount0Base of the maker order
/// @param makerOrderPriceBase The priceBase of the maker order
/// @param isTakerAsk True if taker order is an ask
/// @return amount0BaseReturn The amount0Base to be traded
/// @return amount1BaseReturn The amount1Base to be traded
function getLimitOrderSwapAmounts(
uint64 takerOrderAmount0Base,
uint64 takerOrderPriceBase,
uint64 makerOrderAmount0Base,
uint64 makerOrderPriceBase,
bool isTakerAsk
) external pure returns (uint64, uint128);
/// @notice Returns the amount of token0 and token1 to traded between maker and swapper
/// @param amount0 Exact token0 amount taker wants to trade
/// @param isAsk True if swapper is an ask
/// @param makerAmount0Base The amount0Base of the maker order
/// @param makerPriceBase The priceBase of the maker order
/// @return swapAmount0 The amount of token0 to be swapped
/// @return swapAmount1 The amount of token1 to be swapped
/// @return amount0BaseDelta Maker order baseAmount0 change
/// @return fullTakerFill True if swapper can be fully filled by maker order
function getSwapAmountsForToken0(
uint256 amount0,
bool isAsk,
uint64 makerAmount0Base,
uint64 makerPriceBase
) external view returns (uint256, uint256, uint64, bool);
/// @notice Returns the amount of token0 and token1 to traded between maker and swapper
/// @param amount1 Exact token1 amount taker wants to trade
/// @param isAsk True if swapper is an ask
/// @param makerAmount0Base The amount0Base of the maker order
/// @param makerPriceBase The priceBase of the maker order
/// @return swapAmount0 The amount of token0 to be swapped
/// @return swapAmount1 The amount of token1 to be swapped
/// @return amount0BaseDelta Maker order baseAmount0 change
/// @return fullTakerFill True if swapper can be fully filled by maker order
function getSwapAmountsForToken1(
uint256 amount1,
bool isAsk,
uint64 makerAmount0Base,
uint64 makerPriceBase
) external view returns (uint256, uint256, uint64, bool);
/// @notice Returns price sorted limit orders with pagination
/// @param startOrderId orderId from where the pagination should start (not inclusive)
/// @dev caller can pass 0 to start from the top of the book
/// @param isAsk Whether to return ask or bid orders
/// @param limit Number number of orders to return in the page
/// @return orderData The paginated order data
function getPaginatedOrders(
uint32 startOrderId,
bool isAsk,
uint32 limit
) external view returns (OrderQueryItem memory orderData);
/// @notice Returns the limit order of the given index
/// @param isAsk Whether the order is an ask order
/// @param id The id of the order
/// @return order The limit order
function getLimitOrder(bool isAsk, uint32 id) external view returns (LimitOrder memory);
/// @notice Returns whether an order is active or not
/// @param id The id of the order
/// @return isActive True if the order is active, false otherwise
function isOrderActive(uint32 id) external view returns (bool);
/// @notice Returns whether an order is an ask order or not, fails if order is not active
/// @param id The id of the order
/// @return isAsk True if the order is an ask order, false otherwise
function isAskOrder(uint32 id) external view returns (bool);
/// @notice Returns the constant for Log value of TickThreshold
/// @return LOG10_TICK_THRESHOLD threshold for Log value of TickThreshold
function LOG10_TICK_THRESHOLD() external view returns (uint8);
/// @notice Returns the constant for threshold value of orderId
/// @return ORDER_ID_THRESHOLD threshold for threshold value of orderId
function ORDER_ID_THRESHOLD() external view returns (uint32);
/// @notice Returns the constant for threshold value of creatorId
/// @return CREATOR_ID_THRESHOLD threshold for threshold value of creatorId
function CREATOR_ID_THRESHOLD() external view returns (uint32);
/// @notice The token0 (base token)
/// @return token0 The token0 (base token) contract
function token0() external view returns (IERC20Minimal);
/// @notice The token1 (quote token)
/// @return token1 The token1 (quote token) contract
function token1() external view returns (IERC20Minimal);
/// @notice Id of the order book
/// @return orderBookId The unique identifier of an order book
function orderBookId() external view returns (uint8);
/// @notice The sizeTick of the order book
/// @return sizeTick The sizeTick of the order book
function sizeTick() external view returns (uint128);
/// @notice The priceTick of the order book
/// @return priceTick The priceTick of the order book
function priceTick() external view returns (uint128);
/// @notice The priceMultiplier of the order book
/// @return priceMultiplier The priceMultiplier of the order book
function priceMultiplier() external view returns (uint128);
/// @notice The priceDivider of the order book
/// @return priceDivider The priceMultiplier of the order book
function priceDivider() external view returns (uint128);
/// @notice Returns the id of the next order Id to create
/// @return orderIdCounter id of the next order
function orderIdCounter() external view returns (uint32);
/// @notice minToken0BaseAmount minimum token0Base amount for limit order
/// @return minToken0BaseAmount minToken0BaseAmount of the order book
function minToken0BaseAmount() external view returns (uint64);
/// @notice minToken1BaseAmount minimum token1Base amount (token0Base * priceBase) for limit order
/// @return minToken1BaseAmount minToken1BaseAmount of the order book
function minToken1BaseAmount() external view returns (uint128);
/// @notice Claimable token0 amount for given address
/// @return claimableToken0Balance Claimable token0 amount for given address
function claimableToken0Balance(address owner) external view returns (uint256);
/// @notice Claimable token1 amount for given address
/// @return claimableToken1Balance Claimable token1 amount for given address
function claimableToken1Balance(address owner) external view returns (uint256);
/// @notice id of an order-owner
/// @return addressToOwnerId id of an order-owner
function addressToOwnerId(address owner) external view returns (uint32);
/// @notice address for given creatorId
/// @return addressToCreatorId address for given creatorId
function addressToCreatorId(address creatorAddress) external view returns (uint32);
/// @notice id of a creatorAddress
/// @return creatorIdToAddress id of a creatorAddress
function creatorIdToAddress(uint32 creatorId) external view returns (address);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.18;
/// @title Errors
/// @notice Library containing errors that Lighter V2 Core functions may revert with
library Errors {
/*//////////////////////////////////////////////////////////////////////////
LIGHTER-V2-FACTORY
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when `msg.sender` is not the factory owner for setOwner or createOrderBook
error LighterV2Factory_CallerNotOwner();
/// @notice Thrown when zero address is passed when setting the owner
error LighterV2Factory_OwnerCannotBeZero();
/*//////////////////////////////////////////////////////////////////////////
LIGHTER-V2-CREATE-ORDER-BOOK
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when token0 and token1 are identical or zero in order book creation
error LighterV2CreateOrderBook_InvalidTokenPair();
/// @notice Thrown when an order book already exists with given token0 and token1 in order book creation
error LighterV2CreateOrderBook_OrderBookAlreadyExists();
/// @notice Thrown when order book capacity is already reached in order book creation
error LighterV2CreateOrderBook_OrderBookIdExceedsLimit();
/// @notice Thrown when invalid combination of logSizeTick and logPriceTick is given in order book creation
error LighterV2CreateOrderBook_InvalidTickCombination();
/// @notice Thrown when invalid combination of minToken0BaseAmount and minToken1BaseAmount given in order book creation
error LighterV2CreateOrderBook_InvalidMinAmount();
/*//////////////////////////////////////////////////////////////////////////
LIGHTER-V2-ORDER
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when invalid hintId is given in limit order creation
error LighterV2Order_InvalidHintId();
/// @notice Thrown when given price is too small in order creation
error LighterV2Order_PriceTooSmall();
/// @notice Thrown when given price is too big in order creation
error LighterV2Order_PriceTooBig();
/// @notice Thrown when token0 or token1 amount is too small in limit order creation
error LighterV2Order_AmountTooSmall();
/// @notice Thrown when order capacity is already reached in order creation
error LighterV2Order_OrderIdExceedsLimit();
/// @notice Thrown when creator capacity is already reached in order creation
error LighterV2Order_CreatorIdExceedsLimit();
/// @notice Thrown when tokens sent callback is insufficient in order creation or swap
error LighterV2Order_InsufficentCallbackTransfer();
/// @notice Thrown when claimable balance is insufficient in order creation
error LighterV2Order_InsufficientClaimableBalance();
/// @notice Thrown when FillOrKill order is not fully filled
error LighterV2Order_FoKNotFilled();
/// @notice Thrown when contract balance decrease is larger than the transfered amount
error LighterV2Base_ContractBalanceDoesNotMatchSentAmount();
/// @notice Thrown when caller is not the order creator or owner in order cancelation
error LighterV2Owner_CallerCannotCancel();
/// @notice Thrown when caller tries to erase head or tail orders in order linked list
error LighterV2Order_CannotEraseHeadOrTailOrders();
/// @notice Thrown when caller tries to cancel an order that is not active
error LighterV2Order_CannotCancelInactiveOrders();
/// @notice Thrown when caller asks for order side for a inactive or non-existent order
error LighterV2Order_OrderDoesNotExist();
/// @notice Thrown when caller tries to query an order book page starting from an inactive order
error LighterV2Order_CannotQueryFromInactiveOrder();
/*//////////////////////////////////////////////////////////////////////////
LIGHTER-SWAP
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when order book does not have enough liquidity to fill the swap
error LighterV2Swap_NotEnoughLiquidity();
/// @notice Thrown when swapper receives less than the minimum amount of tokens expected
error LighterV2Swap_NotEnoughOutput();
/// @notice Thrown when swapper needs to pay more than the maximum amount of tokens they are willing to pay
error LighterV2Swap_TooMuchRequested();
/*//////////////////////////////////////////////////////////////////////////
LIGHTER-V2-VAULT
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when caller tries to withdraw more than their balance or withdraw zero
error LighterV2Vault_InvalidClaimAmount();
/// @notice Thrown when caller does not tranfer enough tokens to the vault when depositing
error LighterV2Vault_InsufficentCallbackTransfer();
/*//////////////////////////////////////////////////////////////////////////
LIGHTER-V2-FLASH-LOAN
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when caller does not tranfer enough tokens to repay for the flash loan
error LighterV2FlashLoan_InsufficentCallbackTransfer();
/*//////////////////////////////////////////////////////////////////////////
LIGHTER-V2-TOKEN-TRANSFER
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when token transfer from order book fails
error LighterV2TokenTransfer_Failed();
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.18;
import "./Errors.sol";
import "../interfaces/IOrderBook.sol";
/// @title LinkedList
/// @notice Struct to use for storing sorted linked lists of ask and bid orders
struct LinkedList {
mapping(uint32 => IOrderBook.LimitOrder) asks;
mapping(uint32 => IOrderBook.LimitOrder) bids;
}
/// @title LinkedListLib
/// @notice Implements a sorted linked list of limit orders and provides necessary functions for order management
/// @dev Head is represented by order id 0, tail is represented by order id 1
library LinkedListLib {
/// @notice Inserts an order into the respective linked list and keeps sorted order
/// @param orderId id of the order to insert
/// @param isAsk true if the order is an ask order, false if the order is a bid order
/// @param hintId hint id of the order where the new order should be inserted to the right of
function insert(LinkedList storage self, uint32 orderId, bool isAsk, uint32 hintId) internal {
mapping(uint32 => IOrderBook.LimitOrder) storage orders = isAsk ? self.asks : self.bids;
IOrderBook.LimitOrder storage order = orders[orderId];
if (orders[hintId].next == 0) {
revert Errors.LighterV2Order_InvalidHintId();
}
while (orders[hintId].ownerId == 0) {
hintId = orders[hintId].next;
}
// After the search, hintId will be where the new order should be inserted to the right of
IOrderBook.LimitOrder memory hintOrder = orders[hintId];
while (hintId != 1) {
IOrderBook.LimitOrder memory nextOrder = orders[hintOrder.next];
if (isAsk ? (order.priceBase < nextOrder.priceBase) : (order.priceBase > nextOrder.priceBase)) break;
hintId = hintOrder.next;
hintOrder = nextOrder;
}
while (hintId != 0) {
if (isAsk ? (order.priceBase >= hintOrder.priceBase) : (order.priceBase <= hintOrder.priceBase)) break;
hintId = hintOrder.prev;
hintOrder = orders[hintId];
}
order.prev = hintId;
order.next = orders[hintId].next;
orders[order.prev].next = orderId;
orders[order.next].prev = orderId;
}
/// @notice Removes given order id from the respective linked list
/// @dev Updates the respective linked list but does not delete the order, sets the ownerId to 0 instead
/// @param orderId The order id to remove
/// @param isAsk true if the order is an ask order, false if the order is a bid order
function erase(LinkedList storage self, uint32 orderId, bool isAsk) internal {
if (orderId <= 1) {
revert Errors.LighterV2Order_CannotEraseHeadOrTailOrders();
}
mapping(uint32 => IOrderBook.LimitOrder) storage orders = isAsk ? self.asks : self.bids;
if (orders[orderId].ownerId == 0) {
revert Errors.LighterV2Order_CannotCancelInactiveOrders();
}
IOrderBook.LimitOrder storage order = orders[orderId];
order.ownerId = 0;
uint32 prev = order.prev;
uint32 next = order.next;
orders[prev].next = next;
orders[next].prev = prev;
}
/// @notice Returns a struct that represents order page with given parameters
/// @param startOrderId The order id to start the pagination from (not inclusive)
/// @param isAsk true if the paginated orders are ask orders, false if bid orders
/// @param limit The number of orders to return
/// @param ownerIdToAddress Mapping from owner id to owner address
/// @param sizeTick The size tick of the order book
/// @param priceTick The price tick of the order book
function getPaginatedOrders(
LinkedList storage self,
uint32 startOrderId,
bool isAsk,
uint32 limit,
mapping(uint32 => address) storage ownerIdToAddress,
uint128 sizeTick,
uint128 priceTick
) public view returns (IOrderBook.OrderQueryItem memory paginatedOrders) {
mapping(uint32 => IOrderBook.LimitOrder) storage orders = isAsk ? self.asks : self.bids;
if (orders[startOrderId].ownerId == 0) {
revert Errors.LighterV2Order_CannotQueryFromInactiveOrder();
}
uint32 i = 0;
paginatedOrders.ids = new uint32[](limit);
paginatedOrders.owners = new address[](limit);
paginatedOrders.amount0s = new uint256[](limit);
paginatedOrders.prices = new uint256[](limit);
for (uint32 pointer = orders[startOrderId].next; pointer != 1 && i < limit; pointer = orders[pointer].next) {
IOrderBook.LimitOrder memory order = orders[pointer];
paginatedOrders.ids[i] = pointer;
paginatedOrders.owners[i] = ownerIdToAddress[order.ownerId];
paginatedOrders.amount0s[i] = uint256(order.amount0Base) * sizeTick;
paginatedOrders.prices[i] = order.priceBase * priceTick;
unchecked {
++i;
}
}
paginatedOrders.isAsk = isAsk;
}
/// @notice Finds the order id where the order with given price should be inserted to the right of
/// @param priceBase The priceBase to suggest the hintId for
/// @return hintId The order id where the order with given price should be inserted to the right of
function suggestHintId(LinkedList storage self, uint64 priceBase, bool isAsk) public view returns (uint32) {
mapping(uint32 => IOrderBook.LimitOrder) storage orders = isAsk ? self.asks : self.bids;
uint32 hintOrderId = 0;
IOrderBook.LimitOrder memory hintOrder = orders[hintOrderId];
while (hintOrderId != 1) {
IOrderBook.LimitOrder memory nextOrder = orders[hintOrder.next];
if (isAsk ? (priceBase < nextOrder.priceBase) : (priceBase > nextOrder.priceBase)) break;
hintOrderId = hintOrder.next;
hintOrder = nextOrder;
}
return hintOrderId;
}
}{
"optimizer": {
"enabled": true,
"runs": 4150
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {
"contracts/libraries/LinkedList.sol": {
"LinkedListLib": "0x43ccc6d2a517a9a0955fba24c73f2202da5af8b3"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"uint8","name":"_orderBookId","type":"uint8"},{"internalType":"address","name":"_token0Address","type":"address"},{"internalType":"address","name":"_token1Address","type":"address"},{"internalType":"uint8","name":"_logSizeTick","type":"uint8"},{"internalType":"uint8","name":"_logPriceTick","type":"uint8"},{"internalType":"uint64","name":"_minToken0BaseAmount","type":"uint64"},{"internalType":"uint128","name":"_minToken1BaseAmount","type":"uint128"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"LighterV2Base_ContractBalanceDoesNotMatchSentAmount","type":"error"},{"inputs":[],"name":"LighterV2CreateOrderBook_InvalidMinAmount","type":"error"},{"inputs":[],"name":"LighterV2CreateOrderBook_InvalidTickCombination","type":"error"},{"inputs":[],"name":"LighterV2FlashLoan_InsufficentCallbackTransfer","type":"error"},{"inputs":[],"name":"LighterV2Order_AmountTooSmall","type":"error"},{"inputs":[],"name":"LighterV2Order_CannotCancelInactiveOrders","type":"error"},{"inputs":[],"name":"LighterV2Order_CannotEraseHeadOrTailOrders","type":"error"},{"inputs":[],"name":"LighterV2Order_CreatorIdExceedsLimit","type":"error"},{"inputs":[],"name":"LighterV2Order_FoKNotFilled","type":"error"},{"inputs":[],"name":"LighterV2Order_InsufficentCallbackTransfer","type":"error"},{"inputs":[],"name":"LighterV2Order_InsufficientClaimableBalance","type":"error"},{"inputs":[],"name":"LighterV2Order_InvalidHintId","type":"error"},{"inputs":[],"name":"LighterV2Order_OrderDoesNotExist","type":"error"},{"inputs":[],"name":"LighterV2Order_OrderIdExceedsLimit","type":"error"},{"inputs":[],"name":"LighterV2Order_PriceTooBig","type":"error"},{"inputs":[],"name":"LighterV2Order_PriceTooSmall","type":"error"},{"inputs":[],"name":"LighterV2Owner_CallerCannotCancel","type":"error"},{"inputs":[],"name":"LighterV2Swap_NotEnoughLiquidity","type":"error"},{"inputs":[],"name":"LighterV2Swap_NotEnoughOutput","type":"error"},{"inputs":[],"name":"LighterV2Swap_TooMuchRequested","type":"error"},{"inputs":[],"name":"LighterV2TokenTransfer_Failed","type":"error"},{"inputs":[],"name":"LighterV2Vault_InsufficentCallbackTransfer","type":"error"},{"inputs":[],"name":"LighterV2Vault_InvalidClaimAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"id","type":"uint32"}],"name":"CancelLimitOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isToken0","type":"bool"}],"name":"ClaimableBalanceDecrease","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountDelta","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isToken0","type":"bool"}],"name":"ClaimableBalanceIncrease","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint32","name":"id","type":"uint32"},{"indexed":false,"internalType":"uint64","name":"amount0Base","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"priceBase","type":"uint64"},{"indexed":false,"internalType":"bool","name":"isAsk","type":"bool"},{"indexed":false,"internalType":"enum IOrderBook.OrderType","name":"orderType","type":"uint8"}],"name":"CreateOrder","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"FlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"askId","type":"uint32"},{"indexed":true,"internalType":"uint32","name":"bidId","type":"uint32"},{"indexed":false,"internalType":"address","name":"askOwner","type":"address"},{"indexed":false,"internalType":"address","name":"bidOwner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"bool","name":"isExactInput","type":"bool"},{"indexed":false,"internalType":"bool","name":"isAsk","type":"bool"},{"indexed":false,"internalType":"uint256","name":"swapAmount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"swapAmount1","type":"uint256"}],"name":"SwapExactAmount","type":"event"},{"inputs":[],"name":"CREATOR_ID_THRESHOLD","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LOG10_TICK_THRESHOLD","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PRICE","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ORDER_ID_THRESHOLD","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"addressToCreatorId","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"addressToOwnerId","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"address","name":"owner","type":"address"}],"name":"cancelLimitOrder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountToClaim","type":"uint256"},{"internalType":"bool","name":"isToken0","type":"bool"}],"name":"claimToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimableToken0Balance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimableToken1Balance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"amount0Base","type":"uint64"},{"internalType":"uint64","name":"priceBase","type":"uint64"},{"internalType":"bool","name":"isAsk","type":"bool"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint32","name":"hintId","type":"uint32"},{"internalType":"enum IOrderBook.OrderType","name":"orderType","type":"uint8"},{"internalType":"bytes","name":"callbackData","type":"bytes"}],"name":"createOrder","outputs":[{"internalType":"uint32","name":"newOrderId","type":"uint32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"creatorIdToAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountToDeposit","type":"uint256"},{"internalType":"bool","name":"isToken0","type":"bool"},{"internalType":"bytes","name":"callbackData","type":"bytes"}],"name":"depositToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"bytes","name":"callbackData","type":"bytes"}],"name":"flashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"isAsk","type":"bool"},{"internalType":"uint32","name":"id","type":"uint32"}],"name":"getLimitOrder","outputs":[{"components":[{"internalType":"uint32","name":"perfMode_creatorId","type":"uint32"},{"internalType":"uint32","name":"prev","type":"uint32"},{"internalType":"uint32","name":"next","type":"uint32"},{"internalType":"uint32","name":"ownerId","type":"uint32"},{"internalType":"uint64","name":"amount0Base","type":"uint64"},{"internalType":"uint64","name":"priceBase","type":"uint64"}],"internalType":"struct IOrderBook.LimitOrder","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"takerOrderAmount0Base","type":"uint64"},{"internalType":"uint64","name":"takerOrderPriceBase","type":"uint64"},{"internalType":"uint64","name":"makerOrderAmount0Base","type":"uint64"},{"internalType":"uint64","name":"makerOrderPriceBase","type":"uint64"},{"internalType":"bool","name":"isTakerAsk","type":"bool"}],"name":"getLimitOrderSwapAmounts","outputs":[{"internalType":"uint64","name":"amount0BaseReturn","type":"uint64"},{"internalType":"uint128","name":"amount1BaseReturn","type":"uint128"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint32","name":"startOrderId","type":"uint32"},{"internalType":"bool","name":"isAsk","type":"bool"},{"internalType":"uint32","name":"limit","type":"uint32"}],"name":"getPaginatedOrders","outputs":[{"components":[{"internalType":"bool","name":"isAsk","type":"bool"},{"internalType":"uint32[]","name":"ids","type":"uint32[]"},{"internalType":"address[]","name":"owners","type":"address[]"},{"internalType":"uint256[]","name":"amount0s","type":"uint256[]"},{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"internalType":"struct IOrderBook.OrderQueryItem","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"bool","name":"isAsk","type":"bool"},{"internalType":"uint64","name":"makerAmount0Base","type":"uint64"},{"internalType":"uint64","name":"makerPriceBase","type":"uint64"}],"name":"getSwapAmountsForToken0","outputs":[{"internalType":"uint256","name":"swapAmount0","type":"uint256"},{"internalType":"uint256","name":"swapAmount1","type":"uint256"},{"internalType":"uint64","name":"amount0BaseDelta","type":"uint64"},{"internalType":"bool","name":"fullTakerFill","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount1","type":"uint256"},{"internalType":"bool","name":"isAsk","type":"bool"},{"internalType":"uint64","name":"makerAmount0Base","type":"uint64"},{"internalType":"uint64","name":"makerPriceBase","type":"uint64"}],"name":"getSwapAmountsForToken1","outputs":[{"internalType":"uint256","name":"swapAmount0","type":"uint256"},{"internalType":"uint256","name":"swapAmount1","type":"uint256"},{"internalType":"uint64","name":"amount0BaseDelta","type":"uint64"},{"internalType":"bool","name":"fullTakerFill","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"}],"name":"isAskOrder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"}],"name":"isOrderActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minToken0BaseAmount","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minToken1BaseAmount","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"orderBookId","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"orderIdCounter","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"ownerIdToAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceDivider","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceMultiplier","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceTick","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sizeTick","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"priceBase","type":"uint64"},{"internalType":"bool","name":"isAsk","type":"bool"}],"name":"suggestHintId","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"isAsk","type":"bool"},{"internalType":"bool","name":"isExactInput","type":"bool"},{"internalType":"uint256","name":"exactAmount","type":"uint256"},{"internalType":"uint256","name":"expectedAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"bytes","name":"callbackData","type":"bytes"}],"name":"swapExactSingle","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token0","outputs":[{"internalType":"contract IERC20Minimal","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1","outputs":[{"internalType":"contract IERC20Minimal","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6101a06040523480156200001257600080fd5b5060405162005ceb38038062005ceb8339810160408190526200003591620008c6565b600160009081556001600160a01b03808816608081905290871660a05260ff891660c0526040805163313ce56760e01b8152905163313ce56791600480820192602092909190829003018187875af115801562000096573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000bc919062000979565b9050602660ff8616101580620000d65750602660ff851610155b15620000f5576040516337d89f8960e01b815260040160405180910390fd5b6200010285600a62000ab3565b6001600160801b0316610120526200011c84600a62000ab3565b6001600160801b03166101405260018060ff83166200013c878962000ac4565b60ff1610620001b05760268362000154888a62000ac4565b62000160919062000ae0565b60ff161062000182576040516337d89f8960e01b815260040160405180910390fd5b826200018f878962000ac4565b6200019b919062000ae0565b620001a890600a62000ab3565b915062000216565b602686620001bf898662000ae0565b620001cb919062000ae0565b60ff1610620001ed576040516337d89f8960e01b815260040160405180910390fd5b86620001fa878562000ae0565b62000206919062000ae0565b6200021390600a62000ab3565b90505b6001600160801b03808316610160528116610180526001600160401b03851615806200024957506001600160801b038416155b15620002685760405163bd35b3bb60e01b815260040160405180910390fd5b846001600160401b031660e0816001600160401b031681525050836001600160801b0316610100816001600160801b0316815250506040518060c00160405280600063ffffffff168152602001600063ffffffff168152602001600163ffffffff168152602001600163ffffffff16815260200160006001600160401b0316815260200160006001600160401b0316815250600260000160008063ffffffff16815260200190815260200160002060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160086101000a81548163ffffffff021916908363ffffffff160217905550606082015181600001600c6101000a81548163ffffffff021916908363ffffffff16021790555060808201518160000160106101000a8154816001600160401b0302191690836001600160401b0316021790555060a08201518160000160186101000a8154816001600160401b0302191690836001600160401b031602179055509050506040518060c00160405280600063ffffffff168152602001600063ffffffff168152602001600163ffffffff168152602001600163ffffffff16815260200160006001600160401b031681526020016001600160401b03801681525060026000016000600163ffffffff16815260200190815260200160002060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160086101000a81548163ffffffff021916908363ffffffff160217905550606082015181600001600c6101000a81548163ffffffff021916908363ffffffff16021790555060808201518160000160106101000a8154816001600160401b0302191690836001600160401b0316021790555060a08201518160000160186101000a8154816001600160401b0302191690836001600160401b031602179055509050506040518060c00160405280600063ffffffff168152602001600063ffffffff168152602001600163ffffffff168152602001600163ffffffff16815260200160006001600160401b031681526020016001600160401b038016815250600260010160008063ffffffff16815260200190815260200160002060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160086101000a81548163ffffffff021916908363ffffffff160217905550606082015181600001600c6101000a81548163ffffffff021916908363ffffffff16021790555060808201518160000160106101000a8154816001600160401b0302191690836001600160401b0316021790555060a08201518160000160186101000a8154816001600160401b0302191690836001600160401b031602179055509050506040518060c00160405280600063ffffffff168152602001600063ffffffff168152602001600163ffffffff168152602001600163ffffffff16815260200160006001600160401b0316815260200160006001600160401b031681525060026001016000600163ffffffff16815260200190815260200160002060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548163ffffffff021916908363ffffffff16021790555060408201518160000160086101000a81548163ffffffff021916908363ffffffff160217905550606082015181600001600c6101000a81548163ffffffff021916908363ffffffff16021790555060808201518160000160106101000a8154816001600160401b0302191690836001600160401b0316021790555060a08201518160000160186101000a8154816001600160401b0302191690836001600160401b031602179055509050506002600160006101000a81548163ffffffff021916908363ffffffff1602179055505050505050505050505062000afc565b805160ff81168114620008a957600080fd5b919050565b80516001600160a01b0381168114620008a957600080fd5b600080600080600080600060e0888a031215620008e257600080fd5b620008ed8862000897565b9650620008fd60208901620008ae565b95506200090d60408901620008ae565b94506200091d6060890162000897565b93506200092d6080890162000897565b60a08901519093506001600160401b03811681146200094b57600080fd5b60c08901519092506001600160801b03811681146200096957600080fd5b8091505092959891949750929550565b6000602082840312156200098c57600080fd5b620009978262000897565b9392505050565b634e487b7160e01b600052601160045260246000fd5b600181815b80851115620009f5578160001904821115620009d957620009d96200099e565b80851615620009e757918102915b93841c9390800290620009b9565b509250929050565b60008262000a0e5750600162000aad565b8162000a1d5750600062000aad565b816001811462000a36576002811462000a415762000a61565b600191505062000aad565b60ff84111562000a555762000a556200099e565b50506001821b62000aad565b5060208310610133831016604e8410600b841016171562000a86575081810a62000aad565b62000a928383620009b4565b806000190482111562000aa95762000aa96200099e565b0290505b92915050565b60006200099760ff841683620009fd565b60ff818116838216019081111562000aad5762000aad6200099e565b60ff828116828216039081111562000aad5762000aad6200099e565b60805160a05160c05160e051610100516101205161014051610160516101805161505862000c936000396000818161037c01528181610baf01528181610c2301528181610cd8015281816112210152818161161201528181611ca4015281816122b30152612cd40152600081816106b601528181610bd401528181610c4701528181610d020152818161163c01528181611cce015281816122dd0152612cfc01526000818161040c015261252b0152600081816102db01528181610d6d015281816116a701528181611bd101528181611c0401528181611c7001528181612199015281816125030152612c7d01526000818161029c01526113b10152600081816104c4015261136c015260006104eb0152600081816105ea0152818161073c01528181610e6101528181610f34015281816110c301528181611e4b015281816123570152818161319f015261321a01526000818161025d0152818161076201528181610dcb01528181610ede0152818161100001528181611dd0015281816121f0015281816131c501526131f401526150586000f3fe608060405234801561001057600080fd5b50600436106102265760003560e01c80639087f4741161012a578063be374423116100bd578063d2f2b7661161008c578063d50cb88b11610071578063d50cb88b146106b1578063d819e812146106d8578063e4ae2e281461070157600080fd5b8063d2f2b7661461060c578063d4105e5d1461068957600080fd5b8063be374423146105aa578063cd6c78d0146105ba578063d0e76269146105da578063d21220a7146105e557600080fd5b8063a89883a2116100f9578063a89883a214610546578063b2d6d47514610566578063b866365a1461058c578063b90f2a401461059f57600080fd5b80639087f474146104e657806392250a471461050d57806396e7952e14610520578063a29d824c1461053357600080fd5b8063365044a8116101bd578063620a92351161018c5780637f2ac67a116101715780637f2ac67a14610486578063818748bd14610499578063829d600a146104bf57600080fd5b8063620a92351461042e578063679f05791461047357600080fd5b8063365044a81461037757806348e057ed1461039e57806349d79bc4146103cc5780634a9f29601461040757600080fd5b80632a2ef375116101f95780632a2ef375146102fd5780632cea5cd3146103125780632e53eda51461033557806334d441d31461035d57600080fd5b806301c11d961461022b5780630dfe16811461025857806320957ed31461029757806327d7d68e146102d6575b600080fd5b61023a67ffffffffffffffff81565b60405167ffffffffffffffff90911681526020015b60405180910390f35b61027f7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200161024f565b6102be7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160801b03909116815260200161024f565b6102be7f000000000000000000000000000000000000000000000000000000000000000081565b61031061030b366004614588565b61072a565b005b6103256103203660046145f3565b6109d8565b604051901515815260200161024f565b61034861034336600461462d565b610a42565b60405163ffffffff909116815260200161024f565b610365602681565b60405160ff909116815260200161024f565b6102be7f000000000000000000000000000000000000000000000000000000000000000081565b6103be6103ac366004614679565b60056020526000908152604090205481565b60405190815260200161024f565b6103df6103da366004614696565b610af3565b6040805167ffffffffffffffff90931683526001600160801b0390911660208301520161024f565b6102be7f000000000000000000000000000000000000000000000000000000000000000081565b61044161043c3660046146ff565b610b9b565b60405161024f9493929190938452602084019290925267ffffffffffffffff1660408301521515606082015260800190565b61031061048136600461474e565b610dab565b6103486104943660046147e4565b6111c7565b6103486104a7366004614679565b60076020526000908152604090205463ffffffff1681565b61023a7f000000000000000000000000000000000000000000000000000000000000000081565b6103657f000000000000000000000000000000000000000000000000000000000000000081565b61044161051b3660046146ff565b611bb6565b61031061052e366004614891565b611d38565b6103256105413660046148b6565b611f1a565b6103be610554366004614679565b60046020526000908152604090205481565b610348610574366004614679565b60086020526000908152604090205463ffffffff1681565b61032561059a3660046145f3565b612426565b61034863ffffffff81565b6001546103489063ffffffff1681565b6105cd6105c83660046148e4565b612475565b60405161024f91906149a3565b610348637fffffff81565b61027f7f000000000000000000000000000000000000000000000000000000000000000081565b61061f61061a366004614a5d565b6125be565b60405161024f9190600060c08201905063ffffffff80845116835280602085015116602084015280604085015116604084015280606085015116606084015250608083015167ffffffffffffffff80821660808501528060a08601511660a0850152505092915050565b61069c610697366004614a8b565b61269b565b6040805192835260208301919091520161024f565b6102be7f000000000000000000000000000000000000000000000000000000000000000081565b61027f6106e63660046145f3565b6006602052600090815260409020546001600160a01b031681565b61027f61070f3660046145f3565b6009602052600090815260409020546001600160a01b031681565b610732612792565b33600083610760577f0000000000000000000000000000000000000000000000000000000000000000610782565b7f00000000000000000000000000000000000000000000000000000000000000005b6040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa1580156107cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107f09190614b12565b6040517f727f979c0000000000000000000000000000000000000000000000000000000081529091506001600160a01b0384169063727f979c9061083c90899086908990600401614b2b565b600060405180830381600087803b15801561085657600080fd5b505af115801561086a573d6000803e3d6000fd5b50505050858161087a9190614ba7565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156108be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e29190614b12565b101561091a576040517fe827b48700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8415610953576001600160a01b03831660009081526004602052604081208054889290610948908490614ba7565b909155506109819050565b6001600160a01b0383166000908152600560205260408120805488929061097b908490614ba7565b90915550505b6040805187815286151560208201526001600160a01b038516917ff939ff49cec37eed853259ac7b9b4c0a8c7aacb9feca8467b3d54512ba26429a910160405180910390a25050506109d36001600055565b505050565b60006109e382612426565b610a19576040517f7312861b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5063ffffffff9081166000908152600260205260409020546001600160601b9091049091161190565b6040517fd271c2c20000000000000000000000000000000000000000000000000000000081526002600482015267ffffffffffffffff8316602482015281151560448201526000907343ccc6d2a517a9a0955fba24c73f2202da5af8b39063d271c2c290606401602060405180830381865af4158015610ac6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aea9190614bba565b90505b92915050565b600080828015610b1757508567ffffffffffffffff168467ffffffffffffffff1610155b80610b3f575082158015610b3f57508367ffffffffffffffff168667ffffffffffffffff1610155b15610b8a578467ffffffffffffffff168767ffffffffffffffff161015610b6857869150610b6c565b8491505b81610b778582614bd7565b67ffffffffffffffff1691509150610b91565b5060009050805b9550959350505050565b600080808080610c11896001600160801b037f000000000000000000000000000000000000000000000000000000000000000016610c037f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff8b16614c03565b6001600160801b031661280a565b90508715610ca7576001600160801b037f00000000000000000000000000000000000000000000000000000000000000008116907f000000000000000000000000000000000000000000000000000000000000000016610c7b67ffffffffffffffff891684614c26565b610c859190614c26565b610c8f9190614c53565b935088841015610ca757610ca4600182614ba7565b90505b8667ffffffffffffffff16811115610cce57506000905067ffffffffffffffff8616610cd3565b600191505b8092507f00000000000000000000000000000000000000000000000000000000000000006001600160801b03167f00000000000000000000000000000000000000000000000000000000000000006001600160801b03168767ffffffffffffffff168567ffffffffffffffff16610d4a9190614c26565b610d549190614c26565b610d5e9190614c53565b9350610d9d6001600160801b037f00000000000000000000000000000000000000000000000000000000000000001667ffffffffffffffff8516614c26565b945050945094509450949050565b610db3612792565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015610e1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e3e9190614b12565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015610ea8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ecc9190614b12565b9050600086118015610f065750610f047f00000000000000000000000000000000000000000000000000000000000000008888612916565b155b15610f24576040516309e1fb8b60e31b815260040160405180910390fd5b600085118015610f5c5750610f5a7f00000000000000000000000000000000000000000000000000000000000000008887612916565b155b15610f7a576040516309e1fb8b60e31b815260040160405180910390fd5b6040517f8f36f6400000000000000000000000000000000000000000000000000000000081523390638f36f64090610fb89087908790600401614c75565b600060405180830381600087803b158015610fd257600080fd5b505af1158015610fe6573d6000803e3d6000fd5b50506040516370a0823160e01b81523060048201528492507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691506370a0823190602401602060405180830381865afa158015611050573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110749190614b12565b10156110ac576040517fed7735c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516370a0823160e01b815230600482015281907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015611112573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111369190614b12565b101561116e576040517fed7735c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051878152602081018790526001600160a01b0389169133917f0d7d75e01ab95780d3cd1c8ec0dd6c2ce19e3a20427eec8bf53283b6fb8e95f0910160405180910390a350506111c06001600055565b5050505050565b60006111d1612792565b5060015463ffffffff1667ffffffffffffffff881660000361121f576040517fcdf6143100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160801b03168767ffffffffffffffff16101561128c576040517f807a1e5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000167ffffffffffffffff8816016112ef576040517f6c84672900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083600381111561130357611303614ca4565b14806113205750600183600381111561131e5761131e614ca4565b145b1561141e578063ffffffff168463ffffffff161061136a576040517f6a118cfc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000000067ffffffffffffffff168867ffffffffffffffff1610806113e757506001600160801b037f0000000000000000000000000000000000000000000000000000000000000000166113db8989614bd7565b67ffffffffffffffff16105b1561141e576040517fcdf6143100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915263ffffffff8281161061148f576040517f853a5ad000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61149a826001614cba565b6001805463ffffffff191663ffffffff9283161790556040805160c081018252600080825260208201819052818301819052606082015267ffffffffffffffff8c811660808301528b1660a08201529051909250908316906001600160a01b038816907f7206876180a726787edf45582daa9f64c66460c7f389385372099aa4c64343f090611530908d908d908d908b90614cde565b60405180910390a360008060008061154a85878c8e612ae7565b92965090945092509050600288600381111561156857611568614ca4565b14801561158357506000856080015167ffffffffffffffff16115b156115ba576040517f917126fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808960038111156115cf576115cf614ca4565b14806115ec575060018960038111156115ea576115ea614ca4565b145b801561160657506000866080015167ffffffffffffffff16115b156116eb578b6116a5577f00000000000000000000000000000000000000000000000000000000000000006001600160801b03167f00000000000000000000000000000000000000000000000000000000000000006001600160801b03168760a0015167ffffffffffffffff16886080015167ffffffffffffffff1661168c9190614c26565b6116969190614c26565b6116a09190614c53565b6116e8565b7f00000000000000000000000000000000000000000000000000000000000000006001600160801b0316866080015167ffffffffffffffff166116e89190614c26565b90505b60008511806116fa5750600084115b806117165750600089600381111561171457611714614ca4565b145b806117325750600189600381111561173057611730614ca4565b145b156117a8576040805161012081019091528c151581526117a8906020810160018c600381111561176457611764614ca4565b14151581526020018d6001600160a01b031681526020018781526020018681526020018381526020018563ffffffff1681526020018481526020018a815250613194565b8015611b9b576001600160a01b038b1660009081526007602052604081205463ffffffff166060880181905290036118455763ffffffff8716606087018190526001600160a01b038c166000818152600760209081526040808320805463ffffffff191686179055938252600690529190912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690911790555b336001600160a01b038c1614611929573360009081526008602052604081205463ffffffff16808852900361191b57637fffffff63ffffffff8816106118b7576040517fd1cd82ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff808816808852336000818152600860209081526040808320805463ffffffff19169095179094558a5190941681526009909352912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690911790555b855160011b63fffffffe1686525b600189600381111561193d5761193d614ca4565b0361195057855160011763ffffffff1686525b8b15611a7a5763ffffffff8088166000908152600260208181526040928390208a518154928c0151948c015160608d015160808e015160a08f015167ffffffffffffffff908116600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff91909216600160801b02166001600160801b03928a16600160601b026fffffffff00000000000000000000000019948b166801000000000000000002949094167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff998b16640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909816958b169590951796909617979097169290921717169190911792909217909155611a759189908f908e9061377216565b611b9b565b63ffffffff80881660009081526003602090815260409182902089518154928b0151938b015160608c015160808d015160a08e015167ffffffffffffffff908116600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff91909216600160801b02166001600160801b03928916600160601b026fffffffff00000000000000000000000019948a166801000000000000000002949094167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff988a16640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909816958a1695909517969096179690961692909217171691909117919091179055611b9b9060029089908f908e9061377216565b505050505050611bab6001600055565b979650505050505050565b60008060008060008715611bfe57611bf76001600160801b037f0000000000000000000000000000000000000000000000000000000000000000168a614c53565b9050611c34565b611c31897f00000000000000000000000000000000000000000000000000000000000000006001600160801b0316613b63565b90505b8667ffffffffffffffff16811115611c5b57506000905067ffffffffffffffff8616611c60565b600191505b915081611ca06001600160801b037f00000000000000000000000000000000000000000000000000000000000000001667ffffffffffffffff8316614c26565b94507f00000000000000000000000000000000000000000000000000000000000000006001600160801b03167f00000000000000000000000000000000000000000000000000000000000000006001600160801b03168767ffffffffffffffff168567ffffffffffffffff16611d169190614c26565b611d209190614c26565b611d2a9190614c53565b935050945094509450949050565b611d40612792565b33600082611d66576001600160a01b038216600090815260056020526040902054611d80565b6001600160a01b0382166000908152600460205260409020545b9050600084118015611d925750808411155b15611ed8578215611e18576001600160a01b03821660009081526004602052604081208054869290611dc5908490614d2c565b90915550611df690507f00000000000000000000000000000000000000000000000000000000000000008386612916565b611e13576040516309e1fb8b60e31b815260040160405180910390fd5b611e8e565b6001600160a01b03821660009081526005602052604081208054869290611e40908490614d2c565b90915550611e7190507f00000000000000000000000000000000000000000000000000000000000000008386612916565b611e8e576040516309e1fb8b60e31b815260040160405180910390fd5b6040805185815284151560208201526001600160a01b038416917fccd5b8d697ca09c88b66c7bd627d6bb0c9a161861c693df7fdb380d758b2257a910160405180910390a2611f0a565b6040517f9db10d0100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050611f166001600055565b5050565b6000611f24612792565b611f2d83612426565b611f395750600061241c565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905290611f74856109d8565b905080156120085763ffffffff808616600090815260026020908152604091829020825160c081018452905480851682526401000000008104851692820192909252680100000000000000008204841692810192909252600160601b8104909216606082015267ffffffffffffffff600160801b830481166080830152600160c01b90920490911660a08201529150612090565b63ffffffff808616600090815260036020908152604091829020825160c081018452905480851682526401000000008104851692820192909252680100000000000000008204841692810192909252600160601b8104909216606082015267ffffffffffffffff600160801b830481166080830152600160c01b90920490911660a082015291505b606082015163ffffffff1660009081526006602052604090205482516001600160a01b039091169060011c637fffffff168181156120e9575063ffffffff81166000908152600960205260409020546001600160a01b03165b826001600160a01b0316876001600160a01b03161415806121275750336001600160a01b038216148015906121275750336001600160a01b03841614155b1561215e576040517fa934f3c200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405163ffffffff8916907ff584155be07e1a74cb2bc291299df861c09f00cbc773d1509595ed4ff938398f90600090a283156122af5760007f00000000000000000000000000000000000000000000000000000000000000006001600160801b0316866080015167ffffffffffffffff166121da9190614c26565b86519091506000906001168103612219576122167f00000000000000000000000000000000000000000000000000000000000000008684612916565b90505b8061229c576001600160a01b03851660009081526004602052604081208054849290612246908490614ba7565b9091555050865160011660000361229c5760408051838152600160208201526001600160a01b038716917ff939ff49cec37eed853259ac7b9b4c0a8c7aacb9feca8467b3d54512ba26429a910160405180910390a25b6122a860028b88613b9a565b5050612412565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160801b03167f00000000000000000000000000000000000000000000000000000000000000006001600160801b03168760a0015167ffffffffffffffff16886080015167ffffffffffffffff1661232d9190614c26565b6123379190614c26565b6123419190614c53565b865190915060009060011681036123805761237d7f00000000000000000000000000000000000000000000000000000000000000008684612916565b90505b80612403576001600160a01b038516600090815260056020526040812080548492906123ad908490614ba7565b909155505086516001166000036124035760408051838152600060208201526001600160a01b038716917ff939ff49cec37eed853259ac7b9b4c0a8c7aacb9feca8467b3d54512ba26429a910160405180910390a25b61240f60028b88613b9a565b50505b6001955050505050505b610aed6001600055565b63ffffffff8082166000908152600260205260408120549091600160601b90910416151580610aed57505063ffffffff908116600090815260036020526040902054600160601b900416151590565b6124a96040518060a00160405280600015158152602001606081526020016060815260200160608152602001606081525090565b6040517f91bf05e50000000000000000000000000000000000000000000000000000000081526002600482015263ffffffff8086166024830152841515604483015283166064820152600660848201526001600160801b037f0000000000000000000000000000000000000000000000000000000000000000811660a48301527f00000000000000000000000000000000000000000000000000000000000000001660c48201527343ccc6d2a517a9a0955fba24c73f2202da5af8b3906391bf05e59060e401600060405180830381865af415801561258c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526125b49190810190614ea1565b90505b9392505050565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091528261260f5763ffffffff82166000908152600360205260409020612625565b63ffffffff821660009081526002602052604090205b6040805160c081018252915463ffffffff8082168452640100000000820481166020850152680100000000000000008204811692840192909252600160601b8104909116606083015267ffffffffffffffff600160801b820481166080840152600160c01b9091041660a0820152905092915050565b6000806126a6612792565b6000806000806126b98c8c8c8c8c613cd6565b935093509350935061271e6040518061012001604052808e151581526020016000151581526020018a6001600160a01b03168152602001868152602001858152602001600081526020018463ffffffff16815260200183815260200189815250613194565b604080518c151581528d15156020820152908101859052606081018490526001600160a01b0389169033907fd1a5d53e1dd2f18ece2c41d1516ce4335c4022b00735378ec82a4eb657724bb29060800160405180910390a3509193509150506127876001600055565b965096945050505050565b600260005403612803576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b6002600055565b60008080600019858709858702925082811083820303915050806000036128445783828161283a5761283a614c3d565b04925050506125b7565b8084116128ad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d6174683a206d756c446976206f766572666c6f77000000000000000000000060448201526064016127fa565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6040516370a0823160e01b815230600482015260009081906001600160a01b038616906370a0823190602401602060405180830381865afa15801561295f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129839190614b12565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b0386811660048301526024820186905291925060009187169063a9059cbb906044016020604051808303816000875af1925050508015612a0d575060408051601f3d908101601f19168201909252612a0a91810190614f8b565b60015b612a1957506000612a1c565b90505b600081612a2a576000612a2c565b845b6040516370a0823160e01b8152306004820152909150839082906001600160a01b038a16906370a0823190602401602060405180830381865afa158015612a77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a9b9190614b12565b612aa59190614ba7565b1015612add576040517f4ad2d0b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5095945050505050565b604080516101e08101825260008082526020820181905291810182905260608181018390526080820183905260a0820183905260c0820183905260e08201839052610100820183905261012082018390526101408201839052610160820183905261018082018390526101a082018390526101c082018190528291829190600086612b73576002612b76565b60035b60008080526020829052604090205468010000000000000000900463ffffffff16835290505b816000015163ffffffff16600114158015612bc5575060008a6080015167ffffffffffffffff16115b1561310a57815163ffffffff9081166000908152602083815260408083208054600160601b81049095168452600683529220546001600160a01b03169085015260808c015160a08d01519192612c37929067ffffffffffffffff600160801b8204811691600160c01b9004168c610af3565b6001600160801b031661012085015267ffffffffffffffff1661010084018190521580612c7057506101208301516001600160801b0316155b15612c7b575061310a565b7f00000000000000000000000000000000000000000000000000000000000000006001600160801b031683610100015167ffffffffffffffff16612cbf9190614c26565b60c08401526101208301516001600160801b037f0000000000000000000000000000000000000000000000000000000000000000811691612d24917f000000000000000000000000000000000000000000000000000000000000000081169116614c26565b612d2e9190614c53565b60e08401528715612dbd57826000015163ffffffff168a63ffffffff167fbd8e28ca9aa9ef3945b1e218ce096cb31c1e6ed750c5d1dea41832b27c208ed28b86602001518760c001518860e00151604051612db094939291906001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b60405180910390a3612e2f565b825160208085015160c086015160e0870151604080516001600160a01b039485168152938f169484019490945292820152606081019190915263ffffffff808d169216907fbd8e28ca9aa9ef3945b1e218ce096cb31c1e6ed750c5d1dea41832b27c208ed29060800160405180910390a35b8260c001518360400151612e439190614ba7565b604084015260e08301516060840151612e5c9190614ba7565b60608401526101a083015161018084015163ffffffff918216911603612f9d576101a083015160049063ffffffff1615612ea4576101a0840151612ea1906002614fa8565b90505b60008163ffffffff1667ffffffffffffffff811115612ec557612ec56144a8565b604051908082528060200260200182016040528015612f1057816020015b6040805160608101825260008082526020808301829052928201528252600019909201910181612ee35790505b50905060005b856101a0015163ffffffff168163ffffffff161015612f8957856101c001518163ffffffff1681518110612f4c57612f4c614fc8565b6020026020010151828263ffffffff1681518110612f6c57612f6c614fc8565b6020908102919091010152612f82600182614cba565b9050612f16565b506101c085015263ffffffff166101a08401525b604051806060016040528084602001516001600160a01b0316815260200189612fca578460e00151612fd0565b8460c001515b815282546001908116146020909101526101c08401516101808501805190612ff782614fde565b63ffffffff1663ffffffff1681525063ffffffff168151811061301c5761301c614fc8565b60200260200101819052508261010001518b6080015161303c9190615001565b67ffffffffffffffff90811660808d01526101008401518254908216600160801b9091049091160361308c57600161014084015280546fffffffff000000000000000000000000191681556130f0565b61010083015181546130af9190600160801b900467ffffffffffffffff16615001565b815467ffffffffffffffff91909116600160801b027fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff90911617905561310a565b5468010000000000000000900463ffffffff168252612b9c565b8161014001511561316a57815163ffffffff908116600090815260208390526040808220805467ffffffff00000000191690558451828052912080549190921668010000000000000000026bffffffff0000000000000000199091161790555b50604081015160608201516101808301516101c090930151919b909a509198509650945050505050565b80516000906131c3577f00000000000000000000000000000000000000000000000000000000000000006131e5565b7f00000000000000000000000000000000000000000000000000000000000000005b905060008260000151613218577f000000000000000000000000000000000000000000000000000000000000000061323a565b7f00000000000000000000000000000000000000000000000000000000000000005b905060008360a00151846000015161325657846080015161325c565b84606001515b6132669190614ba7565b90506000846000015161327d578460600151613283565b84608001515b9050801561332b578460200151156132ff578451156132d3576040808601516001600160a01b03166000908152600560205290812080548392906132c8908490614ba7565b9091555061332b9050565b6040808601516001600160a01b03166000908152600460205290812080548392906132c8908490614ba7565b61330e83866040015183612916565b61332b576040516309e1fb8b60e31b815260040160405180910390fd5b846020015115613417578451156133af5733600090815260046020526040902054821115613385576040517fedb4840900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260046020526040812080548492906133a4908490614d2c565b909155506135a99050565b336000908152600560205260409020548211156133f8576040517fedb4840900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260056020526040812080548492906133a4908490614d2c565b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa15801561345e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134829190614b12565b6101008701516040517f727f979c000000000000000000000000000000000000000000000000000000008152919250339163727f979c916134c99187918a91600401614b2b565b600060405180830381600087803b1580156134e357600080fd5b505af11580156134f7573d6000803e3d6000fd5b5050505082816135079190614ba7565b6040516370a0823160e01b81523060048201526001600160a01b038716906370a0823190602401602060405180830381865afa15801561354b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061356f9190614b12565b10156135a7576040517f07bb680200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b60005b8560c0015163ffffffff168163ffffffff16101561376a5760008660e001518263ffffffff16815181106135e2576135e2614fc8565b60200260200101519050806040015115613668578651156136385760208082015182516001600160a01b0316600090815260049092526040822080549192909161362d908490614ba7565b909155506137599050565b60208082015182516001600160a01b0316600090815260059092526040822080549192909161362d908490614ba7565b600061367d8783600001518460200151612916565b905080613757578751156136c65760208083015183516001600160a01b031660009081526004909252604082208054919290916136bb908490614ba7565b909155506136fc9050565b60208083015183516001600160a01b031660009081526005909252604082208054919290916136f6908490614ba7565b90915550505b81600001516001600160a01b03167ff939ff49cec37eed853259ac7b9b4c0a8c7aacb9feca8467b3d54512ba26429a83602001518a6000015160405161374e9291909182521515602082015260400190565b60405180910390a25b505b5061376381614fde565b90506135ac565b505050505050565b6000826137825784600101613784565b845b63ffffffff8086166000908152602083905260408082208684168352908220549394509268010000000000000000900490911690036137ef576040517f6a118cfc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b63ffffffff808416600090815260208490526040812054600160601b900490911690036138435763ffffffff928316600090815260208390526040902054680100000000000000009004909216916137f0565b63ffffffff80841660009081526020848152604091829020825160c081018452905480851682526401000000008104851692820192909252680100000000000000008204841692810192909252600160601b8104909216606082015267ffffffffffffffff600160801b830481166080830152600160c01b90920490911660a08201525b8363ffffffff166001146139bb5760408082015163ffffffff9081166000908152602086815290839020835160c081018552905480841682526401000000008104841692820192909252680100000000000000008204831693810193909352600160601b8104909116606083015267ffffffffffffffff600160801b820481166080840152600160c01b9091041660a0820152856139835760a0810151835467ffffffffffffffff918216600160c01b909104909116116139a3565b60a0810151835467ffffffffffffffff918216600160c01b909104909116105b156139ae57506139bb565b60409091015193506138c7565b63ffffffff841615613aa457846139f15760a0810151825467ffffffffffffffff918216600160c01b9091049091161115613a12565b60a0810151825467ffffffffffffffff918216600160c01b90910490911610155b613aa45760209081015163ffffffff8082166000908152858452604090819020815160c081018352905480841682526401000000008104841695820195909552680100000000000000008504831691810191909152600160601b8404909116606082015267ffffffffffffffff600160801b840481166080830152600160c01b90930490921660a083015293506139bb565b50805467ffffffff000000001980821664010000000063ffffffff968716818102928317865560009081526020969096526040808720547fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff9095166bffffffff00000000000000001993841617680100000000000000009586900489168602178087558290048816875280872080549093169988168086029a909a17909255935492909204909416835290912080549092169302929092179091555050565b60008215613b915781613b77600185614d2c565b613b819190614c53565b613b8c906001614ba7565b610aea565b50600092915050565b60018263ffffffff1611613bda576040517f03d2053e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081613bea5783600101613bec565b835b63ffffffff808516600090815260208390526040812054929350600160601b909204169003613c47576040517fb62f096e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff9283166000908152602091909152604080822080546fffffffff000000000000000000000000191690819055640100000000808204861680855283852080546bffffffff000000000000000019166801000000000000000094859004909816938402979097179096559083529120805467ffffffff00000000191693909102929092179091555050565b604080516101e08101825260008082526020820181905291810182905260608181018390526080820183905260a0820183905260c0820183905260e08201839052610100820183905261012082018390526101408201839052610160820183905261018082018390526101a082018390526101c08201819052829182919060008a613d62576002613d65565b60035b608083018a905260008080526020829052604090205468010000000000000000900463ffffffff168352891561016084015290505b816000015163ffffffff16600114158015613db85750816101600151155b1561430357815163ffffffff1660009081526020829052604090208a8015613ddd57508b5b80613def57508a158015613def57508b155b613e265760808301518154613e2191908e9067ffffffffffffffff600160801b8204811691600160c01b900416610b9b565b613e54565b60808301518154613e5491908e9067ffffffffffffffff600160801b8204811691600160c01b900416611bb6565b151561016087015267ffffffffffffffff1660a086015260e085015260c084018190521580613e85575060e0830151155b15613e905750614303565b8b15613f345782518154600160601b900463ffffffff9081166000908152600660205260408082205460c088015160e08901519251949095169492937fbd8e28ca9aa9ef3945b1e218ce096cb31c1e6ed750c5d1dea41832b27c208ed293613f27938f936001600160a01b0316926001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b60405180910390a3613fd1565b82518154600160601b900463ffffffff9081166000908152600660205260408082205460c088015160e08901519251939594909416937fbd8e28ca9aa9ef3945b1e218ce096cb31c1e6ed750c5d1dea41832b27c208ed293613fc8936001600160a01b03909316928f92916001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b60405180910390a35b8260c0015183604001818151613fe79190614ba7565b90525060e0830151606084018051614000908390614ba7565b9052506101a083015161018084015163ffffffff91821691160361413f576101a083015160049063ffffffff1615614046576101a0840151614043906002614fa8565b90505b60008163ffffffff1667ffffffffffffffff811115614067576140676144a8565b6040519080825280602002602001820160405280156140b257816020015b60408051606081018252600080825260208083018290529282015282526000199092019101816140855790505b50905060005b856101a0015163ffffffff168163ffffffff16101561412b57856101c001518163ffffffff16815181106140ee576140ee614fc8565b6020026020010151828263ffffffff168151811061410e5761410e614fc8565b6020908102919091010152614124600182614cba565b90506140b8565b506101c085015263ffffffff166101a08401525b604080516060810182528254600160601b900463ffffffff16600090815260066020908152929020546001600160a01b031681529081018d614185578460e0015161418b565b8460c001515b815282546001908116146020909101526101c084015161018085018051906141b282614fde565b63ffffffff1663ffffffff1681525063ffffffff16815181106141d7576141d7614fc8565b602090810291909101015260a0830151815467ffffffffffffffff918216600160801b9091049091160361422957600161014084015280546fffffffff0000000000000000000000001916815561427d565b60a083015181548290601090614251908490600160801b900467ffffffffffffffff16615001565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555050614303565b805468010000000000000000900463ffffffff168352610160830151156142a45750614303565b8b80156142ae57508a5b806142c057508b1580156142c057508a155b156142e3578260c00151836080018181516142db9190614d2c565b9052506142fd565b8260e00151836080018181516142f99190614d2c565b9052505b50613d9a565b8161014001511561436357815163ffffffff908116600090815260208390526040808220805467ffffffff00000000191690558451828052912080549190921668010000000000000000026bffffffff0000000000000000199091161790555b81610160015161439f576040517f923dee0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8980156143ce57508a80156143b75750878260600151105b806143ce57508a1580156143ce5750878260400151105b15614405576040517fde5dbbfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8915801561443557508a801561441e5750878260400151115b8061443557508a1580156144355750878260600151115b1561446c576040517f3a4b028c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50604081015160608201516101808301516101c090930151919c909b50919950975095505050505050565b80151581146144a557600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b60405160a0810167ffffffffffffffff811182821017156144e1576144e16144a8565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715614510576145106144a8565b604052919050565b600082601f83011261452957600080fd5b813567ffffffffffffffff811115614543576145436144a8565b6145566020601f19601f840116016144e7565b81815284602083860101111561456b57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060006060848603121561459d57600080fd5b8335925060208401356145af81614497565b9150604084013567ffffffffffffffff8111156145cb57600080fd5b6145d786828701614518565b9150509250925092565b63ffffffff811681146144a557600080fd5b60006020828403121561460557600080fd5b81356125b7816145e1565b803567ffffffffffffffff8116811461462857600080fd5b919050565b6000806040838503121561464057600080fd5b61464983614610565b9150602083013561465981614497565b809150509250929050565b6001600160a01b03811681146144a557600080fd5b60006020828403121561468b57600080fd5b81356125b781614664565b600080600080600060a086880312156146ae57600080fd5b6146b786614610565b94506146c560208701614610565b93506146d360408701614610565b92506146e160608701614610565b915060808601356146f181614497565b809150509295509295909350565b6000806000806080858703121561471557600080fd5b84359350602085013561472781614497565b925061473560408601614610565b915061474360608601614610565b905092959194509250565b60008060008060006080868803121561476657600080fd5b853561477181614664565b94506020860135935060408601359250606086013567ffffffffffffffff8082111561479c57600080fd5b818801915088601f8301126147b057600080fd5b8135818111156147bf57600080fd5b8960208285010111156147d157600080fd5b9699959850939650602001949392505050565b600080600080600080600060e0888a0312156147ff57600080fd5b61480888614610565b965061481660208901614610565b9550604088013561482681614497565b9450606088013561483681614664565b93506080880135614846816145e1565b925060a08801356004811061485a57600080fd5b915060c088013567ffffffffffffffff81111561487657600080fd5b6148828a828b01614518565b91505092959891949750929550565b600080604083850312156148a457600080fd5b82359150602083013561465981614497565b600080604083850312156148c957600080fd5b82356148d4816145e1565b9150602083013561465981614664565b6000806000606084860312156148f957600080fd5b8335614904816145e1565b9250602084013561491481614497565b91506040840135614924816145e1565b809150509250925092565b600081518084526020808501945080840160005b838110156149685781516001600160a01b031687529582019590820190600101614943565b509495945050505050565b600081518084526020808501945080840160005b8381101561496857815187529582019590820190600101614987565b602080825282511515828201528281015160a06040840152805160c0840181905260009291820190839060e08601905b808310156149f957835163ffffffff1682529284019260019290920191908401906149d3565b5060408701519350601f19925082868203016060870152614a1a818561492f565b93505050606085015181858403016080860152614a378382614973565b9250506080850151818584030160a0860152614a538382614973565b9695505050505050565b60008060408385031215614a7057600080fd5b8235614a7b81614497565b91506020830135614659816145e1565b60008060008060008060c08789031215614aa457600080fd5b8635614aaf81614497565b95506020870135614abf81614497565b945060408701359350606087013592506080870135614add81614664565b915060a087013567ffffffffffffffff811115614af957600080fd5b614b0589828a01614518565b9150509295509295509295565b600060208284031215614b2457600080fd5b5051919050565b838152600060206001600160a01b0385168184015260606040840152835180606085015260005b81811015614b6e57858101830151858201608001528201614b52565b506000608082860101526080601f19601f83011685010192505050949350505050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610aed57610aed614b91565b600060208284031215614bcc57600080fd5b81516125b7816145e1565b67ffffffffffffffff818116838216028082169190828114614bfb57614bfb614b91565b505092915050565b6001600160801b03818116838216028082169190828114614bfb57614bfb614b91565b8082028115828204841417610aed57610aed614b91565b634e487b7160e01b600052601260045260246000fd5b600082614c7057634e487b7160e01b600052601260045260246000fd5b500490565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b600052602160045260246000fd5b63ffffffff818116838216019080821115614cd757614cd7614b91565b5092915050565b67ffffffffffffffff85811682528416602082015282151560408201526080810160048310614d1d57634e487b7160e01b600052602160045260246000fd5b82606083015295945050505050565b81810381811115610aed57610aed614b91565b805161462881614497565b600067ffffffffffffffff821115614d6457614d646144a8565b5060051b60200190565b600082601f830112614d7f57600080fd5b81516020614d94614d8f83614d4a565b6144e7565b82815260059290921b84018101918181019086841115614db357600080fd5b8286015b84811015614dd7578051614dca816145e1565b8352918301918301614db7565b509695505050505050565b600082601f830112614df357600080fd5b81516020614e03614d8f83614d4a565b82815260059290921b84018101918181019086841115614e2257600080fd5b8286015b84811015614dd7578051614e3981614664565b8352918301918301614e26565b600082601f830112614e5757600080fd5b81516020614e67614d8f83614d4a565b82815260059290921b84018101918181019086841115614e8657600080fd5b8286015b84811015614dd75780518352918301918301614e8a565b600060208284031215614eb357600080fd5b815167ffffffffffffffff80821115614ecb57600080fd5b9083019060a08286031215614edf57600080fd5b614ee76144be565b614ef083614d3f565b8152602083015182811115614f0457600080fd5b614f1087828601614d6e565b602083015250604083015182811115614f2857600080fd5b614f3487828601614de2565b604083015250606083015182811115614f4c57600080fd5b614f5887828601614e46565b606083015250608083015182811115614f7057600080fd5b614f7c87828601614e46565b60808301525095945050505050565b600060208284031215614f9d57600080fd5b81516125b781614497565b63ffffffff818116838216028082169190828114614bfb57614bfb614b91565b634e487b7160e01b600052603260045260246000fd5b600063ffffffff808316818103614ff757614ff7614b91565b6001019392505050565b67ffffffffffffffff828116828216039080821115614cd757614cd7614b9156fea2646970667358221220b9a1b42fb5f18e557db8fdb009909776e8a88fd858ebaef54cfb0e7b2438879f64736f6c63430008120033000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000001388000000000000000000000000000000000000000000000000000000003b9aca00
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102265760003560e01c80639087f4741161012a578063be374423116100bd578063d2f2b7661161008c578063d50cb88b11610071578063d50cb88b146106b1578063d819e812146106d8578063e4ae2e281461070157600080fd5b8063d2f2b7661461060c578063d4105e5d1461068957600080fd5b8063be374423146105aa578063cd6c78d0146105ba578063d0e76269146105da578063d21220a7146105e557600080fd5b8063a89883a2116100f9578063a89883a214610546578063b2d6d47514610566578063b866365a1461058c578063b90f2a401461059f57600080fd5b80639087f474146104e657806392250a471461050d57806396e7952e14610520578063a29d824c1461053357600080fd5b8063365044a8116101bd578063620a92351161018c5780637f2ac67a116101715780637f2ac67a14610486578063818748bd14610499578063829d600a146104bf57600080fd5b8063620a92351461042e578063679f05791461047357600080fd5b8063365044a81461037757806348e057ed1461039e57806349d79bc4146103cc5780634a9f29601461040757600080fd5b80632a2ef375116101f95780632a2ef375146102fd5780632cea5cd3146103125780632e53eda51461033557806334d441d31461035d57600080fd5b806301c11d961461022b5780630dfe16811461025857806320957ed31461029757806327d7d68e146102d6575b600080fd5b61023a67ffffffffffffffff81565b60405167ffffffffffffffff90911681526020015b60405180910390f35b61027f7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab181565b6040516001600160a01b03909116815260200161024f565b6102be7f000000000000000000000000000000000000000000000000000000003b9aca0081565b6040516001600160801b03909116815260200161024f565b6102be7f000000000000000000000000000000000000000000000000000000e8d4a5100081565b61031061030b366004614588565b61072a565b005b6103256103203660046145f3565b6109d8565b604051901515815260200161024f565b61034861034336600461462d565b610a42565b60405163ffffffff909116815260200161024f565b610365602681565b60405160ff909116815260200161024f565b6102be7f000000000000000000000000000000000000000000000000000000000000006481565b6103be6103ac366004614679565b60056020526000908152604090205481565b60405190815260200161024f565b6103df6103da366004614696565b610af3565b6040805167ffffffffffffffff90931683526001600160801b0390911660208301520161024f565b6102be7f000000000000000000000000000000000000000000000000000000000000271081565b61044161043c3660046146ff565b610b9b565b60405161024f9493929190938452602084019290925267ffffffffffffffff1660408301521515606082015260800190565b61031061048136600461474e565b610dab565b6103486104943660046147e4565b6111c7565b6103486104a7366004614679565b60076020526000908152604090205463ffffffff1681565b61023a7f000000000000000000000000000000000000000000000000000000000000138881565b6103657f000000000000000000000000000000000000000000000000000000000000000081565b61044161051b3660046146ff565b611bb6565b61031061052e366004614891565b611d38565b6103256105413660046148b6565b611f1a565b6103be610554366004614679565b60046020526000908152604090205481565b610348610574366004614679565b60086020526000908152604090205463ffffffff1681565b61032561059a3660046145f3565b612426565b61034863ffffffff81565b6001546103489063ffffffff1681565b6105cd6105c83660046148e4565b612475565b60405161024f91906149a3565b610348637fffffff81565b61027f7f000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc881565b61061f61061a366004614a5d565b6125be565b60405161024f9190600060c08201905063ffffffff80845116835280602085015116602084015280604085015116604084015280606085015116606084015250608083015167ffffffffffffffff80821660808501528060a08601511660a0850152505092915050565b61069c610697366004614a8b565b61269b565b6040805192835260208301919091520161024f565b6102be7f000000000000000000000000000000000000000000000000000000000000000181565b61027f6106e63660046145f3565b6006602052600090815260409020546001600160a01b031681565b61027f61070f3660046145f3565b6009602052600090815260409020546001600160a01b031681565b610732612792565b33600083610760577f000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8610782565b7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab15b6040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa1580156107cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107f09190614b12565b6040517f727f979c0000000000000000000000000000000000000000000000000000000081529091506001600160a01b0384169063727f979c9061083c90899086908990600401614b2b565b600060405180830381600087803b15801561085657600080fd5b505af115801561086a573d6000803e3d6000fd5b50505050858161087a9190614ba7565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156108be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e29190614b12565b101561091a576040517fe827b48700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8415610953576001600160a01b03831660009081526004602052604081208054889290610948908490614ba7565b909155506109819050565b6001600160a01b0383166000908152600560205260408120805488929061097b908490614ba7565b90915550505b6040805187815286151560208201526001600160a01b038516917ff939ff49cec37eed853259ac7b9b4c0a8c7aacb9feca8467b3d54512ba26429a910160405180910390a25050506109d36001600055565b505050565b60006109e382612426565b610a19576040517f7312861b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5063ffffffff9081166000908152600260205260409020546001600160601b9091049091161190565b6040517fd271c2c20000000000000000000000000000000000000000000000000000000081526002600482015267ffffffffffffffff8316602482015281151560448201526000907343ccc6d2a517a9a0955fba24c73f2202da5af8b39063d271c2c290606401602060405180830381865af4158015610ac6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aea9190614bba565b90505b92915050565b600080828015610b1757508567ffffffffffffffff168467ffffffffffffffff1610155b80610b3f575082158015610b3f57508367ffffffffffffffff168667ffffffffffffffff1610155b15610b8a578467ffffffffffffffff168767ffffffffffffffff161015610b6857869150610b6c565b8491505b81610b778582614bd7565b67ffffffffffffffff1691509150610b91565b5060009050805b9550959350505050565b600080808080610c11896001600160801b037f000000000000000000000000000000000000000000000000000000000000006416610c037f000000000000000000000000000000000000000000000000000000000000000167ffffffffffffffff8b16614c03565b6001600160801b031661280a565b90508715610ca7576001600160801b037f00000000000000000000000000000000000000000000000000000000000000648116907f000000000000000000000000000000000000000000000000000000000000000116610c7b67ffffffffffffffff891684614c26565b610c859190614c26565b610c8f9190614c53565b935088841015610ca757610ca4600182614ba7565b90505b8667ffffffffffffffff16811115610cce57506000905067ffffffffffffffff8616610cd3565b600191505b8092507f00000000000000000000000000000000000000000000000000000000000000646001600160801b03167f00000000000000000000000000000000000000000000000000000000000000016001600160801b03168767ffffffffffffffff168567ffffffffffffffff16610d4a9190614c26565b610d549190614c26565b610d5e9190614c53565b9350610d9d6001600160801b037f000000000000000000000000000000000000000000000000000000e8d4a510001667ffffffffffffffff8516614c26565b945050945094509450949050565b610db3612792565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b0316906370a0823190602401602060405180830381865afa158015610e1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e3e9190614b12565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc816906370a0823190602401602060405180830381865afa158015610ea8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ecc9190614b12565b9050600086118015610f065750610f047f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab18888612916565b155b15610f24576040516309e1fb8b60e31b815260040160405180910390fd5b600085118015610f5c5750610f5a7f000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc88887612916565b155b15610f7a576040516309e1fb8b60e31b815260040160405180910390fd5b6040517f8f36f6400000000000000000000000000000000000000000000000000000000081523390638f36f64090610fb89087908790600401614c75565b600060405180830381600087803b158015610fd257600080fd5b505af1158015610fe6573d6000803e3d6000fd5b50506040516370a0823160e01b81523060048201528492507f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab16001600160a01b031691506370a0823190602401602060405180830381865afa158015611050573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110749190614b12565b10156110ac576040517fed7735c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516370a0823160e01b815230600482015281907f000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc86001600160a01b0316906370a0823190602401602060405180830381865afa158015611112573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111369190614b12565b101561116e576040517fed7735c900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051878152602081018790526001600160a01b0389169133917f0d7d75e01ab95780d3cd1c8ec0dd6c2ce19e3a20427eec8bf53283b6fb8e95f0910160405180910390a350506111c06001600055565b5050505050565b60006111d1612792565b5060015463ffffffff1667ffffffffffffffff881660000361121f576040517fcdf6143100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000646001600160801b03168767ffffffffffffffff16101561128c576040517f807a1e5f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000167ffffffffffffffff8816016112ef576040517f6c84672900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600083600381111561130357611303614ca4565b14806113205750600183600381111561131e5761131e614ca4565b145b1561141e578063ffffffff168463ffffffff161061136a576040517f6a118cfc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000000138867ffffffffffffffff168867ffffffffffffffff1610806113e757506001600160801b037f000000000000000000000000000000000000000000000000000000003b9aca00166113db8989614bd7565b67ffffffffffffffff16105b1561141e576040517fcdf6143100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915263ffffffff8281161061148f576040517f853a5ad000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61149a826001614cba565b6001805463ffffffff191663ffffffff9283161790556040805160c081018252600080825260208201819052818301819052606082015267ffffffffffffffff8c811660808301528b1660a08201529051909250908316906001600160a01b038816907f7206876180a726787edf45582daa9f64c66460c7f389385372099aa4c64343f090611530908d908d908d908b90614cde565b60405180910390a360008060008061154a85878c8e612ae7565b92965090945092509050600288600381111561156857611568614ca4565b14801561158357506000856080015167ffffffffffffffff16115b156115ba576040517f917126fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808960038111156115cf576115cf614ca4565b14806115ec575060018960038111156115ea576115ea614ca4565b145b801561160657506000866080015167ffffffffffffffff16115b156116eb578b6116a5577f00000000000000000000000000000000000000000000000000000000000000646001600160801b03167f00000000000000000000000000000000000000000000000000000000000000016001600160801b03168760a0015167ffffffffffffffff16886080015167ffffffffffffffff1661168c9190614c26565b6116969190614c26565b6116a09190614c53565b6116e8565b7f000000000000000000000000000000000000000000000000000000e8d4a510006001600160801b0316866080015167ffffffffffffffff166116e89190614c26565b90505b60008511806116fa5750600084115b806117165750600089600381111561171457611714614ca4565b145b806117325750600189600381111561173057611730614ca4565b145b156117a8576040805161012081019091528c151581526117a8906020810160018c600381111561176457611764614ca4565b14151581526020018d6001600160a01b031681526020018781526020018681526020018381526020018563ffffffff1681526020018481526020018a815250613194565b8015611b9b576001600160a01b038b1660009081526007602052604081205463ffffffff166060880181905290036118455763ffffffff8716606087018190526001600160a01b038c166000818152600760209081526040808320805463ffffffff191686179055938252600690529190912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690911790555b336001600160a01b038c1614611929573360009081526008602052604081205463ffffffff16808852900361191b57637fffffff63ffffffff8816106118b7576040517fd1cd82ab00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff808816808852336000818152600860209081526040808320805463ffffffff19169095179094558a5190941681526009909352912080547fffffffffffffffffffffffff00000000000000000000000000000000000000001690911790555b855160011b63fffffffe1686525b600189600381111561193d5761193d614ca4565b0361195057855160011763ffffffff1686525b8b15611a7a5763ffffffff8088166000908152600260208181526040928390208a518154928c0151948c015160608d015160808e015160a08f015167ffffffffffffffff908116600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff91909216600160801b02166001600160801b03928a16600160601b026fffffffff00000000000000000000000019948b166801000000000000000002949094167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff998b16640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909816958b169590951796909617979097169290921717169190911792909217909155611a759189908f908e9061377216565b611b9b565b63ffffffff80881660009081526003602090815260409182902089518154928b0151938b015160608c015160808d015160a08e015167ffffffffffffffff908116600160c01b0277ffffffffffffffffffffffffffffffffffffffffffffffff91909216600160801b02166001600160801b03928916600160601b026fffffffff00000000000000000000000019948a166801000000000000000002949094167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff988a16640100000000027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000909816958a1695909517969096179690961692909217171691909117919091179055611b9b9060029089908f908e9061377216565b505050505050611bab6001600055565b979650505050505050565b60008060008060008715611bfe57611bf76001600160801b037f000000000000000000000000000000000000000000000000000000e8d4a51000168a614c53565b9050611c34565b611c31897f000000000000000000000000000000000000000000000000000000e8d4a510006001600160801b0316613b63565b90505b8667ffffffffffffffff16811115611c5b57506000905067ffffffffffffffff8616611c60565b600191505b915081611ca06001600160801b037f000000000000000000000000000000000000000000000000000000e8d4a510001667ffffffffffffffff8316614c26565b94507f00000000000000000000000000000000000000000000000000000000000000646001600160801b03167f00000000000000000000000000000000000000000000000000000000000000016001600160801b03168767ffffffffffffffff168567ffffffffffffffff16611d169190614c26565b611d209190614c26565b611d2a9190614c53565b935050945094509450949050565b611d40612792565b33600082611d66576001600160a01b038216600090815260056020526040902054611d80565b6001600160a01b0382166000908152600460205260409020545b9050600084118015611d925750808411155b15611ed8578215611e18576001600160a01b03821660009081526004602052604081208054869290611dc5908490614d2c565b90915550611df690507f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab18386612916565b611e13576040516309e1fb8b60e31b815260040160405180910390fd5b611e8e565b6001600160a01b03821660009081526005602052604081208054869290611e40908490614d2c565b90915550611e7190507f000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc88386612916565b611e8e576040516309e1fb8b60e31b815260040160405180910390fd5b6040805185815284151560208201526001600160a01b038416917fccd5b8d697ca09c88b66c7bd627d6bb0c9a161861c693df7fdb380d758b2257a910160405180910390a2611f0a565b6040517f9db10d0100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050611f166001600055565b5050565b6000611f24612792565b611f2d83612426565b611f395750600061241c565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905290611f74856109d8565b905080156120085763ffffffff808616600090815260026020908152604091829020825160c081018452905480851682526401000000008104851692820192909252680100000000000000008204841692810192909252600160601b8104909216606082015267ffffffffffffffff600160801b830481166080830152600160c01b90920490911660a08201529150612090565b63ffffffff808616600090815260036020908152604091829020825160c081018452905480851682526401000000008104851692820192909252680100000000000000008204841692810192909252600160601b8104909216606082015267ffffffffffffffff600160801b830481166080830152600160c01b90920490911660a082015291505b606082015163ffffffff1660009081526006602052604090205482516001600160a01b039091169060011c637fffffff168181156120e9575063ffffffff81166000908152600960205260409020546001600160a01b03165b826001600160a01b0316876001600160a01b03161415806121275750336001600160a01b038216148015906121275750336001600160a01b03841614155b1561215e576040517fa934f3c200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60405163ffffffff8916907ff584155be07e1a74cb2bc291299df861c09f00cbc773d1509595ed4ff938398f90600090a283156122af5760007f000000000000000000000000000000000000000000000000000000e8d4a510006001600160801b0316866080015167ffffffffffffffff166121da9190614c26565b86519091506000906001168103612219576122167f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab18684612916565b90505b8061229c576001600160a01b03851660009081526004602052604081208054849290612246908490614ba7565b9091555050865160011660000361229c5760408051838152600160208201526001600160a01b038716917ff939ff49cec37eed853259ac7b9b4c0a8c7aacb9feca8467b3d54512ba26429a910160405180910390a25b6122a860028b88613b9a565b5050612412565b60007f00000000000000000000000000000000000000000000000000000000000000646001600160801b03167f00000000000000000000000000000000000000000000000000000000000000016001600160801b03168760a0015167ffffffffffffffff16886080015167ffffffffffffffff1661232d9190614c26565b6123379190614c26565b6123419190614c53565b865190915060009060011681036123805761237d7f000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc88684612916565b90505b80612403576001600160a01b038516600090815260056020526040812080548492906123ad908490614ba7565b909155505086516001166000036124035760408051838152600060208201526001600160a01b038716917ff939ff49cec37eed853259ac7b9b4c0a8c7aacb9feca8467b3d54512ba26429a910160405180910390a25b61240f60028b88613b9a565b50505b6001955050505050505b610aed6001600055565b63ffffffff8082166000908152600260205260408120549091600160601b90910416151580610aed57505063ffffffff908116600090815260036020526040902054600160601b900416151590565b6124a96040518060a00160405280600015158152602001606081526020016060815260200160608152602001606081525090565b6040517f91bf05e50000000000000000000000000000000000000000000000000000000081526002600482015263ffffffff8086166024830152841515604483015283166064820152600660848201526001600160801b037f000000000000000000000000000000000000000000000000000000e8d4a51000811660a48301527f00000000000000000000000000000000000000000000000000000000000027101660c48201527343ccc6d2a517a9a0955fba24c73f2202da5af8b3906391bf05e59060e401600060405180830381865af415801561258c573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526125b49190810190614ea1565b90505b9392505050565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a08101919091528261260f5763ffffffff82166000908152600360205260409020612625565b63ffffffff821660009081526002602052604090205b6040805160c081018252915463ffffffff8082168452640100000000820481166020850152680100000000000000008204811692840192909252600160601b8104909116606083015267ffffffffffffffff600160801b820481166080840152600160c01b9091041660a0820152905092915050565b6000806126a6612792565b6000806000806126b98c8c8c8c8c613cd6565b935093509350935061271e6040518061012001604052808e151581526020016000151581526020018a6001600160a01b03168152602001868152602001858152602001600081526020018463ffffffff16815260200183815260200189815250613194565b604080518c151581528d15156020820152908101859052606081018490526001600160a01b0389169033907fd1a5d53e1dd2f18ece2c41d1516ce4335c4022b00735378ec82a4eb657724bb29060800160405180910390a3509193509150506127876001600055565b965096945050505050565b600260005403612803576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b6002600055565b60008080600019858709858702925082811083820303915050806000036128445783828161283a5761283a614c3d565b04925050506125b7565b8084116128ad576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d6174683a206d756c446976206f766572666c6f77000000000000000000000060448201526064016127fa565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6040516370a0823160e01b815230600482015260009081906001600160a01b038616906370a0823190602401602060405180830381865afa15801561295f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129839190614b12565b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b0386811660048301526024820186905291925060009187169063a9059cbb906044016020604051808303816000875af1925050508015612a0d575060408051601f3d908101601f19168201909252612a0a91810190614f8b565b60015b612a1957506000612a1c565b90505b600081612a2a576000612a2c565b845b6040516370a0823160e01b8152306004820152909150839082906001600160a01b038a16906370a0823190602401602060405180830381865afa158015612a77573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a9b9190614b12565b612aa59190614ba7565b1015612add576040517f4ad2d0b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5095945050505050565b604080516101e08101825260008082526020820181905291810182905260608181018390526080820183905260a0820183905260c0820183905260e08201839052610100820183905261012082018390526101408201839052610160820183905261018082018390526101a082018390526101c082018190528291829190600086612b73576002612b76565b60035b60008080526020829052604090205468010000000000000000900463ffffffff16835290505b816000015163ffffffff16600114158015612bc5575060008a6080015167ffffffffffffffff16115b1561310a57815163ffffffff9081166000908152602083815260408083208054600160601b81049095168452600683529220546001600160a01b03169085015260808c015160a08d01519192612c37929067ffffffffffffffff600160801b8204811691600160c01b9004168c610af3565b6001600160801b031661012085015267ffffffffffffffff1661010084018190521580612c7057506101208301516001600160801b0316155b15612c7b575061310a565b7f000000000000000000000000000000000000000000000000000000e8d4a510006001600160801b031683610100015167ffffffffffffffff16612cbf9190614c26565b60c08401526101208301516001600160801b037f0000000000000000000000000000000000000000000000000000000000000064811691612d24917f000000000000000000000000000000000000000000000000000000000000000181169116614c26565b612d2e9190614c53565b60e08401528715612dbd57826000015163ffffffff168a63ffffffff167fbd8e28ca9aa9ef3945b1e218ce096cb31c1e6ed750c5d1dea41832b27c208ed28b86602001518760c001518860e00151604051612db094939291906001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b60405180910390a3612e2f565b825160208085015160c086015160e0870151604080516001600160a01b039485168152938f169484019490945292820152606081019190915263ffffffff808d169216907fbd8e28ca9aa9ef3945b1e218ce096cb31c1e6ed750c5d1dea41832b27c208ed29060800160405180910390a35b8260c001518360400151612e439190614ba7565b604084015260e08301516060840151612e5c9190614ba7565b60608401526101a083015161018084015163ffffffff918216911603612f9d576101a083015160049063ffffffff1615612ea4576101a0840151612ea1906002614fa8565b90505b60008163ffffffff1667ffffffffffffffff811115612ec557612ec56144a8565b604051908082528060200260200182016040528015612f1057816020015b6040805160608101825260008082526020808301829052928201528252600019909201910181612ee35790505b50905060005b856101a0015163ffffffff168163ffffffff161015612f8957856101c001518163ffffffff1681518110612f4c57612f4c614fc8565b6020026020010151828263ffffffff1681518110612f6c57612f6c614fc8565b6020908102919091010152612f82600182614cba565b9050612f16565b506101c085015263ffffffff166101a08401525b604051806060016040528084602001516001600160a01b0316815260200189612fca578460e00151612fd0565b8460c001515b815282546001908116146020909101526101c08401516101808501805190612ff782614fde565b63ffffffff1663ffffffff1681525063ffffffff168151811061301c5761301c614fc8565b60200260200101819052508261010001518b6080015161303c9190615001565b67ffffffffffffffff90811660808d01526101008401518254908216600160801b9091049091160361308c57600161014084015280546fffffffff000000000000000000000000191681556130f0565b61010083015181546130af9190600160801b900467ffffffffffffffff16615001565b815467ffffffffffffffff91909116600160801b027fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff90911617905561310a565b5468010000000000000000900463ffffffff168252612b9c565b8161014001511561316a57815163ffffffff908116600090815260208390526040808220805467ffffffff00000000191690558451828052912080549190921668010000000000000000026bffffffff0000000000000000199091161790555b50604081015160608201516101808301516101c090930151919b909a509198509650945050505050565b80516000906131c3577f000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc86131e5565b7f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab15b905060008260000151613218577f00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab161323a565b7f000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc85b905060008360a00151846000015161325657846080015161325c565b84606001515b6132669190614ba7565b90506000846000015161327d578460600151613283565b84608001515b9050801561332b578460200151156132ff578451156132d3576040808601516001600160a01b03166000908152600560205290812080548392906132c8908490614ba7565b9091555061332b9050565b6040808601516001600160a01b03166000908152600460205290812080548392906132c8908490614ba7565b61330e83866040015183612916565b61332b576040516309e1fb8b60e31b815260040160405180910390fd5b846020015115613417578451156133af5733600090815260046020526040902054821115613385576040517fedb4840900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260046020526040812080548492906133a4908490614d2c565b909155506135a99050565b336000908152600560205260409020548211156133f8576040517fedb4840900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b33600090815260056020526040812080548492906133a4908490614d2c565b6040516370a0823160e01b81523060048201526000906001600160a01b038616906370a0823190602401602060405180830381865afa15801561345e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134829190614b12565b6101008701516040517f727f979c000000000000000000000000000000000000000000000000000000008152919250339163727f979c916134c99187918a91600401614b2b565b600060405180830381600087803b1580156134e357600080fd5b505af11580156134f7573d6000803e3d6000fd5b5050505082816135079190614ba7565b6040516370a0823160e01b81523060048201526001600160a01b038716906370a0823190602401602060405180830381865afa15801561354b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061356f9190614b12565b10156135a7576040517f07bb680200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505b60005b8560c0015163ffffffff168163ffffffff16101561376a5760008660e001518263ffffffff16815181106135e2576135e2614fc8565b60200260200101519050806040015115613668578651156136385760208082015182516001600160a01b0316600090815260049092526040822080549192909161362d908490614ba7565b909155506137599050565b60208082015182516001600160a01b0316600090815260059092526040822080549192909161362d908490614ba7565b600061367d8783600001518460200151612916565b905080613757578751156136c65760208083015183516001600160a01b031660009081526004909252604082208054919290916136bb908490614ba7565b909155506136fc9050565b60208083015183516001600160a01b031660009081526005909252604082208054919290916136f6908490614ba7565b90915550505b81600001516001600160a01b03167ff939ff49cec37eed853259ac7b9b4c0a8c7aacb9feca8467b3d54512ba26429a83602001518a6000015160405161374e9291909182521515602082015260400190565b60405180910390a25b505b5061376381614fde565b90506135ac565b505050505050565b6000826137825784600101613784565b845b63ffffffff8086166000908152602083905260408082208684168352908220549394509268010000000000000000900490911690036137ef576040517f6a118cfc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5b63ffffffff808416600090815260208490526040812054600160601b900490911690036138435763ffffffff928316600090815260208390526040902054680100000000000000009004909216916137f0565b63ffffffff80841660009081526020848152604091829020825160c081018452905480851682526401000000008104851692820192909252680100000000000000008204841692810192909252600160601b8104909216606082015267ffffffffffffffff600160801b830481166080830152600160c01b90920490911660a08201525b8363ffffffff166001146139bb5760408082015163ffffffff9081166000908152602086815290839020835160c081018552905480841682526401000000008104841692820192909252680100000000000000008204831693810193909352600160601b8104909116606083015267ffffffffffffffff600160801b820481166080840152600160c01b9091041660a0820152856139835760a0810151835467ffffffffffffffff918216600160c01b909104909116116139a3565b60a0810151835467ffffffffffffffff918216600160c01b909104909116105b156139ae57506139bb565b60409091015193506138c7565b63ffffffff841615613aa457846139f15760a0810151825467ffffffffffffffff918216600160c01b9091049091161115613a12565b60a0810151825467ffffffffffffffff918216600160c01b90910490911610155b613aa45760209081015163ffffffff8082166000908152858452604090819020815160c081018352905480841682526401000000008104841695820195909552680100000000000000008504831691810191909152600160601b8404909116606082015267ffffffffffffffff600160801b840481166080830152600160c01b90930490921660a083015293506139bb565b50805467ffffffff000000001980821664010000000063ffffffff968716818102928317865560009081526020969096526040808720547fffffffffffffffffffffffffffffffffffffffff0000000000000000ffffffff9095166bffffffff00000000000000001993841617680100000000000000009586900489168602178087558290048816875280872080549093169988168086029a909a17909255935492909204909416835290912080549092169302929092179091555050565b60008215613b915781613b77600185614d2c565b613b819190614c53565b613b8c906001614ba7565b610aea565b50600092915050565b60018263ffffffff1611613bda576040517f03d2053e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600081613bea5783600101613bec565b835b63ffffffff808516600090815260208390526040812054929350600160601b909204169003613c47576040517fb62f096e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63ffffffff9283166000908152602091909152604080822080546fffffffff000000000000000000000000191690819055640100000000808204861680855283852080546bffffffff000000000000000019166801000000000000000094859004909816938402979097179096559083529120805467ffffffff00000000191693909102929092179091555050565b604080516101e08101825260008082526020820181905291810182905260608181018390526080820183905260a0820183905260c0820183905260e08201839052610100820183905261012082018390526101408201839052610160820183905261018082018390526101a082018390526101c08201819052829182919060008a613d62576002613d65565b60035b608083018a905260008080526020829052604090205468010000000000000000900463ffffffff168352891561016084015290505b816000015163ffffffff16600114158015613db85750816101600151155b1561430357815163ffffffff1660009081526020829052604090208a8015613ddd57508b5b80613def57508a158015613def57508b155b613e265760808301518154613e2191908e9067ffffffffffffffff600160801b8204811691600160c01b900416610b9b565b613e54565b60808301518154613e5491908e9067ffffffffffffffff600160801b8204811691600160c01b900416611bb6565b151561016087015267ffffffffffffffff1660a086015260e085015260c084018190521580613e85575060e0830151155b15613e905750614303565b8b15613f345782518154600160601b900463ffffffff9081166000908152600660205260408082205460c088015160e08901519251949095169492937fbd8e28ca9aa9ef3945b1e218ce096cb31c1e6ed750c5d1dea41832b27c208ed293613f27938f936001600160a01b0316926001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b60405180910390a3613fd1565b82518154600160601b900463ffffffff9081166000908152600660205260408082205460c088015160e08901519251939594909416937fbd8e28ca9aa9ef3945b1e218ce096cb31c1e6ed750c5d1dea41832b27c208ed293613fc8936001600160a01b03909316928f92916001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b60405180910390a35b8260c0015183604001818151613fe79190614ba7565b90525060e0830151606084018051614000908390614ba7565b9052506101a083015161018084015163ffffffff91821691160361413f576101a083015160049063ffffffff1615614046576101a0840151614043906002614fa8565b90505b60008163ffffffff1667ffffffffffffffff811115614067576140676144a8565b6040519080825280602002602001820160405280156140b257816020015b60408051606081018252600080825260208083018290529282015282526000199092019101816140855790505b50905060005b856101a0015163ffffffff168163ffffffff16101561412b57856101c001518163ffffffff16815181106140ee576140ee614fc8565b6020026020010151828263ffffffff168151811061410e5761410e614fc8565b6020908102919091010152614124600182614cba565b90506140b8565b506101c085015263ffffffff166101a08401525b604080516060810182528254600160601b900463ffffffff16600090815260066020908152929020546001600160a01b031681529081018d614185578460e0015161418b565b8460c001515b815282546001908116146020909101526101c084015161018085018051906141b282614fde565b63ffffffff1663ffffffff1681525063ffffffff16815181106141d7576141d7614fc8565b602090810291909101015260a0830151815467ffffffffffffffff918216600160801b9091049091160361422957600161014084015280546fffffffff0000000000000000000000001916815561427d565b60a083015181548290601090614251908490600160801b900467ffffffffffffffff16615001565b92506101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555050614303565b805468010000000000000000900463ffffffff168352610160830151156142a45750614303565b8b80156142ae57508a5b806142c057508b1580156142c057508a155b156142e3578260c00151836080018181516142db9190614d2c565b9052506142fd565b8260e00151836080018181516142f99190614d2c565b9052505b50613d9a565b8161014001511561436357815163ffffffff908116600090815260208390526040808220805467ffffffff00000000191690558451828052912080549190921668010000000000000000026bffffffff0000000000000000199091161790555b81610160015161439f576040517f923dee0300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8980156143ce57508a80156143b75750878260600151105b806143ce57508a1580156143ce5750878260400151105b15614405576040517fde5dbbfa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8915801561443557508a801561441e5750878260400151115b8061443557508a1580156144355750878260600151115b1561446c576040517f3a4b028c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50604081015160608201516101808301516101c090930151919c909b50919950975095505050505050565b80151581146144a557600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b60405160a0810167ffffffffffffffff811182821017156144e1576144e16144a8565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715614510576145106144a8565b604052919050565b600082601f83011261452957600080fd5b813567ffffffffffffffff811115614543576145436144a8565b6145566020601f19601f840116016144e7565b81815284602083860101111561456b57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060006060848603121561459d57600080fd5b8335925060208401356145af81614497565b9150604084013567ffffffffffffffff8111156145cb57600080fd5b6145d786828701614518565b9150509250925092565b63ffffffff811681146144a557600080fd5b60006020828403121561460557600080fd5b81356125b7816145e1565b803567ffffffffffffffff8116811461462857600080fd5b919050565b6000806040838503121561464057600080fd5b61464983614610565b9150602083013561465981614497565b809150509250929050565b6001600160a01b03811681146144a557600080fd5b60006020828403121561468b57600080fd5b81356125b781614664565b600080600080600060a086880312156146ae57600080fd5b6146b786614610565b94506146c560208701614610565b93506146d360408701614610565b92506146e160608701614610565b915060808601356146f181614497565b809150509295509295909350565b6000806000806080858703121561471557600080fd5b84359350602085013561472781614497565b925061473560408601614610565b915061474360608601614610565b905092959194509250565b60008060008060006080868803121561476657600080fd5b853561477181614664565b94506020860135935060408601359250606086013567ffffffffffffffff8082111561479c57600080fd5b818801915088601f8301126147b057600080fd5b8135818111156147bf57600080fd5b8960208285010111156147d157600080fd5b9699959850939650602001949392505050565b600080600080600080600060e0888a0312156147ff57600080fd5b61480888614610565b965061481660208901614610565b9550604088013561482681614497565b9450606088013561483681614664565b93506080880135614846816145e1565b925060a08801356004811061485a57600080fd5b915060c088013567ffffffffffffffff81111561487657600080fd5b6148828a828b01614518565b91505092959891949750929550565b600080604083850312156148a457600080fd5b82359150602083013561465981614497565b600080604083850312156148c957600080fd5b82356148d4816145e1565b9150602083013561465981614664565b6000806000606084860312156148f957600080fd5b8335614904816145e1565b9250602084013561491481614497565b91506040840135614924816145e1565b809150509250925092565b600081518084526020808501945080840160005b838110156149685781516001600160a01b031687529582019590820190600101614943565b509495945050505050565b600081518084526020808501945080840160005b8381101561496857815187529582019590820190600101614987565b602080825282511515828201528281015160a06040840152805160c0840181905260009291820190839060e08601905b808310156149f957835163ffffffff1682529284019260019290920191908401906149d3565b5060408701519350601f19925082868203016060870152614a1a818561492f565b93505050606085015181858403016080860152614a378382614973565b9250506080850151818584030160a0860152614a538382614973565b9695505050505050565b60008060408385031215614a7057600080fd5b8235614a7b81614497565b91506020830135614659816145e1565b60008060008060008060c08789031215614aa457600080fd5b8635614aaf81614497565b95506020870135614abf81614497565b945060408701359350606087013592506080870135614add81614664565b915060a087013567ffffffffffffffff811115614af957600080fd5b614b0589828a01614518565b9150509295509295509295565b600060208284031215614b2457600080fd5b5051919050565b838152600060206001600160a01b0385168184015260606040840152835180606085015260005b81811015614b6e57858101830151858201608001528201614b52565b506000608082860101526080601f19601f83011685010192505050949350505050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610aed57610aed614b91565b600060208284031215614bcc57600080fd5b81516125b7816145e1565b67ffffffffffffffff818116838216028082169190828114614bfb57614bfb614b91565b505092915050565b6001600160801b03818116838216028082169190828114614bfb57614bfb614b91565b8082028115828204841417610aed57610aed614b91565b634e487b7160e01b600052601260045260246000fd5b600082614c7057634e487b7160e01b600052601260045260246000fd5b500490565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b634e487b7160e01b600052602160045260246000fd5b63ffffffff818116838216019080821115614cd757614cd7614b91565b5092915050565b67ffffffffffffffff85811682528416602082015282151560408201526080810160048310614d1d57634e487b7160e01b600052602160045260246000fd5b82606083015295945050505050565b81810381811115610aed57610aed614b91565b805161462881614497565b600067ffffffffffffffff821115614d6457614d646144a8565b5060051b60200190565b600082601f830112614d7f57600080fd5b81516020614d94614d8f83614d4a565b6144e7565b82815260059290921b84018101918181019086841115614db357600080fd5b8286015b84811015614dd7578051614dca816145e1565b8352918301918301614db7565b509695505050505050565b600082601f830112614df357600080fd5b81516020614e03614d8f83614d4a565b82815260059290921b84018101918181019086841115614e2257600080fd5b8286015b84811015614dd7578051614e3981614664565b8352918301918301614e26565b600082601f830112614e5757600080fd5b81516020614e67614d8f83614d4a565b82815260059290921b84018101918181019086841115614e8657600080fd5b8286015b84811015614dd75780518352918301918301614e8a565b600060208284031215614eb357600080fd5b815167ffffffffffffffff80821115614ecb57600080fd5b9083019060a08286031215614edf57600080fd5b614ee76144be565b614ef083614d3f565b8152602083015182811115614f0457600080fd5b614f1087828601614d6e565b602083015250604083015182811115614f2857600080fd5b614f3487828601614de2565b604083015250606083015182811115614f4c57600080fd5b614f5887828601614e46565b606083015250608083015182811115614f7057600080fd5b614f7c87828601614e46565b60808301525095945050505050565b600060208284031215614f9d57600080fd5b81516125b781614497565b63ffffffff818116838216028082169190828114614bfb57614bfb614b91565b634e487b7160e01b600052603260045260246000fd5b600063ffffffff808316818103614ff757614ff7614b91565b6001019392505050565b67ffffffffffffffff828116828216039080821115614cd757614cd7614b9156fea2646970667358221220b9a1b42fb5f18e557db8fdb009909776e8a88fd858ebaef54cfb0e7b2438879f64736f6c63430008120033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000001388000000000000000000000000000000000000000000000000000000003b9aca00
-----Decoded View---------------
Arg [0] : _orderBookId (uint8): 0
Arg [1] : _token0Address (address): 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1
Arg [2] : _token1Address (address): 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8
Arg [3] : _logSizeTick (uint8): 12
Arg [4] : _logPriceTick (uint8): 4
Arg [5] : _minToken0BaseAmount (uint64): 5000
Arg [6] : _minToken1BaseAmount (uint128): 1000000000
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [1] : 00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1
Arg [2] : 000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8
Arg [3] : 000000000000000000000000000000000000000000000000000000000000000c
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [5] : 0000000000000000000000000000000000000000000000000000000000001388
Arg [6] : 000000000000000000000000000000000000000000000000000000003b9aca00
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.15
Net Worth in ETH
0.000062
Token Allocations
USDC.E
100.00%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ARB | 100.00% | $0.999692 | 0.1483 | $0.1482 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.