Source Code
Latest 25 from a total of 1,288 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Claim Lock Up By... | 35121799 | 74 days ago | IN | 0 ETH | 0.00000012 | ||||
| Swap ETH For Tok... | 35121616 | 74 days ago | IN | 0.005 ETH | 0.0000003 | ||||
| Create Sell Orde... | 35121501 | 74 days ago | IN | 0 ETH | 0.00000056 | ||||
| Create Sell Orde... | 30702928 | 176 days ago | IN | 0 ETH | 0.00000096 | ||||
| Claim Lock Up By... | 25348397 | 300 days ago | IN | 0 ETH | 0.0000129 | ||||
| Create Sell Orde... | 25256655 | 302 days ago | IN | 0 ETH | 0.00000532 | ||||
| Cancel Orders | 25256571 | 302 days ago | IN | 0 ETH | 0.00000203 | ||||
| Update Price | 25256554 | 302 days ago | IN | 0 ETH | 0.00000153 | ||||
| Update Price | 25256531 | 302 days ago | IN | 0 ETH | 0.00000154 | ||||
| Update Price | 25255896 | 302 days ago | IN | 0 ETH | 0.00000178 | ||||
| Swap ETH For Tok... | 25255516 | 302 days ago | IN | 0.03 ETH | 0.00000434 | ||||
| Swap ETH For Tok... | 25255498 | 302 days ago | IN | 0.03 ETH | 0.00000438 | ||||
| Swap ETH For Tok... | 25255479 | 302 days ago | IN | 0.03 ETH | 0.00000445 | ||||
| Swap ETH For Tok... | 25255418 | 302 days ago | IN | 0.03 ETH | 0.00000448 | ||||
| Swap ETH For Tok... | 25255401 | 302 days ago | IN | 0.03 ETH | 0.00000452 | ||||
| Swap ETH For Tok... | 25255383 | 302 days ago | IN | 0.03 ETH | 0.00000458 | ||||
| Swap ETH For Tok... | 25255365 | 302 days ago | IN | 0.03 ETH | 0.00000462 | ||||
| Swap ETH For Tok... | 25255345 | 302 days ago | IN | 0.03 ETH | 0.00000466 | ||||
| Swap ETH For Tok... | 25255328 | 302 days ago | IN | 0.03 ETH | 0.0000047 | ||||
| Swap ETH For Tok... | 25255312 | 302 days ago | IN | 0.03 ETH | 0.00000475 | ||||
| Swap ETH For Tok... | 25255295 | 302 days ago | IN | 0.03 ETH | 0.00000475 | ||||
| Swap ETH For Tok... | 25255275 | 302 days ago | IN | 0.03 ETH | 0.00000477 | ||||
| Swap ETH For Tok... | 25255256 | 302 days ago | IN | 0.03 ETH | 0.00000478 | ||||
| Swap ETH For Tok... | 25255234 | 302 days ago | IN | 0.03 ETH | 0.00000483 | ||||
| Swap ETH For Tok... | 25255216 | 302 days ago | IN | 0.03 ETH | 0.00000486 |
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 35121616 | 74 days ago | 0.00005 ETH | ||||
| 35121616 | 74 days ago | 0.00495 ETH | ||||
| 25255516 | 302 days ago | 0.0003 ETH | ||||
| 25255516 | 302 days ago | 0.0297 ETH | ||||
| 25255498 | 302 days ago | 0.0003 ETH | ||||
| 25255498 | 302 days ago | 0.0297 ETH | ||||
| 25255479 | 302 days ago | 0.0003 ETH | ||||
| 25255479 | 302 days ago | 0.0297 ETH | ||||
| 25255418 | 302 days ago | 0.0003 ETH | ||||
| 25255418 | 302 days ago | 0.0297 ETH | ||||
| 25255401 | 302 days ago | 0.0003 ETH | ||||
| 25255401 | 302 days ago | 0.0297 ETH | ||||
| 25255383 | 302 days ago | 0.0003 ETH | ||||
| 25255383 | 302 days ago | 0.0297 ETH | ||||
| 25255365 | 302 days ago | 0.0003 ETH | ||||
| 25255365 | 302 days ago | 0.0297 ETH | ||||
| 25255345 | 302 days ago | 0.0003 ETH | ||||
| 25255345 | 302 days ago | 0.0297 ETH | ||||
| 25255328 | 302 days ago | 0.0003 ETH | ||||
| 25255328 | 302 days ago | 0.0297 ETH | ||||
| 25255312 | 302 days ago | 0.0003 ETH | ||||
| 25255312 | 302 days ago | 0.0297 ETH | ||||
| 25255295 | 302 days ago | 0.0003 ETH | ||||
| 25255295 | 302 days ago | 0.0297 ETH | ||||
| 25255275 | 302 days ago | 0.0003 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
OTSea
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
/*
[.... [... [...... [.. ..
[.. [.. [.. [.. [..
[.. [.. [.. [.. [.. [..
[.. [.. [.. [.. [. [.. [.. [..
[.. [.. [.. [.. [..... [..[.. [..
[.. [.. [.. [.. [..[. [.. [..
[.... [.. [.. .. [.... [.. [...
OTSea Platform.
https://otsea.io
https://t.me/OTSeaPortal
https://twitter.com/OTSeaERC20
*/
// SPDX-License-Identifier: MIT
pragma solidity =0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "contracts/helpers/ListHelper.sol";
import "contracts/helpers/SignatureHelper.sol";
import "contracts/helpers/TransferHelper.sol";
import "contracts/helpers/WhitelistHelper.sol";
import "contracts/libraries/OTSeaErrors.sol";
import "contracts/libraries/OTSeaLibrary.sol";
/**
* @title OTSea Over-the-Counter (OTC) Contract
* @dev This contract facilitates the creation and execution of buy and sell orders for tokens using ETH.
*
* Key definitions:
* - Order: An instruction given by a user to buy or sell tokens for a certain amount of ETH.
* - Trade/Swap: The partial or complete execution of an order.
* - Input:
* - For buy orders: The amount of ETH that will be used to purchase tokens.
* - For sell orders: The amount of tokens that will be up for sale.
* - Output:
* - For buy orders: The amount of tokens desired for the ETH input.
* - For sell orders: The amount of ETH desired for the token input.
*
* Transfer tax tokens:
* - When creating a sell order, if upon transferring tokens into the contract there are fewer tokens than expected. It
* is assumed the token has a transfer tax therefore, the total input and total output are reduced.
*
* Order features:
* - All-or-Nothing (AON): If enabled, an order must be filled in a single trade. If disabled, orders can be partially filled.
* - Whitelisting: Restricts trading to only whitelisted addresses.
* - Lock-up (for sell orders only): If enabled, when swapping ETH for tokens, the tokens are locked for a duration
* set by the contract with the aim of reducing arbitraging. Traders can claim their tokens after the lockup
* period.
* - Hide on Frontend: Hide an order on the frontend interface.
*
* Platform fees:
* - The fee is a percentage of the ETH traded.
* - The percentage charged depends on what fee type (fish or whale) the seller is. This is determined by the sellers
* OTSeaERC20 balance off-chain.
* - Initially the fish fee will be 1% and the whale fee will be 0.3%.
* - Fees cannot be increased, only reduced.
*
* EIP712 is used to sign typed data when creating a sell order or when swapping tokens for ETH. By using a signature,
* the contract can reliably know a user's fee type. If this were to be calculated on-chain it could be subject to
* flash loan attacks and also would limit this contract to only be deployable on Ethereum mainnet.
*
* Partners:
* - Partners of OTSea receive a portion of the platform fee (initially 30%).
* - Partners have the ability to toggle on a lock-up for their project's token. If set, all swaps from ETH to their
* project's token will be locked for a duration set by the contract with the aim of reducing arbitraging.
* Traders can claim their tokens after the lockup period ends for a specific lock-up.
*
* Blacklisting:
* - The owner of the contract has the ability to blacklist user addresses. Doing so results in the blacklisted user
* not being able to create new orders, trade, update the order, on top of this other other users cannot trade with
* orders belonging to blacklisted accounts. Blacklisted users can only cancel orders and claim their
* locked-up tokens.
*/
contract OTSea is
ListHelper,
Ownable,
Pausable,
ReentrancyGuard,
TransferHelper,
SignatureHelper,
WhitelistHelper
{
using SafeERC20 for IERC20;
struct NewOrder {
IERC20 token;
bool isAON;
/**
* @dev withLockUp is a boolean only applicable to sell orders. If true, when swapping ETH for the order's tokens,
* the amount the user should receive will instead be locked up for the duration set by _lockupPeriod.
* After the lockup period has passed, the user is then able to claim their tokens.
*/
bool withLockUp;
bool isHidden;
uint256 totalInput;
uint256 totalOutput;
}
struct Order {
address creator;
OrderType orderType;
State state;
OTSeaLibrary.FeeType feeType;
bool isAON;
bool isHidden;
bool withLockUp;
IERC20 token;
uint256 totalInput;
uint256 inputTransacted;
uint256 totalOutput;
uint256 outputTransacted;
}
struct FeeDetailsSignature {
bytes signature;
uint256 expiresAt;
OTSeaLibrary.FeeType feeType;
}
/**
* @dev the Trade struct represents a trade a user wants to perform.
* - If a user wants to swap ETH for tokens, it is a buy trade that interacts with sell orders (BuyTrade struct is used).
* - If a user wants to swap tokens for ETH, it is a sell trade that interacts with buy orders (Trade struct is used).
*/
struct Trade {
/// @dev valid orders will always have an orderID greater than 0 and less than or equal to the total orders.
uint72 orderID;
/**
* @dev Definition of amountToSwap:
* Buy trade:
* - amount of ETH to swap for tokens.
* Sell trade:
* - amount of tokens to swap for ETH.
*/
uint256 amountToSwap;
/**
* @dev "totalOutput" is used to calculate the amount to receive from a trade.
* - Trade.totalOutput must exactly match Order.totalOutput.
* - Any discrepancy between these values causes the TX to revert.
* - This strict equality check prevents the manipulation of order outputs (e.g., front-running) by the order creators.
*/
uint256 totalOutput;
}
/**
* @dev The partner struct refers to partners of OTSea. account is set to an address owned by the project for the
* purpose of:
* - Receiving the referral fees
* - Being able to manually enforce lock-ups on orders that exchange the project's token
*/
struct Partner {
address account;
bool isLockUpOverrideEnabled;
}
struct LockUp {
address token;
uint88 unlockAt;
uint256 amount;
uint256 withdrawn;
}
struct ClaimLockUp {
uint256 index;
uint256 amount;
}
enum State {
Open,
Fulfilled,
Cancelled
}
enum OrderType {
Buy,
Sell
}
/// @dev Partner referral fees can be set to be between 10-50% (to 2 d.p.) of the platform revenue
uint16 private constant MIN_PARTNER_FEE = 1000;
uint16 private constant MAX_PARTNER_FEE = 5000;
uint8 private constant MAX_TRADES_UPPER_LIMIT = 100;
uint8 private constant MAX_CANCELLATIONS = 100;
uint8 private constant MIN_LOCKUP_TIME = 1 minutes;
uint16 private constant MAX_LOCKUP_TIME = 1 hours;
bytes32 private constant FEE_DETAILS_SIGNATURE_TYPE =
keccak256("FeeDetails(address account,uint256 expiresAt,uint8 feeType)");
address private _revenueDistributor;
uint72 private _totalOrders;
/// @dev _fishFee = 1% of the ETH traded
uint8 private _fishFee = 100;
/// @dev _whaleFee = 0.3% of the ETH traded
uint8 private _whaleFee = 30;
uint8 private _maxTrades = 10;
uint16 private _partnerFee = 3000;
uint16 private _lockupPeriod = 5 minutes;
mapping(uint72 => Order) private _orders;
/// @dev token => partner
mapping(address => Partner) private _partners;
/// @dev user address => lock-up list
mapping(address => LockUp[]) private _lockUps;
mapping(address => bool) private _blacklist;
/// @dev errors
error UnlockDateNotReached(uint256 index);
error LockUpNotAllowed();
error OrderBlacklisted();
error InvalidTradeOrderType();
error OrderNotFound(uint72 orderID);
/// @dev events
event FeesUpdated(uint8 fishFee, uint8 whaleFee, uint16 partnerFee);
event MaxTradesUpdated(uint8 maxSwaps);
event PartnerUpdated(address indexed token, Partner partner);
event LockUpOverrideUpdated(address indexed account, address indexed token, bool enforced);
event LockupPeriodUpdated(uint16 time);
event BlacklistUpdated(address indexed account, bool operation);
event BuyOrderCreated(
uint72 indexed orderID,
address indexed creator,
NewOrder newOrder,
uint8 decimals
);
event SellOrderCreated(
uint72 indexed orderID,
address indexed creator,
NewOrder newOrder,
uint256 actualTotalInput,
uint256 actualTotalOutput,
OTSeaLibrary.FeeType feeType,
uint8 decimals
);
event SwappedETHForTokens(
address indexed account,
address indexed token,
Trade[] trades,
uint256 swapped,
uint256 received,
uint256 claimable
);
event SwappedTokensForETH(
address indexed account,
address indexed token,
Trade[] trades,
uint256 swapped,
uint256 received,
OTSeaLibrary.FeeType feeType
);
event Traded(
uint72 indexed orderID,
address indexed account,
uint256 swapped,
uint256 received
);
event LockUpsClaimed(address indexed account, address indexed receiver, ClaimLockUp[] claims);
event OrderPriceUpdated(uint72 indexed orderID, uint256 newTotalOutput);
event OrderLockUpUpdated(uint72 indexed orderID, bool enforced);
event CancelledOrders(uint72[] orderIDs);
event RevenueTransferred(uint256 eth);
event PartnerFeePaid(address indexed token, address indexed partner, uint256 eth);
/// @param _orderID Order ID
modifier onlyOrderCreator(uint72 _orderID) {
_checkCallerIsOrderCreator(_orderID);
_;
}
modifier whenNotBlacklisted() {
_checkCallerIsNotBlacklisted();
_;
}
/**
* @param _multiSigAdmin Multi-sig admin
* @param revenueDistributor_ Revenue distributor contract
* @param _signer Signer address
*/
constructor(
address _multiSigAdmin,
address revenueDistributor_,
address _signer
) Ownable(_multiSigAdmin) SignatureHelper("OTSea", "v1.0.0", _signer) {
if (address(revenueDistributor_) == address(0)) revert OTSeaErrors.InvalidAddress();
_revenueDistributor = revenueDistributor_;
}
/// @notice Update the Revenue Distributor
function setRevenueDistributor(address revenueDistributor_) external onlyOwner {
if (address(_revenueDistributor) == address(0)) revert OTSeaErrors.InvalidAddress();
_revenueDistributor = revenueDistributor_;
}
/// @notice Pause the contract
function pauseContract() external onlyOwner {
_pause();
}
/// @notice Unpause the contract
function unpauseContract() external onlyOwner {
_unpause();
}
/**
* @notice Set the fish and whale fees
* @param _newFishFee Fish fee
* @param _newWhaleFee Whale fee
* @param _newPartnerFee Partner fee relative to the revenue
*/
function setFees(
uint8 _newFishFee,
uint8 _newWhaleFee,
uint16 _newPartnerFee
) external onlyOwner {
if (
_fishFee < _newFishFee ||
_whaleFee < _newWhaleFee ||
_newFishFee < _newWhaleFee ||
_newPartnerFee < MIN_PARTNER_FEE ||
MAX_PARTNER_FEE < _newPartnerFee
) revert OTSeaErrors.InvalidFee();
_fishFee = _newFishFee;
_whaleFee = _newWhaleFee;
_partnerFee = _newPartnerFee;
emit FeesUpdated(_newFishFee, _newWhaleFee, _newPartnerFee);
}
/**
* @notice Set the maximum number of trades that can occur in a single TX
* @param maxTrades_ Max trades
*/
function setMaxTrades(uint8 maxTrades_) external onlyOwner {
if (maxTrades_ == 0 || MAX_TRADES_UPPER_LIMIT < maxTrades_)
revert OTSeaErrors.InvalidAmount();
_maxTrades = maxTrades_;
emit MaxTradesUpdated(maxTrades_);
}
/**
* @notice Add, remove or update a partner's details
* @param _token Token address
* @param _partner Partner details
*/
function updatePartner(address _token, Partner calldata _partner) external onlyOwner {
if (_token == address(0)) revert OTSeaErrors.InvalidAddress();
if (
_partners[_token].account == _partner.account &&
_partners[_token].isLockUpOverrideEnabled == _partner.isLockUpOverrideEnabled
) revert OTSeaErrors.Unchanged();
if (_partner.account == address(0) && _partner.isLockUpOverrideEnabled)
revert OTSeaErrors.NotAvailable();
_partners[_token] = Partner(_partner.account, _partner.isLockUpOverrideEnabled);
emit PartnerUpdated(_token, _partner);
}
/**
* @notice Add/remove a lock-up override for a token, only the partner and the owner can make this change
* @param _token Token address
* @param _enforce enable (true) or disable (false)
* @dev If token lock-up override is enabled, when swapping ETH for tokens, the tokens will be held in the contract
* for the trader to claim after the _lockupPeriod has passed. If disabled, it will fallback to the Order.withLockUp
* boolean when swapping ETH for tokens.
*/
function updateLockUpOverride(address _token, bool _enforce) external {
Partner storage partner = _partners[_token];
/// @dev no need to check if _token is the zero address because partner.account would equal the zero address
if (partner.account == address(0)) revert OTSeaErrors.NotAvailable();
if (partner.account != _msgSender() && owner() != _msgSender())
revert OTSeaErrors.Unauthorized();
if (partner.isLockUpOverrideEnabled == _enforce) revert OTSeaErrors.Unchanged();
partner.isLockUpOverrideEnabled = _enforce;
emit LockUpOverrideUpdated(_token, _msgSender(), _enforce);
}
/**
* @notice Set the lockup period for orders using a lock-up or for tokens that have enforced a lock-up override
* @param _time Time (in seconds)
*/
function setLockupPeriod(uint16 _time) external onlyOwner {
if (_time < MIN_LOCKUP_TIME || MAX_LOCKUP_TIME < _time) revert OTSeaErrors.InvalidAmount();
_lockupPeriod = _time;
emit LockupPeriodUpdated(_time);
}
/**
* @notice Add/remove an account from the blacklist
* @param _account Account
* @param _operation add (true) or remove (false) "account" to/from the blacklist
* @dev Blacklisting an account prevents them from creating orders, trading, updating an order,
* and other users interacting with their orders. Blacklisted users can only cancel orders and claim
* their locked-up tokens.
*/
function blacklistAccount(address _account, bool _operation) external onlyOwner {
if (_account == address(0)) revert OTSeaErrors.InvalidAddress();
if (_blacklist[_account] == _operation) revert OTSeaErrors.Unchanged();
_blacklist[_account] = _operation;
emit BlacklistUpdated(_account, _operation);
}
/**
* @notice Initiate the creation of a buy order
* @param _newOrder Core new order details
* @param _whitelist List of exclusive users allowed to trade the order (optional)
* @dev no need for nonReentrant modifier because no external calls are made
*/
function createBuyOrder(
NewOrder calldata _newOrder,
address[] calldata _whitelist
) external payable whenNotPaused whenNotBlacklisted {
if (address(_newOrder.token) == address(0)) revert OTSeaErrors.InvalidAddress();
if (msg.value != _newOrder.totalInput) revert OTSeaErrors.InvalidETH(_newOrder.totalInput);
uint72 orderID = _createBuyOrder(_newOrder);
if (_whitelist.length != 0) {
_initializeWhitelist(orderID, _whitelist);
}
}
/**
* @notice Initiate the creation of a sell order
* @param _newOrder Core new order details
* @param _whitelist List of exclusive users allowed to trade the order (optional)
* @param _feeDetailsSignature Fee details signature (optional)
*/
function createSellOrder(
NewOrder calldata _newOrder,
address[] calldata _whitelist,
FeeDetailsSignature calldata _feeDetailsSignature
) external nonReentrant whenNotPaused whenNotBlacklisted {
if (address(_newOrder.token) == address(0)) revert OTSeaErrors.InvalidAddress();
/// @dev retrieve the fee type to be stored against the order
OTSeaLibrary.FeeType feeType = _retrieveFeeDetails(_feeDetailsSignature);
uint72 orderID = _createSellOrder(_newOrder, feeType);
if (_whitelist.length != 0) {
_initializeWhitelist(orderID, _whitelist);
}
}
/**
* @notice Swap ETH for tokens (interacts with sell orders)
* @param _token Token address
* @param _trades Trades
* @param _newOrder Core new order details
* @param _allowLockUps Allow trades to have lock-ups (true), disallow trades to have lock-ups (false)
* @param _expectedLockupPeriod The current lockupPeriod defined by _lockupPeriod
* @dev _allowLockups act as a safety measure to ensure the user is comfortable with some or all trades resulting
* in tokens being locked. _expectedLockupPeriod should match the current _lockupPeriod, this is in case the owner
* changes the _lockupPeriod.
*/
function swapETHForTokens(
IERC20 _token,
Trade[] calldata _trades,
NewOrder calldata _newOrder,
bool _allowLockUps,
uint16 _expectedLockupPeriod
) external payable nonReentrant whenNotPaused whenNotBlacklisted {
if (_allowLockUps && _expectedLockupPeriod != _lockupPeriod)
revert OTSeaErrors.ExpectationMismatch();
(
uint256 totalAmountToSwap,
uint256 totalAmountToReceive,
uint256 totalAmountToClaim,
uint256 totalRevenue
) = _executeBuy(_token, _trades, _allowLockUps);
if (_newOrder.token == _token) {
/// @dev create a buy order
if (totalAmountToSwap + _newOrder.totalInput != msg.value)
revert OTSeaErrors.InvalidETH(totalAmountToSwap + _newOrder.totalInput);
/// @dev _newOrder.totalInput (ETH) is left in the contract for users to sell tokens for
_createBuyOrder(_newOrder);
} else if (totalAmountToSwap != msg.value) {
/**
* @dev if _newOrder.token != _token then the user does not wish to place a new order therefore if
* msg.value is not equal to totalAmountToSwap, then the incorrect amount of ETH has been paid
*/
revert OTSeaErrors.InvalidETH(totalAmountToSwap);
}
_transferRevenue(totalRevenue, address(_token));
/// @dev a swap results in tokens either being locked, directly transferred to the user, or both
if (totalAmountToClaim != 0) {
/// @dev lock-up the (totalAmountToClaim) tokens for the user to claim after the lockup period has passed
_lockUps[_msgSender()].push(
LockUp(
address(_token),
uint88(block.timestamp + _lockupPeriod),
totalAmountToClaim,
0
)
);
}
if (totalAmountToReceive != 0) {
/// @dev transfer the purchased tokens to the caller
_token.safeTransfer(_msgSender(), totalAmountToReceive);
}
emit SwappedETHForTokens(
_msgSender(),
address(_token),
_trades,
totalAmountToSwap,
totalAmountToReceive,
totalAmountToClaim
);
}
/**
* @notice Swap tokens for ETH (interacts with buy orders)
* @param _token Token address
* @param _trades Trades
* @param _newOrder Core new order details
* @param _feeDetailsSignature Signature containing data about msg.sender's fee type
*/
function swapTokensForETH(
IERC20 _token,
Trade[] calldata _trades,
NewOrder calldata _newOrder,
FeeDetailsSignature calldata _feeDetailsSignature
) external nonReentrant whenNotPaused whenNotBlacklisted {
OTSeaLibrary.FeeType feeType = _retrieveFeeDetails(_feeDetailsSignature);
(uint256 totalAmountToSwap, uint256 totalAmountToReceive) = _executeSell(_token, _trades);
if (_newOrder.token == _token) {
/// @dev create a sell order.
_createSellOrder(_newOrder, feeType);
}
/// @dev transfer out ETH.
uint256 revenue = _handleETHPayment(_msgSender(), totalAmountToReceive, feeType);
_transferRevenue(revenue, address(_token));
emit SwappedTokensForETH(
_msgSender(),
address(_token),
_trades,
totalAmountToSwap,
totalAmountToReceive,
feeType
);
}
/**
* @notice Claim multiple lock-ups (supports lock-ups with different tokens)
* @param _receiver Address to receive tokens
* @param _claims A list of claims
* @dev The purpose of the _receiver is in case the transfer were to fail (e.g. max wallet reached). ClaimLockUp
* includes an amount, this is essential because a token may have a max tx limit in place wish could result
* in a transfer failing. Therefore the user simply needs to claim in small chunks.
* Blacklisted users can claim their lock-ups.
*/
function claimLockUps(address _receiver, ClaimLockUp[] calldata _claims) external {
uint256 total = _lockUps[_msgSender()].length;
if (total == 0) revert OTSeaErrors.NotAvailable();
uint256 length = _claims.length;
_validateListLength(length);
for (uint256 i; i < length; ) {
ClaimLockUp calldata _claim = _claims[i];
if (total <= _claim.index) revert OTSeaErrors.InvalidIndex(i);
LockUp memory lockUp = _lockUps[_msgSender()][_claim.index];
if (block.timestamp < lockUp.unlockAt) revert UnlockDateNotReached(i);
uint256 remaining = lockUp.amount - lockUp.withdrawn;
if (_claim.amount == 0 || remaining < _claim.amount)
revert OTSeaErrors.InvalidAmountAtIndex(i);
_lockUps[_msgSender()][_claim.index].withdrawn += _claim.amount;
IERC20(lockUp.token).safeTransfer(_receiver, _claim.amount);
unchecked {
i++;
}
}
emit LockUpsClaimed(_msgSender(), _receiver, _claims);
}
/**
* @notice Claim multiple lock-ups (supports only lock-ups with the same tokens)
* @param _token Token address
* @param _receiver Address to receive tokens
* @param _claims A list of claims
* @dev use this function if claiming lock-ups for the same token as it is more gas efficient. The purpose of
* the _receiver is in case the transfer were to fail (e.g. max wallet reached). ClaimLockUp
* includes an amount, this is essential because a token may have a max tx limit in place wish could result
* in a transfer failing. Therefore the user simply needs to claim in small chunks over multiple txs.
* Blacklisted users can claim their lock-ups.
*/
function claimLockUpByToken(
IERC20 _token,
address _receiver,
ClaimLockUp[] calldata _claims
) external {
if (address(_token) == address(0)) revert OTSeaErrors.InvalidAddress();
uint256 total = _lockUps[_msgSender()].length;
if (total == 0) revert OTSeaErrors.NotAvailable();
uint256 length = _claims.length;
_validateListLength(length);
uint256 totalToClaim;
for (uint256 i; i < length; ) {
ClaimLockUp calldata _claim = _claims[i];
if (total <= _claim.index) revert OTSeaErrors.InvalidIndex(i);
LockUp memory lockUp = _lockUps[_msgSender()][_claim.index];
if (lockUp.token != address(_token)) revert OTSeaErrors.InvalidAddressAtIndex(i);
if (block.timestamp < lockUp.unlockAt) revert UnlockDateNotReached(i);
uint256 remaining = lockUp.amount - lockUp.withdrawn;
if (_claim.amount == 0 || remaining < _claim.amount)
revert OTSeaErrors.InvalidAmountAtIndex(i);
_lockUps[_msgSender()][_claim.index].withdrawn += _claim.amount;
totalToClaim += _claim.amount;
unchecked {
i++;
}
}
_token.safeTransfer(_receiver, totalToClaim);
emit LockUpsClaimed(_msgSender(), _receiver, _claims);
}
/**
* @notice Update the price of an order
* @param _orderID Order ID
* @param _expectedRemainingInput Expected remaining input
* @param _newRemainingOutput New output value for the remaining input
*/
function updatePrice(
uint72 _orderID,
uint256 _expectedRemainingInput,
uint256 _newRemainingOutput
) external onlyOrderCreator(_orderID) whenNotPaused whenNotBlacklisted {
Order storage order = _orders[_orderID];
if (order.state != State.Open) revert OTSeaErrors.NotAvailable();
if (_newRemainingOutput == 0) revert OTSeaErrors.InvalidAmount();
if (order.totalInput - order.inputTransacted != _expectedRemainingInput)
revert OTSeaErrors.ExpectationMismatch();
uint256 newTotalOutput = order.outputTransacted + _newRemainingOutput;
order.totalOutput = newTotalOutput;
emit OrderPriceUpdated(_orderID, newTotalOutput);
}
/**
* @notice Update an order's whitelist
* @param _orderID Order ID
* @param _updates Whitelist updates
*/
function updateWhitelist(
uint72 _orderID,
WhitelistUpdate[] calldata _updates
) external override onlyOrderCreator(_orderID) whenNotPaused whenNotBlacklisted {
if (_orders[_orderID].state != State.Open) revert OTSeaErrors.NotAvailable();
_updateWhitelist(_orderID, _updates);
}
/**
* @notice Update a sell order to enforce or remove a lock-up when traded with
* @param _orderID Order ID
* @param _enforce enable (true) or disable (false)
*/
function updateOrderLockUp(
uint72 _orderID,
bool _enforce
) external onlyOrderCreator(_orderID) whenNotPaused whenNotBlacklisted {
Order storage order = _orders[_orderID];
if (order.state != State.Open || order.orderType == OrderType.Buy)
revert OTSeaErrors.NotAvailable();
if (order.withLockUp == _enforce) revert OTSeaErrors.Unchanged();
order.withLockUp = _enforce;
emit OrderLockUpUpdated(_orderID, _enforce);
}
/**
* @notice Cancel multiple orders (supports orders with different tokens)
* @param _orderIDs A list of order IDs to cancel
* @dev Blacklisted users can cancel orders
*/
function cancelOrders(uint72[] calldata _orderIDs) external nonReentrant {
uint256 total = _orderIDs.length;
if (total == 0 || MAX_CANCELLATIONS < total) revert OTSeaErrors.InvalidArrayLength();
uint256 totalETHOwed;
uint256 i;
for (i; i < total; ) {
Order storage order = _orders[_orderIDs[i]];
if (order.creator != _msgSender()) revert OTSeaErrors.Unauthorized();
if (order.state != State.Open) revert OTSeaErrors.NotAvailable();
order.state = State.Cancelled;
uint256 outstanding = order.totalInput - order.inputTransacted;
if (order.orderType == OrderType.Buy) {
/// @dev transfer unsold ETH.
totalETHOwed += outstanding;
} else {
/// @dev transfer unsold tokens.
order.token.safeTransfer(order.creator, outstanding);
}
unchecked {
i++;
}
}
if (totalETHOwed != 0) {
_safeETHTransfer(_msgSender(), totalETHOwed);
}
emit CancelledOrders(_orderIDs);
}
/**
* @notice Cancel multiple orders (supports only orders with the same tokens)
* @param _token Token address
* @param _orderIDs A list of order IDs to cancel
* @dev use this function if cancelling orders with the same token as it is more gas efficient.
* Blacklisted users can cancel orders
*/
function cancelTokenOrders(IERC20 _token, uint72[] calldata _orderIDs) external nonReentrant {
uint256 total = _orderIDs.length;
if (total == 0 || MAX_CANCELLATIONS < total) revert OTSeaErrors.InvalidArrayLength();
uint256 totalETHOwed;
uint256 totalTokensOwed;
uint256 i;
for (i; i < total; ) {
Order storage order = _orders[_orderIDs[i]];
if (order.creator != _msgSender()) revert OTSeaErrors.Unauthorized();
if (order.state != State.Open || order.token != _token)
revert OTSeaErrors.NotAvailable();
order.state = State.Cancelled;
uint256 outstanding = order.totalInput - order.inputTransacted;
if (order.orderType == OrderType.Buy) {
/// @dev transfer unsold ETH.
totalETHOwed += outstanding;
} else {
/// @dev transfer unsold tokens.
totalTokensOwed += outstanding;
}
unchecked {
i++;
}
}
if (totalETHOwed != 0) {
_safeETHTransfer(_msgSender(), totalETHOwed);
}
if (totalTokensOwed != 0) {
_token.safeTransfer(_msgSender(), totalTokensOwed);
}
emit CancelledOrders(_orderIDs);
}
/**
* @notice Get the total number of orders
* @return uint72 Total orders
*/
function getTotalOrders() external view returns (uint72) {
return _totalOrders;
}
/**
* @notice Get an order by ID
* @param _orderID Order ID
* @return order Order details
*/
function getOrder(uint72 _orderID) external view returns (Order memory order) {
_checkIDExists(_orderID);
return _orders[_orderID];
}
/**
* @notice Get a list of orders in a sequence from an order ID to another order ID
* @param _start Start order ID
* @param _end End order ID
* @return orders A list of orders starting and _start and ending at _end
*/
function getOrdersInSequence(
uint256 _start,
uint256 _end
)
external
view
onlyValidSequence(_start, _end, _totalOrders, DISALLOW_ZERO)
returns (Order[] memory orders)
{
orders = new Order[](_end - _start + 1);
uint256 index;
uint256 orderId = _start;
for (orderId; orderId <= _end; ) {
orders[index++] = _orders[uint72(orderId)];
unchecked {
orderId++;
}
}
return orders;
}
/**
* @notice Get a list of orders by a list of order IDs
* @param _orderIDs Order IDs
* @return orders A list of orders with each index corresponding to _orderIDs
*/
function getOrdersByIDs(
uint72[] calldata _orderIDs
) external view returns (Order[] memory orders) {
uint256 length = _orderIDs.length;
_validateListLength(length);
orders = new Order[](length);
uint256 i;
for (i; i < length; ) {
_checkIDExists(_orderIDs[i]);
orders[i] = _orders[_orderIDs[i]];
unchecked {
i++;
}
}
return orders;
}
/**
* @notice Get the lockup period
* @return uint16 Lockup period
*/
function getLockupPeriod() public view returns (uint16) {
return _lockupPeriod;
}
/**
* @notice Get the total lock-ups for a user
* @param _account Account
* @return uint256 Total lock-ups for _account
*/
function getTotalLockUps(address _account) public view returns (uint256) {
if (_account == address(0)) revert OTSeaErrors.InvalidAddress();
return _lockUps[_account].length;
}
/**
* @notice Get lock-ups for a user
* @param _account Account
* @param _indexes Indexes
* @return lockUps Lock-up list
*/
function getLockUps(
address _account,
uint256[] calldata _indexes
) external view returns (LockUp[] memory lockUps) {
uint256 total = getTotalLockUps(_account);
uint256 length = _indexes.length;
_validateListLength(length);
lockUps = new LockUp[](length);
uint256 i;
for (i; i < length; ) {
if (total <= _indexes[i]) revert OTSeaErrors.InvalidIndex(i);
lockUps[i] = _lockUps[_account][_indexes[i]];
unchecked {
i++;
}
}
return lockUps;
}
/**
* @notice Get fee percents
* @return fishFee Fish fee percent
* @return whaleFee Whale fee percent
* @return partnerFee Partner fee percent
*/
function getFees() external view returns (uint8 fishFee, uint8 whaleFee, uint16 partnerFee) {
return (_fishFee, _whaleFee, _partnerFee);
}
/**
* @notice Get the maximum number of trades that can be executed in a single TX
* @return uint8 Maximum number of trades
*/
function getMaxTrades() external view returns (uint8) {
return _maxTrades;
}
/**
* @notice Get partner details for a token
* @param _token Token address
* @return Partner Partner details
*/
function getPartner(address _token) external view returns (Partner memory) {
if (_token == address(0)) revert OTSeaErrors.InvalidAddress();
return _partners[_token];
}
/**
* @notice Check if an account is blacklisted
* @param _account Account
* @return bool true if blacklisted, false if not
*/
function isAccountBlacklisted(address _account) external view returns (bool) {
if (_account == address(0)) revert OTSeaErrors.InvalidAddress();
return _blacklist[_account];
}
/**
* @notice Check if an order is blacklisted
* @param _orderID Order ID
* @return bool true if blacklisted, false if not
*/
function isOrderBlacklisted(uint72 _orderID) external view returns (bool) {
_checkIDExists(_orderID);
return _blacklist[_orders[_orderID].creator];
}
/**
* @param _newOrder Core new order details
* @return orderID Order ID
*/
function _createBuyOrder(NewOrder calldata _newOrder) private returns (uint72 orderID) {
/// @dev lock-ups can only be used on sell orders
if (_newOrder.withLockUp) revert LockUpNotAllowed();
if (_newOrder.totalInput == 0 || _newOrder.totalOutput == 0)
revert OTSeaErrors.InvalidAmount();
orderID = ++_totalOrders;
_orders[orderID] = Order({
creator: _msgSender(),
orderType: OrderType.Buy,
state: State.Open,
/// @dev feeType is set to the default FeeType (fish) because it is ignored for buy orders
feeType: OTSeaLibrary.FeeType.Fish,
isAON: _newOrder.isAON,
isHidden: _newOrder.isHidden,
withLockUp: false,
token: _newOrder.token,
totalInput: _newOrder.totalInput,
inputTransacted: 0,
totalOutput: _newOrder.totalOutput,
outputTransacted: 0
});
emit BuyOrderCreated(
orderID,
_msgSender(),
_newOrder,
IERC20Metadata(address(_newOrder.token)).decimals()
);
}
/**
* @param _newOrder Core new order details
* @param _feeType Fee type
* @return orderID Order ID
*/
function _createSellOrder(
NewOrder calldata _newOrder,
OTSeaLibrary.FeeType _feeType
) private returns (uint72 orderID) {
if (_newOrder.totalInput == 0 || _newOrder.totalOutput == 0)
revert OTSeaErrors.InvalidAmount();
orderID = ++_totalOrders;
uint256 totalInput = _transferInTokens(_newOrder.token, _newOrder.totalInput);
/// @dev if the tokens transferred does not match the amount, then the total ETH should be adjusted to account for taxes.
uint256 totalOutput = totalInput == _newOrder.totalInput
? _newOrder.totalOutput
: (_newOrder.totalOutput * totalInput) / _newOrder.totalInput;
_orders[orderID] = Order({
creator: _msgSender(),
orderType: OrderType.Sell,
state: State.Open,
feeType: _feeType,
isAON: _newOrder.isAON,
isHidden: _newOrder.isHidden,
withLockUp: _newOrder.withLockUp,
token: _newOrder.token,
totalInput: totalInput,
inputTransacted: 0,
totalOutput: totalOutput,
outputTransacted: 0
});
emit SellOrderCreated(
orderID,
_msgSender(),
_newOrder,
totalInput,
totalOutput,
_feeType,
IERC20Metadata(address(_newOrder.token)).decimals()
);
}
/**
* @param _token Token to buy
* @param _trades Trades to execute
* @param _allowLockUps Allow trades to have lock-ups (true), disallow trades to have lock-ups (false)
* @return totalAmountToSwap Total ETH to swap
* @return totalAmountToReceive Total tokens to receive
* @return totalAmountToClaim Total tokens to claim after the _lockupPeriod
* @return totalRevenue Total revenue
*/
function _executeBuy(
IERC20 _token,
Trade[] calldata _trades,
bool _allowLockUps
)
private
returns (
uint256 totalAmountToSwap,
uint256 totalAmountToReceive,
uint256 totalAmountToClaim,
uint256 totalRevenue
)
{
uint256 total = _trades.length;
if (total == 0 || _maxTrades < total) revert OTSeaErrors.InvalidArrayLength();
bool isLockUpOverrideEnabled = _partners[address(_token)].isLockUpOverrideEnabled;
if (isLockUpOverrideEnabled && !_allowLockUps) revert OTSeaErrors.ExpectationMismatch();
uint256 i;
for (i; i < total; ) {
Trade calldata trade = _trades[i];
Order storage order = _orders[trade.orderID];
if (_blacklist[order.creator]) revert OrderBlacklisted();
/// @dev orders should only be sell orders (which means there is no need to check if the order ID exists).
if (order.orderType == OrderType.Buy) revert InvalidTradeOrderType();
uint256 amountToReceive = _executeTrade(_token, trade);
totalRevenue += _handleETHPayment(order.creator, trade.amountToSwap, order.feeType);
/// @dev total tokens to send to msg.sender.
totalAmountToSwap += trade.amountToSwap;
if (isLockUpOverrideEnabled || order.withLockUp) {
if (!_allowLockUps) revert OTSeaErrors.ExpectationMismatch();
totalAmountToClaim += amountToReceive;
} else {
totalAmountToReceive += amountToReceive;
}
unchecked {
i++;
}
}
return (totalAmountToSwap, totalAmountToReceive, totalAmountToClaim, totalRevenue);
}
/**
* @param _token Token to sell
* @param _trades Trades to execute
* @return totalAmountToSwap Total tokens to swap
* @return totalAmountToReceive Total ETH to receive
*/
function _executeSell(
IERC20 _token,
Trade[] calldata _trades
) private returns (uint256 totalAmountToSwap, uint256 totalAmountToReceive) {
uint256 total = _trades.length;
if (total == 0 || _maxTrades < total) revert OTSeaErrors.InvalidArrayLength();
uint256 i;
for (i; i < total; ) {
Trade calldata trade = _trades[i];
_checkIDExists(trade.orderID);
Order storage order = _orders[trade.orderID];
if (_blacklist[order.creator]) revert OrderBlacklisted();
/// @dev orders should only be buy orders.
if (order.orderType == OrderType.Sell) revert InvalidTradeOrderType();
uint256 amountToReceive = _executeTrade(_token, trade);
_token.safeTransferFrom(_msgSender(), order.creator, trade.amountToSwap);
/// @dev total ETH swapped.
totalAmountToSwap += trade.amountToSwap;
/// @dev total tokens to send to msg.sender.
totalAmountToReceive += amountToReceive;
unchecked {
i++;
}
}
return (totalAmountToSwap, totalAmountToReceive);
}
/**
* @param _token Token to trade
* @param _trade Trade to execute
* @return amountToReceive Amount to receive
* @dev a generic function used both when buying and selling
*/
function _executeTrade(
IERC20 _token,
Trade calldata _trade
) private returns (uint256 amountToReceive) {
Order storage order = _orders[_trade.orderID];
if (order.state != State.Open || order.token != _token) revert OTSeaErrors.NotAvailable();
if (
_getTotalWhitelisted(_trade.orderID) != 0 &&
!_checkIsWhitelisted(_msgSender(), _trade.orderID)
) revert OTSeaErrors.Unauthorized();
if (_trade.amountToSwap == 0) revert OTSeaErrors.InvalidAmount();
/// @dev owner of order can change price therefore we much check the trade totalOutput matches the on-chain value
if (order.totalOutput != _trade.totalOutput) revert OTSeaErrors.ExpectationMismatch();
uint256 remainingInput = order.totalInput - order.inputTransacted;
uint256 remainingOutput = order.totalOutput - order.outputTransacted;
if (
order.isAON
? _trade.amountToSwap != remainingOutput
: remainingOutput < _trade.amountToSwap
) revert OTSeaErrors.InvalidPurchase();
if (_trade.amountToSwap == remainingOutput) {
amountToReceive = remainingInput;
order.state = State.Fulfilled;
} else {
amountToReceive = (remainingInput * _trade.amountToSwap) / remainingOutput;
}
order.inputTransacted += amountToReceive;
order.outputTransacted += _trade.amountToSwap;
emit Traded(_trade.orderID, _msgSender(), _trade.amountToSwap, amountToReceive);
}
/**
* @param _account Account to send ETH to
* @param _amount Amount of ETH to send to _account
* @param _feeType Fee type of _account
* @return revenue Amount of ETH revenue
* @dev a function to calculate the revenue and transfer the remaining ETH to an account
*/
function _handleETHPayment(
address _account,
uint256 _amount,
OTSeaLibrary.FeeType _feeType
) private returns (uint256 revenue) {
revenue =
(_amount * (_feeType == OTSeaLibrary.FeeType.Fish ? _fishFee : _whaleFee)) /
OTSeaLibrary.PERCENT_DENOMINATOR;
_safeETHTransfer(_account, _amount - revenue);
}
/**
* @param _revenue Revenue
* @param _token Token
* @dev Pays partner fee (if applicable) and transfers the remaining revenue to the revenue distributor
*/
function _transferRevenue(uint256 _revenue, address _token) private {
address partner = _partners[_token].account;
if (partner != address(0)) {
uint256 fee = (_revenue * _partnerFee) / OTSeaLibrary.PERCENT_DENOMINATOR;
_revenue -= fee;
_safeETHTransfer(partner, fee);
emit PartnerFeePaid(_token, partner, fee);
}
/// @dev no need to safely transfer ETH because revenueDistributor has a receive() function
_transferETHOrRevert(_revenueDistributor, _revenue);
emit RevenueTransferred(_revenue);
}
/// @param _orderID Order ID
function _checkIDExists(uint72 _orderID) internal view override {
if (_orderID == 0 || _totalOrders < _orderID) revert OrderNotFound(_orderID);
}
/**
* @param _feeDetailsSignature Fee details signature
* @return feeType Fee type
* @dev verifies the signature (if present) and returns the fee type
*/
function _retrieveFeeDetails(
FeeDetailsSignature calldata _feeDetailsSignature
) private view returns (OTSeaLibrary.FeeType feeType) {
/// @dev if no signature is present then the user is a fish
if (_feeDetailsSignature.signature.length == 0) {
return feeType;
}
/// @dev reconstruct data that was signed off-chain
bytes memory data = abi.encode(
FEE_DETAILS_SIGNATURE_TYPE,
_msgSender(),
_feeDetailsSignature.expiresAt,
_feeDetailsSignature.feeType
);
/// @dev check the signature was signed by the signer
_checkSignature(data, _feeDetailsSignature.signature, _feeDetailsSignature.expiresAt);
return _feeDetailsSignature.feeType;
}
/// @param _orderID Order ID
function _checkCallerIsOrderCreator(uint72 _orderID) private view {
/**
* @dev it is more efficient calling _checkIDExists(_orderID) here than in the modifier because using it
* in a modifier would duplicate the same code across all functions where it is used.
*/
_checkIDExists(_orderID);
if (_orders[_orderID].creator != _msgSender()) revert OTSeaErrors.Unauthorized();
}
function _checkCallerIsNotBlacklisted() private view {
if (_blacklist[_msgSender()]) revert OTSeaErrors.Unauthorized();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.20;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.20;
import {MessageHashUtils} from "./MessageHashUtils.sol";
import {ShortStrings, ShortString} from "../ShortStrings.sol";
import {IERC5267} from "../../interfaces/IERC5267.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
* encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
* does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
* produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
* separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
* separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
*
* @custom:oz-upgrades-unsafe-allow state-variable-immutable
*/
abstract contract EIP712 is IERC5267 {
using ShortStrings for *;
bytes32 private constant TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _cachedDomainSeparator;
uint256 private immutable _cachedChainId;
address private immutable _cachedThis;
bytes32 private immutable _hashedName;
bytes32 private immutable _hashedVersion;
ShortString private immutable _name;
ShortString private immutable _version;
string private _nameFallback;
string private _versionFallback;
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
_name = name.toShortStringWithFallback(_nameFallback);
_version = version.toShortStringWithFallback(_versionFallback);
_hashedName = keccak256(bytes(name));
_hashedVersion = keccak256(bytes(version));
_cachedChainId = block.chainid;
_cachedDomainSeparator = _buildDomainSeparator();
_cachedThis = address(this);
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
return _cachedDomainSeparator;
} else {
return _buildDomainSeparator();
}
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
* @dev See {IERC-5267}.
*/
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
return (
hex"0f", // 01111
_EIP712Name(),
_EIP712Version(),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
/**
* @dev The name parameter for the EIP712 domain.
*
* NOTE: By default this function reads _name which is an immutable value.
* It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
*/
// solhint-disable-next-line func-name-mixedcase
function _EIP712Name() internal view returns (string memory) {
return _name.toStringWithFallback(_nameFallback);
}
/**
* @dev The version parameter for the EIP712 domain.
*
* NOTE: By default this function reads _version which is an immutable value.
* It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
*/
// solhint-disable-next-line func-name-mixedcase
function _EIP712Version() internal view returns (string memory) {
return _version.toStringWithFallback(_versionFallback);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/SignatureChecker.sol)
pragma solidity ^0.8.20;
import {ECDSA} from "./ECDSA.sol";
import {IERC1271} from "../../interfaces/IERC1271.sol";
/**
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
* signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
* Argent and Safe Wallet (previously Gnosis Safe).
*/
library SignatureChecker {
/**
* @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
* signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
(address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature);
return
(error == ECDSA.RecoverError.NoError && recovered == signer) ||
isValidERC1271SignatureNow(signer, hash, signature);
}
/**
* @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
* against the signer smart contract using ERC1271.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidERC1271SignatureNow(
address signer,
bytes32 hash,
bytes memory signature
) internal view returns (bool) {
(bool success, bytes memory result) = signer.staticcall(
abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
);
return (success &&
result.length >= 32 &&
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ShortStrings.sol)
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
// | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
// | length | 0x BB |
type ShortString is bytes32;
/**
* @dev This library provides functions to convert short memory strings
* into a `ShortString` type that can be used as an immutable variable.
*
* Strings of arbitrary length can be optimized using this library if
* they are short enough (up to 31 bytes) by packing them with their
* length (1 byte) in a single EVM word (32 bytes). Additionally, a
* fallback mechanism can be used for every other case.
*
* Usage example:
*
* ```solidity
* contract Named {
* using ShortStrings for *;
*
* ShortString private immutable _name;
* string private _nameFallback;
*
* constructor(string memory contractName) {
* _name = contractName.toShortStringWithFallback(_nameFallback);
* }
*
* function name() external view returns (string memory) {
* return _name.toStringWithFallback(_nameFallback);
* }
* }
* ```
*/
library ShortStrings {
// Used as an identifier for strings longer than 31 bytes.
bytes32 private constant FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
error StringTooLong(string str);
error InvalidShortString();
/**
* @dev Encode a string of at most 31 chars into a `ShortString`.
*
* This will trigger a `StringTooLong` error is the input string is too long.
*/
function toShortString(string memory str) internal pure returns (ShortString) {
bytes memory bstr = bytes(str);
if (bstr.length > 31) {
revert StringTooLong(str);
}
return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
}
/**
* @dev Decode a `ShortString` back to a "normal" string.
*/
function toString(ShortString sstr) internal pure returns (string memory) {
uint256 len = byteLength(sstr);
// using `new string(len)` would work locally but is not memory safe.
string memory str = new string(32);
/// @solidity memory-safe-assembly
assembly {
mstore(str, len)
mstore(add(str, 0x20), sstr)
}
return str;
}
/**
* @dev Return the length of a `ShortString`.
*/
function byteLength(ShortString sstr) internal pure returns (uint256) {
uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
if (result > 31) {
revert InvalidShortString();
}
return result;
}
/**
* @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
*/
function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
if (bytes(value).length < 32) {
return toShortString(value);
} else {
StorageSlot.getStringSlot(store).value = value;
return ShortString.wrap(FALLBACK_SENTINEL);
}
}
/**
* @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*/
function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return toString(value);
} else {
return store;
}
}
/**
* @dev Return the length of a string that was encoded to `ShortString` or written to storage using
* {setWithFallback}.
*
* WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
* actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
*/
function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return byteLength(value);
} else {
return bytes(store).length;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}/*
[.... [... [...... [.. ..
[.. [.. [.. [.. [..
[.. [.. [.. [.. [.. [..
[.. [.. [.. [.. [. [.. [.. [..
[.. [.. [.. [.. [..... [..[.. [..
[.. [.. [.. [.. [..[. [.. [..
[.... [.. [.. .. [.... [.. [...
https://otsea.io
https://t.me/OTSeaPortal
https://twitter.com/OTSeaERC20
*/
// SPDX-License-Identifier: MIT
pragma solidity =0.8.20;
import "contracts/libraries/OTSeaErrors.sol";
/// @title A list helper contract
abstract contract ListHelper {
uint16 internal constant LOOP_LIMIT = 500;
bool internal constant ALLOW_ZERO = true;
bool internal constant DISALLOW_ZERO = false;
error InvalidStart();
error InvalidEnd();
error InvalidSequence();
/**
* @param _start Start
* @param _end End
* @param _total List total
* @param _allowZero true - zero is a valid start or end, false - zero is an invalid start or end
*/
modifier onlyValidSequence(
uint256 _start,
uint256 _end,
uint256 _total,
bool _allowZero
) {
_checkSequence(_start, _end, _total, _allowZero);
_;
}
/**
* @param _start Start
* @param _end End
* @param _total Total
* @param _allowZero true - zero is a valid start or end, false - zero is an invalid start or end
* @dev check that a range of indexes is valid.
*/
function _checkSequence(
uint256 _start,
uint256 _end,
uint256 _total,
bool _allowZero
) private pure {
if (_allowZero) {
if (_start >= _total) revert InvalidStart();
if (_end >= _total) revert InvalidEnd();
} else {
if (_start == 0 || _start > _total) revert InvalidStart();
if (_end == 0 || _end > _total) revert InvalidEnd();
}
if (_start > _end) revert InvalidStart();
if (_end - _start + 1 > LOOP_LIMIT) revert InvalidSequence();
}
/// @dev _length List length
function _validateListLength(uint256 _length) internal pure {
if (_length == 0 || LOOP_LIMIT < _length) revert OTSeaErrors.InvalidArrayLength();
}
}/*
[.... [... [...... [.. ..
[.. [.. [.. [.. [..
[.. [.. [.. [.. [.. [..
[.. [.. [.. [.. [. [.. [.. [..
[.. [.. [.. [.. [..... [..[.. [..
[.. [.. [.. [.. [..[. [.. [..
[.... [.. [.. .. [.... [.. [...
https://otsea.io
https://t.me/OTSeaPortal
https://twitter.com/OTSeaERC20
*/
// SPDX-License-Identifier: MIT
pragma solidity =0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import "contracts/libraries/OTSeaErrors.sol";
/// @title Abstract signature helper contract
abstract contract SignatureHelper is EIP712, Ownable {
address public signer;
error ExpiredSignature();
error InvalidSignature();
error SignatureRequired();
event SignerUpdated(address signer);
/**
* @param _name Name of contract
* @param _version Version of contract
* @param _signer Address of signer
*/
constructor(
string memory _name,
string memory _version,
address _signer
) EIP712(_name, _version) {
if (_signer == address(0)) revert OTSeaErrors.InvalidAddress();
signer = _signer;
}
/**
* @notice Set the signer
* @param _signer New signer address
*/
function setSigner(address _signer) external onlyOwner {
if (_signer == address(0)) revert OTSeaErrors.InvalidAddress();
signer = _signer;
emit SignerUpdated(_signer);
}
/**
* @param _data Reconstructed message
* @param _signature Signature
* @dev reverts if signature is invalid
*/
function _checkSignature(
bytes memory _data,
bytes calldata _signature,
uint256 _expiration
) internal view {
if (_expiration < block.number) revert ExpiredSignature();
if (
!SignatureChecker.isValidSignatureNow(
signer,
_hashTypedDataV4(keccak256(_data)),
_signature
)
) revert InvalidSignature();
}
}/*
[.... [... [...... [.. ..
[.. [.. [.. [.. [..
[.. [.. [.. [.. [.. [..
[.. [.. [.. [.. [. [.. [.. [..
[.. [.. [.. [.. [..... [..[.. [..
[.. [.. [.. [.. [..[. [.. [..
[.... [.. [.. .. [.... [.. [...
https://otsea.io
https://t.me/OTSeaPortal
https://twitter.com/OTSeaERC20
*/
// SPDX-License-Identifier: MIT
pragma solidity =0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "contracts/libraries/OTSeaErrors.sol";
/// @title A transfer helper contract for ETH and tokens
contract TransferHelper is Context {
using SafeERC20 for IERC20;
/// @dev account -> Amount of ETH that failed to transfer
mapping(address => uint256) private _maroonedETH;
error NativeTransferFailed();
event MaroonedETH(address account, uint256 amount);
event MaroonedETHClaimed(address account, address receiver, uint256 amount);
/**
* @notice Claim marooned ETH
* @param _receiver Address to receive the marooned ETH
*/
function claimMaroonedETH(address _receiver) external {
if (_receiver == address(0)) revert OTSeaErrors.InvalidAddress();
uint256 amount = _maroonedETH[_msgSender()];
if (amount == 0) revert OTSeaErrors.NotAvailable();
_maroonedETH[_msgSender()] = 0;
_transferETHOrRevert(_receiver, amount);
emit MaroonedETHClaimed(_msgSender(), _receiver, amount);
}
/**
* @notice Get the amount of marooned ETH for an account
* @param _account Account to check
* @return uint256 Marooned ETH
*/
function getMaroonedETH(address _account) external view returns (uint256) {
if (_account == address(0)) revert OTSeaErrors.InvalidAddress();
return _maroonedETH[_account];
}
/**
* @param _account Account to transfer ETH to
* @param _amount Amount of ETH to transfer to _account
* @dev Rather than reverting if the transfer fails, the _amount is stored for the _account to later claim
*/
function _safeETHTransfer(address _account, uint256 _amount) internal {
(bool success, ) = _account.call{value: _amount}("");
if (!success) {
_maroonedETH[_account] += _amount;
emit MaroonedETH(_account, _amount);
}
}
/**
* @param _account Account to transfer ETH to
* @param _amount Amount of ETH to transfer to _account
* @dev The following will revert if the transfer fails
*/
function _transferETHOrRevert(address _account, uint256 _amount) internal {
(bool success, ) = _account.call{value: _amount}("");
if (!success) revert NativeTransferFailed();
}
/**
* @param _token Token to transfer into the contract from msg.sender
* @param _amount Amount of _token to transfer
* @return uint256 Actual amount transferred into the contract
* @dev This function exists due to _token potentially having taxes
*/
function _transferInTokens(IERC20 _token, uint256 _amount) internal returns (uint256) {
uint256 balanceBefore = _token.balanceOf(address(this));
_token.safeTransferFrom(_msgSender(), address(this), _amount);
return _token.balanceOf(address(this)) - balanceBefore;
}
}/*
[.... [... [...... [.. ..
[.. [.. [.. [.. [..
[.. [.. [.. [.. [.. [..
[.. [.. [.. [.. [. [.. [.. [..
[.. [.. [.. [.. [..... [..[.. [..
[.. [.. [.. [.. [..[. [.. [..
[.... [.. [.. .. [.... [.. [...
https://otsea.io
https://t.me/OTSeaPortal
https://twitter.com/OTSeaERC20
*/
// SPDX-License-Identifier: MIT
pragma solidity =0.8.20;
import "contracts/helpers/ListHelper.sol";
import "contracts/libraries/OTSeaErrors.sol";
/**
* @title Abstract whitelist helper contract
* @dev Up to 500 addresses can be added or removed to/from the whitelist within a single TX.
* This is to prevent the TX reverting due to running out of gas. If a user wishes to add or remove more than 500
* addresses, they must do so by performing multiple transactions.
*/
abstract contract WhitelistHelper is ListHelper {
struct WhitelistUpdate {
address account;
/// @dev "operation" represents whether or not to add (true) or remove (false) "account" to/from an order's whitelist.
bool operation;
}
/**
* @dev associated ID => wallet => whitelist ID
* if whitelist ID > 0, then the wallet is whitelisted for the associated ID.
*/
mapping(uint72 => mapping(address => uint256)) private _whitelistIDs;
/// @dev associated ID => whitelist
mapping(uint72 => address[]) private _whitelists;
event WhitelistUpdated(uint72 indexed id, uint256 totalWhitelist);
/**
* @notice Update a whitelist associated with an ID
* @param _id ID
* @param _updates Updates to make to the whitelist associated with _id
*/
function updateWhitelist(uint72 _id, WhitelistUpdate[] calldata _updates) external virtual;
/**
* @notice Get the total whitelisted accounts associated with an ID
* @param _id ID
* @return uint256 Total whitelisted
*/
function getTotalWhitelisted(uint72 _id) external view returns (uint256) {
_checkIDExists(_id);
return _getTotalWhitelisted(_id);
}
/**
* @notice Get a whitelist associated with an ID
* @param _id ID
* @param _start Start whitelist ID
* @param _end End whitelist ID
* @return whitelist An array of whitelisted accounts for _id within the range of _start and _end (inclusive)
*/
function getWhitelist(
uint72 _id,
uint256 _start,
uint256 _end
)
external
view
onlyValidSequence(_start, _end, _getTotalWhitelisted(_id), DISALLOW_ZERO)
returns (address[] memory whitelist)
{
whitelist = new address[](_end - _start + 1);
uint256 index;
uint256 whitelistIndex = _start - 1;
/// @dev whitelistIndex < _end is the same as whitelistIndex <= _end - 1
for (whitelistIndex; whitelistIndex < _end; ) {
whitelist[index] = _whitelists[_id][whitelistIndex];
unchecked {
index++;
whitelistIndex++;
}
}
return whitelist;
}
/**
* @notice Check if an account is whitelisted for an associated ID
* @param _account Account to check
* @param _id ID
* @return bool true if whitelisted, false if not
*/
function checkIsWhitelisted(address _account, uint72 _id) external view returns (bool) {
if (_account == address(0)) revert OTSeaErrors.InvalidAddress();
_checkIDExists(_id);
return _checkIsWhitelisted(_account, _id);
}
/**
* @notice Check if an account is whitelisted for against many IDs
* @param _account Account to check
* @param _ids A list of IDs
* @return isWhitelisted A list of booleans containing true if whitelisted, false if not, each index related to the index in _ids
*/
function checkMultipleIsWhitelisted(
address _account,
uint72[] calldata _ids
) external view returns (bool[] memory isWhitelisted) {
uint256 total = _ids.length;
_validateListLength(total);
if (_account == address(0)) revert OTSeaErrors.InvalidAddress();
isWhitelisted = new bool[](total);
uint256 i;
for (i; i < total; ) {
_checkIDExists(_ids[i]);
isWhitelisted[i] = _checkIsWhitelisted(_account, _ids[i]);
unchecked {
i++;
}
}
return isWhitelisted;
}
/**
* @param _id ID
* @param _whitelist A list of accounts to whitelist for the associated _id
*/
function _initializeWhitelist(uint72 _id, address[] calldata _whitelist) internal {
uint256 total = _whitelist.length;
if (LOOP_LIMIT < total) revert OTSeaErrors.InvalidArrayLength();
uint256 i;
for (i; i < total; ) {
if (_whitelist[i] == address(0)) revert OTSeaErrors.InvalidAddressAtIndex(i);
if (_whitelistIDs[_id][_whitelist[i]] != 0)
revert OTSeaErrors.DuplicateAddressAtIndex(i);
_whitelistIDs[_id][_whitelist[i]] = i + 1;
unchecked {
i++;
}
}
_whitelists[_id] = _whitelist;
emit WhitelistUpdated(_id, total);
}
/**
* @param _id ID
* @param _updates A list of updates to make to the whitelist for the associated _id
*/
function _updateWhitelist(uint72 _id, WhitelistUpdate[] calldata _updates) internal {
uint256 total = _updates.length;
_validateListLength(total);
address[] storage orderWhitelist = _whitelists[_id];
uint256 totalWhitelisted = _whitelists[_id].length;
uint256 i;
for (i; i < total; ) {
if (_updates[i].account == address(0)) revert OTSeaErrors.InvalidAddressAtIndex(i);
if (_updates[i].operation) {
/// @dev add to whitelist, revert if there is a duplicate.
if (_whitelistIDs[_id][_updates[i].account] != 0)
revert OTSeaErrors.DuplicateAddressAtIndex(i);
_whitelistIDs[_id][_updates[i].account] = ++totalWhitelisted;
orderWhitelist.push(_updates[i].account);
} else {
/// @dev remove from whitelist, revert if it doesn't exist.
uint256 id = _whitelistIDs[_id][_updates[i].account];
if (id == 0) revert OTSeaErrors.AddressNotFoundAtIndex(i);
address lastAddress = orderWhitelist[totalWhitelisted - 1];
if (_updates[i].account != lastAddress) {
/// @dev reshuffle array and mapping so that the last address is moved in to the current position.
orderWhitelist[id - 1] = lastAddress;
_whitelistIDs[_id][lastAddress] = id;
}
_whitelistIDs[_id][_updates[i].account] = 0;
orderWhitelist.pop();
totalWhitelisted--;
}
unchecked {
i++;
}
}
emit WhitelistUpdated(_id, totalWhitelisted);
}
/**
* @param _id ID
* @return uint256 Total whitelisted for _id
*/
function _getTotalWhitelisted(uint72 _id) internal view returns (uint256) {
return _whitelists[_id].length;
}
/**
* @param _account Account to check
* @param _id ID
* @return bool true if whitelisted, false if not
*/
function _checkIsWhitelisted(address _account, uint72 _id) internal view returns (bool) {
return _whitelistIDs[_id][_account] != 0;
}
/**
* @param _id ID
* @dev _checkIDExists reverts if _id does not exist
*/
function _checkIDExists(uint72 _id) internal view virtual;
}/*
[.... [... [...... [.. ..
[.. [.. [.. [.. [..
[.. [.. [.. [.. [.. [..
[.. [.. [.. [.. [. [.. [.. [..
[.. [.. [.. [.. [..... [..[.. [..
[.. [.. [.. [.. [..[. [.. [..
[.... [.. [.. .. [.... [.. [...
https://otsea.io
https://t.me/OTSeaPortal
https://twitter.com/OTSeaERC20
*/
// SPDX-License-Identifier: MIT
pragma solidity =0.8.20;
/// @title Common OTSea errors
library OTSeaErrors {
error InvalidAmount();
error InvalidAddress();
error InvalidIndex(uint256 index);
error InvalidAmountAtIndex(uint256 index);
error InvalidAddressAtIndex(uint256 index);
error DuplicateAddressAtIndex(uint256 index);
error AddressNotFoundAtIndex(uint256 index);
error Unauthorized();
error ExpectationMismatch();
error InvalidArrayLength();
error InvalidFee();
error NotAvailable();
error InvalidPurchase();
error InvalidETH(uint256 expected);
error Unchanged();
}/*
[.... [... [...... [.. ..
[.. [.. [.. [.. [..
[.. [.. [.. [.. [.. [..
[.. [.. [.. [.. [. [.. [.. [..
[.. [.. [.. [.. [..... [..[.. [..
[.. [.. [.. [.. [..[. [.. [..
[.... [.. [.. .. [.... [.. [...
https://otsea.io
https://t.me/OTSeaPortal
https://twitter.com/OTSeaERC20
*/
// SPDX-License-Identifier: MIT
pragma solidity =0.8.20;
/// @title Common OTSea variables
library OTSeaLibrary {
enum FeeType {
Fish,
Whale
}
uint16 internal constant PERCENT_DENOMINATOR = 10000;
address internal constant DEAD_ADDRESS = address(0xdead);
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_multiSigAdmin","type":"address"},{"internalType":"address","name":"revenueDistributor_","type":"address"},{"internalType":"address","name":"_signer","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"AddressNotFoundAtIndex","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"DuplicateAddressAtIndex","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectationMismatch","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"ExpiredSignature","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"InvalidAddressAtIndex","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"InvalidAmountAtIndex","type":"error"},{"inputs":[],"name":"InvalidArrayLength","type":"error"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"}],"name":"InvalidETH","type":"error"},{"inputs":[],"name":"InvalidEnd","type":"error"},{"inputs":[],"name":"InvalidFee","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"InvalidIndex","type":"error"},{"inputs":[],"name":"InvalidPurchase","type":"error"},{"inputs":[],"name":"InvalidSequence","type":"error"},{"inputs":[],"name":"InvalidShortString","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidStart","type":"error"},{"inputs":[],"name":"InvalidTradeOrderType","type":"error"},{"inputs":[],"name":"LockUpNotAllowed","type":"error"},{"inputs":[],"name":"NativeTransferFailed","type":"error"},{"inputs":[],"name":"NotAvailable","type":"error"},{"inputs":[],"name":"OrderBlacklisted","type":"error"},{"inputs":[{"internalType":"uint72","name":"orderID","type":"uint72"}],"name":"OrderNotFound","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SignatureRequired","type":"error"},{"inputs":[{"internalType":"string","name":"str","type":"string"}],"name":"StringTooLong","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"Unchanged","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"UnlockDateNotReached","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"operation","type":"bool"}],"name":"BlacklistUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint72","name":"orderID","type":"uint72"},{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"bool","name":"isAON","type":"bool"},{"internalType":"bool","name":"withLockUp","type":"bool"},{"internalType":"bool","name":"isHidden","type":"bool"},{"internalType":"uint256","name":"totalInput","type":"uint256"},{"internalType":"uint256","name":"totalOutput","type":"uint256"}],"indexed":false,"internalType":"struct OTSea.NewOrder","name":"newOrder","type":"tuple"},{"indexed":false,"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"BuyOrderCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint72[]","name":"orderIDs","type":"uint72[]"}],"name":"CancelledOrders","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"fishFee","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"whaleFee","type":"uint8"},{"indexed":false,"internalType":"uint16","name":"partnerFee","type":"uint16"}],"name":"FeesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"bool","name":"enforced","type":"bool"}],"name":"LockUpOverrideUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"components":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"indexed":false,"internalType":"struct OTSea.ClaimLockUp[]","name":"claims","type":"tuple[]"}],"name":"LockUpsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"time","type":"uint16"}],"name":"LockupPeriodUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"MaroonedETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"MaroonedETHClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"maxSwaps","type":"uint8"}],"name":"MaxTradesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint72","name":"orderID","type":"uint72"},{"indexed":false,"internalType":"bool","name":"enforced","type":"bool"}],"name":"OrderLockUpUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint72","name":"orderID","type":"uint72"},{"indexed":false,"internalType":"uint256","name":"newTotalOutput","type":"uint256"}],"name":"OrderPriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"partner","type":"address"},{"indexed":false,"internalType":"uint256","name":"eth","type":"uint256"}],"name":"PartnerFeePaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"isLockUpOverrideEnabled","type":"bool"}],"indexed":false,"internalType":"struct OTSea.Partner","name":"partner","type":"tuple"}],"name":"PartnerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"eth","type":"uint256"}],"name":"RevenueTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint72","name":"orderID","type":"uint72"},{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"bool","name":"isAON","type":"bool"},{"internalType":"bool","name":"withLockUp","type":"bool"},{"internalType":"bool","name":"isHidden","type":"bool"},{"internalType":"uint256","name":"totalInput","type":"uint256"},{"internalType":"uint256","name":"totalOutput","type":"uint256"}],"indexed":false,"internalType":"struct OTSea.NewOrder","name":"newOrder","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"actualTotalInput","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"actualTotalOutput","type":"uint256"},{"indexed":false,"internalType":"enum OTSeaLibrary.FeeType","name":"feeType","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"decimals","type":"uint8"}],"name":"SellOrderCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"signer","type":"address"}],"name":"SignerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"components":[{"internalType":"uint72","name":"orderID","type":"uint72"},{"internalType":"uint256","name":"amountToSwap","type":"uint256"},{"internalType":"uint256","name":"totalOutput","type":"uint256"}],"indexed":false,"internalType":"struct OTSea.Trade[]","name":"trades","type":"tuple[]"},{"indexed":false,"internalType":"uint256","name":"swapped","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"received","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"claimable","type":"uint256"}],"name":"SwappedETHForTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"components":[{"internalType":"uint72","name":"orderID","type":"uint72"},{"internalType":"uint256","name":"amountToSwap","type":"uint256"},{"internalType":"uint256","name":"totalOutput","type":"uint256"}],"indexed":false,"internalType":"struct OTSea.Trade[]","name":"trades","type":"tuple[]"},{"indexed":false,"internalType":"uint256","name":"swapped","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"received","type":"uint256"},{"indexed":false,"internalType":"enum OTSeaLibrary.FeeType","name":"feeType","type":"uint8"}],"name":"SwappedTokensForETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint72","name":"orderID","type":"uint72"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"swapped","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"received","type":"uint256"}],"name":"Traded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint72","name":"id","type":"uint72"},{"indexed":false,"internalType":"uint256","name":"totalWhitelist","type":"uint256"}],"name":"WhitelistUpdated","type":"event"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_operation","type":"bool"}],"name":"blacklistAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint72[]","name":"_orderIDs","type":"uint72[]"}],"name":"cancelOrders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"uint72[]","name":"_orderIDs","type":"uint72[]"}],"name":"cancelTokenOrders","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint72","name":"_id","type":"uint72"}],"name":"checkIsWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint72[]","name":"_ids","type":"uint72[]"}],"name":"checkMultipleIsWhitelisted","outputs":[{"internalType":"bool[]","name":"isWhitelisted","type":"bool[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"address","name":"_receiver","type":"address"},{"components":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct OTSea.ClaimLockUp[]","name":"_claims","type":"tuple[]"}],"name":"claimLockUpByToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_receiver","type":"address"},{"components":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"internalType":"struct OTSea.ClaimLockUp[]","name":"_claims","type":"tuple[]"}],"name":"claimLockUps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_receiver","type":"address"}],"name":"claimMaroonedETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"bool","name":"isAON","type":"bool"},{"internalType":"bool","name":"withLockUp","type":"bool"},{"internalType":"bool","name":"isHidden","type":"bool"},{"internalType":"uint256","name":"totalInput","type":"uint256"},{"internalType":"uint256","name":"totalOutput","type":"uint256"}],"internalType":"struct OTSea.NewOrder","name":"_newOrder","type":"tuple"},{"internalType":"address[]","name":"_whitelist","type":"address[]"}],"name":"createBuyOrder","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"bool","name":"isAON","type":"bool"},{"internalType":"bool","name":"withLockUp","type":"bool"},{"internalType":"bool","name":"isHidden","type":"bool"},{"internalType":"uint256","name":"totalInput","type":"uint256"},{"internalType":"uint256","name":"totalOutput","type":"uint256"}],"internalType":"struct OTSea.NewOrder","name":"_newOrder","type":"tuple"},{"internalType":"address[]","name":"_whitelist","type":"address[]"},{"components":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"expiresAt","type":"uint256"},{"internalType":"enum OTSeaLibrary.FeeType","name":"feeType","type":"uint8"}],"internalType":"struct OTSea.FeeDetailsSignature","name":"_feeDetailsSignature","type":"tuple"}],"name":"createSellOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFees","outputs":[{"internalType":"uint8","name":"fishFee","type":"uint8"},{"internalType":"uint8","name":"whaleFee","type":"uint8"},{"internalType":"uint16","name":"partnerFee","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint256[]","name":"_indexes","type":"uint256[]"}],"name":"getLockUps","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint88","name":"unlockAt","type":"uint88"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"withdrawn","type":"uint256"}],"internalType":"struct OTSea.LockUp[]","name":"lockUps","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLockupPeriod","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getMaroonedETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxTrades","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint72","name":"_orderID","type":"uint72"}],"name":"getOrder","outputs":[{"components":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"enum OTSea.OrderType","name":"orderType","type":"uint8"},{"internalType":"enum OTSea.State","name":"state","type":"uint8"},{"internalType":"enum OTSeaLibrary.FeeType","name":"feeType","type":"uint8"},{"internalType":"bool","name":"isAON","type":"bool"},{"internalType":"bool","name":"isHidden","type":"bool"},{"internalType":"bool","name":"withLockUp","type":"bool"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"totalInput","type":"uint256"},{"internalType":"uint256","name":"inputTransacted","type":"uint256"},{"internalType":"uint256","name":"totalOutput","type":"uint256"},{"internalType":"uint256","name":"outputTransacted","type":"uint256"}],"internalType":"struct OTSea.Order","name":"order","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint72[]","name":"_orderIDs","type":"uint72[]"}],"name":"getOrdersByIDs","outputs":[{"components":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"enum OTSea.OrderType","name":"orderType","type":"uint8"},{"internalType":"enum OTSea.State","name":"state","type":"uint8"},{"internalType":"enum OTSeaLibrary.FeeType","name":"feeType","type":"uint8"},{"internalType":"bool","name":"isAON","type":"bool"},{"internalType":"bool","name":"isHidden","type":"bool"},{"internalType":"bool","name":"withLockUp","type":"bool"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"totalInput","type":"uint256"},{"internalType":"uint256","name":"inputTransacted","type":"uint256"},{"internalType":"uint256","name":"totalOutput","type":"uint256"},{"internalType":"uint256","name":"outputTransacted","type":"uint256"}],"internalType":"struct OTSea.Order[]","name":"orders","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_start","type":"uint256"},{"internalType":"uint256","name":"_end","type":"uint256"}],"name":"getOrdersInSequence","outputs":[{"components":[{"internalType":"address","name":"creator","type":"address"},{"internalType":"enum OTSea.OrderType","name":"orderType","type":"uint8"},{"internalType":"enum OTSea.State","name":"state","type":"uint8"},{"internalType":"enum OTSeaLibrary.FeeType","name":"feeType","type":"uint8"},{"internalType":"bool","name":"isAON","type":"bool"},{"internalType":"bool","name":"isHidden","type":"bool"},{"internalType":"bool","name":"withLockUp","type":"bool"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"totalInput","type":"uint256"},{"internalType":"uint256","name":"inputTransacted","type":"uint256"},{"internalType":"uint256","name":"totalOutput","type":"uint256"},{"internalType":"uint256","name":"outputTransacted","type":"uint256"}],"internalType":"struct OTSea.Order[]","name":"orders","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getPartner","outputs":[{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"isLockUpOverrideEnabled","type":"bool"}],"internalType":"struct OTSea.Partner","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getTotalLockUps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalOrders","outputs":[{"internalType":"uint72","name":"","type":"uint72"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint72","name":"_id","type":"uint72"}],"name":"getTotalWhitelisted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint72","name":"_id","type":"uint72"},{"internalType":"uint256","name":"_start","type":"uint256"},{"internalType":"uint256","name":"_end","type":"uint256"}],"name":"getWhitelist","outputs":[{"internalType":"address[]","name":"whitelist","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isAccountBlacklisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint72","name":"_orderID","type":"uint72"}],"name":"isOrderBlacklisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauseContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"_newFishFee","type":"uint8"},{"internalType":"uint8","name":"_newWhaleFee","type":"uint8"},{"internalType":"uint16","name":"_newPartnerFee","type":"uint16"}],"name":"setFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_time","type":"uint16"}],"name":"setLockupPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"maxTrades_","type":"uint8"}],"name":"setMaxTrades","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"revenueDistributor_","type":"address"}],"name":"setRevenueDistributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_signer","type":"address"}],"name":"setSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"components":[{"internalType":"uint72","name":"orderID","type":"uint72"},{"internalType":"uint256","name":"amountToSwap","type":"uint256"},{"internalType":"uint256","name":"totalOutput","type":"uint256"}],"internalType":"struct OTSea.Trade[]","name":"_trades","type":"tuple[]"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"bool","name":"isAON","type":"bool"},{"internalType":"bool","name":"withLockUp","type":"bool"},{"internalType":"bool","name":"isHidden","type":"bool"},{"internalType":"uint256","name":"totalInput","type":"uint256"},{"internalType":"uint256","name":"totalOutput","type":"uint256"}],"internalType":"struct OTSea.NewOrder","name":"_newOrder","type":"tuple"},{"internalType":"bool","name":"_allowLockUps","type":"bool"},{"internalType":"uint16","name":"_expectedLockupPeriod","type":"uint16"}],"name":"swapETHForTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"components":[{"internalType":"uint72","name":"orderID","type":"uint72"},{"internalType":"uint256","name":"amountToSwap","type":"uint256"},{"internalType":"uint256","name":"totalOutput","type":"uint256"}],"internalType":"struct OTSea.Trade[]","name":"_trades","type":"tuple[]"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"bool","name":"isAON","type":"bool"},{"internalType":"bool","name":"withLockUp","type":"bool"},{"internalType":"bool","name":"isHidden","type":"bool"},{"internalType":"uint256","name":"totalInput","type":"uint256"},{"internalType":"uint256","name":"totalOutput","type":"uint256"}],"internalType":"struct OTSea.NewOrder","name":"_newOrder","type":"tuple"},{"components":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"expiresAt","type":"uint256"},{"internalType":"enum OTSeaLibrary.FeeType","name":"feeType","type":"uint8"}],"internalType":"struct OTSea.FeeDetailsSignature","name":"_feeDetailsSignature","type":"tuple"}],"name":"swapTokensForETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpauseContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"bool","name":"_enforce","type":"bool"}],"name":"updateLockUpOverride","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint72","name":"_orderID","type":"uint72"},{"internalType":"bool","name":"_enforce","type":"bool"}],"name":"updateOrderLockUp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"isLockUpOverrideEnabled","type":"bool"}],"internalType":"struct OTSea.Partner","name":"_partner","type":"tuple"}],"name":"updatePartner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint72","name":"_orderID","type":"uint72"},{"internalType":"uint256","name":"_expectedRemainingInput","type":"uint256"},{"internalType":"uint256","name":"_newRemainingOutput","type":"uint256"}],"name":"updatePrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint72","name":"_orderID","type":"uint72"},{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"operation","type":"bool"}],"internalType":"struct WhitelistHelper.WhitelistUpdate[]","name":"_updates","type":"tuple[]"}],"name":"updateWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
610160604052600880546001600160e81b03166202879960ea1b1790556009805463ffffffff191663012c0bb81790553480156200003c57600080fd5b50604051620064d6380380620064d68339810160408190526200005f916200031b565b60408051808201825260058152644f5453656160d81b60208083019190915282518084019093526006835265076312e302e360d41b908301529082858383620000aa82600062000230565b61012052620000bb81600162000230565b61014052815160208084019190912060e052815190820120610100524660a0526200014960e05161010051604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b60805250503060c0526001600160a01b0381166200018257604051631e4fbdf760e01b8152600060048201526024015b60405180910390fd5b6200018d8162000269565b506002805460ff60a01b1916905560016003556001600160a01b038116620001c85760405163e6c4247b60e01b815260040160405180910390fd5b600580546001600160a01b0319166001600160a01b03928316179055841691506200020890505760405163e6c4247b60e01b815260040160405180910390fd5b50600880546001600160a01b0319166001600160a01b0392909216919091179055506200054b565b600060208351101562000250576200024883620002bb565b905062000263565b816200025d84826200040a565b5060ff90505b92915050565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600080829050601f81511115620002e9578260405163305a27a960e01b8152600401620001799190620004d6565b8051620002f68262000526565b179392505050565b80516001600160a01b03811681146200031657600080fd5b919050565b6000806000606084860312156200033157600080fd5b6200033c84620002fe565b92506200034c60208501620002fe565b91506200035c60408501620002fe565b90509250925092565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200039057607f821691505b602082108103620003b157634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200040557600081815260208120601f850160051c81016020861015620003e05750805b601f850160051c820191505b818110156200040157828155600101620003ec565b5050505b505050565b81516001600160401b0381111562000426576200042662000365565b6200043e816200043784546200037b565b84620003b7565b602080601f8311600181146200047657600084156200045d5750858301515b600019600386901b1c1916600185901b17855562000401565b600085815260208120601f198616915b82811015620004a75788860151825594840194600190910190840162000486565b5085821015620004c65787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208083528351808285015260005b818110156200050557858101830151858201604001528201620004e7565b506000604082860101526040601f19601f8301168501019250505092915050565b80516020808301519190811015620003b15760001960209190910360031b1b16919050565b60805160a05160c05160e051610100516101205161014051615f30620005a66000396000613e3e01526000613e0c01526000614ca901526000614c8101526000614bdc01526000614c0601526000614c300152615f306000f3fe6080604052600436106102725760003560e01c806372ea9ce31161014f578063b54d321d116100c1578063db8d55f11161007a578063db8d55f1146107e1578063e392cc5014610827578063e97f42ec14610847578063edb6fb6614610867578063f2fde38b14610894578063fef11087146108b457600080fd5b8063b54d321d146106fb578063b58b90cf14610728578063c0c2e31f14610748578063c90b58f21461075b578063cccd7cbb1461077b578063d50f6d3a1461079b57600080fd5b80638da5cb5b116101135780638da5cb5b1461063b5780639f1a8de314610659578063a061772314610679578063a968437214610699578063abc37382146106c6578063b33712c5146106e657600080fd5b806372ea9ce314610585578063773c3645146105a5578063833ee422146105c557806384b0196e146105f3578063894bed451461061b57600080fd5b8063375f16a7116101e8578063600e2698116101ac578063600e2698146104c357806362db9e9a146104e357806363975a7c146105035780636c19e78314610530578063715018a6146105505780637187260a1461056557600080fd5b8063375f16a7146104245780633c9c885f1461045c578063439766ce1461046f5780634e4ee182146104845780635c975abb146104a457600080fd5b8063179f9e651161023a578063179f9e651461033f5780631ab0d4581461035f5780631f8f2f661461038c578063238ac933146103ac5780632d004524146103e45780632d5a5d341461040457600080fd5b8063036896c11461027757806305f46092146102995780630bb04b4e146102cf5780630f1d76c1146102ff57806317764d5b1461031f575b600080fd5b34801561028357600080fd5b506102976102923660046150d9565b6108e0565b005b3480156102a557600080fd5b506102b96102b436600461512d565b610ad9565b6040516102c6919061527b565b60405180910390f35b3480156102db57600080fd5b506102ef6102ea3660046152e6565b610d16565b60405190151581526020016102c6565b34801561030b57600080fd5b506102ef61031a366004615301565b610d56565b34801561032b57600080fd5b5061029761033a366004615348565b610d9e565b34801561034b57600080fd5b5061029761035a366004615301565b610e2a565b34801561036b57600080fd5b5061037f61037a3660046150d9565b610e7d565b6040516102c691906153c3565b34801561039857600080fd5b506102976103a736600461540c565b610fa5565b3480156103b857600080fd5b506005546103cc906001600160a01b031681565b6040516001600160a01b0390911681526020016102c6565b3480156103f057600080fd5b506102b96103ff366004615429565b611035565b34801561041057600080fd5b5061029761041f366004615459565b611251565b34801561043057600080fd5b50600854600160a01b90046001600160481b03166040516001600160481b0390911681526020016102c6565b61029761046a3660046154e8565b611321565b34801561047b57600080fd5b5061029761157c565b34801561049057600080fd5b5061029761049f3660046155b8565b61158e565b3480156104b057600080fd5b50600254600160a01b900460ff166102ef565b3480156104cf57600080fd5b506102976104de366004615301565b6117ca565b3480156104ef57600080fd5b506102976104fe3660046155ff565b611888565b34801561050f57600080fd5b5061052361051e3660046150d9565b611914565b6040516102c6919061561a565b34801561053c57600080fd5b5061029761054b366004615301565b611aa9565b34801561055c57600080fd5b50610297611b26565b34801561057157600080fd5b50610297610580366004615690565b611b38565b34801561059157600080fd5b506102976105a03660046156d1565b611cf3565b3480156105b157600080fd5b506102976105c0366004615718565b611e01565b3480156105d157600080fd5b506105e56105e0366004615301565b611f26565b6040519081526020016102c6565b3480156105ff57600080fd5b50610608611f6b565b6040516102c69796959493929190615784565b34801561062757600080fd5b5061029761063636600461512d565b611fb1565b34801561064757600080fd5b506002546001600160a01b03166103cc565b34801561066557600080fd5b5061029761067436600461581a565b612180565b34801561068557600080fd5b50610297610694366004615459565b61226a565b3480156106a557600080fd5b506106b96106b43660046158ac565b612380565b6040516102c691906158df565b3480156106d257600080fd5b506102976106e13660046158ac565b6124ad565b3480156106f257600080fd5b506102976125d1565b34801561070757600080fd5b5060095462010000900461ffff1660405161ffff90911681526020016102c6565b34801561073457600080fd5b50610297610743366004615920565b6125e1565b610297610756366004615984565b61288d565b34801561076757600080fd5b506105e56107763660046152e6565b61291b565b34801561078757600080fd5b506102ef6107963660046159be565b612944565b3480156107a757600080fd5b506107bb6107b6366004615301565b6129aa565b6040805182516001600160a01b03168152602092830151151592810192909252016102c6565b3480156107ed57600080fd5b506008546009546040805160ff600160e81b850481168252600160f01b909404909316602084015261ffff909116908201526060016102c6565b34801561083357600080fd5b506102976108423660046159f3565b612a28565b34801561085357600080fd5b506105e5610862366004615301565b612aa1565b34801561087357600080fd5b506108876108823660046152e6565b612ae6565b6040516102c69190615a11565b3480156108a057600080fd5b506102976108af366004615301565b612c42565b3480156108c057600080fd5b50600854600160f81b900460ff1660405160ff90911681526020016102c6565b6108e8612c80565b808015806108f65750606481115b1561091457604051634ec4810560e11b815260040160405180910390fd5b60008060005b83811015610a63576000600a600088888581811061093a5761093a615a20565b905060200201602081019061094f91906152e6565b6001600160481b03168152602081019190915260400160002080549091506001600160a01b03163314610994576040516282b42960e81b815260040160405180910390fd5b60008154600160a81b900460ff1660028111156109b3576109b361516e565b1415806109d0575060018101546001600160a01b03898116911614155b156109ee5760405163d9b9141960e01b815260040160405180910390fd5b805460ff60a81b1916600160a91b17815560038101546002820154600091610a1591615a4c565b905060008254600160a01b900460ff166001811115610a3657610a3661516e565b03610a4c57610a458186615a5f565b9450610a59565b610a568185615a5f565b93505b505060010161091a565b8215610a7357610a733384612caa565b8115610a8d57610a8d6001600160a01b0388163384612d71565b7f52750d3a44c06a55b289a1181dc2173d8dd600646ca5c810070636c93b47b2598686604051610abe929190615a72565b60405180910390a150505050610ad46001600355565b505050565b606081610ae581612dd0565b806001600160401b03811115610afd57610afd615abe565b604051908082528060200260200182016040528015610b3657816020015b610b23614f9e565b815260200190600190039081610b1b5790505b50915060005b81811015610d0d57610b73858583818110610b5957610b59615a20565b9050602002016020810190610b6e91906152e6565b612dfc565b600a6000868684818110610b8957610b89615a20565b9050602002016020810190610b9e91906152e6565b6001600160481b0316815260208082019290925260409081016000208151610180810190925280546001600160a01b03811683529192909190830190600160a01b900460ff166001811115610bf557610bf561516e565b6001811115610c0657610c0661516e565b81528154602090910190600160a81b900460ff166002811115610c2b57610c2b61516e565b6002811115610c3c57610c3c61516e565b81528154602090910190600160b01b900460ff166001811115610c6157610c6161516e565b6001811115610c7257610c7261516e565b8152815460ff600160b81b8204811615156020840152600160c01b8204811615156040840152600160c81b909104161515606082015260018201546001600160a01b03166080820152600282015460a0820152600382015460c0820152600482015460e0820152600590910154610100909101528351849083908110610cfa57610cfa615a20565b6020908102919091010152600101610b3c565b50505b92915050565b6000610d2182612dfc565b506001600160481b03166000908152600a60209081526040808320546001600160a01b03168352600d90915290205460ff1690565b60006001600160a01b038216610d7f5760405163e6c4247b60e01b815260040160405180910390fd5b506001600160a01b03166000908152600d602052604090205460ff1690565b610da6612c80565b610dae612e4f565b610db6612e7a565b6000610dc56020860186615301565b6001600160a01b031603610dec5760405163e6c4247b60e01b815260040160405180910390fd5b6000610df782612eaa565b90506000610e058683612f4a565b90508315610e1857610e18818686613314565b5050610e246001600355565b50505050565b610e326134ec565b6008546001600160a01b0316610e5b5760405163e6c4247b60e01b815260040160405180910390fd5b600880546001600160a01b0319166001600160a01b0392909216919091179055565b606081610e8981612dd0565b6001600160a01b038516610eb05760405163e6c4247b60e01b815260040160405180910390fd5b806001600160401b03811115610ec857610ec8615abe565b604051908082528060200260200182016040528015610ef1578160200160208202803683370190505b50915060005b81811015610f9b57610f14858583818110610b5957610b59615a20565b610f7186868684818110610f2a57610f2a615a20565b9050602002016020810190610f3f91906152e6565b6001600160481b031660009081526006602090815260408083206001600160a01b039490941683529290522054151590565b838281518110610f8357610f83615a20565b91151560209283029190910190910152600101610ef7565b50505b9392505050565b610fad6134ec565b60ff81161580610fc0575060ff81166064105b15610fde5760405163162908e360e11b815260040160405180910390fd5b600880546001600160f81b0316600160f81b60ff8416908102919091179091556040519081527f1a1640c56c8652d27a565b95b6aaf9168cf7f190203dba9f4a6b769b563696bf906020015b60405180910390a150565b60085460609083908390600160a01b90046001600160481b0316600061105d84848484613519565b6110678787615a4c565b611072906001615a5f565b6001600160401b0381111561108957611089615abe565b6040519080825280602002602001820160405280156110c257816020015b6110af614f9e565b8152602001906001900390816110a75790505b5094506000875b878111611245576001600160481b0381166000908152600a60209081526040918290208251610180810190935280546001600160a01b03811684529091830190600160a01b900460ff1660018111156111245761112461516e565b60018111156111355761113561516e565b81528154602090910190600160a81b900460ff16600281111561115a5761115a61516e565b600281111561116b5761116b61516e565b81528154602090910190600160b01b900460ff1660018111156111905761119061516e565b60018111156111a1576111a161516e565b8152815460ff600160b81b8204811615156020840152600160c01b8204811615156040840152600160c81b909104161515606082015260018201546001600160a01b03166080820152600282015460a0820152600382015460c0820152600482015460e082015260059091015461010090910152878361122081615ad4565b94508151811061123257611232615a20565b60209081029190910101526001016110c9565b50505050505092915050565b6112596134ec565b6001600160a01b0382166112805760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b0382166000908152600d602052604090205481151560ff9091161515036112c15760405163cf7071eb60e01b815260040160405180910390fd5b6001600160a01b0382166000818152600d6020908152604091829020805460ff191685151590811790915591519182527f6a12b3df6cba4203bd7fd06b816789f87de8c594299aed5717ae070fac781bac91015b60405180910390a25050565b611329612c80565b611331612e4f565b611339612e7a565b818015611355575060095461ffff828116620100009092041614155b15611373576040516346e69cdf60e01b815260040160405180910390fd5b6000806000806113858a8a8a89613610565b929650909450925090506001600160a01b038a166113a66020890189615301565b6001600160a01b03160361140a57346113c3608089013586615a5f565b146113fb576113d6608088013585615a5f565b604051634c90b39760e01b81526004016113f291815260200190565b60405180910390fd5b61140487613825565b5061142d565b34841461142d57604051634c90b39760e01b8152600481018590526024016113f2565b611437818b613ba6565b81156114f957336000908152600c60209081526040918290208251608081019093526001600160a01b038d1683526009549092918201906114829062010000900461ffff1642615a5f565b6001600160581b039081168252602080830187905260006040938401819052855460018181018855968252908290208551600390920201805492860151909316600160a01b026001600160f81b03199092166001600160a01b03909116171781559082015192810192909255606001516002909101555b8215611513576115136001600160a01b038b163385612d71565b6001600160a01b038a16336001600160a01b03167fa8d4cc0a1ef01dbc9440131ad91c9c8b1e907863fce25d6184ec791399c1c39f8b8b88888860405161155e959493929190615b48565b60405180910390a3505050506115746001600355565b505050505050565b6115846134ec565b61158c613c96565b565b336000908152600c6020526040812054908190036115bf5760405163d9b9141960e01b815260040160405180910390fd5b816115c981612dd0565b60005b8181101561177557368585838181106115e7576115e7615a20565b9050604002019050806000013584116116165760405163042a2e7160e11b8152600481018390526024016113f2565b336000908152600c602052604081208054833590811061163857611638615a20565b600091825260209182902060408051608081018252600390930290910180546001600160a01b03811684526001600160581b03600160a01b9091041693830184905260018101549183019190915260020154606082015291504210156116b4576040516335f8469160e21b8152600481018490526024016113f2565b6000816060015182604001516116ca9190615a4c565b9050602083013515806116e05750826020013581105b156117015760405163399583bd60e01b8152600481018590526024016113f2565b336000908152600c6020908152604090912080549185013591853590811061172b5761172b615a20565b9060005260206000209060030201600201600082825461174b9190615a5f565b9091555050815161176a906001600160a01b03168a6020860135612d71565b5050506001016115cc565b506001600160a01b038516336001600160a01b03167f967cf83411738666f908e59cf8f6c2f53af7dbff325903ba77b116068b23cb8d86866040516117bb929190615b79565b60405180910390a35050505050565b6001600160a01b0381166117f15760405163e6c4247b60e01b815260040160405180910390fd5b33600090815260046020526040812054908190036118225760405163d9b9141960e01b815260040160405180910390fd5b3360009081526004602052604081205561183c8282613cf6565b604080513381526001600160a01b038416602082015280820183905290517f60e82ffc0c9ca018da948ff9a0e7d95b104367e847eae1ec57d7c9997380ce7f9181900360600190a15050565b6118906134ec565b603c61ffff821610806118a8575061ffff8116610e10105b156118c65760405163162908e360e11b815260040160405180910390fd5b6009805463ffff000019166201000061ffff8416908102919091179091556040519081527fa7664047213e9c77c1932f56213b16ca81ed5e3fd4d2f2a99025b5215ba34d689060200161102a565b6060600061192185611f26565b90508261192d81612dd0565b806001600160401b0381111561194557611945615abe565b60405190808252806020026020018201604052801561199757816020015b6040805160808101825260008082526020808301829052928201819052606082015282526000199092019101816119635790505b50925060005b81811015611a9f578585828181106119b7576119b7615a20565b9050602002013583116119e05760405163042a2e7160e11b8152600481018290526024016113f2565b6001600160a01b0387166000908152600c60205260409020868683818110611a0a57611a0a615a20565b9050602002013581548110611a2157611a21615a20565b600091825260209182902060408051608081018252600390930290910180546001600160a01b03811684526001600160581b03600160a01b909104169383019390935260018301549082015260029091015460608201528451859083908110611a8c57611a8c615a20565b602090810291909101015260010161199d565b5050509392505050565b611ab16134ec565b6001600160a01b038116611ad85760405163e6c4247b60e01b815260040160405180910390fd5b600580546001600160a01b0319166001600160a01b0383169081179091556040519081527f5553331329228fbd4123164423717a4a7539f6dfa1c3279a923b98fd681a6c739060200161102a565b611b2e6134ec565b61158c6000613d6a565b611b406134ec565b6001600160a01b038216611b675760405163e6c4247b60e01b815260040160405180910390fd5b611b746020820182615301565b6001600160a01b038381166000908152600b602052604090205481169116148015611bd55750611baa6040820160208301615bc0565b6001600160a01b0383166000908152600b602052604090205460ff600160a01b909104161515901515145b15611bf35760405163cf7071eb60e01b815260040160405180910390fd5b6000611c026020830183615301565b6001600160a01b0316148015611c235750611c236040820160208301615bc0565b15611c415760405163d9b9141960e01b815260040160405180910390fd5b6040805180820190915280611c596020840184615301565b6001600160a01b03168152602001826020016020810190611c7a9190615bc0565b151590526001600160a01b038084166000818152600b60209081526040918290208551815496909201511515600160a01b026001600160a81b031990961691909416179390931790915590517f9ad4c32594afa555f2c0b23876b6843f5d8f10be382a427e133e2ea458a7063a90611315908490615bdd565b611cfb6134ec565b60085460ff808516600160e81b909204161080611d26575060085460ff808416600160f01b90920416105b80611d3657508160ff168360ff16105b80611d4657506103e861ffff8216105b80611d56575061ffff8116611388105b15611d74576040516358d620b360e01b815260040160405180910390fd5b6008805461ffff60e81b1916600160e81b60ff86811691820260ff60f01b191692909217600160f01b928616928302179092556009805461ffff191661ffff8516908117909155604080519384526020840192909252908201527f2f45d24e3adb3752a7e92308eeeeac27a7cdf7dabce4b2136e8cf23f9fcee976906060015b60405180910390a1505050565b81611e0b81613dbc565b611e13612e4f565b611e1b612e7a565b6001600160481b0383166000908152600a60205260408120908154600160a81b900460ff166002811115611e5157611e5161516e565b141580611e7a575060008154600160a01b900460ff166001811115611e7857611e7861516e565b145b15611e985760405163d9b9141960e01b815260040160405180910390fd5b8054831515600160c81b90910460ff16151503611ec85760405163cf7071eb60e01b815260040160405180910390fd5b805460ff60c81b1916600160c81b8415159081029190911782556040519081526001600160481b038516907f5f248b4371e372d4298ec69af6008e7c07e513e14c34377af2a2fc86365e04209060200160405180910390a250505050565b60006001600160a01b038216611f4f5760405163e6c4247b60e01b815260040160405180910390fd5b506001600160a01b03166000908152600c602052604090205490565b600060608060008060006060611f7f613e05565b611f87613e37565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b611fb9612c80565b80801580611fc75750606481115b15611fe557604051634ec4810560e11b815260040160405180910390fd5b6000805b82811015612126576000600a600087878581811061200957612009615a20565b905060200201602081019061201e91906152e6565b6001600160481b03168152602081019190915260400160002080549091506001600160a01b03163314612063576040516282b42960e81b815260040160405180910390fd5b60008154600160a81b900460ff1660028111156120825761208261516e565b146120a05760405163d9b9141960e01b815260040160405180910390fd5b805460ff60a81b1916600160a91b178155600381015460028201546000916120c791615a4c565b905060008254600160a01b900460ff1660018111156120e8576120e861516e565b036120fe576120f78185615a5f565b935061211c565b8154600183015461211c916001600160a01b03918216911683612d71565b5050600101611fe9565b8115612136576121363383612caa565b7f52750d3a44c06a55b289a1181dc2173d8dd600646ca5c810070636c93b47b2598585604051612167929190615a72565b60405180910390a150505061217c6001600355565b5050565b612188612c80565b612190612e4f565b612198612e7a565b60006121a382612eaa565b90506000806121b3888888613e64565b90925090506001600160a01b0388166121cf6020870187615301565b6001600160a01b0316036121e9576121e78584612f4a565b505b60006121f6338386613fdd565b9050612202818a613ba6565b6001600160a01b038916336001600160a01b03167f37564a4a0bdd799a9e768e7ad616dda363f00fbaaf54817f085c55828c93b5138a8a87878a60405161224d959493929190615c14565b60405180910390a3505050506122636001600355565b5050505050565b6001600160a01b038083166000908152600b6020526040902080549091166122a55760405163d9b9141960e01b815260040160405180910390fd5b80546001600160a01b031633148015906122ca57506002546001600160a01b03163314155b156122e7576040516282b42960e81b815260040160405180910390fd5b8054821515600160a01b90910460ff161515036123175760405163cf7071eb60e01b815260040160405180910390fd5b805460ff60a01b1916600160a01b83151502178155336001600160a01b0316836001600160a01b03167f1a44d70251a391917683825fb43ec17afff144817ee6615ed8325d4775c883cf84604051612373911515815260200190565b60405180910390a3505050565b606082826123a3866001600160481b031660009081526007602052604090205490565b60006123b184848484613519565b6123bb8787615a4c565b6123c6906001615a5f565b6001600160401b038111156123dd576123dd615abe565b604051908082528060200260200182016040528015612406578160200160208202803683370190505b50945060008061241760018a615a4c565b90505b878110156124a0576001600160481b038a16600090815260076020526040902080548290811061244c5761244c615a20565b9060005260206000200160009054906101000a90046001600160a01b031687838151811061247c5761247c615a20565b6001600160a01b03909216602092830291909101909101526001918201910161241a565b5050505050509392505050565b826124b781613dbc565b6124bf612e4f565b6124c7612e7a565b6001600160481b0384166000908152600a60205260408120908154600160a81b900460ff1660028111156124fd576124fd61516e565b1461251b5760405163d9b9141960e01b815260040160405180910390fd5b8260000361253c5760405163162908e360e11b815260040160405180910390fd5b83816003015482600201546125519190615a4c565b1461256f576040516346e69cdf60e01b815260040160405180910390fd5b60008382600501546125819190615a5f565b600483018190556040518181529091506001600160481b038716907ffaed25a36cd23e822e1afbf410755f746ffe05ddebc36d2cd844828adcdd89c29060200160405180910390a2505050505050565b6125d96134ec565b61158c614046565b6001600160a01b0384166126085760405163e6c4247b60e01b815260040160405180910390fd5b336000908152600c6020526040812054908190036126395760405163d9b9141960e01b815260040160405180910390fd5b8161264381612dd0565b6000805b82811015612822573686868381811061266257612662615a20565b9050604002019050806000013585116126915760405163042a2e7160e11b8152600481018390526024016113f2565b336000908152600c60205260408120805483359081106126b3576126b3615a20565b60009182526020918290206040805160808101825260039390930290910180546001600160a01b03808216808652600160a01b9092046001600160581b031695850195909552600182015492840192909252600201546060830152909250908b161461273557604051632070666960e11b8152600481018490526024016113f2565b80602001516001600160581b0316421015612766576040516335f8469160e21b8152600481018490526024016113f2565b60008160600151826040015161277c9190615a4c565b9050602083013515806127925750826020013581105b156127b35760405163399583bd60e01b8152600481018590526024016113f2565b336000908152600c602090815260409091208054918501359185359081106127dd576127dd615a20565b906000526020600020906003020160020160008282546127fd9190615a5f565b909155506128119050602084013586615a5f565b945050600190920191506126479050565b506128376001600160a01b0388168783612d71565b6001600160a01b038616336001600160a01b03167f967cf83411738666f908e59cf8f6c2f53af7dbff325903ba77b116068b23cb8d878760405161287c929190615b79565b60405180910390a350505050505050565b612895612e4f565b61289d612e7a565b60006128ac6020850185615301565b6001600160a01b0316036128d35760405163e6c4247b60e01b815260040160405180910390fd5b826080013534146128fd57604051634c90b39760e01b8152608084013560048201526024016113f2565b600061290884613825565b90508115610e2457610e24818484613314565b600061292682612dfc565b6001600160481b038216600090815260076020526040902054610d10565b60006001600160a01b03831661296d5760405163e6c4247b60e01b815260040160405180910390fd5b61297682612dfc565b6001600160481b03821660009081526006602090815260408083206001600160a01b03871684529091529020541515610f9e565b60408051808201909152600080825260208201526001600160a01b0382166129e55760405163e6c4247b60e01b815260040160405180910390fd5b506001600160a01b039081166000908152600b60209081526040918290208251808401909352549283168252600160a01b90920460ff1615159181019190915290565b82612a3281613dbc565b612a3a612e4f565b612a42612e7a565b60006001600160481b0385166000908152600a6020526040902054600160a81b900460ff166002811115612a7857612a7861516e565b14612a965760405163d9b9141960e01b815260040160405180910390fd5b610e24848484614082565b60006001600160a01b038216612aca5760405163e6c4247b60e01b815260040160405180910390fd5b506001600160a01b031660009081526004602052604090205490565b612aee614f9e565b612af782612dfc565b6001600160481b0382166000908152600a60209081526040918290208251610180810190935280546001600160a01b03811684529091830190600160a01b900460ff166001811115612b4b57612b4b61516e565b6001811115612b5c57612b5c61516e565b81528154602090910190600160a81b900460ff166002811115612b8157612b8161516e565b6002811115612b9257612b9261516e565b81528154602090910190600160b01b900460ff166001811115612bb757612bb761516e565b6001811115612bc857612bc861516e565b8152815460ff600160b81b8204811615156020840152600160c01b8204811615156040840152600160c81b909104161515606082015260018201546001600160a01b03166080820152600282015460a0820152600382015460c0820152600482015460e08201526005909101546101009091015292915050565b612c4a6134ec565b6001600160a01b038116612c7457604051631e4fbdf760e01b8152600060048201526024016113f2565b612c7d81613d6a565b50565b600260035403612ca357604051633ee5aeb560e01b815260040160405180910390fd5b6002600355565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612cf7576040519150601f19603f3d011682016040523d82523d6000602084013e612cfc565b606091505b5050905080610ad4576001600160a01b03831660009081526004602052604081208054849290612d2d908490615a5f565b9091555050604080516001600160a01b0385168152602081018490527f43998e88b0229199fdb7ad21fa2b6f4ac86cc33e98784dd17c5244437afd4e879101611df4565b6040516001600160a01b03838116602483015260448201839052610ad491859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506144c6565b801580612dde57506101f481115b15612c7d57604051634ec4810560e11b815260040160405180910390fd5b6001600160481b0381161580612e2657506008546001600160481b03808316600160a01b90920416105b15612c7d5760405163b092f19760e01b81526001600160481b03821660048201526024016113f2565b600254600160a01b900460ff161561158c5760405163d93c066560e01b815260040160405180910390fd5b336000908152600d602052604090205460ff161561158c576040516282b42960e81b815260040160405180910390fd5b6000612eb68280615c4f565b9050600003612ec457919050565b60007f8d2233fff76359558ed65b9b090b4ad52b4b4c57f4f06d9c177974d3bc10f1c6336020850135612efd6060870160408801615c95565b604051602001612f109493929190615cb6565b60408051601f198184030181529190529050612f3a81612f308580615c4f565b8660200135614529565b610f9e6060840160408501615c95565b600060808301351580612f5f575060a0830135155b15612f7d5760405163162908e360e11b815260040160405180910390fd5b60088054601490612f9d90600160a01b90046001600160481b0316615ceb565b91906101000a8154816001600160481b0302191690836001600160481b03160217905590506000612fe4846000016020810190612fda9190615301565b85608001356145c4565b90506000846080013582146130155760808501356130068360a0880135615d11565b6130109190615d28565b61301b565b8460a001355b90506040518061018001604052806130303390565b6001600160a01b03168152602001600181526020016000815260200185600181111561305e5761305e61516e565b81526020018660200160208101906130769190615bc0565b1515815260200161308d6080880160608901615bc0565b151581526020016130a46060880160408901615bc0565b151581526020908101906130ba90880188615301565b6001600160a01b0390811682526020808301869052600060408085018290526060850187905260809094018190526001600160481b0388168152600a8252929092208351815492166001600160a01b0319831681178255928401519092909183916001600160a81b031990911617600160a01b83600181111561313f5761313f61516e565b021790555060408201518154829060ff60a81b1916600160a81b83600281111561316b5761316b61516e565b021790555060608201518154829060ff60b01b1916600160b01b8360018111156131975761319761516e565b02179055506080820151815460a084015160c08501511515600160c81b0260ff60c81b19911515600160c01b0260ff60c01b19941515600160b81b029490941661ffff60b81b1990931692909217929092179190911617815560e08201516001820180546001600160a01b039092166001600160a01b03199092169190911790556101008201516002820155610120820151600382015561014082015160048201556101609091015160059091015561324d3390565b6001600160a01b03166001600160481b0384167f326dd3d01fb14c6741ab639ba7c102fa77bcb1511cdd2d4bbabfce92acda2293878585896132926020850185615301565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132f39190615d4a565b604051613304959493929190615dd1565b60405180910390a3505092915050565b806101f481111561333857604051634ec4810560e11b815260040160405180910390fd5b60005b8181101561347e57600084848381811061335757613357615a20565b905060200201602081019061336c9190615301565b6001600160a01b03160361339657604051632070666960e11b8152600481018290526024016113f2565b6001600160481b0385166000908152600660205260408120908585848181106133c1576133c1615a20565b90506020020160208101906133d69190615301565b6001600160a01b031681526020810191909152604001600020541561341157604051633bda1e3d60e21b8152600481018290526024016113f2565b61341c816001615a5f565b6001600160481b03861660009081526006602052604081209086868581811061344757613447615a20565b905060200201602081019061345c9190615301565b6001600160a01b0316815260208101919091526040016000205560010161333b565b6001600160481b03851660009081526007602052604090206134a1908585615001565b50846001600160481b03167f37343902ee9028104eff4525c5f797b1e85c580c9345605e81a4d2a9caf18220836040516134dd91815260200190565b60405180910390a25050505050565b6002546001600160a01b0316331461158c5760405163118cdaa760e01b81523360048201526024016113f2565b80156135645781841061353f57604051630e0d5b9360e21b815260040160405180910390fd5b81831061355f57604051631a38ee6360e21b815260040160405180910390fd5b6135b8565b83158061357057508184115b1561358e57604051630e0d5b9360e21b815260040160405180910390fd5b82158061359a57508183115b156135b857604051631a38ee6360e21b815260040160405180910390fd5b828411156135d957604051630e0d5b9360e21b815260040160405180910390fd5b6101f46135e68585615a4c565b6135f1906001615a5f565b1115610e24576040516328e2aa3760e01b815260040160405180910390fd5b60008080808580158061362e5750600854600160f81b900460ff1681115b1561364c57604051634ec4810560e11b815260040160405180910390fd5b6001600160a01b0389166000908152600b6020526040902054600160a01b900460ff1680801561367a575086155b15613698576040516346e69cdf60e01b815260040160405180910390fd5b60005b8281101561381757368a8a838181106136b6576136b6615a20565b60600291909101915060009050600a816136d360208501856152e6565b6001600160481b031681526020808201929092526040908101600090812080546001600160a01b03168252600d909352205490915060ff16156137295760405163ea78d96b60e01b815260040160405180910390fd5b60008154600160a01b900460ff1660018111156137485761374861516e565b036137665760405163e393f90760e01b815260040160405180910390fd5b60006137728e846146c4565b825490915061379b906001600160a01b03811690602086013590600160b01b900460ff16613fdd565b6137a59088615a5f565b96506137b560208401358b615a5f565b995084806137cb57508154600160c81b900460ff165b156137ff578a6137ee576040516346e69cdf60e01b815260040160405180910390fd5b6137f88189615a5f565b975061380c565b613809818a615a5f565b98505b50505060010161369b565b505050945094509450949050565b60006138376060830160408401615bc0565b1561385557604051630f83d91360e41b815260040160405180910390fd5b60808201351580613868575060a0820135155b156138865760405163162908e360e11b815260040160405180910390fd5b600880546014906138a690600160a01b90046001600160481b0316615ceb565b91906101000a8154816001600160481b0302191690836001600160481b03160217905590506040518061018001604052806138de3390565b6001600160a01b031681526020016000815260200160008152602001600081526020018360200160208101906139149190615bc0565b1515815260200161392b6080850160608601615bc0565b1515815260200160001515815260200183600001602081019061394e9190615301565b6001600160a01b0390811682526080808601356020808501919091526000604080860182905260a08901356060870152929094018490526001600160481b0386168452600a815292208351815492166001600160a01b0319831681178255928401519092909183916001600160a81b031990911617600160a01b8360018111156139da576139da61516e565b021790555060408201518154829060ff60a81b1916600160a81b836002811115613a0657613a0661516e565b021790555060608201518154829060ff60b01b1916600160b01b836001811115613a3257613a3261516e565b02179055506080820151815460a084015160c08501511515600160c81b0260ff60c81b19911515600160c01b0260ff60c01b19941515600160b81b029490941661ffff60b81b1990931692909217929092179190911617815560e08201516001820180546001600160a01b039092166001600160a01b031990921691909117905561010082015160028201556101208201516003820155610140820151600482015561016090910151600590910155613ae83390565b6001600160a01b03166001600160481b0382167f6e3c6fc53ebb509f549d0e6a6d92ac1dcd83ad26ad91db192c5ccbe99933e2a284613b2a6020820182615301565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613b67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b8b9190615d4a565b604051613b99929190615e10565b60405180910390a3919050565b6001600160a01b038082166000908152600b6020526040902054168015613c505760095460009061271090613bdf9061ffff1686615d11565b613be99190615d28565b9050613bf58185615a4c565b9350613c018282612caa565b816001600160a01b0316836001600160a01b03167ff9a36a9a40d6dd2811cf7906ec1fc2c05e04b53ce1c9ac8a4e5eb1826abfa6d083604051613c4691815260200190565b60405180910390a3505b600854613c66906001600160a01b031684613cf6565b6040518381527f39eb33dd67e27a3c5bc9a2cf81ba2465d1908cf85a5f927589bb61e4bc7ce6c490602001611df4565b613c9e612e4f565b6002805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258613cd93390565b6040516001600160a01b03909116815260200160405180910390a1565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613d43576040519150601f19603f3d011682016040523d82523d6000602084013e613d48565b606091505b5050905080610ad457604051633d2cec6f60e21b815260040160405180910390fd5b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b613dc581612dfc565b6001600160481b0381166000908152600a60205260409020546001600160a01b03163314612c7d576040516282b42960e81b815260040160405180910390fd5b6060613e327f00000000000000000000000000000000000000000000000000000000000000006000614952565b905090565b6060613e327f00000000000000000000000000000000000000000000000000000000000000006001614952565b60008082801580613e805750600854600160f81b900460ff1681115b15613e9e57604051634ec4810560e11b815260040160405180910390fd5b60005b81811015613fd35736868683818110613ebc57613ebc615a20565b606002919091019150613ed79050610b6e60208301836152e6565b6000600a81613ee960208501856152e6565b6001600160481b031681526020808201929092526040908101600090812080546001600160a01b03168252600d909352205490915060ff1615613f3f5760405163ea78d96b60e01b815260040160405180910390fd5b60018154600160a01b900460ff166001811115613f5e57613f5e61516e565b03613f7c5760405163e393f90760e01b815260040160405180910390fd5b6000613f888a846146c4565b9050613fa83383546001600160a01b038d811692911660208701356149fd565b613fb6602084013588615a5f565b9650613fc28187615a5f565b95505060019092019150613ea19050565b5050935093915050565b600061271081836001811115613ff557613ff561516e565b1461400c57600854600160f01b900460ff1661401a565b600854600160e81b900460ff165b6140279060ff1685615d11565b6140319190615d28565b9050610f9e846140418386615a4c565b612caa565b61404e614a36565b6002805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33613cd9565b8061408c81612dd0565b6001600160481b0384166000908152600760205260408120805490915b8381101561447a5760008686838181106140c5576140c5615a20565b6140db9260206040909202019081019150615301565b6001600160a01b03160361410557604051632070666960e11b8152600481018290526024016113f2565b85858281811061411757614117615a20565b905060400201602001602081019061412f9190615bc0565b15614279576001600160481b03871660009081526006602052604081209087878481811061415f5761415f615a20565b6141759260206040909202019081019150615301565b6001600160a01b03168152602081019190915260400160002054156141b057604051633bda1e3d60e21b8152600481018290526024016113f2565b6141b982615ad4565b6001600160481b03881660009081526006602052604081209193508391908888858181106141e9576141e9615a20565b6141ff9260206040909202019081019150615301565b6001600160a01b031681526020810191909152604001600020558286868381811061422c5761422c615a20565b6142429260206040909202019081019150615301565b81546001810183556000928352602090922090910180546001600160a01b0319166001600160a01b03909216919091179055614472565b6001600160481b0387166000908152600660205260408120818888858181106142a4576142a4615a20565b6142ba9260206040909202019081019150615301565b6001600160a01b03166001600160a01b0316815260200190815260200160002054905080600003614301576040516347d1183960e01b8152600481018390526024016113f2565b60008461430f600186615a4c565b8154811061431f5761431f615a20565b6000918252602090912001546001600160a01b031690508088888581811061434957614349615a20565b61435f9260206040909202019081019150615301565b6001600160a01b0316146143d457808561437a600185615a4c565b8154811061438a5761438a615a20565b600091825260208083209190910180546001600160a01b0319166001600160a01b039485161790556001600160481b038c1682526006815260408083209385168352929052208290555b6001600160481b0389166000908152600660205260408120818a8a878181106143ff576143ff615a20565b6144159260206040909202019081019150615301565b6001600160a01b03168152602081019190915260400160002055845485908061444057614440615e2e565b600082815260209020810160001990810180546001600160a01b03191690550190558361446c81615e44565b94505050505b6001016140a9565b866001600160481b03167f37343902ee9028104eff4525c5f797b1e85c580c9345605e81a4d2a9caf18220836040516144b591815260200190565b60405180910390a250505050505050565b60006144db6001600160a01b03841683614a60565b905080516000141580156145005750808060200190518101906144fe9190615e5b565b155b15610ad457604051635274afe760e01b81526001600160a01b03841660048201526024016113f2565b4381101561454a5760405163df4cc36d60e01b815260040160405180910390fd5b600554845160208601206145a7916001600160a01b03169061456b90614a6e565b85858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250614a9b92505050565b610e2457604051638baa579f60e01b815260040160405180910390fd5b6040516370a0823160e01b815230600482015260009081906001600160a01b038516906370a0823190602401602060405180830381865afa15801561460d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146319190615e78565b90506146486001600160a01b0385163330866149fd565b6040516370a0823160e01b815230600482015281906001600160a01b038616906370a0823190602401602060405180830381865afa15801561468e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146b29190615e78565b6146bc9190615a4c565b949350505050565b600080600a816146d760208601866152e6565b6001600160481b031681526020810191909152604001600090812091508154600160a81b900460ff1660028111156147115761471161516e565b14158061472e575060018101546001600160a01b03858116911614155b1561474c5760405163d9b9141960e01b815260040160405180910390fd5b61477761475c60208501856152e6565b6001600160481b031660009081526007602052604090205490565b15801590614793575061479133610f3f60208601866152e6565b155b156147b0576040516282b42960e81b815260040160405180910390fd5b82602001356000036147d55760405163162908e360e11b815260040160405180910390fd5b82604001358160040154146147fd576040516346e69cdf60e01b815260040160405180910390fd5b6000816003015482600201546148139190615a4c565b905060008260050154836004015461482b9190615a4c565b8354909150600160b81b900460ff1661484a5784602001358110614853565b80856020013514155b15614871576040516329e89cc960e11b815260040160405180910390fd5b8085602001350361489757825460ff60a81b1916600160a81b17835590925082906148b3565b806148a6602087013584615d11565b6148b09190615d28565b93505b838360030160008282546148c79190615a5f565b9250508190555084602001358360050160008282546148e69190615a5f565b909155503390506148fa60208701876152e6565b6001600160481b03167fdf3eb0906a8cde45401490bca800772256dfd4f6ee94b6280de698c15499b578876020013587604051614941929190918252602082015260400190565b60405180910390a350505092915050565b606060ff831461496c5761496583614afd565b9050610d10565b81805461497890615e91565b80601f01602080910402602001604051908101604052809291908181526020018280546149a490615e91565b80156149f15780601f106149c6576101008083540402835291602001916149f1565b820191906000526020600020905b8154815290600101906020018083116149d457829003601f168201915b50505050509050610d10565b6040516001600160a01b038481166024830152838116604483015260648201839052610e249186918216906323b872dd90608401612d9e565b600254600160a01b900460ff1661158c57604051638dfc202b60e01b815260040160405180910390fd5b6060610f9e83836000614b3c565b6000610d10614a7b614bcf565b8360405161190160f01b8152600281019290925260228201526042902090565b6000806000614aaa8585614cfa565b5090925090506000816003811115614ac457614ac461516e565b148015614ae25750856001600160a01b0316826001600160a01b0316145b80614af35750614af3868686614d47565b9695505050505050565b60606000614b0a83614e22565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b606081471015614b615760405163cd78605960e01b81523060048201526024016113f2565b600080856001600160a01b03168486604051614b7d9190615ec5565b60006040518083038185875af1925050503d8060008114614bba576040519150601f19603f3d011682016040523d82523d6000602084013e614bbf565b606091505b5091509150614af3868383614e4a565b6000306001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016148015614c2857507f000000000000000000000000000000000000000000000000000000000000000046145b15614c5257507f000000000000000000000000000000000000000000000000000000000000000090565b613e32604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b60008060008351604103614d345760208401516040850151606086015160001a614d2688828585614ea6565b955095509550505050614d40565b50508151600091506002905b9250925092565b6000806000856001600160a01b03168585604051602401614d69929190615ee1565b60408051601f198184030181529181526020820180516001600160e01b0316630b135d3f60e11b17905251614d9e9190615ec5565b600060405180830381855afa9150503d8060008114614dd9576040519150601f19603f3d011682016040523d82523d6000602084013e614dde565b606091505b5091509150818015614df257506020815110155b8015614af357508051630b135d3f60e11b90614e179083016020908101908401615e78565b149695505050505050565b600060ff8216601f811115610d1057604051632cd44ac360e21b815260040160405180910390fd5b606082614e5f57614e5a82614f75565b610f9e565b8151158015614e7657506001600160a01b0384163b155b15614e9f57604051639996b31560e01b81526001600160a01b03851660048201526024016113f2565b5080610f9e565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115614ee15750600091506003905082614f6b565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015614f35573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116614f6157506000925060019150829050614f6b565b9250600091508190505b9450945094915050565b805115614f855780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b604080516101808101909152600080825260208201908152602001600081526020016000815260006020820181905260408201819052606082018190526080820181905260a0820181905260c0820181905260e082018190526101009091015290565b828054828255906000526020600020908101928215615054579160200282015b828111156150545781546001600160a01b0319166001600160a01b03843516178255602090920191600190910190615021565b50615060929150615064565b5090565b5b808211156150605760008155600101615065565b6001600160a01b0381168114612c7d57600080fd5b60008083601f8401126150a057600080fd5b5081356001600160401b038111156150b757600080fd5b6020830191508360208260051b85010111156150d257600080fd5b9250929050565b6000806000604084860312156150ee57600080fd5b83356150f981615079565b925060208401356001600160401b0381111561511457600080fd5b6151208682870161508e565b9497909650939450505050565b6000806020838503121561514057600080fd5b82356001600160401b0381111561515657600080fd5b6151628582860161508e565b90969095509350505050565b634e487b7160e01b600052602160045260246000fd5b60028110612c7d57612c7d61516e565b61519d81615184565b9052565b6003811061519d5761519d61516e565b80516001600160a01b0316825260208101516151d06020840182615194565b5060408101516151e360408401826151a1565b5060608101516151f66060840182615194565b50608081015161520a608084018215159052565b5060a081015161521e60a084018215159052565b5060c081015161523260c084018215159052565b5060e081015161524d60e08401826001600160a01b03169052565b5061010081810151908301526101208082015190830152610140808201519083015261016090810151910152565b6020808252825182820181905260009190848201906040850190845b818110156152be576152aa8385516151b1565b928401926101809290920191600101615297565b50909695505050505050565b80356001600160481b03811681146152e157600080fd5b919050565b6000602082840312156152f857600080fd5b610f9e826152ca565b60006020828403121561531357600080fd5b8135610f9e81615079565b600060c0828403121561533057600080fd5b50919050565b60006060828403121561533057600080fd5b600080600080610100858703121561535f57600080fd5b615369868661531e565b935060c08501356001600160401b038082111561538557600080fd5b6153918883890161508e565b909550935060e08701359150808211156153aa57600080fd5b506153b787828801615336565b91505092959194509250565b6020808252825182820181905260009190848201906040850190845b818110156152be5783511515835292840192918401916001016153df565b60ff81168114612c7d57600080fd5b60006020828403121561541e57600080fd5b8135610f9e816153fd565b6000806040838503121561543c57600080fd5b50508035926020909101359150565b8015158114612c7d57600080fd5b6000806040838503121561546c57600080fd5b823561547781615079565b915060208301356154878161544b565b809150509250929050565b60008083601f8401126154a457600080fd5b5081356001600160401b038111156154bb57600080fd5b6020830191508360206060830285010111156150d257600080fd5b803561ffff811681146152e157600080fd5b600080600080600080610140878903121561550257600080fd5b863561550d81615079565b955060208701356001600160401b0381111561552857600080fd5b61553489828a01615492565b90965094506155489050886040890161531e565b92506101008701356155598161544b565b915061556861012088016154d6565b90509295509295509295565b60008083601f84011261558657600080fd5b5081356001600160401b0381111561559d57600080fd5b6020830191508360208260061b85010111156150d257600080fd5b6000806000604084860312156155cd57600080fd5b83356155d881615079565b925060208401356001600160401b038111156155f357600080fd5b61512086828701615574565b60006020828403121561561157600080fd5b610f9e826154d6565b602080825282518282018190526000919060409081850190868401855b8281101561568357815180516001600160a01b03168552868101516001600160581b03168786015285810151868601526060908101519085015260809093019290850190600101615637565b5091979650505050505050565b60008082840360608112156156a457600080fd5b83356156af81615079565b92506040601f19820112156156c357600080fd5b506020830190509250929050565b6000806000606084860312156156e657600080fd5b83356156f1816153fd565b92506020840135615701816153fd565b915061570f604085016154d6565b90509250925092565b6000806040838503121561572b57600080fd5b615477836152ca565b60005b8381101561574f578181015183820152602001615737565b50506000910152565b60008151808452615770816020860160208601615734565b601f01601f19169290920160200192915050565b60ff60f81b881681526000602060e0818401526157a460e084018a615758565b83810360408501526157b6818a615758565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b81811015615808578351835292840192918401916001016157ec565b50909c9b505050505050505050505050565b6000806000806000610120868803121561583357600080fd5b853561583e81615079565b945060208601356001600160401b038082111561585a57600080fd5b61586689838a01615492565b909650945084915061587b8960408a0161531e565b935061010088013591508082111561589257600080fd5b5061589f88828901615336565b9150509295509295909350565b6000806000606084860312156158c157600080fd5b6158ca846152ca565b95602085013595506040909401359392505050565b6020808252825182820181905260009190848201906040850190845b818110156152be5783516001600160a01b0316835292840192918401916001016158fb565b6000806000806060858703121561593657600080fd5b843561594181615079565b9350602085013561595181615079565b925060408501356001600160401b0381111561596c57600080fd5b61597887828801615574565b95989497509550505050565b600080600060e0848603121561599957600080fd5b6159a3858561531e565b925060c08401356001600160401b0381111561511457600080fd5b600080604083850312156159d157600080fd5b82356159dc81615079565b91506159ea602084016152ca565b90509250929050565b600080600060408486031215615a0857600080fd5b6155d8846152ca565b6101808101610d1082846151b1565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115610d1057610d10615a36565b80820180821115610d1057610d10615a36565b60208082528181018390526000908460408401835b86811015615ab3576001600160481b03615aa0846152ca565b1682529183019190830190600101615a87565b509695505050505050565b634e487b7160e01b600052604160045260246000fd5b600060018201615ae657615ae6615a36565b5060010190565b8183526000602080850194508260005b85811015615b3d576001600160481b03615b16836152ca565b16875281830135838801526040808301359088015260609687019690910190600101615afd565b509495945050505050565b608081526000615b5c608083018789615aed565b602083019590955250604081019290925260609091015292915050565b6020808252818101839052600090604080840186845b87811015615bb3578135835284820135858401529183019190830190600101615b8f565b5090979650505050505050565b600060208284031215615bd257600080fd5b8135610f9e8161544b565b604081018235615bec81615079565b6001600160a01b031682526020830135615c058161544b565b80151560208401525092915050565b608081526000615c28608083018789615aed565b9050846020830152836040830152615c3f83615184565b8260608301529695505050505050565b6000808335601e19843603018112615c6657600080fd5b8301803591506001600160401b03821115615c8057600080fd5b6020019150368190038213156150d257600080fd5b600060208284031215615ca757600080fd5b813560028110610f9e57600080fd5b8481526001600160a01b03841660208201526040810183905260808101615cdc83615184565b82606083015295945050505050565b60006001600160481b03808316818103615d0757615d07615a36565b6001019392505050565b8082028115828204841417610d1057610d10615a36565b600082615d4557634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215615d5c57600080fd5b8151610f9e816153fd565b8035615d7281615079565b6001600160a01b031682526020810135615d8b8161544b565b151560208301526040810135615da08161544b565b151560408301526060810135615db58161544b565b151560608301526080818101359083015260a090810135910152565b6101408101615de08288615d67565b8560c08301528460e0830152615df584615184565b8361010083015260ff83166101208301529695505050505050565b60e08101615e1e8285615d67565b60ff831660c08301529392505050565b634e487b7160e01b600052603160045260246000fd5b600081615e5357615e53615a36565b506000190190565b600060208284031215615e6d57600080fd5b8151610f9e8161544b565b600060208284031215615e8a57600080fd5b5051919050565b600181811c90821680615ea557607f821691505b60208210810361533057634e487b7160e01b600052602260045260246000fd5b60008251615ed7818460208701615734565b9190910192915050565b8281526040602082015260006146bc604083018461575856fea264697066735822122008685fbde8c882c35e4508862eb5f6d89c93e2cbdaf8638e5427ad0b777e2b4364736f6c63430008140033000000000000000000000000f28072b734c19e573d4cdc090c21edef19f10c66000000000000000000000000a5e43669cfaac8f834c3ab419a2092a78a096dde00000000000000000000000084cf61259efb32244258c0ab910ab4f11e13842b
Deployed Bytecode
0x6080604052600436106102725760003560e01c806372ea9ce31161014f578063b54d321d116100c1578063db8d55f11161007a578063db8d55f1146107e1578063e392cc5014610827578063e97f42ec14610847578063edb6fb6614610867578063f2fde38b14610894578063fef11087146108b457600080fd5b8063b54d321d146106fb578063b58b90cf14610728578063c0c2e31f14610748578063c90b58f21461075b578063cccd7cbb1461077b578063d50f6d3a1461079b57600080fd5b80638da5cb5b116101135780638da5cb5b1461063b5780639f1a8de314610659578063a061772314610679578063a968437214610699578063abc37382146106c6578063b33712c5146106e657600080fd5b806372ea9ce314610585578063773c3645146105a5578063833ee422146105c557806384b0196e146105f3578063894bed451461061b57600080fd5b8063375f16a7116101e8578063600e2698116101ac578063600e2698146104c357806362db9e9a146104e357806363975a7c146105035780636c19e78314610530578063715018a6146105505780637187260a1461056557600080fd5b8063375f16a7146104245780633c9c885f1461045c578063439766ce1461046f5780634e4ee182146104845780635c975abb146104a457600080fd5b8063179f9e651161023a578063179f9e651461033f5780631ab0d4581461035f5780631f8f2f661461038c578063238ac933146103ac5780632d004524146103e45780632d5a5d341461040457600080fd5b8063036896c11461027757806305f46092146102995780630bb04b4e146102cf5780630f1d76c1146102ff57806317764d5b1461031f575b600080fd5b34801561028357600080fd5b506102976102923660046150d9565b6108e0565b005b3480156102a557600080fd5b506102b96102b436600461512d565b610ad9565b6040516102c6919061527b565b60405180910390f35b3480156102db57600080fd5b506102ef6102ea3660046152e6565b610d16565b60405190151581526020016102c6565b34801561030b57600080fd5b506102ef61031a366004615301565b610d56565b34801561032b57600080fd5b5061029761033a366004615348565b610d9e565b34801561034b57600080fd5b5061029761035a366004615301565b610e2a565b34801561036b57600080fd5b5061037f61037a3660046150d9565b610e7d565b6040516102c691906153c3565b34801561039857600080fd5b506102976103a736600461540c565b610fa5565b3480156103b857600080fd5b506005546103cc906001600160a01b031681565b6040516001600160a01b0390911681526020016102c6565b3480156103f057600080fd5b506102b96103ff366004615429565b611035565b34801561041057600080fd5b5061029761041f366004615459565b611251565b34801561043057600080fd5b50600854600160a01b90046001600160481b03166040516001600160481b0390911681526020016102c6565b61029761046a3660046154e8565b611321565b34801561047b57600080fd5b5061029761157c565b34801561049057600080fd5b5061029761049f3660046155b8565b61158e565b3480156104b057600080fd5b50600254600160a01b900460ff166102ef565b3480156104cf57600080fd5b506102976104de366004615301565b6117ca565b3480156104ef57600080fd5b506102976104fe3660046155ff565b611888565b34801561050f57600080fd5b5061052361051e3660046150d9565b611914565b6040516102c6919061561a565b34801561053c57600080fd5b5061029761054b366004615301565b611aa9565b34801561055c57600080fd5b50610297611b26565b34801561057157600080fd5b50610297610580366004615690565b611b38565b34801561059157600080fd5b506102976105a03660046156d1565b611cf3565b3480156105b157600080fd5b506102976105c0366004615718565b611e01565b3480156105d157600080fd5b506105e56105e0366004615301565b611f26565b6040519081526020016102c6565b3480156105ff57600080fd5b50610608611f6b565b6040516102c69796959493929190615784565b34801561062757600080fd5b5061029761063636600461512d565b611fb1565b34801561064757600080fd5b506002546001600160a01b03166103cc565b34801561066557600080fd5b5061029761067436600461581a565b612180565b34801561068557600080fd5b50610297610694366004615459565b61226a565b3480156106a557600080fd5b506106b96106b43660046158ac565b612380565b6040516102c691906158df565b3480156106d257600080fd5b506102976106e13660046158ac565b6124ad565b3480156106f257600080fd5b506102976125d1565b34801561070757600080fd5b5060095462010000900461ffff1660405161ffff90911681526020016102c6565b34801561073457600080fd5b50610297610743366004615920565b6125e1565b610297610756366004615984565b61288d565b34801561076757600080fd5b506105e56107763660046152e6565b61291b565b34801561078757600080fd5b506102ef6107963660046159be565b612944565b3480156107a757600080fd5b506107bb6107b6366004615301565b6129aa565b6040805182516001600160a01b03168152602092830151151592810192909252016102c6565b3480156107ed57600080fd5b506008546009546040805160ff600160e81b850481168252600160f01b909404909316602084015261ffff909116908201526060016102c6565b34801561083357600080fd5b506102976108423660046159f3565b612a28565b34801561085357600080fd5b506105e5610862366004615301565b612aa1565b34801561087357600080fd5b506108876108823660046152e6565b612ae6565b6040516102c69190615a11565b3480156108a057600080fd5b506102976108af366004615301565b612c42565b3480156108c057600080fd5b50600854600160f81b900460ff1660405160ff90911681526020016102c6565b6108e8612c80565b808015806108f65750606481115b1561091457604051634ec4810560e11b815260040160405180910390fd5b60008060005b83811015610a63576000600a600088888581811061093a5761093a615a20565b905060200201602081019061094f91906152e6565b6001600160481b03168152602081019190915260400160002080549091506001600160a01b03163314610994576040516282b42960e81b815260040160405180910390fd5b60008154600160a81b900460ff1660028111156109b3576109b361516e565b1415806109d0575060018101546001600160a01b03898116911614155b156109ee5760405163d9b9141960e01b815260040160405180910390fd5b805460ff60a81b1916600160a91b17815560038101546002820154600091610a1591615a4c565b905060008254600160a01b900460ff166001811115610a3657610a3661516e565b03610a4c57610a458186615a5f565b9450610a59565b610a568185615a5f565b93505b505060010161091a565b8215610a7357610a733384612caa565b8115610a8d57610a8d6001600160a01b0388163384612d71565b7f52750d3a44c06a55b289a1181dc2173d8dd600646ca5c810070636c93b47b2598686604051610abe929190615a72565b60405180910390a150505050610ad46001600355565b505050565b606081610ae581612dd0565b806001600160401b03811115610afd57610afd615abe565b604051908082528060200260200182016040528015610b3657816020015b610b23614f9e565b815260200190600190039081610b1b5790505b50915060005b81811015610d0d57610b73858583818110610b5957610b59615a20565b9050602002016020810190610b6e91906152e6565b612dfc565b600a6000868684818110610b8957610b89615a20565b9050602002016020810190610b9e91906152e6565b6001600160481b0316815260208082019290925260409081016000208151610180810190925280546001600160a01b03811683529192909190830190600160a01b900460ff166001811115610bf557610bf561516e565b6001811115610c0657610c0661516e565b81528154602090910190600160a81b900460ff166002811115610c2b57610c2b61516e565b6002811115610c3c57610c3c61516e565b81528154602090910190600160b01b900460ff166001811115610c6157610c6161516e565b6001811115610c7257610c7261516e565b8152815460ff600160b81b8204811615156020840152600160c01b8204811615156040840152600160c81b909104161515606082015260018201546001600160a01b03166080820152600282015460a0820152600382015460c0820152600482015460e0820152600590910154610100909101528351849083908110610cfa57610cfa615a20565b6020908102919091010152600101610b3c565b50505b92915050565b6000610d2182612dfc565b506001600160481b03166000908152600a60209081526040808320546001600160a01b03168352600d90915290205460ff1690565b60006001600160a01b038216610d7f5760405163e6c4247b60e01b815260040160405180910390fd5b506001600160a01b03166000908152600d602052604090205460ff1690565b610da6612c80565b610dae612e4f565b610db6612e7a565b6000610dc56020860186615301565b6001600160a01b031603610dec5760405163e6c4247b60e01b815260040160405180910390fd5b6000610df782612eaa565b90506000610e058683612f4a565b90508315610e1857610e18818686613314565b5050610e246001600355565b50505050565b610e326134ec565b6008546001600160a01b0316610e5b5760405163e6c4247b60e01b815260040160405180910390fd5b600880546001600160a01b0319166001600160a01b0392909216919091179055565b606081610e8981612dd0565b6001600160a01b038516610eb05760405163e6c4247b60e01b815260040160405180910390fd5b806001600160401b03811115610ec857610ec8615abe565b604051908082528060200260200182016040528015610ef1578160200160208202803683370190505b50915060005b81811015610f9b57610f14858583818110610b5957610b59615a20565b610f7186868684818110610f2a57610f2a615a20565b9050602002016020810190610f3f91906152e6565b6001600160481b031660009081526006602090815260408083206001600160a01b039490941683529290522054151590565b838281518110610f8357610f83615a20565b91151560209283029190910190910152600101610ef7565b50505b9392505050565b610fad6134ec565b60ff81161580610fc0575060ff81166064105b15610fde5760405163162908e360e11b815260040160405180910390fd5b600880546001600160f81b0316600160f81b60ff8416908102919091179091556040519081527f1a1640c56c8652d27a565b95b6aaf9168cf7f190203dba9f4a6b769b563696bf906020015b60405180910390a150565b60085460609083908390600160a01b90046001600160481b0316600061105d84848484613519565b6110678787615a4c565b611072906001615a5f565b6001600160401b0381111561108957611089615abe565b6040519080825280602002602001820160405280156110c257816020015b6110af614f9e565b8152602001906001900390816110a75790505b5094506000875b878111611245576001600160481b0381166000908152600a60209081526040918290208251610180810190935280546001600160a01b03811684529091830190600160a01b900460ff1660018111156111245761112461516e565b60018111156111355761113561516e565b81528154602090910190600160a81b900460ff16600281111561115a5761115a61516e565b600281111561116b5761116b61516e565b81528154602090910190600160b01b900460ff1660018111156111905761119061516e565b60018111156111a1576111a161516e565b8152815460ff600160b81b8204811615156020840152600160c01b8204811615156040840152600160c81b909104161515606082015260018201546001600160a01b03166080820152600282015460a0820152600382015460c0820152600482015460e082015260059091015461010090910152878361122081615ad4565b94508151811061123257611232615a20565b60209081029190910101526001016110c9565b50505050505092915050565b6112596134ec565b6001600160a01b0382166112805760405163e6c4247b60e01b815260040160405180910390fd5b6001600160a01b0382166000908152600d602052604090205481151560ff9091161515036112c15760405163cf7071eb60e01b815260040160405180910390fd5b6001600160a01b0382166000818152600d6020908152604091829020805460ff191685151590811790915591519182527f6a12b3df6cba4203bd7fd06b816789f87de8c594299aed5717ae070fac781bac91015b60405180910390a25050565b611329612c80565b611331612e4f565b611339612e7a565b818015611355575060095461ffff828116620100009092041614155b15611373576040516346e69cdf60e01b815260040160405180910390fd5b6000806000806113858a8a8a89613610565b929650909450925090506001600160a01b038a166113a66020890189615301565b6001600160a01b03160361140a57346113c3608089013586615a5f565b146113fb576113d6608088013585615a5f565b604051634c90b39760e01b81526004016113f291815260200190565b60405180910390fd5b61140487613825565b5061142d565b34841461142d57604051634c90b39760e01b8152600481018590526024016113f2565b611437818b613ba6565b81156114f957336000908152600c60209081526040918290208251608081019093526001600160a01b038d1683526009549092918201906114829062010000900461ffff1642615a5f565b6001600160581b039081168252602080830187905260006040938401819052855460018181018855968252908290208551600390920201805492860151909316600160a01b026001600160f81b03199092166001600160a01b03909116171781559082015192810192909255606001516002909101555b8215611513576115136001600160a01b038b163385612d71565b6001600160a01b038a16336001600160a01b03167fa8d4cc0a1ef01dbc9440131ad91c9c8b1e907863fce25d6184ec791399c1c39f8b8b88888860405161155e959493929190615b48565b60405180910390a3505050506115746001600355565b505050505050565b6115846134ec565b61158c613c96565b565b336000908152600c6020526040812054908190036115bf5760405163d9b9141960e01b815260040160405180910390fd5b816115c981612dd0565b60005b8181101561177557368585838181106115e7576115e7615a20565b9050604002019050806000013584116116165760405163042a2e7160e11b8152600481018390526024016113f2565b336000908152600c602052604081208054833590811061163857611638615a20565b600091825260209182902060408051608081018252600390930290910180546001600160a01b03811684526001600160581b03600160a01b9091041693830184905260018101549183019190915260020154606082015291504210156116b4576040516335f8469160e21b8152600481018490526024016113f2565b6000816060015182604001516116ca9190615a4c565b9050602083013515806116e05750826020013581105b156117015760405163399583bd60e01b8152600481018590526024016113f2565b336000908152600c6020908152604090912080549185013591853590811061172b5761172b615a20565b9060005260206000209060030201600201600082825461174b9190615a5f565b9091555050815161176a906001600160a01b03168a6020860135612d71565b5050506001016115cc565b506001600160a01b038516336001600160a01b03167f967cf83411738666f908e59cf8f6c2f53af7dbff325903ba77b116068b23cb8d86866040516117bb929190615b79565b60405180910390a35050505050565b6001600160a01b0381166117f15760405163e6c4247b60e01b815260040160405180910390fd5b33600090815260046020526040812054908190036118225760405163d9b9141960e01b815260040160405180910390fd5b3360009081526004602052604081205561183c8282613cf6565b604080513381526001600160a01b038416602082015280820183905290517f60e82ffc0c9ca018da948ff9a0e7d95b104367e847eae1ec57d7c9997380ce7f9181900360600190a15050565b6118906134ec565b603c61ffff821610806118a8575061ffff8116610e10105b156118c65760405163162908e360e11b815260040160405180910390fd5b6009805463ffff000019166201000061ffff8416908102919091179091556040519081527fa7664047213e9c77c1932f56213b16ca81ed5e3fd4d2f2a99025b5215ba34d689060200161102a565b6060600061192185611f26565b90508261192d81612dd0565b806001600160401b0381111561194557611945615abe565b60405190808252806020026020018201604052801561199757816020015b6040805160808101825260008082526020808301829052928201819052606082015282526000199092019101816119635790505b50925060005b81811015611a9f578585828181106119b7576119b7615a20565b9050602002013583116119e05760405163042a2e7160e11b8152600481018290526024016113f2565b6001600160a01b0387166000908152600c60205260409020868683818110611a0a57611a0a615a20565b9050602002013581548110611a2157611a21615a20565b600091825260209182902060408051608081018252600390930290910180546001600160a01b03811684526001600160581b03600160a01b909104169383019390935260018301549082015260029091015460608201528451859083908110611a8c57611a8c615a20565b602090810291909101015260010161199d565b5050509392505050565b611ab16134ec565b6001600160a01b038116611ad85760405163e6c4247b60e01b815260040160405180910390fd5b600580546001600160a01b0319166001600160a01b0383169081179091556040519081527f5553331329228fbd4123164423717a4a7539f6dfa1c3279a923b98fd681a6c739060200161102a565b611b2e6134ec565b61158c6000613d6a565b611b406134ec565b6001600160a01b038216611b675760405163e6c4247b60e01b815260040160405180910390fd5b611b746020820182615301565b6001600160a01b038381166000908152600b602052604090205481169116148015611bd55750611baa6040820160208301615bc0565b6001600160a01b0383166000908152600b602052604090205460ff600160a01b909104161515901515145b15611bf35760405163cf7071eb60e01b815260040160405180910390fd5b6000611c026020830183615301565b6001600160a01b0316148015611c235750611c236040820160208301615bc0565b15611c415760405163d9b9141960e01b815260040160405180910390fd5b6040805180820190915280611c596020840184615301565b6001600160a01b03168152602001826020016020810190611c7a9190615bc0565b151590526001600160a01b038084166000818152600b60209081526040918290208551815496909201511515600160a01b026001600160a81b031990961691909416179390931790915590517f9ad4c32594afa555f2c0b23876b6843f5d8f10be382a427e133e2ea458a7063a90611315908490615bdd565b611cfb6134ec565b60085460ff808516600160e81b909204161080611d26575060085460ff808416600160f01b90920416105b80611d3657508160ff168360ff16105b80611d4657506103e861ffff8216105b80611d56575061ffff8116611388105b15611d74576040516358d620b360e01b815260040160405180910390fd5b6008805461ffff60e81b1916600160e81b60ff86811691820260ff60f01b191692909217600160f01b928616928302179092556009805461ffff191661ffff8516908117909155604080519384526020840192909252908201527f2f45d24e3adb3752a7e92308eeeeac27a7cdf7dabce4b2136e8cf23f9fcee976906060015b60405180910390a1505050565b81611e0b81613dbc565b611e13612e4f565b611e1b612e7a565b6001600160481b0383166000908152600a60205260408120908154600160a81b900460ff166002811115611e5157611e5161516e565b141580611e7a575060008154600160a01b900460ff166001811115611e7857611e7861516e565b145b15611e985760405163d9b9141960e01b815260040160405180910390fd5b8054831515600160c81b90910460ff16151503611ec85760405163cf7071eb60e01b815260040160405180910390fd5b805460ff60c81b1916600160c81b8415159081029190911782556040519081526001600160481b038516907f5f248b4371e372d4298ec69af6008e7c07e513e14c34377af2a2fc86365e04209060200160405180910390a250505050565b60006001600160a01b038216611f4f5760405163e6c4247b60e01b815260040160405180910390fd5b506001600160a01b03166000908152600c602052604090205490565b600060608060008060006060611f7f613e05565b611f87613e37565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b611fb9612c80565b80801580611fc75750606481115b15611fe557604051634ec4810560e11b815260040160405180910390fd5b6000805b82811015612126576000600a600087878581811061200957612009615a20565b905060200201602081019061201e91906152e6565b6001600160481b03168152602081019190915260400160002080549091506001600160a01b03163314612063576040516282b42960e81b815260040160405180910390fd5b60008154600160a81b900460ff1660028111156120825761208261516e565b146120a05760405163d9b9141960e01b815260040160405180910390fd5b805460ff60a81b1916600160a91b178155600381015460028201546000916120c791615a4c565b905060008254600160a01b900460ff1660018111156120e8576120e861516e565b036120fe576120f78185615a5f565b935061211c565b8154600183015461211c916001600160a01b03918216911683612d71565b5050600101611fe9565b8115612136576121363383612caa565b7f52750d3a44c06a55b289a1181dc2173d8dd600646ca5c810070636c93b47b2598585604051612167929190615a72565b60405180910390a150505061217c6001600355565b5050565b612188612c80565b612190612e4f565b612198612e7a565b60006121a382612eaa565b90506000806121b3888888613e64565b90925090506001600160a01b0388166121cf6020870187615301565b6001600160a01b0316036121e9576121e78584612f4a565b505b60006121f6338386613fdd565b9050612202818a613ba6565b6001600160a01b038916336001600160a01b03167f37564a4a0bdd799a9e768e7ad616dda363f00fbaaf54817f085c55828c93b5138a8a87878a60405161224d959493929190615c14565b60405180910390a3505050506122636001600355565b5050505050565b6001600160a01b038083166000908152600b6020526040902080549091166122a55760405163d9b9141960e01b815260040160405180910390fd5b80546001600160a01b031633148015906122ca57506002546001600160a01b03163314155b156122e7576040516282b42960e81b815260040160405180910390fd5b8054821515600160a01b90910460ff161515036123175760405163cf7071eb60e01b815260040160405180910390fd5b805460ff60a01b1916600160a01b83151502178155336001600160a01b0316836001600160a01b03167f1a44d70251a391917683825fb43ec17afff144817ee6615ed8325d4775c883cf84604051612373911515815260200190565b60405180910390a3505050565b606082826123a3866001600160481b031660009081526007602052604090205490565b60006123b184848484613519565b6123bb8787615a4c565b6123c6906001615a5f565b6001600160401b038111156123dd576123dd615abe565b604051908082528060200260200182016040528015612406578160200160208202803683370190505b50945060008061241760018a615a4c565b90505b878110156124a0576001600160481b038a16600090815260076020526040902080548290811061244c5761244c615a20565b9060005260206000200160009054906101000a90046001600160a01b031687838151811061247c5761247c615a20565b6001600160a01b03909216602092830291909101909101526001918201910161241a565b5050505050509392505050565b826124b781613dbc565b6124bf612e4f565b6124c7612e7a565b6001600160481b0384166000908152600a60205260408120908154600160a81b900460ff1660028111156124fd576124fd61516e565b1461251b5760405163d9b9141960e01b815260040160405180910390fd5b8260000361253c5760405163162908e360e11b815260040160405180910390fd5b83816003015482600201546125519190615a4c565b1461256f576040516346e69cdf60e01b815260040160405180910390fd5b60008382600501546125819190615a5f565b600483018190556040518181529091506001600160481b038716907ffaed25a36cd23e822e1afbf410755f746ffe05ddebc36d2cd844828adcdd89c29060200160405180910390a2505050505050565b6125d96134ec565b61158c614046565b6001600160a01b0384166126085760405163e6c4247b60e01b815260040160405180910390fd5b336000908152600c6020526040812054908190036126395760405163d9b9141960e01b815260040160405180910390fd5b8161264381612dd0565b6000805b82811015612822573686868381811061266257612662615a20565b9050604002019050806000013585116126915760405163042a2e7160e11b8152600481018390526024016113f2565b336000908152600c60205260408120805483359081106126b3576126b3615a20565b60009182526020918290206040805160808101825260039390930290910180546001600160a01b03808216808652600160a01b9092046001600160581b031695850195909552600182015492840192909252600201546060830152909250908b161461273557604051632070666960e11b8152600481018490526024016113f2565b80602001516001600160581b0316421015612766576040516335f8469160e21b8152600481018490526024016113f2565b60008160600151826040015161277c9190615a4c565b9050602083013515806127925750826020013581105b156127b35760405163399583bd60e01b8152600481018590526024016113f2565b336000908152600c602090815260409091208054918501359185359081106127dd576127dd615a20565b906000526020600020906003020160020160008282546127fd9190615a5f565b909155506128119050602084013586615a5f565b945050600190920191506126479050565b506128376001600160a01b0388168783612d71565b6001600160a01b038616336001600160a01b03167f967cf83411738666f908e59cf8f6c2f53af7dbff325903ba77b116068b23cb8d878760405161287c929190615b79565b60405180910390a350505050505050565b612895612e4f565b61289d612e7a565b60006128ac6020850185615301565b6001600160a01b0316036128d35760405163e6c4247b60e01b815260040160405180910390fd5b826080013534146128fd57604051634c90b39760e01b8152608084013560048201526024016113f2565b600061290884613825565b90508115610e2457610e24818484613314565b600061292682612dfc565b6001600160481b038216600090815260076020526040902054610d10565b60006001600160a01b03831661296d5760405163e6c4247b60e01b815260040160405180910390fd5b61297682612dfc565b6001600160481b03821660009081526006602090815260408083206001600160a01b03871684529091529020541515610f9e565b60408051808201909152600080825260208201526001600160a01b0382166129e55760405163e6c4247b60e01b815260040160405180910390fd5b506001600160a01b039081166000908152600b60209081526040918290208251808401909352549283168252600160a01b90920460ff1615159181019190915290565b82612a3281613dbc565b612a3a612e4f565b612a42612e7a565b60006001600160481b0385166000908152600a6020526040902054600160a81b900460ff166002811115612a7857612a7861516e565b14612a965760405163d9b9141960e01b815260040160405180910390fd5b610e24848484614082565b60006001600160a01b038216612aca5760405163e6c4247b60e01b815260040160405180910390fd5b506001600160a01b031660009081526004602052604090205490565b612aee614f9e565b612af782612dfc565b6001600160481b0382166000908152600a60209081526040918290208251610180810190935280546001600160a01b03811684529091830190600160a01b900460ff166001811115612b4b57612b4b61516e565b6001811115612b5c57612b5c61516e565b81528154602090910190600160a81b900460ff166002811115612b8157612b8161516e565b6002811115612b9257612b9261516e565b81528154602090910190600160b01b900460ff166001811115612bb757612bb761516e565b6001811115612bc857612bc861516e565b8152815460ff600160b81b8204811615156020840152600160c01b8204811615156040840152600160c81b909104161515606082015260018201546001600160a01b03166080820152600282015460a0820152600382015460c0820152600482015460e08201526005909101546101009091015292915050565b612c4a6134ec565b6001600160a01b038116612c7457604051631e4fbdf760e01b8152600060048201526024016113f2565b612c7d81613d6a565b50565b600260035403612ca357604051633ee5aeb560e01b815260040160405180910390fd5b6002600355565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114612cf7576040519150601f19603f3d011682016040523d82523d6000602084013e612cfc565b606091505b5050905080610ad4576001600160a01b03831660009081526004602052604081208054849290612d2d908490615a5f565b9091555050604080516001600160a01b0385168152602081018490527f43998e88b0229199fdb7ad21fa2b6f4ac86cc33e98784dd17c5244437afd4e879101611df4565b6040516001600160a01b03838116602483015260448201839052610ad491859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506144c6565b801580612dde57506101f481115b15612c7d57604051634ec4810560e11b815260040160405180910390fd5b6001600160481b0381161580612e2657506008546001600160481b03808316600160a01b90920416105b15612c7d5760405163b092f19760e01b81526001600160481b03821660048201526024016113f2565b600254600160a01b900460ff161561158c5760405163d93c066560e01b815260040160405180910390fd5b336000908152600d602052604090205460ff161561158c576040516282b42960e81b815260040160405180910390fd5b6000612eb68280615c4f565b9050600003612ec457919050565b60007f8d2233fff76359558ed65b9b090b4ad52b4b4c57f4f06d9c177974d3bc10f1c6336020850135612efd6060870160408801615c95565b604051602001612f109493929190615cb6565b60408051601f198184030181529190529050612f3a81612f308580615c4f565b8660200135614529565b610f9e6060840160408501615c95565b600060808301351580612f5f575060a0830135155b15612f7d5760405163162908e360e11b815260040160405180910390fd5b60088054601490612f9d90600160a01b90046001600160481b0316615ceb565b91906101000a8154816001600160481b0302191690836001600160481b03160217905590506000612fe4846000016020810190612fda9190615301565b85608001356145c4565b90506000846080013582146130155760808501356130068360a0880135615d11565b6130109190615d28565b61301b565b8460a001355b90506040518061018001604052806130303390565b6001600160a01b03168152602001600181526020016000815260200185600181111561305e5761305e61516e565b81526020018660200160208101906130769190615bc0565b1515815260200161308d6080880160608901615bc0565b151581526020016130a46060880160408901615bc0565b151581526020908101906130ba90880188615301565b6001600160a01b0390811682526020808301869052600060408085018290526060850187905260809094018190526001600160481b0388168152600a8252929092208351815492166001600160a01b0319831681178255928401519092909183916001600160a81b031990911617600160a01b83600181111561313f5761313f61516e565b021790555060408201518154829060ff60a81b1916600160a81b83600281111561316b5761316b61516e565b021790555060608201518154829060ff60b01b1916600160b01b8360018111156131975761319761516e565b02179055506080820151815460a084015160c08501511515600160c81b0260ff60c81b19911515600160c01b0260ff60c01b19941515600160b81b029490941661ffff60b81b1990931692909217929092179190911617815560e08201516001820180546001600160a01b039092166001600160a01b03199092169190911790556101008201516002820155610120820151600382015561014082015160048201556101609091015160059091015561324d3390565b6001600160a01b03166001600160481b0384167f326dd3d01fb14c6741ab639ba7c102fa77bcb1511cdd2d4bbabfce92acda2293878585896132926020850185615301565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132cf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132f39190615d4a565b604051613304959493929190615dd1565b60405180910390a3505092915050565b806101f481111561333857604051634ec4810560e11b815260040160405180910390fd5b60005b8181101561347e57600084848381811061335757613357615a20565b905060200201602081019061336c9190615301565b6001600160a01b03160361339657604051632070666960e11b8152600481018290526024016113f2565b6001600160481b0385166000908152600660205260408120908585848181106133c1576133c1615a20565b90506020020160208101906133d69190615301565b6001600160a01b031681526020810191909152604001600020541561341157604051633bda1e3d60e21b8152600481018290526024016113f2565b61341c816001615a5f565b6001600160481b03861660009081526006602052604081209086868581811061344757613447615a20565b905060200201602081019061345c9190615301565b6001600160a01b0316815260208101919091526040016000205560010161333b565b6001600160481b03851660009081526007602052604090206134a1908585615001565b50846001600160481b03167f37343902ee9028104eff4525c5f797b1e85c580c9345605e81a4d2a9caf18220836040516134dd91815260200190565b60405180910390a25050505050565b6002546001600160a01b0316331461158c5760405163118cdaa760e01b81523360048201526024016113f2565b80156135645781841061353f57604051630e0d5b9360e21b815260040160405180910390fd5b81831061355f57604051631a38ee6360e21b815260040160405180910390fd5b6135b8565b83158061357057508184115b1561358e57604051630e0d5b9360e21b815260040160405180910390fd5b82158061359a57508183115b156135b857604051631a38ee6360e21b815260040160405180910390fd5b828411156135d957604051630e0d5b9360e21b815260040160405180910390fd5b6101f46135e68585615a4c565b6135f1906001615a5f565b1115610e24576040516328e2aa3760e01b815260040160405180910390fd5b60008080808580158061362e5750600854600160f81b900460ff1681115b1561364c57604051634ec4810560e11b815260040160405180910390fd5b6001600160a01b0389166000908152600b6020526040902054600160a01b900460ff1680801561367a575086155b15613698576040516346e69cdf60e01b815260040160405180910390fd5b60005b8281101561381757368a8a838181106136b6576136b6615a20565b60600291909101915060009050600a816136d360208501856152e6565b6001600160481b031681526020808201929092526040908101600090812080546001600160a01b03168252600d909352205490915060ff16156137295760405163ea78d96b60e01b815260040160405180910390fd5b60008154600160a01b900460ff1660018111156137485761374861516e565b036137665760405163e393f90760e01b815260040160405180910390fd5b60006137728e846146c4565b825490915061379b906001600160a01b03811690602086013590600160b01b900460ff16613fdd565b6137a59088615a5f565b96506137b560208401358b615a5f565b995084806137cb57508154600160c81b900460ff165b156137ff578a6137ee576040516346e69cdf60e01b815260040160405180910390fd5b6137f88189615a5f565b975061380c565b613809818a615a5f565b98505b50505060010161369b565b505050945094509450949050565b60006138376060830160408401615bc0565b1561385557604051630f83d91360e41b815260040160405180910390fd5b60808201351580613868575060a0820135155b156138865760405163162908e360e11b815260040160405180910390fd5b600880546014906138a690600160a01b90046001600160481b0316615ceb565b91906101000a8154816001600160481b0302191690836001600160481b03160217905590506040518061018001604052806138de3390565b6001600160a01b031681526020016000815260200160008152602001600081526020018360200160208101906139149190615bc0565b1515815260200161392b6080850160608601615bc0565b1515815260200160001515815260200183600001602081019061394e9190615301565b6001600160a01b0390811682526080808601356020808501919091526000604080860182905260a08901356060870152929094018490526001600160481b0386168452600a815292208351815492166001600160a01b0319831681178255928401519092909183916001600160a81b031990911617600160a01b8360018111156139da576139da61516e565b021790555060408201518154829060ff60a81b1916600160a81b836002811115613a0657613a0661516e565b021790555060608201518154829060ff60b01b1916600160b01b836001811115613a3257613a3261516e565b02179055506080820151815460a084015160c08501511515600160c81b0260ff60c81b19911515600160c01b0260ff60c01b19941515600160b81b029490941661ffff60b81b1990931692909217929092179190911617815560e08201516001820180546001600160a01b039092166001600160a01b031990921691909117905561010082015160028201556101208201516003820155610140820151600482015561016090910151600590910155613ae83390565b6001600160a01b03166001600160481b0382167f6e3c6fc53ebb509f549d0e6a6d92ac1dcd83ad26ad91db192c5ccbe99933e2a284613b2a6020820182615301565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613b67573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b8b9190615d4a565b604051613b99929190615e10565b60405180910390a3919050565b6001600160a01b038082166000908152600b6020526040902054168015613c505760095460009061271090613bdf9061ffff1686615d11565b613be99190615d28565b9050613bf58185615a4c565b9350613c018282612caa565b816001600160a01b0316836001600160a01b03167ff9a36a9a40d6dd2811cf7906ec1fc2c05e04b53ce1c9ac8a4e5eb1826abfa6d083604051613c4691815260200190565b60405180910390a3505b600854613c66906001600160a01b031684613cf6565b6040518381527f39eb33dd67e27a3c5bc9a2cf81ba2465d1908cf85a5f927589bb61e4bc7ce6c490602001611df4565b613c9e612e4f565b6002805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258613cd93390565b6040516001600160a01b03909116815260200160405180910390a1565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613d43576040519150601f19603f3d011682016040523d82523d6000602084013e613d48565b606091505b5050905080610ad457604051633d2cec6f60e21b815260040160405180910390fd5b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b613dc581612dfc565b6001600160481b0381166000908152600a60205260409020546001600160a01b03163314612c7d576040516282b42960e81b815260040160405180910390fd5b6060613e327f4f545365610000000000000000000000000000000000000000000000000000056000614952565b905090565b6060613e327f76312e302e3000000000000000000000000000000000000000000000000000066001614952565b60008082801580613e805750600854600160f81b900460ff1681115b15613e9e57604051634ec4810560e11b815260040160405180910390fd5b60005b81811015613fd35736868683818110613ebc57613ebc615a20565b606002919091019150613ed79050610b6e60208301836152e6565b6000600a81613ee960208501856152e6565b6001600160481b031681526020808201929092526040908101600090812080546001600160a01b03168252600d909352205490915060ff1615613f3f5760405163ea78d96b60e01b815260040160405180910390fd5b60018154600160a01b900460ff166001811115613f5e57613f5e61516e565b03613f7c5760405163e393f90760e01b815260040160405180910390fd5b6000613f888a846146c4565b9050613fa83383546001600160a01b038d811692911660208701356149fd565b613fb6602084013588615a5f565b9650613fc28187615a5f565b95505060019092019150613ea19050565b5050935093915050565b600061271081836001811115613ff557613ff561516e565b1461400c57600854600160f01b900460ff1661401a565b600854600160e81b900460ff165b6140279060ff1685615d11565b6140319190615d28565b9050610f9e846140418386615a4c565b612caa565b61404e614a36565b6002805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33613cd9565b8061408c81612dd0565b6001600160481b0384166000908152600760205260408120805490915b8381101561447a5760008686838181106140c5576140c5615a20565b6140db9260206040909202019081019150615301565b6001600160a01b03160361410557604051632070666960e11b8152600481018290526024016113f2565b85858281811061411757614117615a20565b905060400201602001602081019061412f9190615bc0565b15614279576001600160481b03871660009081526006602052604081209087878481811061415f5761415f615a20565b6141759260206040909202019081019150615301565b6001600160a01b03168152602081019190915260400160002054156141b057604051633bda1e3d60e21b8152600481018290526024016113f2565b6141b982615ad4565b6001600160481b03881660009081526006602052604081209193508391908888858181106141e9576141e9615a20565b6141ff9260206040909202019081019150615301565b6001600160a01b031681526020810191909152604001600020558286868381811061422c5761422c615a20565b6142429260206040909202019081019150615301565b81546001810183556000928352602090922090910180546001600160a01b0319166001600160a01b03909216919091179055614472565b6001600160481b0387166000908152600660205260408120818888858181106142a4576142a4615a20565b6142ba9260206040909202019081019150615301565b6001600160a01b03166001600160a01b0316815260200190815260200160002054905080600003614301576040516347d1183960e01b8152600481018390526024016113f2565b60008461430f600186615a4c565b8154811061431f5761431f615a20565b6000918252602090912001546001600160a01b031690508088888581811061434957614349615a20565b61435f9260206040909202019081019150615301565b6001600160a01b0316146143d457808561437a600185615a4c565b8154811061438a5761438a615a20565b600091825260208083209190910180546001600160a01b0319166001600160a01b039485161790556001600160481b038c1682526006815260408083209385168352929052208290555b6001600160481b0389166000908152600660205260408120818a8a878181106143ff576143ff615a20565b6144159260206040909202019081019150615301565b6001600160a01b03168152602081019190915260400160002055845485908061444057614440615e2e565b600082815260209020810160001990810180546001600160a01b03191690550190558361446c81615e44565b94505050505b6001016140a9565b866001600160481b03167f37343902ee9028104eff4525c5f797b1e85c580c9345605e81a4d2a9caf18220836040516144b591815260200190565b60405180910390a250505050505050565b60006144db6001600160a01b03841683614a60565b905080516000141580156145005750808060200190518101906144fe9190615e5b565b155b15610ad457604051635274afe760e01b81526001600160a01b03841660048201526024016113f2565b4381101561454a5760405163df4cc36d60e01b815260040160405180910390fd5b600554845160208601206145a7916001600160a01b03169061456b90614a6e565b85858080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250614a9b92505050565b610e2457604051638baa579f60e01b815260040160405180910390fd5b6040516370a0823160e01b815230600482015260009081906001600160a01b038516906370a0823190602401602060405180830381865afa15801561460d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146319190615e78565b90506146486001600160a01b0385163330866149fd565b6040516370a0823160e01b815230600482015281906001600160a01b038616906370a0823190602401602060405180830381865afa15801561468e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906146b29190615e78565b6146bc9190615a4c565b949350505050565b600080600a816146d760208601866152e6565b6001600160481b031681526020810191909152604001600090812091508154600160a81b900460ff1660028111156147115761471161516e565b14158061472e575060018101546001600160a01b03858116911614155b1561474c5760405163d9b9141960e01b815260040160405180910390fd5b61477761475c60208501856152e6565b6001600160481b031660009081526007602052604090205490565b15801590614793575061479133610f3f60208601866152e6565b155b156147b0576040516282b42960e81b815260040160405180910390fd5b82602001356000036147d55760405163162908e360e11b815260040160405180910390fd5b82604001358160040154146147fd576040516346e69cdf60e01b815260040160405180910390fd5b6000816003015482600201546148139190615a4c565b905060008260050154836004015461482b9190615a4c565b8354909150600160b81b900460ff1661484a5784602001358110614853565b80856020013514155b15614871576040516329e89cc960e11b815260040160405180910390fd5b8085602001350361489757825460ff60a81b1916600160a81b17835590925082906148b3565b806148a6602087013584615d11565b6148b09190615d28565b93505b838360030160008282546148c79190615a5f565b9250508190555084602001358360050160008282546148e69190615a5f565b909155503390506148fa60208701876152e6565b6001600160481b03167fdf3eb0906a8cde45401490bca800772256dfd4f6ee94b6280de698c15499b578876020013587604051614941929190918252602082015260400190565b60405180910390a350505092915050565b606060ff831461496c5761496583614afd565b9050610d10565b81805461497890615e91565b80601f01602080910402602001604051908101604052809291908181526020018280546149a490615e91565b80156149f15780601f106149c6576101008083540402835291602001916149f1565b820191906000526020600020905b8154815290600101906020018083116149d457829003601f168201915b50505050509050610d10565b6040516001600160a01b038481166024830152838116604483015260648201839052610e249186918216906323b872dd90608401612d9e565b600254600160a01b900460ff1661158c57604051638dfc202b60e01b815260040160405180910390fd5b6060610f9e83836000614b3c565b6000610d10614a7b614bcf565b8360405161190160f01b8152600281019290925260228201526042902090565b6000806000614aaa8585614cfa565b5090925090506000816003811115614ac457614ac461516e565b148015614ae25750856001600160a01b0316826001600160a01b0316145b80614af35750614af3868686614d47565b9695505050505050565b60606000614b0a83614e22565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b606081471015614b615760405163cd78605960e01b81523060048201526024016113f2565b600080856001600160a01b03168486604051614b7d9190615ec5565b60006040518083038185875af1925050503d8060008114614bba576040519150601f19603f3d011682016040523d82523d6000602084013e614bbf565b606091505b5091509150614af3868383614e4a565b6000306001600160a01b037f000000000000000000000000a836af59d35b3da3c9fb5cfd6f84d9e3bd8c5fd516148015614c2857507f000000000000000000000000000000000000000000000000000000000000210546145b15614c5257507fd6d53b048ded6c2b9c576b1c1efe9fc55625259e44d71d98b5f9c6bdc8a4fb1690565b613e32604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527fa33add9fc07a5f8e391e2daeadc1d72ac38823d5f634b087cb6c64f146f03c34918101919091527f15124d26d1272f8d4d5266a24ca397811f414b8cd05a53b26b745f63af5ae2fc60608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b60008060008351604103614d345760208401516040850151606086015160001a614d2688828585614ea6565b955095509550505050614d40565b50508151600091506002905b9250925092565b6000806000856001600160a01b03168585604051602401614d69929190615ee1565b60408051601f198184030181529181526020820180516001600160e01b0316630b135d3f60e11b17905251614d9e9190615ec5565b600060405180830381855afa9150503d8060008114614dd9576040519150601f19603f3d011682016040523d82523d6000602084013e614dde565b606091505b5091509150818015614df257506020815110155b8015614af357508051630b135d3f60e11b90614e179083016020908101908401615e78565b149695505050505050565b600060ff8216601f811115610d1057604051632cd44ac360e21b815260040160405180910390fd5b606082614e5f57614e5a82614f75565b610f9e565b8151158015614e7657506001600160a01b0384163b155b15614e9f57604051639996b31560e01b81526001600160a01b03851660048201526024016113f2565b5080610f9e565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115614ee15750600091506003905082614f6b565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015614f35573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116614f6157506000925060019150829050614f6b565b9250600091508190505b9450945094915050565b805115614f855780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b604080516101808101909152600080825260208201908152602001600081526020016000815260006020820181905260408201819052606082018190526080820181905260a0820181905260c0820181905260e082018190526101009091015290565b828054828255906000526020600020908101928215615054579160200282015b828111156150545781546001600160a01b0319166001600160a01b03843516178255602090920191600190910190615021565b50615060929150615064565b5090565b5b808211156150605760008155600101615065565b6001600160a01b0381168114612c7d57600080fd5b60008083601f8401126150a057600080fd5b5081356001600160401b038111156150b757600080fd5b6020830191508360208260051b85010111156150d257600080fd5b9250929050565b6000806000604084860312156150ee57600080fd5b83356150f981615079565b925060208401356001600160401b0381111561511457600080fd5b6151208682870161508e565b9497909650939450505050565b6000806020838503121561514057600080fd5b82356001600160401b0381111561515657600080fd5b6151628582860161508e565b90969095509350505050565b634e487b7160e01b600052602160045260246000fd5b60028110612c7d57612c7d61516e565b61519d81615184565b9052565b6003811061519d5761519d61516e565b80516001600160a01b0316825260208101516151d06020840182615194565b5060408101516151e360408401826151a1565b5060608101516151f66060840182615194565b50608081015161520a608084018215159052565b5060a081015161521e60a084018215159052565b5060c081015161523260c084018215159052565b5060e081015161524d60e08401826001600160a01b03169052565b5061010081810151908301526101208082015190830152610140808201519083015261016090810151910152565b6020808252825182820181905260009190848201906040850190845b818110156152be576152aa8385516151b1565b928401926101809290920191600101615297565b50909695505050505050565b80356001600160481b03811681146152e157600080fd5b919050565b6000602082840312156152f857600080fd5b610f9e826152ca565b60006020828403121561531357600080fd5b8135610f9e81615079565b600060c0828403121561533057600080fd5b50919050565b60006060828403121561533057600080fd5b600080600080610100858703121561535f57600080fd5b615369868661531e565b935060c08501356001600160401b038082111561538557600080fd5b6153918883890161508e565b909550935060e08701359150808211156153aa57600080fd5b506153b787828801615336565b91505092959194509250565b6020808252825182820181905260009190848201906040850190845b818110156152be5783511515835292840192918401916001016153df565b60ff81168114612c7d57600080fd5b60006020828403121561541e57600080fd5b8135610f9e816153fd565b6000806040838503121561543c57600080fd5b50508035926020909101359150565b8015158114612c7d57600080fd5b6000806040838503121561546c57600080fd5b823561547781615079565b915060208301356154878161544b565b809150509250929050565b60008083601f8401126154a457600080fd5b5081356001600160401b038111156154bb57600080fd5b6020830191508360206060830285010111156150d257600080fd5b803561ffff811681146152e157600080fd5b600080600080600080610140878903121561550257600080fd5b863561550d81615079565b955060208701356001600160401b0381111561552857600080fd5b61553489828a01615492565b90965094506155489050886040890161531e565b92506101008701356155598161544b565b915061556861012088016154d6565b90509295509295509295565b60008083601f84011261558657600080fd5b5081356001600160401b0381111561559d57600080fd5b6020830191508360208260061b85010111156150d257600080fd5b6000806000604084860312156155cd57600080fd5b83356155d881615079565b925060208401356001600160401b038111156155f357600080fd5b61512086828701615574565b60006020828403121561561157600080fd5b610f9e826154d6565b602080825282518282018190526000919060409081850190868401855b8281101561568357815180516001600160a01b03168552868101516001600160581b03168786015285810151868601526060908101519085015260809093019290850190600101615637565b5091979650505050505050565b60008082840360608112156156a457600080fd5b83356156af81615079565b92506040601f19820112156156c357600080fd5b506020830190509250929050565b6000806000606084860312156156e657600080fd5b83356156f1816153fd565b92506020840135615701816153fd565b915061570f604085016154d6565b90509250925092565b6000806040838503121561572b57600080fd5b615477836152ca565b60005b8381101561574f578181015183820152602001615737565b50506000910152565b60008151808452615770816020860160208601615734565b601f01601f19169290920160200192915050565b60ff60f81b881681526000602060e0818401526157a460e084018a615758565b83810360408501526157b6818a615758565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b81811015615808578351835292840192918401916001016157ec565b50909c9b505050505050505050505050565b6000806000806000610120868803121561583357600080fd5b853561583e81615079565b945060208601356001600160401b038082111561585a57600080fd5b61586689838a01615492565b909650945084915061587b8960408a0161531e565b935061010088013591508082111561589257600080fd5b5061589f88828901615336565b9150509295509295909350565b6000806000606084860312156158c157600080fd5b6158ca846152ca565b95602085013595506040909401359392505050565b6020808252825182820181905260009190848201906040850190845b818110156152be5783516001600160a01b0316835292840192918401916001016158fb565b6000806000806060858703121561593657600080fd5b843561594181615079565b9350602085013561595181615079565b925060408501356001600160401b0381111561596c57600080fd5b61597887828801615574565b95989497509550505050565b600080600060e0848603121561599957600080fd5b6159a3858561531e565b925060c08401356001600160401b0381111561511457600080fd5b600080604083850312156159d157600080fd5b82356159dc81615079565b91506159ea602084016152ca565b90509250929050565b600080600060408486031215615a0857600080fd5b6155d8846152ca565b6101808101610d1082846151b1565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b81810381811115610d1057610d10615a36565b80820180821115610d1057610d10615a36565b60208082528181018390526000908460408401835b86811015615ab3576001600160481b03615aa0846152ca565b1682529183019190830190600101615a87565b509695505050505050565b634e487b7160e01b600052604160045260246000fd5b600060018201615ae657615ae6615a36565b5060010190565b8183526000602080850194508260005b85811015615b3d576001600160481b03615b16836152ca565b16875281830135838801526040808301359088015260609687019690910190600101615afd565b509495945050505050565b608081526000615b5c608083018789615aed565b602083019590955250604081019290925260609091015292915050565b6020808252818101839052600090604080840186845b87811015615bb3578135835284820135858401529183019190830190600101615b8f565b5090979650505050505050565b600060208284031215615bd257600080fd5b8135610f9e8161544b565b604081018235615bec81615079565b6001600160a01b031682526020830135615c058161544b565b80151560208401525092915050565b608081526000615c28608083018789615aed565b9050846020830152836040830152615c3f83615184565b8260608301529695505050505050565b6000808335601e19843603018112615c6657600080fd5b8301803591506001600160401b03821115615c8057600080fd5b6020019150368190038213156150d257600080fd5b600060208284031215615ca757600080fd5b813560028110610f9e57600080fd5b8481526001600160a01b03841660208201526040810183905260808101615cdc83615184565b82606083015295945050505050565b60006001600160481b03808316818103615d0757615d07615a36565b6001019392505050565b8082028115828204841417610d1057610d10615a36565b600082615d4557634e487b7160e01b600052601260045260246000fd5b500490565b600060208284031215615d5c57600080fd5b8151610f9e816153fd565b8035615d7281615079565b6001600160a01b031682526020810135615d8b8161544b565b151560208301526040810135615da08161544b565b151560408301526060810135615db58161544b565b151560608301526080818101359083015260a090810135910152565b6101408101615de08288615d67565b8560c08301528460e0830152615df584615184565b8361010083015260ff83166101208301529695505050505050565b60e08101615e1e8285615d67565b60ff831660c08301529392505050565b634e487b7160e01b600052603160045260246000fd5b600081615e5357615e53615a36565b506000190190565b600060208284031215615e6d57600080fd5b8151610f9e8161544b565b600060208284031215615e8a57600080fd5b5051919050565b600181811c90821680615ea557607f821691505b60208210810361533057634e487b7160e01b600052602260045260246000fd5b60008251615ed7818460208701615734565b9190910192915050565b8281526040602082015260006146bc604083018461575856fea264697066735822122008685fbde8c882c35e4508862eb5f6d89c93e2cbdaf8638e5427ad0b777e2b4364736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000f28072b734c19e573d4cdc090c21edef19f10c66000000000000000000000000a5e43669cfaac8f834c3ab419a2092a78a096dde00000000000000000000000084cf61259efb32244258c0ab910ab4f11e13842b
-----Decoded View---------------
Arg [0] : _multiSigAdmin (address): 0xf28072b734c19e573D4cDc090c21eDEf19F10C66
Arg [1] : revenueDistributor_ (address): 0xa5e43669CfaAc8F834c3AB419A2092a78a096ddE
Arg [2] : _signer (address): 0x84cf61259EFB32244258C0aB910ab4F11E13842B
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000f28072b734c19e573d4cdc090c21edef19f10c66
Arg [1] : 000000000000000000000000a5e43669cfaac8f834c3ab419a2092a78a096dde
Arg [2] : 00000000000000000000000084cf61259efb32244258c0ab910ab4f11e13842b
Loading...
Loading
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.