| Token | Symbol | Balance | Price | Value |
|---|---|---|---|---|
| DIH | 1 | $0.0000106 | $0 | |
| UNICORN (CORN) | CORN | 0.000000 | — | — |
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {AuctionStorage} from './AuctionStorage.sol';
import {BidStorage} from './BidStorage.sol';
import {Checkpoint, CheckpointStorage} from './CheckpointStorage.sol';
import {StepStorage} from './StepStorage.sol';
import {Tick, TickStorage} from './TickStorage.sol';
import {AuctionParameters, IContinuousClearingAuction} from './interfaces/IContinuousClearingAuction.sol';
import {IValidationHook} from './interfaces/IValidationHook.sol';
import {Bid, BidLib} from './libraries/BidLib.sol';
import {CheckpointAccountingLib} from './libraries/CheckpointAccountingLib.sol';
import {CheckpointLib} from './libraries/CheckpointLib.sol';
import {ConstantsLib} from './libraries/ConstantsLib.sol';
import {Currency, CurrencyLibrary} from './libraries/CurrencyLibrary.sol';
import {DemandLib} from './libraries/DemandLib.sol';
import {FixedPoint96} from './libraries/FixedPoint96.sol';
import {MaxBidPriceLib} from './libraries/MaxBidPriceLib.sol';
import {PriceLib} from './libraries/PriceLib.sol';
import {AuctionStep, StepLib} from './libraries/StepLib.sol';
import {ValidationHookLib} from './libraries/ValidationHookLib.sol';
import {ValueX7} from './libraries/ValueX7Lib.sol';
import {IERC165} from '@openzeppelin/contracts/utils/introspection/IERC165.sol';
import {
ILBPInitializer,
ILBP_INITIALIZER_INTERFACE_ID,
LBPInitializationParams
} from 'liquidity-launcher/src/interfaces/ILBPInitializer.sol';
import {ProtocolFeeLib} from 'liquidity-launcher/src/libraries/ProtocolFeeLib.sol';
import {FixedPointMathLib} from 'solady/utils/FixedPointMathLib.sol';
import {ReentrancyGuardTransient} from 'solady/utils/ReentrancyGuardTransient.sol';
import {SafeTransferLib} from 'solady/utils/SafeTransferLib.sol';
/// @title ContinuousClearingAuction
/// @custom:security-contact security@uniswap.org
/// @notice Implements a time weighted uniform clearing price auction
/// @dev Can be constructed directly or through the ContinuousClearingAuctionFactory. In either case, users must validate
/// that the auction parameters are correct and not incorrectly set.
contract ContinuousClearingAuction is
BidStorage,
CheckpointStorage,
StepStorage,
TickStorage,
AuctionStorage,
ReentrancyGuardTransient,
IContinuousClearingAuction
{
using FixedPointMathLib for *;
using CurrencyLibrary for Currency;
using BidLib for *;
using StepLib for *;
using CheckpointLib for Checkpoint;
using ValidationHookLib for IValidationHook;
using PriceLib for *;
using DemandLib for uint256;
/// @notice The maximum Q96 price which a bid can be submitted at
/// @dev Set during construction using MaxBidPriceLib.maxBidPrice() based on TOTAL_SUPPLY
uint256 public immutable MAX_BID_PRICE;
/// @notice An optional hook to be called before a bid is registered
IValidationHook internal immutable VALIDATION_HOOK;
constructor(
address _token,
uint128 _totalSupply,
AuctionParameters memory _parameters,
address _protocolFeeController
)
StepStorage(_parameters.auctionStepsData, _parameters.startBlock, _parameters.endBlock, _parameters.claimBlock)
AuctionStorage(
_token,
_parameters.currency,
_totalSupply,
_parameters.tokensRecipient,
_parameters.fundsRecipient,
_parameters.requiredCurrencyRaised,
_protocolFeeController
)
TickStorage(_parameters.tickSpacing, _parameters.floorPrice)
{
VALIDATION_HOOK = IValidationHook(_parameters.validationHook);
// See MaxBidPriceLib library for more details on the bid price calculations.
MAX_BID_PRICE = MaxBidPriceLib.maxBidPrice(TOTAL_SUPPLY);
// The floor price and tick spacing must allow for at least one tick above the floor price to be initialized
if (_parameters.tickSpacing > MAX_BID_PRICE || _parameters.floorPrice > MAX_BID_PRICE - _parameters.tickSpacing)
{
revert FloorPriceAndTickSpacingGreaterThanMaxBidPrice(
_parameters.floorPrice + _parameters.tickSpacing, MAX_BID_PRICE
);
}
$clearingPriceQ96 = FLOOR_PRICE_Q96;
emit ClearingPriceUpdated(_getBlockNumberish(), $clearingPriceQ96);
}
/// @notice Modifier for functions which can only be called after the auction is started and the tokens have been received
modifier onlyActiveAuction() {
_onlyActiveAuction();
_;
}
/// @notice Internal function to check if the auction is active
/// @dev Submitting bids or checkpointing is not allowed unless the auction is active
function _onlyActiveAuction() internal view {
if (_getBlockNumberish() < START_BLOCK) revert AuctionNotStarted();
if (!$_tokensReceived) revert TokensNotReceived();
}
/// @notice Modifier for functions which require the latest checkpoint to be up to date
modifier ensureEndBlockIsCheckpointed() {
if ($lastCheckpointedBlock != END_BLOCK) {
checkpoint();
}
_;
}
/// @notice Notify the auction that the token supply has been deposited.
function onTokensReceived() external override {
// Don't check balance or emit the TokensReceived event if the tokens have already been received
if ($_tokensReceived) return;
// Use the normal totalSupply value instead of the Q96 value
if (TOKEN.balanceOf(address(this)) < uint256(TOTAL_SUPPLY)) {
revert InvalidTokenAmountReceived();
}
$_tokensReceived = true;
emit TokensReceived(TOTAL_SUPPLY);
}
/// @inheritdoc ILBPInitializer
/// @dev Reverts if the auction has not graduated, since `currencyRaised` and `tokensSold` are not actual settled
/// values for an unsuccessful auction.
/// @dev Protocol fees are queried from the controller at call time and may differ from fees at auction creation.
function lbpInitializationParams() external view returns (LBPInitializationParams memory params) {
// Require that the auction has been checkpointed at the end block before returning initialization params
if ($lastCheckpointedBlock != END_BLOCK) revert AuctionIsNotFinalized();
if (!_isGraduated()) revert NotGraduated();
// Subtract the protocol fee from the currency raised
uint256 currencyRaised = currencyRaised();
uint256 protocolFeeAmount =
ProtocolFeeLib.getProtocolFeeAmount(PROTOCOL_FEE_CONTROLLER, Currency.unwrap(CURRENCY), currencyRaised);
return LBPInitializationParams({
initialPriceX96: $clearingPriceQ96,
tokensSold: totalCleared(),
currencyRaised: currencyRaised - protocolFeeAmount
});
}
/// @inheritdoc IContinuousClearingAuction
function supportsInterface(bytes4 interfaceId) external pure returns (bool) {
return interfaceId == ILBP_INITIALIZER_INTERFACE_ID || interfaceId == IERC165.supportsInterface.selector;
}
/// @inheritdoc IContinuousClearingAuction
function clearingPrice() external view returns (uint256) {
return $clearingPriceQ96;
}
/// @inheritdoc IContinuousClearingAuction
function isGraduated() external view returns (bool) {
return _isGraduated();
}
/// @notice Whether the auction has graduated as of the given checkpoint
/// @dev The auction is considered `graduated` if the currency raised is greater than or equal to the required currency raised
function _isGraduated() internal view returns (bool) {
return $currencyRaisedQ96X7 >= REQUIRED_CURRENCY_RAISED_Q96X7;
}
/// @notice Iterate to find the tick where the total demand at and above it is strictly less than the remaining supply in the auction
/// @dev If the loop reaches the highest tick in the book, `nextActiveTickPrice` will be set to MAX_TICK_PTR
/// @param _untilTickPriceQ96 The tick price to iterate until
/// @param _cumulativeMps The cumulative mps unlocked so far
/// @return The new clearing price
function _iterateOverTicksAndFindClearingPrice(uint256 _untilTickPriceQ96, uint24 _cumulativeMps)
internal
returns (uint256)
{
// The new clearing price can never be lower than the current clearing price
uint256 minimumClearingPriceQ96 = $clearingPriceQ96;
// Place state variables on the stack to save gas
bool updateStateVariables;
uint256 demandAboveClearingQ96 = $sumCurrencyDemandAboveClearingQ96;
uint256 nextActiveTickPriceQ96 = $nextActiveTickPriceQ96;
uint256 remainingMps = ConstantsLib.MPS - _cumulativeMps;
// Unwrap as we defer dividing by 1e7 by moving it to the LHS as multiplication
uint256 remainingSupplyQ96X7_ = ValueX7.unwrap(_remainingSupplyQ96X7());
// If there are no more remaining supply or schedule, return the minimum clearing price
// Note: it is possible that because of rounding, remainingSupply can be zero even though
// the auction schedule is not fully completed (remainingMps > 0). The correct treatment
// for this case is to NOT advance the clearing price (since we cannot sell any more tokens)
if (remainingSupplyQ96X7_ == 0 || remainingMps == 0) return minimumClearingPriceQ96;
uint256 clearingPriceQ96 = demandAboveClearingQ96.toPriceCeiling(remainingSupplyQ96X7_, remainingMps);
while (
// Loop while demand above the last clearing price >= required demand at the next active tick price
// See `DemandLib.canClearSupplyAtPrice()` for more details
(nextActiveTickPriceQ96 != _untilTickPriceQ96
&& demandAboveClearingQ96.canClearSupplyAtPrice(
remainingSupplyQ96X7_, nextActiveTickPriceQ96, remainingMps
))
// If rounding up the demand above clearing equals `nextActiveTickPriceQ96`, keep iterating over ticks
// to ensure that `nextActiveTickPriceQ96` is always the next initialized tick strictly above the clearing price
|| clearingPriceQ96 == nextActiveTickPriceQ96
) {
Tick storage $nextActiveTick = _getTick(nextActiveTickPriceQ96);
// Subtract the demand at the current nextActiveTick from the total demand
demandAboveClearingQ96 -= $nextActiveTick.currencyDemandQ96;
// Save the previous next active tick price
minimumClearingPriceQ96 = nextActiveTickPriceQ96;
// Advance to the next tick
nextActiveTickPriceQ96 = $nextActiveTick.next;
clearingPriceQ96 = demandAboveClearingQ96.toPriceCeiling(remainingSupplyQ96X7_, remainingMps);
updateStateVariables = true;
}
// Set the values into storage if we found a new next active tick price
if (updateStateVariables) {
$sumCurrencyDemandAboveClearingQ96 = demandAboveClearingQ96;
$nextActiveTickPriceQ96 = nextActiveTickPriceQ96;
emit NextActiveTickUpdated(nextActiveTickPriceQ96);
}
// The auction had sufficient demand at the last iterated tick so the minimum clearing price is the lower bound
if (clearingPriceQ96 < minimumClearingPriceQ96) {
return minimumClearingPriceQ96;
}
// Otherwise, return the calculated clearing price
return clearingPriceQ96;
}
/// @notice Internal function for checkpointing at a specific block number
/// @dev This updates the state of the auction accounting for the bids placed after the last checkpoint
/// Checkpoints are created at the top of each block with a new bid and does NOT include that bid
/// Because of this, we need to calculate what the new state of the Auction should be before updating
/// purely on the supply we will sell to the potentially updated `sumCurrencyDemandAboveClearingQ96` value
/// @param _blockNumber The block number to checkpoint at
function _checkpointAtBlock(uint64 _blockNumber) internal returns (Checkpoint memory _checkpoint) {
uint64 lastCheckpointedBlock = $lastCheckpointedBlock;
if (_blockNumber == lastCheckpointedBlock) return latestCheckpoint();
_checkpoint = latestCheckpoint();
// If there are no more remaining mps in the auction, we don't need to iterate over ticks
// Or update the clearing price
if (_checkpoint.remainingMpsInAuction() > 0) {
// Iterate over all ticks until MAX_TICK_PTR to find the clearing price
// This can revert with out of gas if there are a large number of ticks
uint256 newClearingPriceQ96 = _iterateOverTicksAndFindClearingPrice(MAX_TICK_PTR, _checkpoint.cumulativeMps);
// checkpoint has the stale clearing price
if (newClearingPriceQ96 != _checkpoint.clearingPrice) {
// Set the new clearing price
_checkpoint.clearingPrice = newClearingPriceQ96;
// Reset the currencyRaisedAtClearingPrice to zero since the clearing price has changed
_checkpoint.currencyRaisedAtClearingPriceQ96X7 = ValueX7.wrap(0);
// Write the new clearing price to storage
$clearingPriceQ96 = newClearingPriceQ96;
emit ClearingPriceUpdated(_blockNumber, newClearingPriceQ96);
}
}
uint24 deltaMps;
{
AuctionStep memory step;
// Calculate the percentage of the supply that has been sold since the last checkpoint and the start of the current step
(step, deltaMps) = _advanceToStartOfCurrentStep(_blockNumber, lastCheckpointedBlock);
// `deltaMps` above is equal to the percentage of tokens sold up until the start of the current step.
// If the last checkpointed block is more recent than the start of the current step, account for the percentage
// sold since the last checkpointed block. Otherwise, add the percent sold since the start of the current step.
deltaMps += uint24(
(_blockNumber - uint64(FixedPointMathLib.max(step.startBlock, lastCheckpointedBlock))) * step.mps
);
}
// Save gas for zero mps checkpoints
if (deltaMps > 0) {
ValueX7 remainingSupplyQ96X7_ = _remainingSupplyQ96X7();
// Only need to update currencyRaised and totalCleared if there is remaining supply
if (ValueX7.unwrap(remainingSupplyQ96X7_) > 0) {
// Put variables on the stack to save gas
uint256 sumAboveClearingPriceQ96 = $sumCurrencyDemandAboveClearingQ96;
uint256 clearingPriceQ96 = _checkpoint.clearingPrice;
// The base case is where all demand sits strictly above the clearing price
ValueX7 currencyRaisedDeltaQ96X7 = ValueX7.wrap(sumAboveClearingPriceQ96 * deltaMps);
// However, we need to find currency raised at clearing price if there are bids there
if (clearingPriceQ96 % TICK_SPACING_Q96 == 0) {
uint256 demandAtClearingPriceQ96 = _getTick(clearingPriceQ96).currencyDemandQ96;
if (demandAtClearingPriceQ96 > 0) {
ValueX7 currencyRaisedAtClearingQ96X7 = DemandLib.currencyRaisedAtPrice(
remainingSupplyQ96X7_,
demandAtClearingPriceQ96,
sumAboveClearingPriceQ96,
clearingPriceQ96,
deltaMps,
ConstantsLib.MPS - _checkpoint.cumulativeMps // guaranteed to be > 0 because deltaMps > 0
);
// Total change in currencyRaised = currency raised above clearing + currency raised at clearing
currencyRaisedDeltaQ96X7 = currencyRaisedDeltaQ96X7 + currencyRaisedAtClearingQ96X7;
// Track cumulative currency raised exactly at this clearing price (used for partial exits)
_checkpoint.currencyRaisedAtClearingPriceQ96X7 =
_checkpoint.currencyRaisedAtClearingPriceQ96X7 + currencyRaisedAtClearingQ96X7;
}
}
// Convert currency to tokens at price, rounding up, and update global cleared tokens.
// Intentional rounding up of totalCleared may leave dust in the contract which cannot be swept.
uint256 tokensClearedQ96X7 =
ValueX7.unwrap(currencyRaisedDeltaQ96X7).toTokensRoundingUp(clearingPriceQ96);
// Ensure that totalCleared is never greater than total supply.
$totalClearedQ96X7 = ($totalClearedQ96X7 + ValueX7.wrap(tokensClearedQ96X7)).min(TOTAL_SUPPLY_Q96X7);
// Update global currency raised
$currencyRaisedQ96X7 = $currencyRaisedQ96X7 + currencyRaisedDeltaQ96X7;
// Add to the cumulative mps per price sum, weighted by `mps`. This is an inverse sum.
_checkpoint.cumulativeMpsPerPrice += (uint256(deltaMps) << 192) / clearingPriceQ96;
}
// Increment cumulativeMps even if remainingSupply is zero. This ensures that the auction schedule concludes as expected.
_checkpoint.cumulativeMps += deltaMps;
}
// Insert the checkpoint into storage, updating latest pointer and the linked list
_insertCheckpoint(_checkpoint, _blockNumber);
emit CheckpointUpdated(_blockNumber, _checkpoint.clearingPrice, _checkpoint.cumulativeMps);
}
/// @notice Return the final checkpoint of the auction
/// @dev Only called when the auction is over
function _getFinalCheckpoint() internal returns (Checkpoint memory) {
return _checkpointAtBlock(END_BLOCK);
}
/// @notice Internal function for bid submission
/// @dev Validates `maxPriceQ96`, calls the validation hook (if set) and updates global state variables.
/// For gas efficiency, `prevTickPriceQ96` should be the Q96 price of the tick immediately before `maxPriceQ96`.
/// @dev Implementing functions must check that the actual value `amount` is received by the contract
/// @return bidId The id of the created bid
function _submitBid(
uint256 _maxPriceQ96,
uint128 _amount,
address _owner,
uint256 _prevTickPriceQ96,
bytes calldata _hookData
) internal returns (uint256 bidId) {
// Reject bids which would cause TOTAL_SUPPLY * maxPrice to overflow a uint256
if (_maxPriceQ96 > MAX_BID_PRICE) revert InvalidBidPriceTooHigh(_maxPriceQ96, MAX_BID_PRICE);
// Get the latest checkpoint before validating the bid
uint64 currentBlockNumberIsh = uint64(_getBlockNumberish());
Checkpoint memory _checkpoint = _checkpointAtBlock(currentBlockNumberIsh);
// Call the validation hook and bubble up the revert reason if it reverts
VALIDATION_HOOK.handleValidate(_maxPriceQ96, _amount, _owner, msg.sender, _hookData);
// Revert if there are no more tokens to be sold
if (_checkpoint.remainingMpsInAuction() == 0 || ValueX7.unwrap(_remainingSupplyQ96X7()) == 0) {
revert AuctionSoldOut();
}
// We don't allow bids to be submitted at or below the clearing price
if (_maxPriceQ96 <= _checkpoint.clearingPrice) revert BidMustBeAboveClearingPrice();
// Initialize the tick if needed. This will no-op if the tick is already initialized.
_initializeTickIfNeeded(_prevTickPriceQ96, _maxPriceQ96);
Bid memory bid;
uint256 amountQ96 = uint256(_amount) << FixedPoint96.RESOLUTION;
(bid, bidId) = _createBid(currentBlockNumberIsh, amountQ96, _owner, _maxPriceQ96, _checkpoint.cumulativeMps);
// Scale the amount according to the rest of the supply schedule, accounting for past blocks
// This is only used in demand related internal calculations
uint256 bidEffectiveAmountQ96 = bid.toEffectiveAmount();
// Update the tick demand with the bid's scaled amount
_updateTickDemand(_maxPriceQ96, bidEffectiveAmountQ96);
// Update the global sum of currency demand above the clearing price tracker
// Per the validation checks above this bid must be above the clearing price
$sumCurrencyDemandAboveClearingQ96 += bidEffectiveAmountQ96;
// If the sum of demand above clearing price becomes large enough to overflow a multiplication an X7 value,
// revert to prevent the bid from being submitted.
if ($sumCurrencyDemandAboveClearingQ96 >= ConstantsLib.X7_UPPER_BOUND) {
revert InvalidBidUnableToClear();
}
emit BidSubmitted(bidId, _owner, _maxPriceQ96, _amount);
}
/// @notice Internal function for processing the exit of a bid
/// @dev Given a bid, tokens filled and refund, process the transfers and refund
/// `exitedBlock` MUST be checked by the caller to prevent double spending
/// @param _bidId The id of the bid to exit
/// @param _tokensFilled The number of tokens filled
/// @param _currencySpentQ96 The amount of currency the bid spent
function _processExit(uint256 _bidId, uint256 _tokensFilled, uint256 _currencySpentQ96) internal {
Bid storage $bid = _getBid(_bidId);
address owner = $bid.owner;
uint256 bidAmountQ96 = $bid.amountQ96;
// In edge cases where a bid spends all of its currency across fully filled and partially filled checkpoints,
// the sum of currencySpent can be rounded up to one wei more than the bid amount. We clamp the refund to the bid amount.
uint256 refund = FixedPointMathLib.saturatingSub(bidAmountQ96, _currencySpentQ96) >> FixedPoint96.RESOLUTION;
$bid.tokensFilled = _tokensFilled;
$bid.exitedBlock = uint64(_getBlockNumberish());
if (refund > 0) {
CURRENCY.transfer(owner, refund);
}
emit BidExited(_bidId, owner, _tokensFilled, refund);
}
/// @inheritdoc IContinuousClearingAuction
function checkpoint() public onlyActiveAuction returns (Checkpoint memory) {
uint64 currentBlockNumberIsh = uint64(_getBlockNumberish());
if (currentBlockNumberIsh > END_BLOCK) {
return _getFinalCheckpoint();
} else {
return _checkpointAtBlock(currentBlockNumberIsh);
}
}
/// @notice Manually iterate over ticks to update the clearing price
/// @dev This is used to prevent DoS attacks which initialize a large number of ticks
/// @param _untilTickPriceQ96 The tick price to iterate until
function forceIterateOverTicks(uint256 _untilTickPriceQ96)
external
onlyActiveAuction
nonReentrant
returns (uint256)
{
if ($lastCheckpointedBlock == uint64(_getBlockNumberish())) {
revert CheckpointAlreadyExistsForBlock();
}
if (_untilTickPriceQ96 != MAX_TICK_PTR) {
// Ensure that the Q96 price is at a tick boundary
Tick storage $tick = _getTick(_untilTickPriceQ96);
// The tick must be initialized otherwise it will be an infinite loop
if ($tick.next == 0) revert TickNotInitialized();
// The untilTickPrice must be greater than the current next active tick price
if (_untilTickPriceQ96 <= $nextActiveTickPriceQ96) {
revert TickHintMustBeGreaterThanNextActiveTickPrice(_untilTickPriceQ96, $nextActiveTickPriceQ96);
}
}
uint256 newClearingPriceQ96 =
_iterateOverTicksAndFindClearingPrice(_untilTickPriceQ96, latestCheckpoint().cumulativeMps);
// Update the clearing price in storage if it has changed
if (newClearingPriceQ96 != $clearingPriceQ96) {
$clearingPriceQ96 = newClearingPriceQ96;
emit ClearingPriceUpdated(_getBlockNumberish(), newClearingPriceQ96);
}
return newClearingPriceQ96;
}
/// @inheritdoc IContinuousClearingAuction
/// @dev Bids can be submitted anytime between the startBlock and the endBlock.
function submitBid(
uint256 _maxPriceQ96,
uint128 _amount,
address _owner,
uint256 _prevTickPriceQ96,
bytes calldata _hookData
) public payable onlyActiveAuction nonReentrant returns (uint256) {
// Bids cannot be submitted at the endBlock or after
if (_getBlockNumberish() >= END_BLOCK) revert AuctionIsOver();
if (_amount == 0) revert BidAmountTooSmall();
if (_owner == address(0)) revert BidOwnerCannotBeZeroAddress();
if (CURRENCY.isAddressZero()) {
if (msg.value != _amount) revert InvalidAmount();
} else {
if (msg.value != 0) revert CurrencyIsNotNative();
SafeTransferLib.permit2TransferFrom(Currency.unwrap(CURRENCY), msg.sender, address(this), _amount);
}
return _submitBid(_maxPriceQ96, _amount, _owner, _prevTickPriceQ96, _hookData);
}
/// @inheritdoc IContinuousClearingAuction
/// @dev The call to `submitBid` checks `onlyActiveAuction` so it's not required on this function
function submitBid(uint256 _maxPriceQ96, uint128 _amount, address _owner, bytes calldata _hookData)
external
payable
returns (uint256)
{
return submitBid(_maxPriceQ96, _amount, _owner, FLOOR_PRICE_Q96, _hookData);
}
/// @inheritdoc IContinuousClearingAuction
function exitBid(uint256 _bidId) external onlyAfterAuctionIsOver {
Bid memory bid = _getBid(_bidId);
if (bid.exitedBlock != 0) revert BidAlreadyExited();
Checkpoint memory finalCheckpoint = _getFinalCheckpoint();
if (!_isGraduated()) {
// Fully refund the bid if the auction did not graduate, since it is over
return _processExit(_bidId, 0, 0);
}
// Only bids with a maxPrice strictly above the final clearing price can be exited in this function
if (bid.maxPrice <= finalCheckpoint.clearingPrice) revert CannotExitBid();
// Calculate the tokens and currency spent from the fully filled checkpoints
(uint256 tokensFilled, uint256 currencySpentQ96) =
CheckpointAccountingLib.accountFullyFilledCheckpoints(finalCheckpoint, _getCheckpoint(bid.startBlock), bid);
_processExit(_bidId, tokensFilled, currencySpentQ96);
}
/// @inheritdoc IContinuousClearingAuction
function exitPartiallyFilledBid(uint256 _bidId, uint64 _lastFullyFilledCheckpointBlock, uint64 _outbidBlock)
external
{
// Checkpoint first as the validity of the hints depend on the latest state
Checkpoint memory currentBlockCheckpoint = checkpoint();
// Cache the current block number
uint256 currentBlockNumberIsh = _getBlockNumberish();
Bid memory bid = _getBid(_bidId);
if (bid.exitedBlock != 0) revert BidAlreadyExited();
// Prevent bids from being exited before graduation
if (!_isGraduated()) {
if (currentBlockNumberIsh >= END_BLOCK) {
// If the auction is over, fully refund the bid
return _processExit(_bidId, 0, 0);
}
revert CannotPartiallyExitBidBeforeGraduation();
}
uint256 bidMaxPrice = bid.maxPrice;
uint64 bidStartBlock = bid.startBlock;
Checkpoint memory lastFullyFilledCheckpoint = _getCheckpoint(_lastFullyFilledCheckpointBlock);
// Since `lastFullyFilledCheckpointBlock` must be the last fully filled Checkpoint, it must be < bid.maxPrice
// And the bid must be partially filled or outbid (clearingPrice >= bid.maxPrice) in the next Checkpoint.
// `lastFullyFilledCheckpoint` MUST be at least the bid's startCheckpoint since new bids must be at or above the current clearing price.
if (
lastFullyFilledCheckpoint.clearingPrice >= bidMaxPrice
|| _getCheckpoint(lastFullyFilledCheckpoint.next).clearingPrice < bidMaxPrice
|| _lastFullyFilledCheckpointBlock < bidStartBlock
) {
revert InvalidLastFullyFilledCheckpointHint();
}
// Calculate the tokens and currency spent for the fully filled checkpoints
// If the bid is outbid in the same block it is submitted in, these two checkpoints will be identical.
// The extra gas to check for this isn't worth it since the returned values will be 0.
(uint256 tokensFilled, uint256 currencySpentQ96) = CheckpointAccountingLib.accountFullyFilledCheckpoints(
lastFullyFilledCheckpoint, _getCheckpoint(bidStartBlock), bid
);
// Upper checkpoint is the last checkpoint where the bid is partially filled
Checkpoint memory upperCheckpoint;
// If outbidBlock is not zero, the bid was outbid and the bidder is requesting an early exit before the end of the auction
if (_outbidBlock != 0) {
// If the provided hint is the current block, use the checkpoint on the stack instead of getting it from storage
Checkpoint memory outbidCheckpoint;
if (_outbidBlock == currentBlockNumberIsh) {
outbidCheckpoint = currentBlockCheckpoint;
} else {
outbidCheckpoint = _getCheckpoint(_outbidBlock);
}
upperCheckpoint = _getCheckpoint(outbidCheckpoint.prev);
// We require that the outbid checkpoint is > bid max price AND the checkpoint before it is <= bid max price, revert if either of these conditions are not met
if (outbidCheckpoint.clearingPrice <= bidMaxPrice || upperCheckpoint.clearingPrice > bidMaxPrice) {
revert InvalidOutbidBlockCheckpointHint();
}
} else {
// The only other valid partial exit case is if the final clearing price is equal to the bid's maxPrice.
// These bids can only be exited after the auction ends
if (currentBlockNumberIsh < END_BLOCK) revert CannotPartiallyExitBidBeforeEndBlock();
// Set the upper checkpoint to the current checkpoint, which is also the final checkpoint since we already validated that the auction is over
upperCheckpoint = currentBlockCheckpoint;
// Revert if the final checkpoint's clearing price is not equal to the bid's max price
if (upperCheckpoint.clearingPrice != bidMaxPrice) {
revert CannotExitBid();
}
}
// If there is an `upperCheckpoint` that means that the bid had a period where it was partially filled.
// From the logic above, `upperCheckpoint` now points to the last checkpoint where the clearingPrice == bidMaxPrice.
// Because the clearing price can never decrease between checkpoints, and the fact that you cannot enter a bid
// at or below the current clearing price, the bid MUST have been active during the entire partial fill period.
// And `upperCheckpoint` tracks the cumulative currency raised at that clearing price since the first partially filled checkpoint.
if (upperCheckpoint.clearingPrice == bidMaxPrice) {
uint256 tickDemandQ96 = _getTick(bidMaxPrice).currencyDemandQ96;
(uint256 partialTokensFilled, uint256 partialCurrencySpentQ96) = CheckpointAccountingLib.accountPartiallyFilledCheckpoints(
bid, tickDemandQ96, upperCheckpoint.currencyRaisedAtClearingPriceQ96X7
);
// Add the tokensFilled and currencySpentQ96 from the partially filled checkpoints to the total
tokensFilled += partialTokensFilled;
currencySpentQ96 += partialCurrencySpentQ96;
}
_processExit(_bidId, tokensFilled, currencySpentQ96);
}
/// @inheritdoc IContinuousClearingAuction
function claimTokens(uint256 _bidId) external onlyAfterClaimBlock ensureEndBlockIsCheckpointed {
// Tokens cannot be claimed if the auction did not graduate
if (!_isGraduated()) revert NotGraduated();
(address owner, uint256 tokensFilled) = _internalClaimTokens(_bidId);
if (tokensFilled > 0) {
Currency.wrap(address(TOKEN)).transfer(owner, tokensFilled);
emit TokensClaimed(_bidId, owner, tokensFilled);
}
}
/// @inheritdoc IContinuousClearingAuction
function claimTokensBatch(address _owner, uint256[] calldata _bidIds)
external
onlyAfterClaimBlock
ensureEndBlockIsCheckpointed
{
// Tokens cannot be claimed if the auction did not graduate
if (!_isGraduated()) revert NotGraduated();
uint256 tokensFilled = 0;
for (uint256 i = 0; i < _bidIds.length; i++) {
(address bidOwner, uint256 bidTokensFilled) = _internalClaimTokens(_bidIds[i]);
if (bidOwner != _owner) {
revert BatchClaimDifferentOwner(bidOwner, _owner);
}
tokensFilled += bidTokensFilled;
if (bidTokensFilled > 0) {
emit TokensClaimed(_bidIds[i], bidOwner, bidTokensFilled);
}
}
if (tokensFilled > 0) {
Currency.wrap(address(TOKEN)).transfer(_owner, tokensFilled);
}
}
/// @notice Internal function to claim tokens for a single bid
/// @param _bidId The id of the bid
/// @return owner The owner of the bid
/// @return tokensFilled The amount of tokens filled
function _internalClaimTokens(uint256 _bidId) internal returns (address owner, uint256 tokensFilled) {
Bid storage $bid = _getBid(_bidId);
if ($bid.exitedBlock == 0) revert BidNotExited();
// Set return values
owner = $bid.owner;
tokensFilled = $bid.tokensFilled;
// Set the tokens filled to 0
$bid.tokensFilled = 0;
}
/// @inheritdoc ILBPInitializer
/// @dev Protocol fees are queried from the controller at sweep time and may differ from fees at auction creation.
function sweepCurrency() external onlyAfterAuctionIsOver ensureEndBlockIsCheckpointed {
// Only recipient can sweep
if (msg.sender != FUNDS_RECIPIENT) revert NotAuthorized(FUNDS_RECIPIENT, msg.sender);
// Cannot sweep if already swept
if (sweepCurrencyBlock != 0) revert CannotSweepCurrency();
// If the auction did not graduate there is no currency to sweep as it all must be refunded to bidders
if (!_isGraduated()) {
_sweepCurrency(_getBlockNumberish(), 0);
return;
}
// Sweep the currency and the protocol fee
uint256 currencyRaised = currencyRaised();
uint256 protocolFeeAmount =
ProtocolFeeLib.getProtocolFeeAmount(PROTOCOL_FEE_CONTROLLER, Currency.unwrap(CURRENCY), currencyRaised);
// Clamp the protocol fee to the currency raised so a misbehaving fee controller returning a fee
// greater than the currency raised cannot underflow the subtraction and permanently brick the sweep
if (protocolFeeAmount > currencyRaised) protocolFeeAmount = currencyRaised;
_sweepCurrency(_getBlockNumberish(), currencyRaised - protocolFeeAmount);
if (protocolFeeAmount > 0) {
ProtocolFeeLib.transferProtocolFee(PROTOCOL_FEE_CONTROLLER, Currency.unwrap(CURRENCY), protocolFeeAmount);
}
}
/// @inheritdoc ILBPInitializer
function sweepUnsoldTokens() external onlyAfterAuctionIsOver ensureEndBlockIsCheckpointed {
// Only recipient can sweep
if (msg.sender != TOKENS_RECIPIENT) revert NotAuthorized(TOKENS_RECIPIENT, msg.sender);
// Cannot sweep if already swept
if (sweepUnsoldTokensBlock != 0) revert CannotSweepTokens();
uint256 unsoldTokens;
if (_isGraduated()) {
unsoldTokens = remainingSupply();
} else {
unsoldTokens = TOTAL_SUPPLY;
}
_sweepUnsoldTokens(_getBlockNumberish(), unsoldTokens);
}
// State getters
/// @inheritdoc IContinuousClearingAuction
function requiredDemandQ96(uint256 _priceQ96) public view returns (uint256) {
uint256 remainingMps = ConstantsLib.MPS - latestCheckpoint().cumulativeMps;
if (remainingMps == 0) return 0;
return DemandLib.requiredDemandAtPrice(_remainingSupplyQ96X7(), _priceQ96, remainingMps);
}
/// @inheritdoc IContinuousClearingAuction
function requiredDemandQ96AtNextActiveTick() public view returns (uint256) {
if ($nextActiveTickPriceQ96 == MAX_TICK_PTR) return 0;
return requiredDemandQ96($nextActiveTickPriceQ96);
}
// Immutable getters
/// @inheritdoc IContinuousClearingAuction
function currency() external view returns (address) {
return Currency.unwrap(CURRENCY);
}
/// @inheritdoc IContinuousClearingAuction
function token() external view returns (address) {
return address(TOKEN);
}
/// @inheritdoc IContinuousClearingAuction
function totalSupply() external view returns (uint128) {
return TOTAL_SUPPLY;
}
/// @inheritdoc IContinuousClearingAuction
function tokensRecipient() external view returns (address) {
return TOKENS_RECIPIENT;
}
/// @inheritdoc IContinuousClearingAuction
function fundsRecipient() external view returns (address) {
return FUNDS_RECIPIENT;
}
/// @inheritdoc IContinuousClearingAuction
function startBlock() external view returns (uint64) {
return START_BLOCK;
}
/// @inheritdoc IContinuousClearingAuction
function endBlock() external view returns (uint64) {
return END_BLOCK;
}
/// @inheritdoc IContinuousClearingAuction
function claimBlock() external view returns (uint64) {
return CLAIM_BLOCK;
}
/// @inheritdoc IContinuousClearingAuction
function validationHook() external view returns (IValidationHook) {
return VALIDATION_HOOK;
}
}[
{
"type": "constructor",
"inputs": [
{
"name": "_token",
"type": "address",
"internalType": "address"
},
{
"name": "_totalSupply",
"type": "uint128",
"internalType": "uint128"
},
{
"name": "_parameters",
"type": "tuple",
"components": [
{
"name": "currency",
"type": "address",
"internalType": "address"
},
{
"name": "tokensRecipient",
"type": "address",
"internalType": "address"
},
{
"name": "fundsRecipient",
"type": "address",
"internalType": "address"
},
{
"name": "startBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "endBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "claimBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "tickSpacing",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "validationHook",
"type": "address",
"internalType": "address"
},
{
"name": "floorPrice",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "requiredCurrencyRaised",
"type": "uint128",
"internalType": "uint128"
},
{
"name": "auctionStepsData",
"type": "bytes",
"internalType": "bytes"
}
],
"internalType": "struct AuctionParameters"
},
{
"name": "_protocolFeeController",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "nonpayable"
},
{
"name": "AuctionIsNotFinalized",
"type": "error",
"inputs": []
},
{
"name": "AuctionIsNotOver",
"type": "error",
"inputs": []
},
{
"name": "AuctionIsOver",
"type": "error",
"inputs": []
},
{
"name": "AuctionNotStarted",
"type": "error",
"inputs": []
},
{
"name": "AuctionSoldOut",
"type": "error",
"inputs": []
},
{
"name": "BatchClaimDifferentOwner",
"type": "error",
"inputs": [
{
"name": "expectedOwner",
"type": "address",
"internalType": "address"
},
{
"name": "receivedOwner",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "BidAlreadyExited",
"type": "error",
"inputs": []
},
{
"name": "BidAmountTooSmall",
"type": "error",
"inputs": []
},
{
"name": "BidIdDoesNotExist",
"type": "error",
"inputs": [
{
"name": "bidId",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "BidMustBeAboveClearingPrice",
"type": "error",
"inputs": []
},
{
"name": "BidNotExited",
"type": "error",
"inputs": []
},
{
"name": "BidOwnerCannotBeZeroAddress",
"type": "error",
"inputs": []
},
{
"name": "CannotExitBid",
"type": "error",
"inputs": []
},
{
"name": "CannotPartiallyExitBidBeforeEndBlock",
"type": "error",
"inputs": []
},
{
"name": "CannotPartiallyExitBidBeforeGraduation",
"type": "error",
"inputs": []
},
{
"name": "CannotSweepCurrency",
"type": "error",
"inputs": []
},
{
"name": "CannotSweepTokens",
"type": "error",
"inputs": []
},
{
"name": "CannotUpdateUninitializedTick",
"type": "error",
"inputs": []
},
{
"name": "CheckpointAlreadyExistsForBlock",
"type": "error",
"inputs": []
},
{
"name": "CheckpointBlockNotIncreasing",
"type": "error",
"inputs": []
},
{
"name": "ClaimBlockIsBeforeEndBlock",
"type": "error",
"inputs": []
},
{
"name": "CurrencyIsNotNative",
"type": "error",
"inputs": []
},
{
"name": "ERC20TransferFailed",
"type": "error",
"inputs": []
},
{
"name": "FloorPriceAndTickSpacingGreaterThanMaxBidPrice",
"type": "error",
"inputs": [
{
"name": "nextTickQ96",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "maxBidPriceQ96",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "FloorPriceAndTickSpacingTooLarge",
"type": "error",
"inputs": []
},
{
"name": "FloorPriceIsZero",
"type": "error",
"inputs": []
},
{
"name": "FloorPriceTooLow",
"type": "error",
"inputs": []
},
{
"name": "FundsRecipientIsZero",
"type": "error",
"inputs": []
},
{
"name": "InvalidAmount",
"type": "error",
"inputs": []
},
{
"name": "InvalidAmountReceived",
"type": "error",
"inputs": [
{
"name": "expected",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "received",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "InvalidAuctionDataLength",
"type": "error",
"inputs": []
},
{
"name": "InvalidBidPriceTooHigh",
"type": "error",
"inputs": [
{
"name": "maxPriceQ96",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "maxBidPriceQ96",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "InvalidBidUnableToClear",
"type": "error",
"inputs": []
},
{
"name": "InvalidEndBlock",
"type": "error",
"inputs": []
},
{
"name": "InvalidEndBlockGivenStepData",
"type": "error",
"inputs": [
{
"name": "actualEndBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "expectedEndBlock",
"type": "uint64",
"internalType": "uint64"
}
]
},
{
"name": "InvalidLastFullyFilledCheckpointHint",
"type": "error",
"inputs": []
},
{
"name": "InvalidOutbidBlockCheckpointHint",
"type": "error",
"inputs": []
},
{
"name": "InvalidStepDataMps",
"type": "error",
"inputs": [
{
"name": "actualMps",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "expectedMps",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "InvalidTickPrice",
"type": "error",
"inputs": []
},
{
"name": "InvalidToken",
"type": "error",
"inputs": [
{
"name": "token",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "InvalidTokenAmountReceived",
"type": "error",
"inputs": []
},
{
"name": "MpsRemainingIsZero",
"type": "error",
"inputs": []
},
{
"name": "NativeTransferFailed",
"type": "error",
"inputs": []
},
{
"name": "NotAuthorized",
"type": "error",
"inputs": [
{
"name": "authorized",
"type": "address",
"internalType": "address"
},
{
"name": "caller",
"type": "address",
"internalType": "address"
}
]
},
{
"name": "NotClaimable",
"type": "error",
"inputs": []
},
{
"name": "NotGraduated",
"type": "error",
"inputs": []
},
{
"name": "Reentrancy",
"type": "error",
"inputs": []
},
{
"name": "StepBlockDeltaCannotBeZero",
"type": "error",
"inputs": []
},
{
"name": "StepLib__InvalidOffsetNotAtStepBoundary",
"type": "error",
"inputs": []
},
{
"name": "StepLib__InvalidOffsetTooLarge",
"type": "error",
"inputs": []
},
{
"name": "TickHintMustBeGreaterThanNextActiveTickPrice",
"type": "error",
"inputs": [
{
"name": "tickPriceQ96",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "nextActiveTickPriceQ96",
"type": "uint256",
"internalType": "uint256"
}
]
},
{
"name": "TickNotInitialized",
"type": "error",
"inputs": []
},
{
"name": "TickPreviousPriceInvalid",
"type": "error",
"inputs": []
},
{
"name": "TickPriceNotAtBoundary",
"type": "error",
"inputs": []
},
{
"name": "TickPriceNotIncreasing",
"type": "error",
"inputs": []
},
{
"name": "TickSpacingTooSmall",
"type": "error",
"inputs": []
},
{
"name": "TokenAndCurrencyCannotBeTheSame",
"type": "error",
"inputs": []
},
{
"name": "TokenIsAddressZero",
"type": "error",
"inputs": []
},
{
"name": "TokenTransferFailed",
"type": "error",
"inputs": []
},
{
"name": "TokensNotReceived",
"type": "error",
"inputs": []
},
{
"name": "TokensRecipientIsZero",
"type": "error",
"inputs": []
},
{
"name": "TotalSupplyIsTooLarge",
"type": "error",
"inputs": []
},
{
"name": "TotalSupplyIsZero",
"type": "error",
"inputs": []
},
{
"name": "ValidationHookCallFailed",
"type": "error",
"inputs": [
{
"name": "reason",
"type": "bytes",
"internalType": "bytes"
}
]
},
{
"name": "AuctionStepRecorded",
"type": "event",
"inputs": [
{
"name": "startBlock",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "endBlock",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "mps",
"type": "uint24",
"indexed": false,
"internalType": "uint24"
}
],
"anonymous": false
},
{
"name": "BidExited",
"type": "event",
"inputs": [
{
"name": "bidId",
"type": "uint256",
"indexed": true,
"internalType": "uint256"
},
{
"name": "owner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokensFilled",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "currencyRefunded",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "BidSubmitted",
"type": "event",
"inputs": [
{
"name": "id",
"type": "uint256",
"indexed": true,
"internalType": "uint256"
},
{
"name": "owner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "priceQ96",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "amount",
"type": "uint128",
"indexed": false,
"internalType": "uint128"
}
],
"anonymous": false
},
{
"name": "CheckpointUpdated",
"type": "event",
"inputs": [
{
"name": "blockNumber",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "clearingPriceQ96",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "cumulativeMps",
"type": "uint24",
"indexed": false,
"internalType": "uint24"
}
],
"anonymous": false
},
{
"name": "ClearingPriceUpdated",
"type": "event",
"inputs": [
{
"name": "blockNumber",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
},
{
"name": "clearingPriceQ96",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "CurrencySwept",
"type": "event",
"inputs": [
{
"name": "fundsRecipient",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "currencyAmount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "NextActiveTickUpdated",
"type": "event",
"inputs": [
{
"name": "priceQ96",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "ProtocolFeeTransferred",
"type": "event",
"inputs": [
{
"name": "currency",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "protocolFeeAmount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "TickInitialized",
"type": "event",
"inputs": [
{
"name": "priceQ96",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "TokensClaimed",
"type": "event",
"inputs": [
{
"name": "bidId",
"type": "uint256",
"indexed": true,
"internalType": "uint256"
},
{
"name": "owner",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokensFilled",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "TokensReceived",
"type": "event",
"inputs": [
{
"name": "totalSupply",
"type": "uint128",
"indexed": false,
"internalType": "uint128"
}
],
"anonymous": false
},
{
"name": "TokensSwept",
"type": "event",
"inputs": [
{
"name": "tokensRecipient",
"type": "address",
"indexed": true,
"internalType": "address"
},
{
"name": "tokensAmount",
"type": "uint256",
"indexed": false,
"internalType": "uint256"
}
],
"anonymous": false
},
{
"name": "MAX_BID_PRICE",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "MAX_BLOCK_NUMBER",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint64",
"internalType": "uint64"
}
],
"stateMutability": "view"
},
{
"name": "MAX_TICK_PTR",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "bids",
"type": "function",
"inputs": [
{
"name": "bidId",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"components": [
{
"name": "startBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "startCumulativeMps",
"type": "uint24",
"internalType": "uint24"
},
{
"name": "exitedBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "maxPrice",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "owner",
"type": "address",
"internalType": "address"
},
{
"name": "amountQ96",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "tokensFilled",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct Bid"
}
],
"stateMutability": "view"
},
{
"name": "checkpoint",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "tuple",
"components": [
{
"name": "clearingPrice",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "currencyRaisedAtClearingPriceQ96X7",
"type": "uint256",
"internalType": "ValueX7"
},
{
"name": "cumulativeMpsPerPrice",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "cumulativeMps",
"type": "uint24",
"internalType": "uint24"
},
{
"name": "prev",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "next",
"type": "uint64",
"internalType": "uint64"
}
],
"internalType": "struct Checkpoint"
}
],
"stateMutability": "nonpayable"
},
{
"name": "checkpoints",
"type": "function",
"inputs": [
{
"name": "blockNumber",
"type": "uint64",
"internalType": "uint64"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"components": [
{
"name": "clearingPrice",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "currencyRaisedAtClearingPriceQ96X7",
"type": "uint256",
"internalType": "ValueX7"
},
{
"name": "cumulativeMpsPerPrice",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "cumulativeMps",
"type": "uint24",
"internalType": "uint24"
},
{
"name": "prev",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "next",
"type": "uint64",
"internalType": "uint64"
}
],
"internalType": "struct Checkpoint"
}
],
"stateMutability": "view"
},
{
"name": "claimBlock",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint64",
"internalType": "uint64"
}
],
"stateMutability": "view"
},
{
"name": "claimTokens",
"type": "function",
"inputs": [
{
"name": "_bidId",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "claimTokensBatch",
"type": "function",
"inputs": [
{
"name": "_owner",
"type": "address",
"internalType": "address"
},
{
"name": "_bidIds",
"type": "uint256[]",
"internalType": "uint256[]"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "clearingPrice",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "currency",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "currencyRaised",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "currencyRaisedQ96X7",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "ValueX7"
}
],
"stateMutability": "view"
},
{
"name": "endBlock",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint64",
"internalType": "uint64"
}
],
"stateMutability": "view"
},
{
"name": "exitBid",
"type": "function",
"inputs": [
{
"name": "_bidId",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "exitPartiallyFilledBid",
"type": "function",
"inputs": [
{
"name": "_bidId",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_lastFullyFilledCheckpointBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "_outbidBlock",
"type": "uint64",
"internalType": "uint64"
}
],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "floorPrice",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "forceIterateOverTicks",
"type": "function",
"inputs": [
{
"name": "_untilTickPriceQ96",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "nonpayable"
},
{
"name": "fundsRecipient",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "isGraduated",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "view"
},
{
"name": "lastCheckpointedBlock",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint64",
"internalType": "uint64"
}
],
"stateMutability": "view"
},
{
"name": "latestCheckpoint",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "tuple",
"components": [
{
"name": "clearingPrice",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "currencyRaisedAtClearingPriceQ96X7",
"type": "uint256",
"internalType": "ValueX7"
},
{
"name": "cumulativeMpsPerPrice",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "cumulativeMps",
"type": "uint24",
"internalType": "uint24"
},
{
"name": "prev",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "next",
"type": "uint64",
"internalType": "uint64"
}
],
"internalType": "struct Checkpoint"
}
],
"stateMutability": "view"
},
{
"name": "lbpInitializationParams",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "params",
"type": "tuple",
"components": [
{
"name": "initialPriceX96",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "tokensSold",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "currencyRaised",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct LBPInitializationParams"
}
],
"stateMutability": "view"
},
{
"name": "nextActiveTickPrice",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "nextBidId",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "onTokensReceived",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "pointer",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "remainingSupply",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "remainingSupplyQ96X7",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "ValueX7"
}
],
"stateMutability": "view"
},
{
"name": "requiredDemandQ96",
"type": "function",
"inputs": [
{
"name": "_priceQ96",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "requiredDemandQ96AtNextActiveTick",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "startBlock",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint64",
"internalType": "uint64"
}
],
"stateMutability": "view"
},
{
"name": "step",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "tuple",
"components": [
{
"name": "mps",
"type": "uint24",
"internalType": "uint24"
},
{
"name": "startBlock",
"type": "uint64",
"internalType": "uint64"
},
{
"name": "endBlock",
"type": "uint64",
"internalType": "uint64"
}
],
"internalType": "struct AuctionStep"
}
],
"stateMutability": "view"
},
{
"name": "submitBid",
"type": "function",
"inputs": [
{
"name": "_maxPriceQ96",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_amount",
"type": "uint128",
"internalType": "uint128"
},
{
"name": "_owner",
"type": "address",
"internalType": "address"
},
{
"name": "_hookData",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "payable"
},
{
"name": "submitBid",
"type": "function",
"inputs": [
{
"name": "_maxPriceQ96",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_amount",
"type": "uint128",
"internalType": "uint128"
},
{
"name": "_owner",
"type": "address",
"internalType": "address"
},
{
"name": "_prevTickPriceQ96",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "_hookData",
"type": "bytes",
"internalType": "bytes"
}
],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "payable"
},
{
"name": "sumCurrencyDemandAboveClearingQ96",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "supportsInterface",
"type": "function",
"inputs": [
{
"name": "interfaceId",
"type": "bytes4",
"internalType": "bytes4"
}
],
"outputs": [
{
"name": "",
"type": "bool",
"internalType": "bool"
}
],
"stateMutability": "pure"
},
{
"name": "sweepCurrency",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "sweepCurrencyBlock",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "sweepUnsoldTokens",
"type": "function",
"inputs": [],
"outputs": [],
"stateMutability": "nonpayable"
},
{
"name": "sweepUnsoldTokensBlock",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "tickSpacing",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "ticks",
"type": "function",
"inputs": [
{
"name": "priceQ96",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "",
"type": "tuple",
"components": [
{
"name": "next",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "currencyDemandQ96",
"type": "uint256",
"internalType": "uint256"
}
],
"internalType": "struct Tick"
}
],
"stateMutability": "view"
},
{
"name": "token",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "tokensRecipient",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "address"
}
],
"stateMutability": "view"
},
{
"name": "totalCleared",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"name": "totalClearedQ96X7",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint256",
"internalType": "ValueX7"
}
],
"stateMutability": "view"
},
{
"name": "totalSupply",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "uint128",
"internalType": "uint128"
}
],
"stateMutability": "view"
},
{
"name": "validationHook",
"type": "function",
"inputs": [],
"outputs": [
{
"name": "",
"type": "address",
"internalType": "contract IValidationHook"
}
],
"stateMutability": "view"
}
]0x60806040526004361015610011575f80fd5b5f3560e01c806301ffc9a714610324578063083c63231461031f5780630e9a83cd1461031a5780630eff2dfb14610315578063111a977d1461031057806311ea09d01461030b578063140fe8ee1461030657806318160ddd146103015780631f699fe8146102fc5780632f5f3b3c146102f757806332a0f2d7146102f2578063331f2f65146102ed57806336dec5f2146102e857806337dfbc4b146102e35780633b6fd2cf146102de5780633e9d9174146102d95780634423c5f1146102d457806346e04a2f146102cf57806348cd4cb1146102ca578063534cb30d146102c5578063565c3cba146102c05780635ba38798146102bb5780635dd13ca7146102b657806360d3ded7146102b157806365dc990b146102ac5780637c121574146102a75780638134f027146102a25780638e4deb171461029d578063907c0f92146102985780639363c81214610293578063998ba4fc1461028e5780639e5f260214610289578063a52c872814610284578063a9176e451461027f578063ad7473e51461027a578063ae91fa3314610275578063b122db6014610270578063b8f163d61461026b578063c2c4c5c114610266578063cab8bedc14610261578063d0c93a7c1461025c578063da0239a614610257578063dc26904914610252578063e1d97d1f1461024d578063e25fe17514610248578063e5a6b10f14610243578063f04026df1461023e578063fc0c546a146102395763fd63755714610234575f80fd5b611442565b6113ff565b6113e5565b6113a2565b611352565b6111e0565b6111c4565b61119a565b611160565b611143565b611117565b6110ab565b61107a565b611040565b611023565b611006565b610faf565b610f67565b610f44565b610f0a565b610e99565b610e44565b610e01565b610d7d565b610d60565b610d43565b610b95565b610b77565b610b5d565b610b03565b610abf565b610927565b6107e8565b6107c5565b610782565b61073e565b6106f8565b6106de565b6106c1565b61067e565b610661565b610615565b610599565b610514565b6104f2565b610432565b610417565b6103d3565b346103c55760206003193601126103c5576004357fffffffff0000000000000000000000000000000000000000000000000000000081168091036103c557807f475b347e000000000000000000000000000000000000000000000000000000006020921490811561039b575b506040519015158152f35b7f01ffc9a7000000000000000000000000000000000000000000000000000000009150145f610390565b5f80fd5b5f9103126103c557565b346103c5575f6003193601126103c557602060405167ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e9aa168152f35b346103c5575f6003193601126103c55760206040515f198152f35b346103c55760206003193601126103c55760043561044e61263f565b4660011480156104c957688000000000ab143c065c6104b75761047f6104a29230688000000000ab143c065d611485565b90156104a6575f688000000000ab143c065d5b6040519081529081906020820190565b0390f35b688000000000ab143c068055610492565b688000000000ab143c065f526004601cfd5b30688000000000ab143c0654146104b75761047f6104a29230688000000000ab143c0655611485565b346103c5575f6003193601126103c557602060405167ffffffffffffffff8152f35b346103c5575f6003193601126103c557602067ffffffffffffffff60035416604051908152f35b602435906fffffffffffffffffffffffffffffffff821682036103c557565b6001600160a01b038116036103c557565b9181601f840112156103c55782359167ffffffffffffffff83116103c557602083818601950101116103c557565b60806003193601126103c5576004356105b061053b565b6044356105bc8161055a565b6064359167ffffffffffffffff83116103c5576020936105e361060d94369060040161056b565b9390927f0000000000000000000000000000000000000000000000087d30f4677f66aa809261208f565b604051908152f35b346103c5575f6003193601126103c55760206040516fffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000019d971e4fe8401e74000000168152f35b346103c5575f6003193601126103c5576020600954604051908152f35b346103c5575f6003193601126103c55760206040516001600160a01b037f0000000000000000000000008d9ae96a3a00bac9ea738629264a14964fc736e9168152f35b346103c5575f6003193601126103c5576020600b54604051908152f35b346103c5575f6003193601126103c5576106f66116b9565b005b346103c55760606003193601126103c55760043560243567ffffffffffffffff811681036103c5576044359067ffffffffffffffff821682036103c5576106f69261196d565b346103c5575f6003193601126103c557602060405167ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e9aa168152f35b346103c5575f6003193601126103c55760206040516001600160a01b037f00000000000000000000000005d552391067389ee44fec3924157ed33f976000168152f35b346103c5575f6003193601126103c557602061060d6298968060095460601c0490565b346103c55760206003193601126103c5576104a261081060043561080a611cf3565b5061298f565b60046040519161081f83611607565b610874610863825467ffffffffffffffff811686526108536108478262ffffff9060401c1690565b62ffffff166020880152565b60581c67ffffffffffffffff1690565b67ffffffffffffffff166040850152565b600181015460608401526108a561089560028301546001600160a01b031690565b6001600160a01b03166080850152565b600381015460a0840152015460c08201526040519182918291909160c08060e083019467ffffffffffffffff815116845262ffffff602082015116602085015267ffffffffffffffff6040820151166040850152606081015160608501526001600160a01b03608082015116608085015260a081015160a08501520151910152565b346103c55760206003193601126103c5576004356109436126d4565b67ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e9aa1611610a975767ffffffffffffffff6003541667ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e9aa1603610a78575b6008547f000000000000000000082cb50007abed5be1e98000000000000000000000000011610a50576109e381612c6b565b9190826109ec57005b60206001600160a01b0382610a45867f880f2ef2613b092f1a0a819f294155c98667eb294b7e6bf7a3810278142c1a1c95847f0000000000000000000000008ad265268d66a551cf282cc1fdd0af2231accb0c16612ccb565b6040519586521693a3005b7fd66173a5000000000000000000000000000000000000000000000000000000005f5260045ffd5b610a806118f0565b50610a8961263f565b610a9161255a565b506109b1565b7f6247a84e000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103c5575f6003193601126103c557602060405167ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e7e8168152f35b346103c55760206003193601126103c5576040610b346004355f60208451610b2a81611628565b828152015261272a565b8151610b3f81611628565b60206001835493848452015491019081528251918252516020820152f35b346103c5575f6003193601126103c557602061060d612da9565b346103c55760206003193601126103c557602061060d600435611d58565b346103c5575f6003193601126103c557610bad6126d4565b67ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e9aa16809110610d1c5767ffffffffffffffff6003541603610cfd575b6001600160a01b037f0000000000000000000000005fb260c6d9477fd50a4e13e2c3ccf8eda007cc6c16803303610cce57600e54610ca6576008547f000000000000000000082cb50007abed5be1e98000000000000000000000000011610c6b576106f6610c5e6125bd565b610c666126d4565b612dd9565b6106f66fffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000019d971e4fe8401e7400000016610c5e565b7f8fd6c3f9000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fc55ddc97000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b610d056118f0565b50610d0e61263f565b610d1661255a565b50610bf2565b7e175ba8000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103c5575f6003193601126103c5576020600754604051908152f35b346103c5575f6003193601126103c5576020600854604051908152f35b346103c5575f6003193601126103c557610d956126d4565b67ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e9aa16809110610d1c5767ffffffffffffffff6003541603610de2575b6106f6611dc8565b610dea6118f0565b50610df361263f565b610dfb61255a565b50610dda565b346103c5575f6003193601126103c55760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346103c55760206003193601126103c557600435610e606126d4565b67ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e9aa1611610d1c576106f690611f46565b346103c5575f6003193601126103c5576104a2610eb4612071565b6040519182918291909160a067ffffffffffffffff8160c084019580518552602081015160208601526040810151604086015262ffffff6060820151166060860152826080820151166080860152015116910152565b346103c5575f6003193601126103c55760206040517f0000000000000000000000000000000000000000000000087d30f4677f66aa808152f35b346103c5575f6003193601126103c557602061060d6298968060085460601c0490565b346103c5575f6003193601126103c5576020610fa56008547f000000000000000000082cb50007abed5be1e980000000000000000000000000111590565b6040519015158152f35b60a06003193601126103c557600435610fc661053b565b9060443591610fd48361055a565b6084359160643567ffffffffffffffff84116103c557602094610ffe61060d95369060040161056b565b94909361208f565b346103c5575f6003193601126103c5576020600a54604051908152f35b346103c5575f6003193601126103c5576020600e54604051908152f35b346103c5575f6003193601126103c55760206040517f00000000000000000000000000000006214682d523a8f2610c097b6953b336418152f35b346103c55760206003193601126103c55760043567ffffffffffffffff811681036103c557610eb46104a291612299565b346103c55760406003193601126103c5576004356110c88161055a565b6024359067ffffffffffffffff82116103c557366023830112156103c55781600401359067ffffffffffffffff82116103c5573660248360051b850101116103c55760246106f6930190612327565b346103c5575f6003193601126103c55761112f6118f0565b5061113861263f565b6104a2610eb461255a565b346103c5575f6003193601126103c5576020600d54604051908152f35b346103c5575f6003193601126103c55760206040517f00000000000000000000000000000000000000000000000015bb5e9aa28dd3a08152f35b346103c5575f6003193601126103c5576020629896806111b8612da9565b60601c04604051908152f35b346103c5575f6003193601126103c55760205f54604051908152f35b346103c5575f6003193601126103c5576111f86125d0565b5060035467ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e9aa811691160361132a5761126261125e6008547f000000000000000000082cb50007abed5be1e980000000000000000000000000111590565b1590565b610a50576104a261127a6298968060085460601c0490565b6112ce816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f0000000000000000000000000000000000000000000000000000000000000000612f9c565b906112ed600b54926112e76298968060095460601c0490565b92611dbb565b906112f6611845565b9283526020830152604082015260405191829182919091604080606083019480518452602081015160208501520151910152565b7ffc5e3bd5000000000000000000000000000000000000000000000000000000005f5260045ffd5b346103c5575f6003193601126103c55761136a6125d0565b5060606113756125ee565b67ffffffffffffffff604080519262ffffff81511684528260208201511660208501520151166040820152f35b346103c5575f6003193601126103c55760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346103c5575f6003193601126103c557602061060d612626565b346103c5575f6003193601126103c55760206040516001600160a01b037f0000000000000000000000008ad265268d66a551cf282cc1fdd0af2231accb0c168152f35b346103c5575f6003193601126103c55760206040516001600160a01b037f0000000000000000000000005fb260c6d9477fd50a4e13e2c3ccf8eda007cc6c168152f35b60035467ffffffffffffffff1667ffffffffffffffff6114b66114a96114a96126d4565b67ffffffffffffffff1690565b9116146115b2575f198103611539575b6114e7906114e160606114d7612071565b015162ffffff1690565b906128b6565b600b5481036114f35790565b6114fc81600b55565b7f30adbe996d7a69a21fdebcc1f8a46270bf6c22d505a7d872c1ab4767aa707609816115266126d4565b604080519182526020820192909252a190565b6115428161272a565b541561158a576007548082111561155957506114c6565b7f2453f670000000000000000000000000000000000000000000000000000000005f5260049190915260245260445ffd5b7fb61d0ca3000000000000000000000000000000000000000000000000000000005f5260045ffd5b7ffd61db11000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60e0810190811067ffffffffffffffff82111761162357604052565b6115da565b6040810190811067ffffffffffffffff82111761162357604052565b60c0810190811067ffffffffffffffff82111761162357604052565b6060810190811067ffffffffffffffff82111761162357604052565b90601f601f19910116810190811067ffffffffffffffff82111761162357604052565b908160209103126103c5575190565b6040513d5f823e3d90fd5b600c5460ff16611843576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526020816024817f0000000000000000000000008ad265268d66a551cf282cc1fdd0af2231accb0c6001600160a01b03165afa90811561183e575f9161180f575b507f0000000000000000000000000000000000000000019d971e4fe8401e74000000906fffffffffffffffffffffffffffffffff8216116117e7576117e27f468160b6769cb8abc9324bc14fe70ee0ce87f1e92087186c6ae22a964a04c572916117bf60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00600c541617600c55565b6040516fffffffffffffffffffffffffffffffff90911681529081906020820190565b0390a1565b7f268128ba000000000000000000000000000000000000000000000000000000005f5260045ffd5b611831915060203d602011611837575b611829818361167c565b81019061169f565b5f61172f565b503d61181f565b6116ae565b565b6040519061184360608361167c565b9060405161186181611607565b60c0600482946118ae61189d825467ffffffffffffffff8116875262ffffff808260401c1616602088015267ffffffffffffffff9060581c1690565b67ffffffffffffffff166040860152565b600181015460608501526118df6118cf60028301546001600160a01b031690565b6001600160a01b03166080860152565b600381015460a08501520154910152565b604051906118fd82611644565b5f60a0838281528260208201528260408201528260608201528260808201520152565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b906008820180921161195b57565b611920565b9190820180921161195b57565b909161197f61197a6118f0565b61254d565b916119886126d4565b9261199a6119958361298f565b611854565b9067ffffffffffffffff6119b9604084015167ffffffffffffffff1690565b16611c8f576119ed61125e6008547f000000000000000000082cb50007abed5be1e980000000000000000000000000111590565b611c2857606082015195611a09835167ffffffffffffffff1690565b611a1282612299565b918883511090811591611c05575b8115611bee575b50611bc657611a4091611a3a8592612299565b90612b71565b92909182958497611a4f6118f0565b5067ffffffffffffffff82168015611b3257611a696118f0565b5003611b2357505b87611a8f611a8a608084015167ffffffffffffffff1690565b612299565b915111801590611b19575b611af157611843975b80825114611ab6575b5050505050612abb565b611ae6959750611ae0939496509060206001611ad5611ae0959461272a565b015491015191612c18565b92611960565b915f80808080611aac565b7f516cb4ba000000000000000000000000000000000000000000000000000000005f5260045ffd5b5087815111611a9a565b611b2d9150612299565b611a71565b50905067ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e9aa1611611b9e5787815103611b765761184397611aa3565b7f0ba98457000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fe08d8a4f000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f6a009455000000000000000000000000000000000000000000000000000000005f5260045ffd5b905067ffffffffffffffff8083169116105f611a27565b905088611c20611a8a60a086015167ffffffffffffffff1690565b511090611a20565b505092505067ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e9aa161115611c86577f8af474ff000000000000000000000000000000000000000000000000000000005f5260045ffd5b611843906129d1565b7f34588221000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b8115611cee570490565b611cb7565b60405190611d0082611607565b5f60c0838281528260208201528260408201528260608201528260808201528260a08201520152565b62ffffff1662989680039062ffffff821161195b57565b9062ffffff8091169116039062ffffff821161195b57565b611d7162ffffff6060611d69612071565b015116611d29565b9062ffffff8216908115611db457611d87612da9565b9162ffffff84160361195b576effffff000000000000000000000000611db19360601b1691613932565b90565b5050505f90565b9190820391821161195b57565b7f00000000000000000000000005d552391067389ee44fec3924157ed33f9760006001600160a01b0381163303611f0e5750600d54611ee657611e3061125e6008547f000000000000000000082cb50007abed5be1e980000000000000000000000000111590565b611ed657611e456298968060085460601c0490565b7f0000000000000000000000000000000000000000000000000000000000000000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016611e9c828285612f9c565b91808311611ece575b611ebb90611eb5846112e76126d4565b90612eec565b81611ec557505050565b61184392613025565b915081611ea5565b611843611ee16126d4565b612e92565b7f76ae8ed7000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fc55ddc97000000000000000000000000000000000000000000000000000000005f526001600160a01b03166004523360245260445ffd5b611f4f8161298f565b9060405191611f5d83611607565b67ffffffffffffffff611ff8611f9283548381168752610853611f868262ffffff9060401c1690565b62ffffff166020890152565b67ffffffffffffffff1660408601908152600460018501549460608801958652611fd9611fc960028301546001600160a01b031690565b6001600160a01b031660808a0152565b600381015460a0890152015460c08701525167ffffffffffffffff1690565b16611c8f5761200561319f565b9061203561125e6008547f000000000000000000082cb50007abed5be1e980000000000000000000000000111590565b612065575181511015611b76578261205f91611a3a611a8a611843965167ffffffffffffffff1690565b91612abb565b505061184391506129d1565b6120796118f0565b50611db167ffffffffffffffff60035416612299565b93919492909461209d61263f565b4660011495861561227757688000000000ab143c065c6104b75730688000000000ab143c065d5b6120cc6126d4565b67ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e9aa16111561224f576fffffffffffffffffffffffffffffffff81168015612227576001600160a01b038316156121ff577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0381166121b0575034036121885761216495613274565b9015612179575f688000000000ab143c065d90565b688000000000ab143c06805590565b7f2c5211c6000000000000000000000000000000000000000000000000000000005f5260045ffd5b969096346121d757612164976121d29130906001600160a01b033391166131d1565b613274565b7fe3e0a9bd000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f4076a98c000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fc8731608000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f5f0ae8b5000000000000000000000000000000000000000000000000000000005f5260045ffd5b30688000000000ab143c0654146104b75730688000000000ab143c06556120c4565b67ffffffffffffffff906122ab6118f0565b50165f52600260205260405f20611db16123166003604051936122cd85611644565b805485526001810154602086015260028101546040860152015462ffffff8116606085015267ffffffffffffffff8160181c16608085015267ffffffffffffffff9060581c1690565b67ffffffffffffffff1660a0830152565b916123306126d4565b67ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e9aa1611610a975767ffffffffffffffff6003541667ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e9aa16036124f1575b6123cd61125e6008547f000000000000000000082cb50007abed5be1e980000000000000000000000000111590565b610a50575f916001600160a01b038416835b8381106124265750505050806123f3575050565b611843916001600160a01b037f0000000000000000000000008ad265268d66a551cf282cc1fdd0af2231accb0c16612ccb565b61243a612434828686612510565b35612c6b565b956001600160a01b038216918483036124b657509061245c8760019493611960565b968061246b575b5050016123df565b7f880f2ef2613b092f1a0a819f294155c98667eb294b7e6bf7a3810278142c1a1c6124ac61249a858a8a612510565b60405193845235929081906020820190565b0390a35f80612463565b7f1515875c000000000000000000000000000000000000000000000000000000005f526001600160a01b03908116600452881660245260445ffd5b6124f96118f0565b5061250261263f565b61250a61255a565b5061239e565b91908110156125205760051b0190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b5061255661263f565b611db15b67ffffffffffffffff61256b6126d4565b7f000000000000000000000000000000000000000000000000000000000086e9aa911667ffffffffffffffff82168111156125b35750611db1906125ad6118f0565b50613524565b611db19150613524565b629896806125c9612da9565b60601c0490565b604051906125dd82611660565b5f6040838281528260208201520152565b604051906125fb82611660565b81604067ffffffffffffffff60055462ffffff81168452818160181c16602085015260581c16910152565b6007545f19811461263a57611db190611d58565b505f90565b6126476126d4565b67ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e7e816116126ac5760ff600c54161561268457565b7f8d6b8a82000000000000000000000000000000000000000000000000000000005f5260045ffd5b7feffaea80000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f00000000000000000000000000000000000000000000000000000000000000011561271c5763a3b1b31d5f5260205f6004601c60645afa153d60201415176103c5575f5190565b4390565b8115611cee570690565b7f00000000000000000000000000000000000000000000000015bb5e9aa28dd3a08015611cee578106612765575f52600660205260405f2090565b7fd76fb50f000000000000000000000000000000000000000000000000000000005f5260045ffd5b600b54905f90600a546127ad6127a560075493611d29565b62ffffff1690565b926127b6612da9565b94851580156128ae575b6128a5576127d9866127d487869796612c05565b6138f5565b915b5f1984141580612893575b801561288a575b1561282a5750505061280d6128018261272a565b92600184015490611dbb565b9091549161281f856127d48685612c05565b9060019392936127db565b9195509350919091612849575b5050808210612844575090565b905090565b7fb9a86892440ed5515518351623ecfc523d283b21e92f1505e533ef26137be5b09161287761288092600a55565b61049281600755565b0390a15f80612837565b508383146127ed565b506128a086858988613876565b6127e6565b94505050505090565b5084156127c0565b90600b54905f92600a54906128d06127a560075494611d29565b936128d9612da9565b9586158015612987575b61297d57906128f8876127d488879897612c05565b915b838514158061296b575b8015612962575b156129485750505061292b61291f8361272a565b93600185015490611dbb565b9192549261293d866127d48786612c05565b9293929060016128fa565b925094509450919091612849575050808210612844575090565b5084831461290b565b5061297887868a89613876565b612904565b5094505050505090565b5085156128e3565b5f548110156129a6575f52600160205260405f2090565b7f9076e8b9000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6129da8161298f565b907f054fe6469466a0b4d2a6ae4b100e5f9c494c958f04b4000f44d470088dd9793060406001600160a01b0360028501541693612a7760038201548015150260601c915f600482015567ffffffffffffffff612a346126d4565b82547fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff16911660581b72ffffffffffffffff000000000000000000000016179055565b80612a8b575b8151905f82526020820152a3565b612ab681867f0000000000000000000000000000000000000000000000000000000000000000612ccb565b612a7d565b907f054fe6469466a0b4d2a6ae4b100e5f9c494c958f04b4000f44d470088dd9793090612ae78361298f565b90612b216001600160a01b036002840154169560038401549080820391110260601c9282600482015567ffffffffffffffff612a346126d4565b81612b41575b60408051918252602082019290925290819081015b0390a3565b612b6c82867f0000000000000000000000000000000000000000000000000000000000000000612ccb565b612b27565b91906040830151926040820151840393841161195b5760a09162ffffff60608181612ba3950151169201511690611d40565b92612bca612bb0846138e3565b9362ffffff8516938491019562ffffff8751911690613932565b93519162ffffff84160361195b577affffff000000000000000000000000000000000000000000000000612c029360c01b169161393c565b91565b8181029291811591840414171561195b57565b908015612c6257612c54612c3c60609262ffffff612c35866138e3565b1690612c05565b60a0840194612c4d82828851613932565b955161393c565b910151908115611cee570491565b5050505f905f90565b612c749061298f565b9067ffffffffffffffff825460581c1615612ca35760046001600160a01b036002840154169201905f82549255565b7f7e138882000000000000000000000000000000000000000000000000000000005f5260045ffd5b6001600160a01b038116612d1757505f8080612ce99481945af11590565b612cef57565b7ff4b3b1bc000000000000000000000000000000000000000000000000000000005f5260045ffd5b60205f60448194826040956001600160a01b03988751998a947fa9059cbb00000000000000000000000000000000000000000000000000000000865216600485015260248401525af13d15601f3d11600185511416171692828152826020820152015215612d8157565b7ff27f64e4000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f000000000000f684df56c3e01bc6c73200000000000000000000000000000000600954810390811161195b5790565b600e5580612e39575b6040519081527fdd9a81eb1b5197489c3ccfdab7b542e2e6dbdcf4120324e2688fab56fd23f98b60206001600160a01b037f0000000000000000000000005fb260c6d9477fd50a4e13e2c3ccf8eda007cc6c1692a2565b612e8d817f0000000000000000000000005fb260c6d9477fd50a4e13e2c3ccf8eda007cc6c6001600160a01b037f0000000000000000000000008ad265268d66a551cf282cc1fdd0af2231accb0c16612ccb565b612de2565b600d556040515f81527f053dfa7183794b221b03c5109dfb5a07b67d719cb3e98262d48bc66b2a132ad360206001600160a01b037f00000000000000000000000005d552391067389ee44fec3924157ed33f9760001692a2565b600d5580612f4c575b6040519081527f053dfa7183794b221b03c5109dfb5a07b67d719cb3e98262d48bc66b2a132ad360206001600160a01b037f00000000000000000000000005d552391067389ee44fec3924157ed33f9760001692a2565b612f97817f00000000000000000000000005d552391067389ee44fec3924157ed33f9760007f0000000000000000000000000000000000000000000000000000000000000000612ccb565b612ef5565b6001600160a01b0316908115611db45760446001600160a01b039160209360405195869485937f4e5d81cd00000000000000000000000000000000000000000000000000000000855216600484015260248301525afa5f9181613004575b50611db157505f90565b61301e91925060203d60201161183757611829818361167c565b905f612ffa565b919060206001600160a01b03809216936004604051809481937f64df049e000000000000000000000000000000000000000000000000000000008352165afa90811561183e575f91613164575b506001600160a01b0383166130cd576130905f80808086865af11590565b6130c857506040519081527f0880b0e717a57f3b5cbd3fc19396ae3c8cdee5c624c9289477869c38014c84959080602081015b0390a2565b614308565b6001600160a01b03604051917fa9059cbb0000000000000000000000000000000000000000000000000000000083521660048201528160248201525f604060208260448582895af13d15601f3d1160018551141617169282815282602082015201521561315e576130c37f0880b0e717a57f3b5cbd3fc19396ae3c8cdee5c624c9289477869c38014c849591610492565b50614253565b90506020813d602011613197575b8161317f6020938361167c565b810103126103c557516131918161055a565b5f613072565b3d9150613172565b6131a76118f0565b50611db17f000000000000000000000000000000000000000000000000000000000086e9aa613524565b5f91604051936001600160a01b0383166074860152856054860152603485015260601b60208401526f36c7851600000000000000000000000083526001461490811561325a575b3b15109160846010389201836e22d473030f116ddee9f6b43ac78ba35af1161561323f5750565b600490677939f4248757f0fd5f5260a01c151560021b601801fd5b6e22d473030f116ddee9f6b43ac78ba33b15159150613218565b93959490959291927f00000000000000000000000000000006214682d523a8f2610c097b6953b3364180861161349557506132e86132b36114a96126d4565b926132bd84613524565b9233878b8a7f0000000000000000000000000000000000000000000000000000000000000000613a2b565b62ffffff6132f582613b4f565b16158015613486575b61345e5780518511156134365783859161331b8361336596613b61565b613323611cf3565b5067ffffffffffffffff61335e60607bffffffffffffffffffffffffffffffff0000000000000000000000008c821b1693015162ffffff1690565b9416613c96565b91909461338f61338a6133788598613e4d565b6133828188613ea9565b600a54611960565b600a55565b600a547d01ad7f29abcaf485787a6520ec08d23699194119a5c37387b71906614310111561340e57604080519485526fffffffffffffffffffffffffffffffff90911660208501526001600160a01b03909116927f650baad5cd8ca09b8f580be220fa04ce2ba905a041f764b6a3fe2c848eb705409181908101612b3c565b7fa37fb9e3000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f5f259e52000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f5cd54361000000000000000000000000000000000000000000000000000000005f5260045ffd5b5061348f612da9565b156132fe565b7fae9d6dc2000000000000000000000000000000000000000000000000000000005f52600486905260245260445ffd5b9067ffffffffffffffff8091169116039067ffffffffffffffff821161195b57565b9067ffffffffffffffff8091169116029067ffffffffffffffff821691820361195b57565b9062ffffff8091169116019062ffffff821161195b57565b9061352d6118f0565b5060035467ffffffffffffffff169167ffffffffffffffff83168067ffffffffffffffff83161461386a57906136066114a96136006127a57ff1e4b6d7d0d7c5deb6393a39862d66a2f2ecb034f3283a8a597f9bf0c36f76fa956135fa6127a56135f06135ea6135c361359e612071565b9d8e62ffffff6135ad82613b4f565b166137df575b506135bc6125d0565b508b613ef3565b9890956135de6114a9602089015167ffffffffffffffff1690565b90818111908218021890565b896134c5565b925162ffffff1690565b906134e7565b9061350c565b62ffffff81169081613663575b505061361f8185614071565b8351906117e2613635606087015162ffffff1690565b6040519384938491604091949362ffffff9167ffffffffffffffff6060860197168552602085015216910152565b61369a9161366f612da9565b806136a1575b5050613691606087019161368c835162ffffff1690565b61350c565b62ffffff169052565b5f80613613565b61375961375e9261375361374e8b600a5481519685886136c18285612c05565b936136ec7f00000000000000000000000000000000000000000000000015bb5e9aa28dd3a083612720565b15613776575b508394506137419250613715915061370d90613746946138f5565b600954611960565b7f000000000000f684df56c3e01bc6c73200000000000000000000000000000000818110908218021890565b600955565b600854611960565b600855565b60c01b90565b611ce4565b61376d60408801918251611960565b90525f80613675565b60016137818361272a565b01548061378f575b506136f2565b936020936137c7936137c0936137d1976137ba6127a56137b560608e015162ffffff1690565b611d29565b9461401e565b8094611960565b9301918251611960565b90528c905f85888280613789565b6137f76137f2606083015162ffffff1690565b61278d565b81518103613806575b506135b3565b5f602083837f30adbe996d7a69a21fdebcc1f8a46270bf6c22d505a7d872c1ab4767aa7076099552015261383981600b55565b8d613860604051928392836020909392919367ffffffffffffffff60408201951681520152565b0390a18e5f613800565b50509050611db1612071565b92613882919293612c05565b905f196c0100000000000000000000000083099160601b9182808210910303925f19818309910290818082109103038084119384156138c3575b5050505090565b14925090826138d8575b50505f8080806138bc565b101590505f806138cd565b62ffffff6020611db192015116611d29565b91906c0100000000000000000000000061391082828661393c565b930961391857565b9060010190811561392557565b63ae47f7025f526004601cfd5b9291906139108282865b81810292918115828504821417830215613957575050900490565b805f198492840985811086019003920990825f03831692818111156139255783900480600302600218808202600203028082026002030280820260020302808202600203028082026002030280910260020302936001848483030494805f0304019211900302170290565b3d156139fc573d9067ffffffffffffffff821161162357604051916139f16020601f19601f840116018461167c565b82523d5f602084013e565b606090565b601f19601f602060409481855280519182918282880152018686015e5f8582860101520116010190565b6001600160a01b039096919293949616948515613b4657853b156103c557819060405197889687967f22c44b5f000000000000000000000000000000000000000000000000000000008852600488019687526fffffffffffffffffffffffffffffffff1660208701526001600160a01b031660408601526001600160a01b031660608501526080840160a090528160a085015260c084013780820160c0015f9052601f01601f19160160c00103815a5f948591f19081613b2c575b5061184357613b28613af66139c2565b6040519182917f5d73cdc500000000000000000000000000000000000000000000000000000000835260048301613a01565b0390fd5b80613b3a5f613b409361167c565b806103c9565b5f613ae6565b50505050505050565b62ffffff6060611db192015116611d29565b5f198214613c6057613b728261272a565b908154613c5b5782811015613c3357613b8a8161272a565b54918215613c33575b838310613c20579183613bcd7f7fdd20e2dbf90ff60a7d9be5ad62f1ec6d9d9cba8b36174a3839cafd059f09589593836117e2965561272a565b5560075414613be6576040519081529081906020820190565b613bef81600755565b6040518181527fb9a86892440ed5515518351623ecfc523d283b21e92f1505e533ef26137be5b090602090a1610492565b919050613c2c8161272a565b5491613b93565b7fa16c4535000000000000000000000000000000000000000000000000000000005f5260045ffd5b505050565b7f75385102000000000000000000000000000000000000000000000000000000005f5260045ffd5b5f19811461195b5760010190565b939262ffffff6001600160a01b03939294613caf611cf3565b5067ffffffffffffffff60405197613cc689611607565b1687521660208601525f6040860152606085015216608083015260a08201525f60c082015280915f5491825f526001602052600460c060405f2092613d4567ffffffffffffffff825116859067ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000825416179055565b602081015184547fffffffffffffffffffffffffffffffffffffffffff000000ffffffffffffffff16604091821b6affffff000000000000000016178555810151613dd89067ffffffffffffffff1685547fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff1660589190911b72ffffffffffffffff000000000000000000000016178555565b60608101516001850155613e30613df960808301516001600160a01b031690565b60028601906001600160a01b03167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b60a081015160038501550151910155613e495f54613c88565b5f55565b62ffffff613e5a826138e3565b16908115613e815760a001516298968081029080820462989680149015171561195b570490565b7f15604cc5000000000000000000000000000000000000000000000000000000005f5260045ffd5b613eb29061272a565b805415613ecb57600101805491820180921161195b5755565b7f997768ad000000000000000000000000000000000000000000000000000000005f5260045ffd5b9091613efd6125d0565b505f613f3c6114a9613f0d6125ee565b9567ffffffffffffffff613f2f6114a960208a015167ffffffffffffffff1690565b9116818111908218021890565b93613f52604082015167ffffffffffffffff1690565b90613f60815162ffffff1690565b9267ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000086e9aa16935b67ffffffffffffffff8416978867ffffffffffffffff89161115614014579162ffffff809281613fbf8996896134c5565b91160216011692961461400d575067ffffffffffffffff90613fdf614429565b95613fed875162ffffff1690565b90614003604089015167ffffffffffffffff1690565b9350969190613f8e565b9450925050565b5050945094505050565b919361403b81614035816140419598999799612c05565b96612c05565b95612c05565b6001600160a01b038316830361195b5761405e9260601b91613932565b9080820391110290818110908218021890565b9067ffffffffffffffff6003541667ffffffffffffffff8216928184111561422b5761184393826141f693608084015267ffffffffffffffff60a08401525f52600260205261410584600360405f2001907fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff72ffffffffffffffff000000000000000000000083549260581b169116179055565b5f52600260205267ffffffffffffffff60a0600360405f20845181556020850151600182015560408501516002820155019262ffffff80606083015116167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000008554161784556141b18360808301511685907fffffffffffffffffffffffffffffffffffffffffff0000000000000000ffffff6affffffffffffffff00000083549260181b169116179055565b015182547fffffffffffffffffffffffffff0000000000000000ffffffffffffffffffffff16911660581b72ffffffffffffffff000000000000000000000016179055565b67ffffffffffffffff167fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006003541617600355565b7fca31f40d000000000000000000000000000000000000000000000000000000005f5260045ffd5b601f19601f3d01166001600160a01b03604051927f90bfb8650000000000000000000000000000000000000000000000000000000084521660048301527fa9059cbb000000000000000000000000000000000000000000000000000000006024830152608060448301528060a00160648301523d60848301523d5f60a484013e7ff27f64e40000000000000000000000000000000000000000000000000000000060c4828401600460a4820152015260e40190fd5b601f19601f3d01166001600160a01b03604051927f90bfb8650000000000000000000000000000000000000000000000000000000084521660048301525f6024830152608060448301528060a00160648301523d60848301523d5f60a484013e7ff4b3b1bc0000000000000000000000000000000000000000000000000000000060c4828401600460a4820152015260e40190fd5b90602082519201517fffffffffffffffff000000000000000000000000000000000000000000000000811692600881106143d5575050565b7fffffffffffffffff000000000000000000000000000000000000000000000000929350829060080360031b1b161690565b9067ffffffffffffffff8091169116019067ffffffffffffffff821161195b57565b6144316125d0565b506004547f000000000000000000000000000000000000000000000000000000000000006881101561224f576144d06144bd6144b8836144917f6863f2b489f9186bf89231dc73aa0e9836f536b9ddb0f708f74260ed3160f2979561194d565b907f0000000000000000000000008d9ae96a3a00bac9ea738629264a14964fc736e9614608565b61439d565b9064ffffffffff8260e81c9260c01c1690565b60055460581c67ffffffffffffffff169067ffffffffffffffff8216156145e0575b61450664ffffffffff6145d5921683614407565b92614592614512611845565b62ffffff831680825267ffffffffffffffff86811660208401528716604090920191909152600580547fffffffffffffffffffffffffff0000000000000000000000000000000000000016909117601886901b6affffffffffffffff0000001617605887901b72ffffffffffffffff000000000000000000000016179055565b6145a56145a060045461194d565b600455565b6040519384938491604091949367ffffffffffffffff62ffffff9281606087019816865216602085015216910152565b0390a1611db16125ee565b7f000000000000000000000000000000000000000000000000000000000086e7e891506144f2565b60409193929382519461ffff811015614677575b80821082820302926001840183601f8901833c8387015160ff1615614650575b50505080845283015f602082015201604052565b90600193913b5f1981019485918260281c3d3d3e83030191110290039111025f808061463c565b5061ffff61461c56fea164736f6c634300081a000a
| Transaction Hash | Method | Block | Age | From | To | Type | Item | ||
|---|---|---|---|---|---|---|---|---|---|
| no NFT transfers for this address yet | |||||||||
| Type | Age | Block | Details |
|---|---|---|---|
| no cross-chain L1↔L2 transactions for this address | |||
| Txn Hash | Age | Event | Topics / Data |
|---|---|---|---|
| 0xcb58e7…04aa64 | 33 days agoMon, 13 Jul 2026 21:38:57 UTC | 0x880f2e…1a1c | [0] 0x000000000000…00000004 [1] 0x000000000000…4a1ae9c7 data: 0x000000000000000000…4d813c6a |
| 0xcb58e7…04aa64 | 33 days agoMon, 13 Jul 2026 21:38:57 UTC | 0x054fe6…7930 | [0] 0x000000000000…00000004 [1] 0x000000000000…4a1ae9c7 data: 0x000000000000000000…20a145f8 |
| 0x839664…3cea47 | 33 days agoMon, 13 Jul 2026 17:23:58 UTC | 0x880f2e…1a1c | [0] 0x000000000000…00000006 [1] 0x000000000000…6f0d216d data: 0x000000000000000000…d3462b55 |
| 0x839664…3cea47 | 33 days agoMon, 13 Jul 2026 17:23:58 UTC | 0x054fe6…7930 | [0] 0x000000000000…00000006 [1] 0x000000000000…6f0d216d data: 0x000000000000000000…00000000 |
| 0xfecd1b…887523 | 33 days agoMon, 13 Jul 2026 17:20:22 UTC | 0x880f2e…1a1c | [0] 0x000000000000…00000003 [1] 0x000000000000…afb369eb data: 0x000000000000000000…5ec648d4 |
| 0xfecd1b…887523 | 33 days agoMon, 13 Jul 2026 17:20:22 UTC | 0x054fe6…7930 | [0] 0x000000000000…00000003 [1] 0x000000000000…afb369eb data: 0x000000000000000000…135a0d3f |
| 0x1e8860…791908 | 33 days agoMon, 13 Jul 2026 17:19:51 UTC | 0x880f2e…1a1c | [0] 0x000000000000…00000001 [1] 0x000000000000…7f0e282e data: 0x000000000000000000…f731fb0b |
| 0x1e8860…791908 | 33 days agoMon, 13 Jul 2026 17:19:51 UTC | 0x054fe6…7930 | [0] 0x000000000000…00000001 [1] 0x000000000000…7f0e282e data: 0x000000000000000000…26e71943 |
| 0x77c157…cfe79b | 33 days agoMon, 13 Jul 2026 17:19:50 UTC | 0x880f2e…1a1c | [0] 0x000000000000…00000005 [1] 0x000000000000…a5f72a4a data: 0x000000000000000000…71eb6d95 |
| 0x77c157…cfe79b | 33 days agoMon, 13 Jul 2026 17:19:50 UTC | 0x054fe6…7930 | [0] 0x000000000000…00000005 [1] 0x000000000000…a5f72a4a data: 0x000000000000000000…00000000 |
| 0x4cfde0…cff8b6 | 33 days agoMon, 13 Jul 2026 17:19:42 UTC | 0x880f2e…1a1c | [0] 0x000000000000…00000000 [1] 0x000000000000…9d93f2d2 data: 0x000000000000000000…3a3bf9bf |
| 0x4cfde0…cff8b6 | 33 days agoMon, 13 Jul 2026 17:19:42 UTC | 0x054fe6…7930 | [0] 0x000000000000…00000000 [1] 0x000000000000…9d93f2d2 data: 0x000000000000000000…00000000 |
| 0xcd5dc0…d18587 | 33 days agoMon, 13 Jul 2026 17:19:42 UTC | 0x880f2e…1a1c | [0] 0x000000000000…00000007 [1] 0x000000000000…12a407a2 data: 0x000000000000000000…3453372f |
| 0xcd5dc0…d18587 | 33 days agoMon, 13 Jul 2026 17:19:42 UTC | 0x054fe6…7930 | [0] 0x000000000000…00000007 [1] 0x000000000000…12a407a2 data: 0x000000000000000000…6ac86462 |
| 0xf9b36e…cc304c | 33 days agoMon, 13 Jul 2026 17:19:36 UTC | 0x053dfa…2ad3 | [0] 0x000000000000…3f976000 data: 0x000000000000000000…b6c8c421 |
| 0xf9b36e…cc304c | 33 days agoMon, 13 Jul 2026 17:19:36 UTC | 0x880f2e…1a1c | [0] 0x000000000000…00000002 [1] 0x000000000000…98c0e2dc data: 0x000000000000000000…1cc5b5db |
| 0xf9b36e…cc304c | 33 days agoMon, 13 Jul 2026 17:19:36 UTC | 0x054fe6…7930 | [0] 0x000000000000…00000002 [1] 0x000000000000…98c0e2dc data: 0x000000000000000000…b9b66b00 |
| 0xf9b36e…cc304c | 33 days agoMon, 13 Jul 2026 17:19:36 UTC | 0xf1e4b6…76fa | data: 0x000000000000000000…00989680 |
| 0xf9b36e…cc304c | 33 days agoMon, 13 Jul 2026 17:19:36 UTC | 0x6863f2…f297 | data: 0x000000000000000000…002dc694 |
| 0xf9b36e…cc304c | 33 days agoMon, 13 Jul 2026 17:19:36 UTC | 0x6863f2…f297 | data: 0x000000000000000000…00004981 |
| 0xf9b36e…cc304c | 33 days agoMon, 13 Jul 2026 17:19:36 UTC | 0x6863f2…f297 | data: 0x000000000000000000…00004735 |
| 0xf9b36e…cc304c | 33 days agoMon, 13 Jul 2026 17:19:36 UTC | 0x6863f2…f297 | data: 0x000000000000000000…0000450d |
| 0xf9b36e…cc304c | 33 days agoMon, 13 Jul 2026 17:19:36 UTC | 0x30adbe…7609 | data: 0x000000000000000000…c48251c0 |
| 0xf9b36e…cc304c | 33 days agoMon, 13 Jul 2026 17:19:36 UTC | 0xb9a868…e5b0 | data: 0x000000000000000000…4301e7e0 |
| 0x73d74e…4e69b8 | 33 days agoMon, 13 Jul 2026 17:19:12 UTC | 0x650baa…0540 | [0] 0x000000000000…00000007 [1] 0x000000000000…12a407a2 data: 0x000000000000000000…bb140000 |
| Transaction Hash | Method | Block | Age | From | To | Amount | Token | ||
|---|---|---|---|---|---|---|---|---|---|
| 0x04a03aa2…d79693 | Transfer | 19,822,855 | 20 days agoSun, 26 Jul 2026 11:08:29 UTC | 0xcaf7…5d11 | IN | 0xee5b…3646 | $0.001 DIH | ||
| 0xcb58e705…04aa64 | Transfer | 8,997,045 | 33 days agoMon, 13 Jul 2026 21:38:57 UTC | 0xee5b…3646 | OUT | 0x8678…e9c7 | 15,428,184.515396 CORN | UNICORN (CORN) | |
| 0x839664a5…3cea47 | Transfer | 8,844,375 | 33 days agoMon, 13 Jul 2026 17:23:58 UTC | 0xee5b…3646 | OUT | 0xe286…216d | 149,143,527.384322 CORN | UNICORN (CORN) | |
| 0xfecd1b23…887523 | Transfer | 8,842,210 | 33 days agoMon, 13 Jul 2026 17:20:22 UTC | 0xee5b…3646 | OUT | 0x5b7e…69eb | 16,869,320.452695 CORN | UNICORN (CORN) | |
| 0x1e8860fe…791908 | Transfer | 8,841,903 | 33 days agoMon, 13 Jul 2026 17:19:51 UTC | 0xee5b…3646 | OUT | 0x0b2d…282e | 22,647,207.978469 CORN | UNICORN (CORN) | |
| 0x77c157b3…cfe79b | Transfer | 8,841,896 | 33 days agoMon, 13 Jul 2026 17:19:50 UTC | 0xee5b…3646 | OUT | 0x73ae…2a4a | 124,394,611.206345 CORN | UNICORN (CORN) | |
| 0x4cfde000…cff8b6 | Transfer | 8,841,816 | 33 days agoMon, 13 Jul 2026 17:19:42 UTC | 0xee5b…3646 | OUT | 0xc54f…f2d2 | 99,717,859.948321 CORN | UNICORN (CORN) | |
| 0xcd5dc059…d18587 | Transfer | 8,841,809 | 33 days agoMon, 13 Jul 2026 17:19:42 UTC | 0xee5b…3646 | OUT | 0x17e4…07a2 | 58,912,023.618111 CORN | UNICORN (CORN) | |
| 0xf9b36e33…cc304c | Transfer | 8,841,756 | 33 days agoMon, 13 Jul 2026 17:19:36 UTC | 0xee5b…3646 | OUT | 0x9451…e2dc | 12,887,264.896337 CORN | UNICORN (CORN) | |
| 0x0f1cd3e8…4255d8 | Transfer | 8,837,782 | 33 days agoMon, 13 Jul 2026 17:12:59 UTC | 0x0000…d4e9 | IN | 0xee5b…3646 | 500,000,000.000000 CORN | UNICORN (CORN) |
| Transaction Hash | Method ? | Block | Age | From | To | Amount | Txn Fee ? | ||
|---|---|---|---|---|---|---|---|---|---|
| 0x73d74e…4e69b8 | submitBid | 8,841,518 | 33 days agoMon, 13 Jul 2026 17:19:12 UTC | 0x17e4…07a2 | IN | ContinuousClearingAuction | $376.460.2 ETH | 0.00001759 | |
| 0x2adb59…769f2b | submitBid | 8,841,427 | 33 days agoMon, 13 Jul 2026 17:19:03 UTC | 0xe286…216d | IN | ContinuousClearingAuction | $564.700.3 ETH | 0.00001752 | |
| 0x7db400…2e7897 | submitBid | 8,841,373 | 33 days agoMon, 13 Jul 2026 17:18:58 UTC | 0x73ae…2a4a | IN | ContinuousClearingAuction | $470.580.25 ETH | 0.00001578 | |
| 0xf9db98…dc7627 | submitBid | 8,841,358 | 33 days agoMon, 13 Jul 2026 17:18:56 UTC | 0x8678…e9c7 | IN | ContinuousClearingAuction | $376.460.2 ETH | 0.00001310 | |
| 0x37d2b7…e458ce | submitBid | 8,841,349 | 33 days agoMon, 13 Jul 2026 17:18:55 UTC | 0x5b7e…69eb | IN | ContinuousClearingAuction | $376.460.2 ETH | 0.00001294 | |
| 0x6c8f92…9d5304 | submitBid | 8,841,347 | 33 days agoMon, 13 Jul 2026 17:18:55 UTC | 0x9451…e2dc | IN | ContinuousClearingAuction | $282.350.15 ETH | 0.00001362 | |
| 0xf6638c…93b8c6 | submitBid | 8,841,309 | 33 days agoMon, 13 Jul 2026 17:18:51 UTC | 0x0b2d…282e | IN | ContinuousClearingAuction | $376.460.2 ETH | 0.00001719 | |
| 0x41d89d…81a12c | submitBid | 8,841,302 | 33 days agoMon, 13 Jul 2026 17:18:51 UTC | 0xc54f…f2d2 | IN | ContinuousClearingAuction | $376.460.2 ETH | 0.00002065 |
| Block | Age | Parent Transaction Hash | Type | Method | From | To | Value | |
|---|---|---|---|---|---|---|---|---|
| 8,841,756 | 33 days agoMon, 13 Jul 2026 17:19:36 UTC | 0xf9b36e…cc304c | CALL | aggregate3 | 0xee5b…3646 | OUT | 0x05d5…6000 | 1.00373 ETH |
| 8,841,756 | 33 days agoMon, 13 Jul 2026 17:19:36 UTC | 0xf9b36e…cc304c | CALL | aggregate3 | 0xee5b…3646 | OUT | 0x9451…e2dc | 0.12434 ETH |