This nametag was submitted by Kleros Scout.
Latest 25 from a total of 25,900 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Update Order Exe... | 368191017 | 165 days ago | IN | 0 ETH | 0.00000051 | ||||
| Cancel Decrease ... | 234097948 | 554 days ago | IN | 0 ETH | 0.00000072 | ||||
| Create Take Prof... | 191583777 | 678 days ago | IN | 0.0006 ETH | 0.00004234 | ||||
| Create Take Prof... | 191583674 | 678 days ago | IN | 0.0006 ETH | 0.00004244 | ||||
| Cancel Decrease ... | 190815307 | 681 days ago | IN | 0 ETH | 0.00001276 | ||||
| Cancel Decrease ... | 190815219 | 681 days ago | IN | 0 ETH | 0.00001276 | ||||
| Cancel Decrease ... | 190729052 | 681 days ago | IN | 0 ETH | 0.00001229 | ||||
| Cancel Decrease ... | 190728974 | 681 days ago | IN | 0 ETH | 0.00001229 | ||||
| Create Take Prof... | 187991368 | 689 days ago | IN | 0.0006 ETH | 0.00030389 | ||||
| Create Take Prof... | 187954363 | 689 days ago | IN | 0.0006 ETH | 0.00025709 | ||||
| Create Increase ... | 187953701 | 689 days ago | IN | 0.0003 ETH | 0.00022618 | ||||
| Cancel Decrease ... | 185022871 | 698 days ago | IN | 0 ETH | 0.00011287 | ||||
| Cancel Decrease ... | 185022737 | 698 days ago | IN | 0 ETH | 0.00011287 | ||||
| Create Take Prof... | 184281425 | 700 days ago | IN | 0.0006 ETH | 0.00014809 | ||||
| Create Take Prof... | 184281094 | 700 days ago | IN | 0.0006 ETH | 0.00014779 | ||||
| Cancel Increase ... | 183682471 | 702 days ago | IN | 0 ETH | 0.00007415 | ||||
| Update Increase ... | 183511049 | 703 days ago | IN | 0 ETH | 0.00010809 | ||||
| Create Increase ... | 183277767 | 703 days ago | IN | 0.0003 ETH | 0.00015329 | ||||
| Cancel Decrease ... | 183277592 | 703 days ago | IN | 0 ETH | 0.00007356 | ||||
| Update Decrease ... | 183156579 | 704 days ago | IN | 0 ETH | 0.00009003 | ||||
| Create Decrease ... | 183064817 | 704 days ago | IN | 0.0003 ETH | 0.00022648 | ||||
| Cancel Increase ... | 183064467 | 704 days ago | IN | 0 ETH | 0.00011693 | ||||
| Create Increase ... | 182819763 | 705 days ago | IN | 0.0003 ETH | 0.00023544 | ||||
| Cancel Increase ... | 182817620 | 705 days ago | IN | 0 ETH | 0.00012053 | ||||
| Create Increase ... | 182682782 | 705 days ago | IN | 0.0003 ETH | 0.00020872 |
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH | ||||
| 368197709 | 165 days ago | 0.0003 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
OrderBook
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 100000000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.21;
import "./Router.sol";
import "./interfaces/IOrderBook.sol";
import "../libraries/ReentrancyGuard.sol";
contract OrderBook is IOrderBook, Governable, ReentrancyGuard {
using SafeERC20 for IERC20;
using Address for address payable;
IERC20 public immutable usd;
Router public immutable router;
uint256 public minExecutionFee;
uint256 public executionGasLimit = 1_000_000 wei;
mapping(address => bool) public orderExecutors;
uint256 public increaseOrdersIndexNext;
uint256 public decreaseOrdersIndexNext;
mapping(uint256 => IncreaseOrder) public increaseOrders;
mapping(uint256 => DecreaseOrder) public decreaseOrders;
modifier onlyOrderExecutor() {
if (!orderExecutors[msg.sender]) revert Forbidden();
_;
}
constructor(IERC20 _usd, Router _router, uint256 _minExecutionFee) {
usd = _usd;
router = _router;
minExecutionFee = _minExecutionFee;
emit MinExecutionFeeUpdated(_minExecutionFee);
}
/// @inheritdoc IOrderBook
function updateMinExecutionFee(uint256 _minExecutionFee) external override onlyGov {
minExecutionFee = _minExecutionFee;
emit MinExecutionFeeUpdated(_minExecutionFee);
}
/// @inheritdoc IOrderBook
function updateOrderExecutor(address _account, bool _active) external override onlyGov {
orderExecutors[_account] = _active;
emit OrderExecutorUpdated(_account, _active);
}
/// @inheritdoc IOrderBook
function updateExecutionGasLimit(uint256 _executionGasLimit) external override onlyGov {
executionGasLimit = _executionGasLimit;
}
/// @inheritdoc IOrderBook
function createIncreaseOrder(
IPool _pool,
Side _side,
uint128 _marginDelta,
uint128 _sizeDelta,
uint160 _triggerMarketPriceX96,
bool _triggerAbove,
uint160 _acceptableTradePriceX96
) external payable override nonReentrant returns (uint256 index) {
_side.requireValid();
if (msg.value < minExecutionFee) revert InsufficientExecutionFee(msg.value, minExecutionFee);
if (_marginDelta > 0) router.pluginTransfer(usd, msg.sender, address(this), _marginDelta);
index = increaseOrdersIndexNext++;
increaseOrders[index] = IncreaseOrder({
account: msg.sender,
pool: _pool,
side: _side,
marginDelta: _marginDelta,
sizeDelta: _sizeDelta,
triggerMarketPriceX96: _triggerMarketPriceX96,
triggerAbove: _triggerAbove,
acceptableTradePriceX96: _acceptableTradePriceX96,
executionFee: msg.value
});
emit IncreaseOrderCreated(
msg.sender,
_pool,
_side,
_marginDelta,
_sizeDelta,
_triggerMarketPriceX96,
_triggerAbove,
_acceptableTradePriceX96,
msg.value,
index
);
}
/// @inheritdoc IOrderBook
function updateIncreaseOrder(
uint256 _orderIndex,
uint160 _triggerMarketPriceX96,
uint160 _acceptableTradePriceX96
) external override nonReentrant {
IncreaseOrder storage order = increaseOrders[_orderIndex];
if (order.account != msg.sender) revert Forbidden();
order.triggerMarketPriceX96 = _triggerMarketPriceX96;
order.acceptableTradePriceX96 = _acceptableTradePriceX96;
emit IncreaseOrderUpdated(_orderIndex, _triggerMarketPriceX96, _acceptableTradePriceX96);
}
/// @inheritdoc IOrderBook
function cancelIncreaseOrder(uint256 _orderIndex, address payable _feeReceiver) external override nonReentrant {
IncreaseOrder memory order = increaseOrders[_orderIndex];
if (order.account == address(0)) revert OrderNotExists(_orderIndex);
if (order.account != msg.sender && !orderExecutors[msg.sender]) revert Forbidden();
usd.safeTransfer(order.account, order.marginDelta);
_transferOutETH(order.executionFee, _feeReceiver);
delete increaseOrders[_orderIndex];
emit IncreaseOrderCancelled(_orderIndex, _feeReceiver);
}
/// @inheritdoc IOrderBook
function executeIncreaseOrder(
uint256 _orderIndex,
address payable _feeReceiver
) external override nonReentrant onlyOrderExecutor {
IncreaseOrder memory order = increaseOrders[_orderIndex];
if (order.account == address(0)) revert OrderNotExists(_orderIndex);
uint160 marketPriceX96 = order.pool.marketPriceX96(order.side);
_validateTriggerMarketPriceX96(order.triggerAbove, marketPriceX96, order.triggerMarketPriceX96);
usd.safeTransfer(address(order.pool), order.marginDelta);
// Note that the gas specified here is just an upper limit,
// when the gas left is lower than this value, code can still be executed
uint160 tradePriceX96 = router.pluginIncreasePosition{gas: executionGasLimit}(
order.pool,
order.account,
order.side,
order.marginDelta,
order.sizeDelta
);
_validateTradePriceX96(order.side, tradePriceX96, order.acceptableTradePriceX96);
_transferOutETH(order.executionFee, _feeReceiver);
delete increaseOrders[_orderIndex];
emit IncreaseOrderExecuted(_orderIndex, marketPriceX96, _feeReceiver);
}
/// @inheritdoc IOrderBook
function createDecreaseOrder(
IPool _pool,
Side _side,
uint128 _marginDelta,
uint128 _sizeDelta,
uint160 _triggerMarketPriceX96,
bool _triggerAbove,
uint160 _acceptableTradePriceX96,
address _receiver
) external payable override nonReentrant returns (uint256 index) {
_side.requireValid();
if (msg.value < minExecutionFee) revert InsufficientExecutionFee(msg.value, minExecutionFee);
index = _createDecreaseOrder(
msg.sender,
_pool,
_side,
_marginDelta,
_sizeDelta,
_triggerMarketPriceX96,
_triggerAbove,
_acceptableTradePriceX96,
_receiver,
msg.value
);
}
/// @inheritdoc IOrderBook
function updateDecreaseOrder(
uint256 _orderIndex,
uint160 _triggerMarketPriceX96,
uint160 _acceptableTradePriceX96
) external override nonReentrant {
DecreaseOrder storage order = decreaseOrders[_orderIndex];
if (msg.sender != order.account) revert Forbidden();
order.triggerMarketPriceX96 = _triggerMarketPriceX96;
order.acceptableTradePriceX96 = _acceptableTradePriceX96;
emit DecreaseOrderUpdated(_orderIndex, _triggerMarketPriceX96, _acceptableTradePriceX96);
}
/// @inheritdoc IOrderBook
function cancelDecreaseOrder(uint256 _orderIndex, address payable _feeReceiver) external override nonReentrant {
DecreaseOrder memory order = decreaseOrders[_orderIndex];
if (order.account == address(0)) revert OrderNotExists(_orderIndex);
if (order.account != msg.sender && !orderExecutors[msg.sender]) revert Forbidden();
_transferOutETH(order.executionFee, _feeReceiver);
delete decreaseOrders[_orderIndex];
emit DecreaseOrderCancelled(_orderIndex, _feeReceiver);
}
/// @inheritdoc IOrderBook
function executeDecreaseOrder(
uint256 _orderIndex,
address payable _feeReceiver
) external override nonReentrant onlyOrderExecutor {
DecreaseOrder memory order = decreaseOrders[_orderIndex];
if (order.account == address(0)) revert OrderNotExists(_orderIndex);
uint160 marketPriceX96 = order.pool.marketPriceX96(order.side.flip());
_validateTriggerMarketPriceX96(order.triggerAbove, marketPriceX96, order.triggerMarketPriceX96);
uint128 sizeDeltaAfter = order.sizeDelta;
uint128 marginDeltaAfter = order.marginDelta;
if (order.sizeDelta == 0) {
// if `sizeDelta` is 0, close the position without checking the trade price
(, sizeDeltaAfter, , ) = order.pool.positions(order.account, order.side);
marginDeltaAfter = 0;
}
uint160 tradePriceX96 = router.pluginDecreasePosition{gas: executionGasLimit}(
order.pool,
order.account,
order.side,
marginDeltaAfter,
sizeDeltaAfter,
order.receiver
);
if (order.sizeDelta != 0)
_validateTradePriceX96(order.side.flip(), tradePriceX96, order.acceptableTradePriceX96);
_transferOutETH(order.executionFee, _feeReceiver);
delete decreaseOrders[_orderIndex];
emit DecreaseOrderExecuted(_orderIndex, marketPriceX96, _feeReceiver);
}
/// @inheritdoc IOrderBook
function createTakeProfitAndStopLossOrders(
IPool _pool,
Side _side,
uint128[2] calldata _marginDeltas,
uint128[2] calldata _sizeDeltas,
uint160[2] calldata _triggerMarketPriceX96s,
uint160[2] calldata _acceptableTradePriceX96s,
address _receiver
) external payable override nonReentrant {
_side.requireValid();
uint256 fee0 = msg.value >> 1;
if (fee0 < minExecutionFee) revert InsufficientExecutionFee(fee0, minExecutionFee);
_createDecreaseOrder(
msg.sender,
_pool,
_side,
_marginDeltas[0],
_sizeDeltas[0],
_triggerMarketPriceX96s[0],
_side.isLong(),
_acceptableTradePriceX96s[0],
_receiver,
fee0
);
_createDecreaseOrder(
msg.sender,
_pool,
_side,
_marginDeltas[1],
_sizeDeltas[1],
_triggerMarketPriceX96s[1],
!_side.isLong(),
_acceptableTradePriceX96s[1],
_receiver,
msg.value - fee0
);
}
function _createDecreaseOrder(
address _sender,
IPool _pool,
Side _side,
uint128 _marginDelta,
uint128 _sizeDelta,
uint160 _triggerMarketPriceX96,
bool _triggerAbove,
uint160 _acceptableTradePriceX96,
address _receiver,
uint256 _executionFee
) internal returns (uint256 index) {
index = decreaseOrdersIndexNext++;
decreaseOrders[index] = DecreaseOrder({
account: _sender,
pool: _pool,
side: _side,
marginDelta: _marginDelta,
sizeDelta: _sizeDelta,
triggerMarketPriceX96: _triggerMarketPriceX96,
triggerAbove: _triggerAbove,
acceptableTradePriceX96: _acceptableTradePriceX96,
receiver: _receiver,
executionFee: _executionFee
});
emit DecreaseOrderCreated(
_sender,
_pool,
_side,
_marginDelta,
_sizeDelta,
_triggerMarketPriceX96,
_triggerAbove,
_acceptableTradePriceX96,
_receiver,
_executionFee,
index
);
}
function _validateTriggerMarketPriceX96(
bool _triggerAbove,
uint160 _marketPriceX96,
uint160 _triggerMarketPriceX96
) private pure {
if (
(_triggerAbove && (_marketPriceX96 < _triggerMarketPriceX96)) ||
(!_triggerAbove && (_marketPriceX96 > _triggerMarketPriceX96))
) revert InvalidMarketPriceToTrigger(_marketPriceX96, _triggerMarketPriceX96);
}
function _validateTradePriceX96(Side _side, uint160 _tradePriceX96, uint160 _acceptableTradePriceX96) private pure {
// long makes price up, short makes price down
if (
(_side.isLong() && (_tradePriceX96 > _acceptableTradePriceX96)) ||
(_side.isShort() && (_tradePriceX96 < _acceptableTradePriceX96))
) revert InvalidTradePrice(_tradePriceX96, _acceptableTradePriceX96);
}
function _transferOutETH(uint256 _amountOut, address payable _receiver) private {
_receiver.sendValue(_amountOut);
}
}// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; // EIP-2612 is Final as of 2022-11-01. This file is deprecated. import "./IERC20Permit.sol";
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @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.
*/
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].
*/
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 v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @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 amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://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.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) 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(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "./IPoolErrors.sol";
import "./IPoolPosition.sol";
import "./IPoolLiquidityPosition.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title Perpetual Pool Position Interface
/// @notice This interface defines the functions for managing positions and liquidity positions in a perpetual pool
interface IPool is IPoolLiquidityPosition, IPoolPosition, IPoolErrors {
struct PriceVertex {
uint128 size;
uint128 premiumRateX96;
}
struct PriceState {
uint128 maxPriceImpactLiquidity;
uint128 premiumRateX96;
PriceVertex[7] priceVertices;
uint8 pendingVertexIndex;
uint8 liquidationVertexIndex;
uint8 currentVertexIndex;
uint128[7] liquidationBufferNetSizes;
}
/// @notice Emitted when the price vertex is changed
event PriceVertexChanged(uint8 index, uint128 sizeAfter, uint128 premiumRateAfterX96);
/// @notice Emitted when the protocol fee is increased
/// @param amount The increased protocol fee
event ProtocolFeeIncreased(uint128 amount);
/// @notice Emitted when the protocol fee is collected
/// @param amount The collected protocol fee
event ProtocolFeeCollected(uint128 amount);
/// @notice Emitted when the referral fee is increased
/// @param referee The address of the referee
/// @param referralToken The id of the referral token
/// @param referralFee The amount of referral fee
/// @param referralParentToken The id of the referral parent token
/// @param referralParentFee The amount of referral parent fee
event ReferralFeeIncreased(
address indexed referee,
uint256 indexed referralToken,
uint128 referralFee,
uint256 indexed referralParentToken,
uint128 referralParentFee
);
/// @notice Emitted when the referral fee is collected
/// @param referralToken The id of the referral token
/// @param receiver The address to receive the referral fee
/// @param amount The collected referral fee
event ReferralFeeCollected(uint256 indexed referralToken, address indexed receiver, uint256 amount);
function token() external view returns (IERC20);
/// @notice Change the token config
/// @dev The call will fail if caller is not the pool factory
function onChangeTokenConfig() external;
/// @notice Sample and adjust the funding rate
function sampleAndAdjustFundingRate() external;
/// @notice Return the price state
/// @return maxPriceImpactLiquidity The maximum LP liquidity value used to calculate
/// premium rate when trader increase or decrease positions
/// @return premiumRateX96 The premium rate during the last position adjustment by the trader, as a Q32.96
/// @return priceVertices The price vertices used to determine the pricing function
/// @return pendingVertexIndex The index used to track the pending update of the price vertex
/// @return liquidationVertexIndex The index used to store the net position of the liquidation
/// @return currentVertexIndex The index used to track the current used price vertex
/// @return liquidationBufferNetSizes The net sizes of the liquidation buffer
function priceState()
external
view
returns (
uint128 maxPriceImpactLiquidity,
uint128 premiumRateX96,
PriceVertex[7] memory priceVertices,
uint8 pendingVertexIndex,
uint8 liquidationVertexIndex,
uint8 currentVertexIndex,
uint128[7] memory liquidationBufferNetSizes
);
/// @notice Get the market price
/// @param side The side of the position adjustment, 1 for opening long or closing short positions,
/// 2 for opening short or closing long positions
/// @return marketPriceX96 The market price, as a Q64.96
function marketPriceX96(Side side) external view returns (uint160 marketPriceX96);
/// @notice Change the price vertex
/// @param startExclusive The start index of the price vertex to be changed, exclusive
/// @param endInclusive The end index of the price vertex to be changed, inclusive
function changePriceVertex(uint8 startExclusive, uint8 endInclusive) external;
/// @notice Return the protocol fee
function protocolFee() external view returns (uint128);
/// @notice Collect the protocol fee
/// @dev This function can be called without authorization
function collectProtocolFee() external;
/// @notice Return the referral fee
/// @param referralToken The id of the referral token
function referralFees(uint256 referralToken) external view returns (uint256);
/// @notice Collect the referral fee
/// @param referralToken The id of the referral token
/// @param receiver The address to receive the referral fee
/// @return The collected referral fee
function collectReferralFee(uint256 referralToken, address receiver) external returns (uint256);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Side} from "../../types/Side.sol";
interface IPoolErrors {
/// @notice Liquidity is not enough to open a liquidity position
error InvalidLiquidityToOpen();
/// @notice Invalid caller
error InvalidCaller(address requiredCaller);
/// @notice Insufficient size to decrease
error InsufficientSizeToDecrease(uint128 size, uint128 requiredSize);
/// @notice Insufficient margin
error InsufficientMargin();
/// @notice Position not found
error PositionNotFound(address requiredAccount, Side requiredSide);
/// @notice Liquidity position not found
error LiquidityPositionNotFound(uint256 requiredPositionID);
/// @notice Last liquidity position cannot be closed
error LastLiquidityPositionCannotBeClosed();
/// @notice Caller is not the liquidator
error CallerNotLiquidator();
/// @notice Insufficient balance
error InsufficientBalance(uint128 balance, uint128 requiredAmount);
/// @notice Leverage is too high
error LeverageTooHigh(uint256 margin, uint128 liquidity, uint32 maxLeverage);
/// @notice Insufficient global liquidity
error InsufficientGlobalLiquidity();
/// @notice Risk rate is too high
error RiskRateTooHigh(uint256 margin, uint64 liquidationExecutionFee, uint128 positionUnrealizedLoss);
/// @notice Risk rate is too low
error RiskRateTooLow(uint256 margin, uint64 liquidationExecutionFee, uint128 positionUnrealizedLoss);
/// @notice Position margin rate is too low
error MarginRateTooLow(int256 margin, int256 unrealizedPnL, uint256 maintenanceMargin);
/// @notice Position margin rate is too high
error MarginRateTooHigh(int256 margin, int256 unrealizedPnL, uint256 maintenanceMargin);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Side} from "../../types/Side.sol";
/// @title Perpetual Pool Liquidity Position Interface
/// @notice This interface defines the functions for managing liquidity positions in a perpetual pool
interface IPoolLiquidityPosition {
/// @notice Emitted when the unrealized loss metrics of the global liquidity position are changed
/// @param lastZeroLossTimeAfter The time when the LP's net position no longer has unrealized losses
/// or the risk buffer fund has enough balance to cover the unrealized losses of all LPs
/// @param liquidityAfter The total liquidity of all LPs whose entry time is
/// after `lastZeroLossTime`
/// @param liquidityTimesUnrealizedLossAfter The product of liquidity and unrealized loss for
/// each LP whose entry time is after `lastZeroLossTime`
event GlobalUnrealizedLossMetricsChanged(
uint64 lastZeroLossTimeAfter,
uint128 liquidityAfter,
uint256 liquidityTimesUnrealizedLossAfter
);
/// @notice Emitted when an LP opens a liquidity position
/// @param account The owner of the position
/// @param positionID The position ID
/// @param margin The margin of the position
/// @param liquidity The liquidity of the position
/// @param entryUnrealizedLoss The snapshot of the unrealized loss of LP at the time of opening the position
/// @param realizedProfitGrowthX64 The snapshot of `GlobalLiquidityPosition.realizedProfitGrowthX64`
/// at the time of opening the position, as a Q192.64
event LiquidityPositionOpened(
address indexed account,
uint96 positionID,
uint128 margin,
uint128 liquidity,
uint256 entryUnrealizedLoss,
uint256 realizedProfitGrowthX64
);
/// @notice Emitted when an LP closes a liquidity position
/// @param positionID The position ID
/// @param margin The margin removed from the position after closing
/// @param unrealizedLoss The unrealized loss incurred by the position at the time of closing,
/// which will be transferred to `GlobalLiquidityPosition.riskBufferFund`
/// @param realizedProfit The realized profit of the position at the time of closing
/// @param receiver The address that receives the margin upon closing
event LiquidityPositionClosed(
uint96 indexed positionID,
uint128 margin,
uint128 unrealizedLoss,
uint256 realizedProfit,
address receiver
);
/// @notice Emitted when the margin of an LP's position is adjusted
/// @param positionID The position ID
/// @param marginDelta Change in margin, positive for increase and negative for decrease
/// @param marginAfter Adjusted margin
/// @param entryRealizedProfitGrowthAfterX64 The snapshot of `GlobalLiquidityPosition.realizedProfitGrowthX64`
/// after adjustment, as a Q192.64
/// @param receiver The address that receives the margin when it is decreased
event LiquidityPositionMarginAdjusted(
uint96 indexed positionID,
int128 marginDelta,
uint128 marginAfter,
uint256 entryRealizedProfitGrowthAfterX64,
address receiver
);
/// @notice Emitted when an LP's position is liquidated
/// @param liquidator The address that executes the liquidation of the position
/// @param positionID The position ID to be liquidated
/// @param realizedProfit The realized profit of the position at the time of liquidation
/// @param riskBufferFundDelta The remaining margin of the position after liquidation,
/// which will be transferred to `GlobalLiquidityPosition.riskBufferFund`
/// @param liquidationExecutionFee The liquidation execution fee paid by the position
/// @param feeReceiver The address that receives the liquidation execution fee
event LiquidityPositionLiquidated(
address indexed liquidator,
uint96 indexed positionID,
uint256 realizedProfit,
uint256 riskBufferFundDelta,
uint64 liquidationExecutionFee,
address feeReceiver
);
/// @notice Emitted when the net position of all LP's is adjusted
/// @param netSizeAfter The adjusted net position size
/// @param liquidationBufferNetSizeAfter The adjusted net position size in the liquidation buffer
/// @param entryPriceAfterX96 The adjusted entry price, as a Q64.96
/// @param sideAfter The adjusted side of the net position
event GlobalLiquidityPositionNetPositionAdjusted(
uint128 netSizeAfter,
uint128 liquidationBufferNetSizeAfter,
uint160 entryPriceAfterX96,
Side sideAfter
);
/// @notice Emitted when the `realizedProfitGrowthX64` of the global liquidity position is changed
/// @param realizedProfitGrowthAfterX64 The adjusted `realizedProfitGrowthX64`, as a Q192.64
event GlobalLiquidityPositionRealizedProfitGrowthChanged(uint256 realizedProfitGrowthAfterX64);
/// @notice Emitted when the risk buffer fund is used by `Gov`
/// @param receiver The address that receives the risk buffer fund
/// @param riskBufferFundDelta The amount of risk buffer fund used
event GlobalRiskBufferFundGovUsed(address indexed receiver, uint128 riskBufferFundDelta);
/// @notice Emitted when the risk buffer fund is changed
event GlobalRiskBufferFundChanged(int256 riskBufferFundAfter);
/// @notice Emitted when the liquidity of the risk buffer fund is increased
/// @param account The owner of the position
/// @param liquidityAfter The total liquidity of the position after the increase
/// @param unlockTimeAfter The unlock time of the position after the increase
event RiskBufferFundPositionIncreased(address indexed account, uint128 liquidityAfter, uint64 unlockTimeAfter);
/// @notice Emitted when the liquidity of the risk buffer fund is decreased
/// @param account The owner of the position
/// @param liquidityAfter The total liquidity of the position after the decrease
/// @param receiver The address that receives the liquidity when it is decreased
event RiskBufferFundPositionDecreased(address indexed account, uint128 liquidityAfter, address receiver);
struct GlobalLiquidityPosition {
uint128 netSize;
uint128 liquidationBufferNetSize;
uint160 entryPriceX96;
Side side;
uint128 liquidity;
uint256 realizedProfitGrowthX64;
}
struct GlobalRiskBufferFund {
int256 riskBufferFund;
uint256 liquidity;
}
struct GlobalUnrealizedLossMetrics {
uint64 lastZeroLossTime;
uint128 liquidity;
uint256 liquidityTimesUnrealizedLoss;
}
struct LiquidityPosition {
uint128 margin;
uint128 liquidity;
uint256 entryUnrealizedLoss;
uint256 entryRealizedProfitGrowthX64;
uint64 entryTime;
address account;
}
struct RiskBufferFundPosition {
uint128 liquidity;
uint64 unlockTime;
}
/// @notice Get the global liquidity position
/// @return netSize The size of the net position held by all LPs
/// @return liquidationBufferNetSize The size of the net position held by all LPs in the liquidation buffer
/// @return entryPriceX96 The entry price of the net position held by all LPs, as a Q64.96
/// @return side The side of the position (Long or Short)
/// @return liquidity The total liquidity of all LPs
/// @return realizedProfitGrowthX64 The accumulated realized profit growth per liquidity unit, as a Q192.64
function globalLiquidityPosition()
external
view
returns (
uint128 netSize,
uint128 liquidationBufferNetSize,
uint160 entryPriceX96,
Side side,
uint128 liquidity,
uint256 realizedProfitGrowthX64
);
/// @notice Get the global unrealized loss metrics
/// @return lastZeroLossTime The time when the LP's net position no longer has unrealized losses
/// or the risk buffer fund has enough balance to cover the unrealized losses of all LPs
/// @return liquidity The total liquidity of all LPs whose entry time is
/// after `lastZeroLossTime`
/// @return liquidityTimesUnrealizedLoss The product of liquidity and unrealized loss for
/// each LP whose entry time is after `lastZeroLossTime`
function globalUnrealizedLossMetrics()
external
view
returns (uint64 lastZeroLossTime, uint128 liquidity, uint256 liquidityTimesUnrealizedLoss);
/// @notice Get the information of a liquidity position
/// @param positionID The position ID
/// @return margin The margin of the position
/// @return liquidity The liquidity (value) of the position
/// @return entryUnrealizedLoss The snapshot of unrealized loss of LP at the time of opening the position
/// @return entryRealizedProfitGrowthX64 The snapshot of `GlobalLiquidityPosition.realizedProfitGrowthX64`
/// at the time of opening the position, as a Q192.64
/// @return entryTime The time when the position is opened
/// @return account The owner of the position
function liquidityPositions(
uint96 positionID
)
external
view
returns (
uint128 margin,
uint128 liquidity,
uint256 entryUnrealizedLoss,
uint256 entryRealizedProfitGrowthX64,
uint64 entryTime,
address account
);
/// @notice Get the owner of a specific liquidity position
/// @param positionID The position ID
/// @return account The owner of the position, `address(0)` returned if the position does not exist
function liquidityPositionAccount(uint96 positionID) external view returns (address account);
/// @notice Open a new liquidity position
/// @dev The call will fail if the caller is not the `IRouter`
/// @param account The owner of the position
/// @param margin The margin of the position
/// @param liquidity The liquidity (value) of the position
/// @return positionID The position ID
function openLiquidityPosition(
address account,
uint128 margin,
uint128 liquidity
) external returns (uint96 positionID);
/// @notice Close a liquidity position
/// @dev The call will fail if the caller is not the `IRouter` or the position does not exist
/// @param positionID The position ID
/// @param receiver The address to receive the margin at the time of closing
function closeLiquidityPosition(uint96 positionID, address receiver) external;
/// @notice Adjust the margin of a liquidity position
/// @dev The call will fail if the caller is not the `IRouter` or the position does not exist
/// @param positionID The position ID
/// @param marginDelta The change in margin, positive for increasing margin and negative for decreasing margin
/// @param receiver The address to receive the margin when the margin is decreased
function adjustLiquidityPositionMargin(uint96 positionID, int128 marginDelta, address receiver) external;
/// @notice Liquidate a liquidity position
/// @dev The call will fail if the caller is not the liquidator or the position does not exist
/// @param positionID The position ID
/// @param feeReceiver The address to receive the liquidation execution fee
function liquidateLiquidityPosition(uint96 positionID, address feeReceiver) external;
/// @notice `Gov` uses the risk buffer fund
/// @dev The call will fail if the caller is not the `Gov` or
/// the adjusted remaining risk buffer fund cannot cover the unrealized loss
/// @param receiver The address to receive the risk buffer fund
/// @param riskBufferFundDelta The used risk buffer fund
function govUseRiskBufferFund(address receiver, uint128 riskBufferFundDelta) external;
/// @notice Get the global risk buffer fund
/// @return riskBufferFund The risk buffer fund, which accumulated by unrealized losses and price impact fees
/// paid by LPs when positions are closed or liquidated. It also accumulates the remaining margin of LPs
/// after liquidation. Additionally, the net profit or loss from closing LP's net position is also accumulated
/// in the risk buffer fund
/// @return liquidity The total liquidity of the risk buffer fund
function globalRiskBufferFund() external view returns (int256 riskBufferFund, uint256 liquidity);
/// @notice Get the liquidity of the risk buffer fund
/// @param account The owner of the position
/// @return liquidity The liquidity of the risk buffer fund
/// @return unlockTime The time when the liquidity can be withdrawn
function riskBufferFundPositions(address account) external view returns (uint128 liquidity, uint64 unlockTime);
/// @notice Increase the liquidity of a risk buffer fund position
/// @dev The call will fail if the caller is not the `IRouter`
/// @param account The owner of the position
/// @param liquidityDelta The increase in liquidity
function increaseRiskBufferFundPosition(address account, uint128 liquidityDelta) external;
/// @notice Decrease the liquidity of a risk buffer fund position
/// @dev The call will fail if the caller is not the `IRouter`
/// @param account The owner of the position
/// @param liquidityDelta The decrease in liquidity
/// @param receiver The address to receive the liquidity when it is decreased
function decreaseRiskBufferFundPosition(address account, uint128 liquidityDelta, address receiver) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Side} from "../../types/Side.sol";
/// @title Perpetual Pool Position Interface
/// @notice This interface defines the functions for managing positions in a perpetual pool
interface IPoolPosition {
/// @notice Emitted when the funding rate growth is adjusted
/// @param fundingRateDeltaX96 The change in funding rate, a positive value means longs pay shorts,
/// when a negative value means shorts pay longs, as a Q160.96
/// @param longFundingRateGrowthAfterX96 The adjusted `GlobalPosition.longFundingRateGrowthX96`, as a Q96.96
/// @param shortFundingRateGrowthAfterX96 The adjusted `GlobalPosition.shortFundingRateGrowthX96`, as a Q96.96
/// @param lastAdjustFundingRateTime The adjusted `GlobalFundingRateSample.lastAdjustFundingRateTime`
event FundingRateGrowthAdjusted(
int256 fundingRateDeltaX96,
int192 longFundingRateGrowthAfterX96,
int192 shortFundingRateGrowthAfterX96,
uint64 lastAdjustFundingRateTime
);
/// @notice Emitted when the position margin/liquidity (value) is increased
/// @param account The owner of the position
/// @param side The side of the position (Long or Short)
/// @param marginDelta The increased margin
/// @param marginAfter The adjusted margin
/// @param sizeAfter The adjusted position size
/// @param tradePriceX96 The trade price at which the position is adjusted.
/// If only adding margin, it returns 0, as a Q64.96
/// @param entryPriceAfterX96 The adjusted entry price of the position, as a Q64.96
/// @param fundingFee The funding fee, a positive value means the position receives funding fee,
/// while a negative value means the position positive pays funding fee
/// @param tradingFee The trading fee paid by the position
event PositionIncreased(
address indexed account,
Side side,
uint128 marginDelta,
uint128 marginAfter,
uint128 sizeAfter,
uint160 tradePriceX96,
uint160 entryPriceAfterX96,
int256 fundingFee,
uint128 tradingFee
);
/// @notice Emitted when the position margin/liquidity (value) is decreased
/// @param account The owner of the position
/// @param side The side of the position (Long or Short)
/// @param marginDelta The decreased margin
/// @param marginAfter The adjusted margin
/// @param sizeAfter The adjusted position size
/// @param tradePriceX96 The trade price at which the position is adjusted.
/// If only reducing margin, it returns 0, as a Q64.96
/// @param realizedPnLDelta The realized PnL
/// @param fundingFee The funding fee, a positive value means the position receives a funding fee,
/// while a negative value means the position pays funding fee
/// @param tradingFee The trading fee paid by the position
/// @param receiver The address that receives the margin
event PositionDecreased(
address indexed account,
Side side,
uint128 marginDelta,
uint128 marginAfter,
uint128 sizeAfter,
uint160 tradePriceX96,
int256 realizedPnLDelta,
int256 fundingFee,
uint128 tradingFee,
address receiver
);
/// @notice Emitted when a position is liquidated
/// @param liquidator The address that executes the liquidation of the position
/// @param account The owner of the position
/// @param side The side of the position (Long or Short)
/// @param indexPriceX96 The index price when liquidating the position, as a Q64.96
/// @param liquidationPriceX96 The liquidation price of the position, as a Q64.96
/// @param fundingFee The funding fee, a positive value means the position receives a funding fee,
/// while a negative value means the position pays funding fee. If it's negative,
/// it represents the actual funding fee paid during liquidation
/// @param tradingFee The trading fee paid by the position
/// @param liquidationFee The liquidation fee paid by the position
/// @param liquidationExecutionFee The liquidation execution fee paid by the position
/// @param feeReceiver The address that receives the liquidation execution fee
event PositionLiquidated(
address indexed liquidator,
address indexed account,
Side side,
uint160 indexPriceX96,
uint160 liquidationPriceX96,
int256 fundingFee,
uint128 tradingFee,
uint128 liquidationFee,
uint64 liquidationExecutionFee,
address feeReceiver
);
struct GlobalPosition {
uint128 longSize;
uint128 shortSize;
int192 longFundingRateGrowthX96;
int192 shortFundingRateGrowthX96;
}
struct PreviousGlobalFundingRate {
int192 longFundingRateGrowthX96;
int192 shortFundingRateGrowthX96;
}
struct GlobalFundingRateSample {
uint64 lastAdjustFundingRateTime;
uint16 sampleCount;
int176 cumulativePremiumRateX96;
}
struct Position {
uint128 margin;
uint128 size;
uint160 entryPriceX96;
int192 entryFundingRateGrowthX96;
}
/// @notice Get the global position
/// @return longSize The sum of long position sizes
/// @return shortSize The sum of short position sizes
/// @return longFundingRateGrowthX96 The funding rate growth per unit of long position sizes, as a Q96.96
/// @return shortFundingRateGrowthX96 The funding rate growth per unit of short position sizes, as a Q96.96
function globalPosition()
external
view
returns (
uint128 longSize,
uint128 shortSize,
int192 longFundingRateGrowthX96,
int192 shortFundingRateGrowthX96
);
/// @notice Get the previous global funding rate growth
/// @return longFundingRateGrowthX96 The funding rate growth per unit of long position sizes, as a Q96.96
/// @return shortFundingRateGrowthX96 The funding rate growth per unit of short position sizes, as a Q96.96
function previousGlobalFundingRate()
external
view
returns (int192 longFundingRateGrowthX96, int192 shortFundingRateGrowthX96);
/// @notice Get the global funding rate sample
/// @return lastAdjustFundingRateTime The timestamp of the last funding rate adjustment
/// @return sampleCount The number of samples taken since the last funding rate adjustment
/// @return cumulativePremiumRateX96 The cumulative premium rate of the samples taken
/// since the last funding rate adjustment, as a Q80.96
function globalFundingRateSample()
external
view
returns (uint64 lastAdjustFundingRateTime, uint16 sampleCount, int176 cumulativePremiumRateX96);
/// @notice Get the information of a position
/// @param account The owner of the position
/// @param side The side of the position (Long or Short)
/// @return margin The margin of the position
/// @return size The size of the position
/// @return entryPriceX96 The entry price of the position, as a Q64.96
/// @return entryFundingRateGrowthX96 The snapshot of the funding rate growth at the time the position was opened.
/// For long positions it is `GlobalPosition.longFundingRateGrowthX96`,
/// and for short positions it is `GlobalPosition.shortFundingRateGrowthX96`
function positions(
address account,
Side side
) external view returns (uint128 margin, uint128 size, uint160 entryPriceX96, int192 entryFundingRateGrowthX96);
/// @notice Increase the margin/liquidity (value) of a position
/// @dev The call will fail if the caller is not the `IRouter`
/// @param account The owner of the position
/// @param side The side of the position (Long or Short)
/// @param marginDelta The increase in margin, which can be 0
/// @param sizeDelta The increase in size, which can be 0
/// @return tradePriceX96 The trade price at which the position is adjusted.
/// If only adding margin, it returns 0, as a Q64.96
function increasePosition(
address account,
Side side,
uint128 marginDelta,
uint128 sizeDelta
) external returns (uint160 tradePriceX96);
/// @notice Decrease the margin/liquidity (value) of a position
/// @dev The call will fail if the caller is not the `IRouter` or the position does not exist
/// @param account The owner of the position
/// @param side The side of the position (Long or Short)
/// @param marginDelta The decrease in margin, which can be 0
/// @param sizeDelta The decrease in size, which can be 0
/// @param receiver The address to receive the margin
/// @return tradePriceX96 The trade price at which the position is adjusted.
/// If only reducing margin, it returns 0, as a Q64.96
function decreasePosition(
address account,
Side side,
uint128 marginDelta,
uint128 sizeDelta,
address receiver
) external returns (uint160 tradePriceX96);
/// @notice Liquidate a position
/// @dev The call will fail if the caller is not the liquidator or the position does not exist
/// @param account The owner of the position
/// @param side The side of the position (Long or Short)
/// @param feeReceiver The address that receives the liquidation execution fee
function liquidatePosition(address account, Side side, address feeReceiver) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "../../core/interfaces/IPool.sol";
import {Bitmap} from "../../types/Bitmap.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IRewardFarm {
/// @notice Emitted when the reward debt is changed
/// @param pool The address of the pool
/// @param account The owner of the liquidity reward
/// @param rewardDebtDelta The change in reward debt for the account
event LiquidityRewardDebtChanged(IPool indexed pool, address indexed account, uint256 rewardDebtDelta);
/// @notice Emitted when the liquidity reward is collected
/// @param pools The pool addresses
/// @param account The owner of the liquidity reward
/// @param receiver The address to receive the liquidity reward
/// @param rewardDebt The amount of liquidity reward received
event LiquidityRewardCollected(
IPool[] pools,
address indexed account,
address indexed receiver,
uint256 rewardDebt
);
/// @notice Emitted when the risk buffer fund reward debt is changed
/// @param pool The address of the pool
/// @param account The owner of the risk buffer fund reward
/// @param rewardDebtDelta The change in reward debt for the account
event RiskBufferFundRewardDebtChanged(IPool indexed pool, address indexed account, uint256 rewardDebtDelta);
/// @notice Emitted when the risk buffer fund reward is collected
/// @param pools The pool addresses
/// @param account The owner of the risk buffer fund reward
/// @param receiver The address to receive the liquidity reward
/// @param rewardDebt The amount of risk buffer fund reward received
event RiskBufferFundRewardCollected(
IPool[] pools,
address indexed account,
address indexed receiver,
uint256 rewardDebt
);
/// @notice Emitted when the liquidity reward growth is increased
/// @param pool The address of the pool
/// @param rewardDelta The change in liquidity reward for the pool
/// @param rewardGrowthAfterX64 The adjusted `PoolReward.liquidityRewardGrowthX64`, as a Q64.64
event PoolLiquidityRewardGrowthIncreased(IPool indexed pool, uint256 rewardDelta, uint128 rewardGrowthAfterX64);
/// @notice Emitted when the referral token reward growth is increased
/// @param pool The address of the pool
/// @param rewardDelta The change in referral token reward for the pool
/// @param rewardGrowthAfterX64 The adjusted `PoolReward.referralTokenRewardGrowthX64`, as a Q64.64
/// @param positionRewardDelta The change in referral token position reward for the pool
/// @param positionRewardGrowthAfterX64 The adjusted
/// `PoolReward.referralTokenPositionRewardGrowthX64`, as a Q64.64
event PoolReferralTokenRewardGrowthIncreased(
IPool indexed pool,
uint256 rewardDelta,
uint128 rewardGrowthAfterX64,
uint256 positionRewardDelta,
uint128 positionRewardGrowthAfterX64
);
/// @notice Emitted when the referral token reward growth is increased
/// @param pool The address of the pool
/// @param rewardDelta The change in referral parent token reward for the pool
/// @param rewardGrowthAfterX64 The adjusted `PoolReward.referralParentTokenRewardGrowthX64`, as a Q64.64
/// @param positionRewardDelta The change in referral parent token position reward for the pool
/// @param positionRewardGrowthAfterX64 The adjusted
/// `PoolReward.referralParentTokenPositionRewardGrowthX64`, as a Q64.64
event PoolReferralParentTokenRewardGrowthIncreased(
IPool indexed pool,
uint256 rewardDelta,
uint128 rewardGrowthAfterX64,
uint256 positionRewardDelta,
uint128 positionRewardGrowthAfterX64
);
/// @notice Emitted when the risk buffer fund reward growth is increased
/// @param pool The address of the pool
/// @param rewardDelta The change in risk buffer fund reward for the pool
/// @param rewardGrowthAfterX64 The adjusted `PoolReward.riskBufferFundRewardGrowthX64`, as a Q64.64
event PoolRiskBufferFundRewardGrowthIncreased(
IPool indexed pool,
uint256 rewardDelta,
uint128 rewardGrowthAfterX64
);
/// @notice Emitted when the pool reward updated
/// @param pool The address of the pool
/// @param rewardPerSecond The amount minted per second
event PoolRewardUpdated(IPool indexed pool, uint160 rewardPerSecond);
/// @notice Emitted when the referral liquidity reward debt is changed
/// @param pool The address of the pool
/// @param referralToken The ID of the referral token
/// @param rewardDebtDelta The change in reward debt for the referral token
event ReferralLiquidityRewardDebtChanged(
IPool indexed pool,
uint256 indexed referralToken,
uint256 rewardDebtDelta
);
/// @notice Emitted when the referral position reward debt is changed
/// @param pool The address of the pool
/// @param referralToken The ID of the referral token
/// @param rewardDebtDelta The change in reward debt for the referral token
event ReferralPositionRewardDebtChanged(IPool indexed pool, uint256 indexed referralToken, uint256 rewardDebtDelta);
/// @notice Emitted when the referral reward is collected
/// @param pools The pool addresses
/// @param referralTokens The IDs of the referral tokens
/// @param receiver The address to receive the referral reward
/// @param rewardDebt The amount of the referral reward received
event ReferralRewardCollected(
IPool[] pools,
uint256[] referralTokens,
address indexed receiver,
uint256 rewardDebt
);
/// @notice Emitted when configuration is changed
/// @param newConfig The new configuration
event ConfigChanged(Config newConfig);
/// @notice Emitted when the reward cap is changed
/// @param rewardCapAfter The reward cap after change
event RewardCapChanged(uint128 rewardCapAfter);
/// @notice Invalid caller
error InvalidCaller(address caller);
/// @notice Invalid argument
error InvalidArgument();
/// @notice Invalid pool
error InvalidPool(IPool pool);
/// @notice Invalid mint time
/// @param mintTime The time of starting minting
error InvalidMintTime(uint64 mintTime);
/// @notice Invalid mining rate
/// @param rate The rate of mining
error InvalidMiningRate(uint256 rate);
/// @notice Too many pools
error TooManyPools();
/// @notice Invalid reward cap
error InvalidRewardCap();
struct Config {
uint32 liquidityRate;
uint32 riskBufferFundLiquidityRate;
uint32 referralTokenRate;
uint32 referralParentTokenRate;
}
struct PoolReward {
uint128 liquidity;
uint128 liquidityRewardGrowthX64;
uint128 referralLiquidity;
uint128 referralTokenRewardGrowthX64;
uint128 referralParentTokenRewardGrowthX64;
uint128 referralPosition;
uint128 referralTokenPositionRewardGrowthX64;
uint128 referralParentTokenPositionRewardGrowthX64;
uint128 riskBufferFundLiquidity;
uint128 riskBufferFundRewardGrowthX64;
uint128 rewardPerSecond;
uint128 lastMintTime;
}
struct Reward {
/// @dev The liquidity of risk buffer fund position or LP position
uint128 liquidity;
/// @dev The snapshot of `PoolReward.riskBufferFundRewardGrowthX64` or `PoolReward.liquidityRewardGrowthX64`
uint128 rewardGrowthX64;
}
struct RewardWithPosition {
/// @dev The total liquidity of all referees
uint128 liquidity;
/// @dev The snapshot of
/// `PoolReward.referralTokenRewardGrowthX64` or `PoolReward.referralParentTokenRewardGrowthX64`
uint128 rewardGrowthX64;
/// @dev The total position value of all referees
uint128 position;
/// @dev The snapshot of
/// `PoolReward.referralTokenPositionRewardGrowthX64` or `PoolReward.referralParentTokenPositionRewardGrowthX64`
uint128 positionRewardGrowthX64;
}
struct ReferralReward {
/// @dev Unclaimed reward amount
uint256 rewardDebt;
/// @dev Mapping of pool to referral reward
mapping(IPool => RewardWithPosition) rewards;
}
struct RiskBufferFundReward {
/// @dev Unclaimed reward amount
uint256 rewardDebt;
/// @dev Mapping of pool to risk buffer fund reward
mapping(IPool => Reward) rewards;
}
struct LiquidityReward {
/// @dev The bitwise representation of the pool index with existing LP position
Bitmap bitmap;
/// @dev Unclaimed reward amount
uint256 rewardDebt;
/// @dev Mapping of pool to liquidity reward
mapping(IPool => Reward) rewards;
}
struct SidePosition {
/// @dev Value of long position
uint128 long;
/// @dev Value of short position
uint128 short;
}
struct Position {
/// @dev The bitwise representation of the pool index with existing position
Bitmap bitmap;
/// @dev Mapping of pool to position value
mapping(IPool => SidePosition) sidePositions;
}
/// @notice Get mining rate configuration
/// @return liquidityRate The liquidity rate as a percentage of mining,
/// denominated in ten thousandths of a bip (i.e. 1e-8)
/// @return riskBufferFundLiquidityRate The risk buffer fund liquidity rate as a percentage of mining,
/// denominated in ten thousandths of a bip (i.e. 1e-8)
/// @return referralTokenRate The referral token rate as a percentage of mining,
/// denominated in ten thousandths of a bip (i.e. 1e-8)
/// @return referralParentTokenRate The referral parent token rate as a percentage of mining,
/// denominated in ten thousandths of a bip (i.e. 1e-8)
function config()
external
view
returns (
uint32 liquidityRate,
uint32 riskBufferFundLiquidityRate,
uint32 referralTokenRate,
uint32 referralParentTokenRate
);
/// @notice Get the pool reward
/// @param pool The address of the pool
/// @return liquidity The sum of all liquidity in this pool
/// @return liquidityRewardGrowthX64 The reward growth per unit of liquidity, as a Q64.64
/// @return referralLiquidity The sum of all the referral liquidity
/// @return referralTokenRewardGrowthX64 The reward growth per unit of referral token liquidity, as a Q64.64
/// @return referralParentTokenRewardGrowthX64 The reward growth per unit of referral token parent liquidity,
/// as a Q64.64
/// @return referralPosition The sum of all the referral position liquidity
/// @return referralTokenPositionRewardGrowthX64 The reward growth per unit of referral token position liquidity,
/// as a Q64.64
/// @return referralParentTokenPositionRewardGrowthX64 The reward growth per unit of referral token parent
/// position, as a Q64.64
/// @return riskBufferFundLiquidity The sum of the liquidity of all risk buffer fund
/// @return riskBufferFundRewardGrowthX64 The reward growth per unit of risk buffer fund liquidity, as a Q64.64
/// @return rewardPerSecond The amount minted per second
/// @return lastMintTime The Last mint time
function poolRewards(
IPool pool
)
external
view
returns (
uint128 liquidity,
uint128 liquidityRewardGrowthX64,
uint128 referralLiquidity,
uint128 referralTokenRewardGrowthX64,
uint128 referralParentTokenRewardGrowthX64,
uint128 referralPosition,
uint128 referralTokenPositionRewardGrowthX64,
uint128 referralParentTokenPositionRewardGrowthX64,
uint128 riskBufferFundLiquidity,
uint128 riskBufferFundRewardGrowthX64,
uint128 rewardPerSecond,
uint128 lastMintTime
);
/// @notice Collect the liquidity reward
/// @param pools The pool addresses
/// @param account The owner of the liquidity reward
/// @param receiver The address to receive the reward
/// @return rewardDebt The amount of liquidity reward received
function collectLiquidityRewardBatch(
IPool[] calldata pools,
address account,
address receiver
) external returns (uint256 rewardDebt);
/// @notice Collect the risk buffer fund reward
/// @param pools The pool addresses
/// @param account The owner of the risk buffer fund reward
/// @param receiver The address to receive the reward
/// @return rewardDebt The amount of risk buffer fund reward received
function collectRiskBufferFundRewardBatch(
IPool[] calldata pools,
address account,
address receiver
) external returns (uint256 rewardDebt);
/// @notice Collect the referral reward
/// @param pools The pool addresses
/// @param referralTokens The IDs of the referral tokens
/// @param receiver The address to receive the referral reward
/// @return rewardDebt The amount of the referral reward
function collectReferralRewardBatch(
IPool[] calldata pools,
uint256[] calldata referralTokens,
address receiver
) external returns (uint256 rewardDebt);
/// @notice Set reward data for the pool
/// @param pools The pool addresses
/// @param rewardsPerSecond The EQU amount minted per second for pools
function setPoolsReward(IPool[] calldata pools, uint128[] calldata rewardsPerSecond) external;
/// @notice Set configuration information
/// @param config The configuration
function setConfig(Config memory config) external;
/// @notice Set the reward cap
/// @param rewardCap The reward cap
function setRewardCap(uint128 rewardCap) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
abstract contract Governable {
address private _gov;
address private _pendingGov;
event ChangeGovStarted(address indexed previousGov, address indexed newGov);
event GovChanged(address indexed previousGov, address indexed newGov);
error Forbidden();
modifier onlyGov() {
_onlyGov();
_;
}
constructor() {
_changeGov(msg.sender);
}
function gov() public view virtual returns (address) {
return _gov;
}
function pendingGov() public view virtual returns (address) {
return _pendingGov;
}
function changeGov(address _newGov) public virtual onlyGov {
_pendingGov = _newGov;
emit ChangeGovStarted(_gov, _newGov);
}
function acceptGov() public virtual {
if (msg.sender != _pendingGov) revert Forbidden();
delete _pendingGov;
_changeGov(msg.sender);
}
function _changeGov(address _newGov) internal virtual {
address previousGov = _gov;
_gov = _newGov;
emit GovChanged(previousGov, _newGov);
}
function _onlyGov() internal view {
if (msg.sender != _gov) revert Forbidden();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.19;
/**
* @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 v4.9.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.19;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
import {Address} from "@openzeppelin/contracts/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 Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
if (nonceAfter != nonceBefore + 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "../../core/interfaces/IPool.sol";
interface IOrderBook {
/// @notice Emitted when min execution fee updated
/// @param minExecutionFee The new min execution fee after the update
event MinExecutionFeeUpdated(uint256 minExecutionFee);
/// @notice Emitted when order executor updated
/// @param account The account to update
/// @param active Updated status
event OrderExecutorUpdated(address indexed account, bool active);
/// @notice Emitted when increase order created
/// @param account Owner of the increase order
/// @param pool The address of the pool to increase position
/// @param side The side of the position (Long or Short)
/// @param marginDelta The increase in margin
/// @param sizeDelta The increase in size
/// @param triggerMarketPriceX96 Market price to trigger the order, as a Q64.96
/// @param triggerAbove Execute the order when current price is greater than or
/// equal to trigger price if `true` and vice versa
/// @param acceptableTradePriceX96 Acceptable worst trade price of the order, as a Q64.96
/// @param executionFee Amount of fee for the executor to carry out the order
/// @param orderIndex Index of the order
event IncreaseOrderCreated(
address indexed account,
IPool indexed pool,
Side side,
uint128 marginDelta,
uint128 sizeDelta,
uint160 triggerMarketPriceX96,
bool triggerAbove,
uint160 acceptableTradePriceX96,
uint256 executionFee,
uint256 indexed orderIndex
);
/// @notice Emitted when increase order updated
/// @param orderIndex Index of the updated order
/// @param triggerMarketPriceX96 The new market price to trigger the order, as a Q64.96
/// @param acceptableTradePriceX96 The new acceptable worst trade price of the order, as a Q64.96
event IncreaseOrderUpdated(
uint256 indexed orderIndex,
uint160 triggerMarketPriceX96,
uint160 acceptableTradePriceX96
);
/// @notice Emitted when increase order cancelled
/// @param orderIndex Index of the cancelled order
/// @param feeReceiver Receiver of the order execution fee
event IncreaseOrderCancelled(uint256 indexed orderIndex, address payable feeReceiver);
/// @notice Emitted when order executed
/// @param orderIndex Index of the executed order
/// @param marketPriceX96 Actual execution price, as a Q64.96
/// @param feeReceiver Receiver of the order execution fee
event IncreaseOrderExecuted(uint256 indexed orderIndex, uint160 marketPriceX96, address payable feeReceiver);
/// @notice Emitted when decrease order created
/// @param account Owner of the decrease order
/// @param pool The address of the pool to decrease position
/// @param side The side of the position (Long or Short)
/// @param marginDelta The decrease in margin
/// @param sizeDelta The decrease in size
/// Note if zero, we treat it as a close position request, which will close the position,
/// ignoring the `marginDelta` and `acceptableTradePriceX96`
/// @param triggerMarketPriceX96 Market price to trigger the order, as a Q64.96
/// @param triggerAbove Execute the order when current price is greater than or
/// equal to trigger price if `true` and vice versa
/// @param acceptableTradePriceX96 Acceptable worst trade price of the order, as a Q64.96
/// @param receiver Margin recipient address
/// @param executionFee Amount of fee for the executor to carry out the order
/// @param orderIndex Index of the order
event DecreaseOrderCreated(
address indexed account,
IPool indexed pool,
Side side,
uint128 marginDelta,
uint128 sizeDelta,
uint160 triggerMarketPriceX96,
bool triggerAbove,
uint160 acceptableTradePriceX96,
address receiver,
uint256 executionFee,
uint256 indexed orderIndex
);
/// @notice Emitted when decrease order updated
/// @param orderIndex Index of the decrease order
/// @param triggerMarketPriceX96 The new market price to trigger the order, as a Q64.96
/// @param acceptableTradePriceX96 The new acceptable worst trade price of the order, as a Q64.96
event DecreaseOrderUpdated(
uint256 indexed orderIndex,
uint160 triggerMarketPriceX96,
uint160 acceptableTradePriceX96
);
/// @notice Emitted when decrease order cancelled
/// @param orderIndex Index of the cancelled order
/// @param feeReceiver Receiver of the order execution fee
event DecreaseOrderCancelled(uint256 indexed orderIndex, address feeReceiver);
/// @notice Emitted when decrease order executed
/// @param orderIndex Index of the executed order
/// @param marketPriceX96 The market price when execution, as a Q64.96
/// @param feeReceiver Receiver of the order execution fee
event DecreaseOrderExecuted(uint256 indexed orderIndex, uint160 marketPriceX96, address payable feeReceiver);
/// @notice Execution fee is insufficient
/// @param available The available execution fee amount
/// @param required The required minimum execution fee amount
error InsufficientExecutionFee(uint256 available, uint256 required);
/// @notice Order not exists
/// @param orderIndex The order index
error OrderNotExists(uint256 orderIndex);
/// @notice Current market price is invalid to trigger the order
/// @param marketPriceX96 The current market price, as a Q64.96
/// @param triggerMarketPriceX96 The trigger market price, as a Q64.96
error InvalidMarketPriceToTrigger(uint160 marketPriceX96, uint160 triggerMarketPriceX96);
/// @notice Trade price exceeds limit
/// @param tradePriceX96 The trade price, as a Q64.96
/// @param acceptableTradePriceX96 The acceptable trade price, as a Q64.96
error InvalidTradePrice(uint160 tradePriceX96, uint160 acceptableTradePriceX96);
struct IncreaseOrder {
address account;
IPool pool;
Side side;
uint128 marginDelta;
uint128 sizeDelta;
uint160 triggerMarketPriceX96;
bool triggerAbove;
uint160 acceptableTradePriceX96;
uint256 executionFee;
}
struct DecreaseOrder {
address account;
IPool pool;
Side side;
uint128 marginDelta;
uint128 sizeDelta;
uint160 triggerMarketPriceX96;
bool triggerAbove;
uint160 acceptableTradePriceX96;
address receiver;
uint256 executionFee;
}
/// @notice Update minimum execution fee
/// @param minExecutionFee New min execution fee
function updateMinExecutionFee(uint256 minExecutionFee) external;
/// @notice Update order executor
/// @param account Account to update
/// @param active Updated status
function updateOrderExecutor(address account, bool active) external;
/// @notice Update the gas limit for executing requests
/// @param executionGasLimit New execution gas limit
function updateExecutionGasLimit(uint256 executionGasLimit) external;
/// @notice Create an order to open or increase the size of an existing position
/// @param pool The pool address of position to create increase order
/// @param side The side of the position (Long or Short)
/// @param marginDelta The increase in margin
/// @param sizeDelta The increase in size
/// @param triggerMarketPriceX96 Market price to trigger the order, as a Q64.96
/// @param triggerAbove Execute the order when current price is greater than or
/// equal to trigger price if `true` and vice versa
/// @param acceptableTradePriceX96 Acceptable worst trade price of the order, as a Q64.96
/// @return orderIndex Index of the order
function createIncreaseOrder(
IPool pool,
Side side,
uint128 marginDelta,
uint128 sizeDelta,
uint160 triggerMarketPriceX96,
bool triggerAbove,
uint160 acceptableTradePriceX96
) external payable returns (uint256 orderIndex);
/// @notice Update an existing increase order
/// @param orderIndex The index of order to update
/// @param triggerMarketPriceX96 The new market price to trigger the order, as a Q64.96
/// @param acceptableTradePriceX96 The new acceptable worst trade price of the order, as a Q64.96
function updateIncreaseOrder(
uint256 orderIndex,
uint160 triggerMarketPriceX96,
uint160 acceptableTradePriceX96
) external;
/// @notice Cancel an existing increase order
/// @param orderIndex The index of order to cancel
/// @param feeReceiver Receiver of the order execution fee
function cancelIncreaseOrder(uint256 orderIndex, address payable feeReceiver) external;
/// @notice Execute an existing increase order
/// @param orderIndex The index of order to execute
/// @param feeReceiver Receiver of the order execution fee
function executeIncreaseOrder(uint256 orderIndex, address payable feeReceiver) external;
/// @notice Create an order to close or decrease the size of an existing position
/// @param pool The address of the pool to create decrease order
/// @param side The side of the position (Long or Short)
/// @param marginDelta The decrease in margin
/// @param sizeDelta The decrease in size
/// Note if zero, we treat it as a close position request, which will close the position,
/// ignoring the `marginDelta` and `acceptableTradePriceX96`
/// @param triggerMarketPriceX96 Market price to trigger the order, as a Q64.96
/// @param triggerAbove Execute the order when current price is greater than or
/// equal to trigger price if `true` and vice versa
/// @param acceptableTradePriceX96 Acceptable worst trade price of the order, as a Q64.96
/// @param receiver Margin recipient address
/// @return orderIndex Index of the order
function createDecreaseOrder(
IPool pool,
Side side,
uint128 marginDelta,
uint128 sizeDelta,
uint160 triggerMarketPriceX96,
bool triggerAbove,
uint160 acceptableTradePriceX96,
address receiver
) external payable returns (uint256 orderIndex);
/// @notice Update an existing decrease order
/// @param orderIndex The index of order to update
/// @param triggerMarketPriceX96 The new market price to trigger the order, as a Q64.96
/// @param acceptableTradePriceX96 The new acceptable worst trade price of the order, as a Q64.96
function updateDecreaseOrder(
uint256 orderIndex,
uint160 triggerMarketPriceX96,
uint160 acceptableTradePriceX96
) external;
/// @notice Cancel an existing decrease order
/// @param orderIndex The index of order to cancel
/// @param feeReceiver Receiver of the order execution fee
function cancelDecreaseOrder(uint256 orderIndex, address payable feeReceiver) external;
/// @notice Execute an existing decrease order
/// @param orderIndex The index of order to execute
/// @param feeReceiver Receiver of the order execution fee
function executeDecreaseOrder(uint256 orderIndex, address payable feeReceiver) external;
/// @notice Create take-profit and stop-loss orders in a single call
/// @param pool The pool address of position to create orders
/// @param side The side of the position (Long or Short)
/// @param marginDeltas The decreases in margin
/// @param sizeDeltas The decreases in size
/// @param triggerMarketPriceX96s Market prices to trigger the order, as Q64.96s
/// @param acceptableTradePriceX96s Acceptable worst trade prices of the orders, as Q64.96s
/// @param receiver Margin recipient address
function createTakeProfitAndStopLossOrders(
IPool pool,
Side side,
uint128[2] calldata marginDeltas,
uint128[2] calldata sizeDeltas,
uint160[2] calldata triggerMarketPriceX96s,
uint160[2] calldata acceptableTradePriceX96s,
address receiver
) external payable;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title Plugin Manager Interface
/// @notice The interface defines the functions to manage plugins
interface IPluginManager {
/// @notice Emitted when a new plugin is registered
/// @param plugin The registered plugin
event PluginRegistered(address indexed plugin);
/// @notice Emitted when a plugin is approved
/// @param account The account that approved the plugin
/// @param plugin The approved plugin
event PluginApproved(address indexed account, address indexed plugin);
/// @notice Emitted when a plugin is revoked
/// @param account The account that revoked the plugin
/// @param plugin The revoked plugin
event PluginRevoked(address indexed account, address indexed plugin);
/// @notice Emitted when a new liquidator is registered
/// @param liquidator The registered liquidator
event LiquidatorRegistered(address indexed liquidator);
/// @notice Plugin is already registered
error PluginAlreadyRegistered(address plugin);
/// @notice Plugin is not registered
error PluginNotRegistered(address plugin);
/// @notice Plugin is already approved
error PluginAlreadyApproved(address sender, address plugin);
/// @notice Plugin is not approved
error PluginNotApproved(address sender, address plugin);
/// @notice Liquidator is already registered
error LiquidatorAlreadyRegistered(address liquidator);
/// @notice Register a new plugin
/// @dev The call will fail if the caller is not the governor or the plugin is already registered
/// @param plugin The plugin to register
function registerPlugin(address plugin) external;
/// @notice Checks if a plugin is registered
/// @param plugin The plugin to check
/// @return True if the plugin is registered, false otherwise
function registeredPlugins(address plugin) external view returns (bool);
/// @notice Approve a plugin
/// @dev The call will fail if the plugin is not registered or already approved
/// @param plugin The plugin to approve
function approvePlugin(address plugin) external;
/// @notice Revoke approval for a plugin
/// @dev The call will fail if the plugin is not approved
/// @param plugin The plugin to revoke
function revokePlugin(address plugin) external;
/// @notice Checks if a plugin is approved for an account
/// @param account The account to check
/// @param plugin The plugin to check
/// @return True if the plugin is approved for the account, false otherwise
function isPluginApproved(address account, address plugin) external view returns (bool);
/// @notice Register a new liquidator
/// @dev The call will fail if the caller if not the governor or the liquidator is already registered
/// @param liquidator The liquidator to register
function registerLiquidator(address liquidator) external;
/// @notice Checks if a liquidator is registered
/// @param liquidator The liquidator to check
/// @return True if the liquidator is registered, false otherwise
function isRegisteredLiquidator(address liquidator) external view returns (bool);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import "../governance/Governable.sol";
import "./interfaces/IPluginManager.sol";
abstract contract PluginManager is IPluginManager, Governable {
/// @inheritdoc IPluginManager
mapping(address => bool) public override registeredPlugins;
mapping(address => bool) private registeredLiquidators;
mapping(address => mapping(address => bool)) private pluginApprovals;
/// @inheritdoc IPluginManager
function registerPlugin(address _plugin) external override onlyGov {
if (registeredPlugins[_plugin]) revert PluginAlreadyRegistered(_plugin);
registeredPlugins[_plugin] = true;
emit PluginRegistered(_plugin);
}
/// @inheritdoc IPluginManager
function approvePlugin(address _plugin) external override {
if (pluginApprovals[msg.sender][_plugin]) revert PluginAlreadyApproved(msg.sender, _plugin);
if (!registeredPlugins[_plugin]) revert PluginNotRegistered(_plugin);
pluginApprovals[msg.sender][_plugin] = true;
emit PluginApproved(msg.sender, _plugin);
}
/// @inheritdoc IPluginManager
function revokePlugin(address _plugin) external {
if (!pluginApprovals[msg.sender][_plugin]) revert PluginNotApproved(msg.sender, _plugin);
delete pluginApprovals[msg.sender][_plugin];
emit PluginRevoked(msg.sender, _plugin);
}
/// @inheritdoc IPluginManager
function isPluginApproved(address _account, address _plugin) public view override returns (bool) {
return pluginApprovals[_account][_plugin];
}
/// @inheritdoc IPluginManager
function registerLiquidator(address _liquidator) external override onlyGov {
if (registeredLiquidators[_liquidator]) revert LiquidatorAlreadyRegistered(_liquidator);
registeredLiquidators[_liquidator] = true;
emit LiquidatorRegistered(_liquidator);
}
/// @inheritdoc IPluginManager
function isRegisteredLiquidator(address _liquidator) public view override returns (bool) {
return registeredLiquidators[_liquidator];
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.21;
import "./PluginManager.sol";
import "../libraries/SafeERC20.sol";
import "../tokens/interfaces/IEFC.sol";
import "../farming/interfaces/IRewardFarm.sol";
import "../staking/interfaces/IFeeDistributor.sol";
contract Router is PluginManager {
IEFC public immutable EFC;
IRewardFarm public immutable rewardFarm;
IFeeDistributor public immutable feeDistributor;
/// @notice Caller is not a plugin or not approved
error CallerUnauthorized();
/// @notice Owner mismatch
error OwnerMismatch(address owner, address expectedOwner);
constructor(IEFC _EFC, IRewardFarm _rewardFarm, IFeeDistributor _feeDistributor) {
(EFC, rewardFarm, feeDistributor) = (_EFC, _rewardFarm, _feeDistributor);
}
/// @notice Transfers `_amount` of `_token` from `_from` to `_to`
/// @param _token The address of the ERC20 token
/// @param _from The address to transfer the tokens from
/// @param _to The address to transfer the tokens to
/// @param _amount The amount of tokens to transfer
function pluginTransfer(IERC20 _token, address _from, address _to, uint256 _amount) external {
_onlyPluginApproved(_from);
SafeERC20.safeTransferFrom(_token, _from, _to, _amount);
}
/// @notice Transfers an NFT token from `_from` to `_to`
/// @param _token The address of the ERC721 token to transfer
/// @param _from The address to transfer the NFT from
/// @param _to The address to transfer the NFT to
/// @param _tokenId The ID of the NFT token to transfer
function pluginTransferNFT(IERC721 _token, address _from, address _to, uint256 _tokenId) external {
_onlyPluginApproved(_from);
_token.safeTransferFrom(_from, _to, _tokenId);
}
/// @notice Open a new liquidity position
/// @param _pool The pool in which to open liquidity position
/// @param _account The owner of the position
/// @param _margin The margin of the position
/// @param _liquidity The liquidity (value) of the position
/// @return positionID The position ID
function pluginOpenLiquidityPosition(
IPool _pool,
address _account,
uint128 _margin,
uint128 _liquidity
) external returns (uint96 positionID) {
_onlyPluginApproved(_account);
return _pool.openLiquidityPosition(_account, _margin, _liquidity);
}
/// @notice Close a liquidity position
/// @param _pool The pool in which to close liquidity position
/// @param _positionID The position ID
/// @param _receiver The address to receive the margin at the time of closing
function pluginCloseLiquidityPosition(IPool _pool, uint96 _positionID, address _receiver) external {
_onlyPluginApproved(_pool.liquidityPositionAccount(_positionID));
_pool.closeLiquidityPosition(_positionID, _receiver);
}
/// @notice Adjust the margin of a liquidity position
/// @param _pool The pool in which to adjust liquidity position margin
/// @param _positionID The position ID
/// @param _marginDelta The change in margin, positive for increasing margin and negative for decreasing margin
/// @param _receiver The address to receive the margin when the margin is decreased
function pluginAdjustLiquidityPositionMargin(
IPool _pool,
uint96 _positionID,
int128 _marginDelta,
address _receiver
) external {
_onlyPluginApproved(_pool.liquidityPositionAccount(_positionID));
_pool.adjustLiquidityPositionMargin(_positionID, _marginDelta, _receiver);
}
/// @notice Increase the liquidity of a risk buffer fund position
/// @param _pool The pool in which to increase liquidity
/// @param _account The owner of the position
/// @param _liquidityDelta The increase in liquidity
function pluginIncreaseRiskBufferFundPosition(IPool _pool, address _account, uint128 _liquidityDelta) external {
_onlyPluginApproved(_account);
_pool.increaseRiskBufferFundPosition(_account, _liquidityDelta);
}
/// @notice Decrease the liquidity of a risk buffer fund position
/// @param _pool The pool in which to decrease liquidity
/// @param _account The owner of the position
/// @param _liquidityDelta The decrease in liquidity
/// @param _receiver The address to receive the liquidity when it is decreased
function pluginDecreaseRiskBufferFundPosition(
IPool _pool,
address _account,
uint128 _liquidityDelta,
address _receiver
) external {
_onlyPluginApproved(_account);
_pool.decreaseRiskBufferFundPosition(_account, _liquidityDelta, _receiver);
}
/// @notice Increase the margin/liquidity (value) of a position
/// @param _pool The pool in which to increase position
/// @param _account The owner of the position
/// @param _side The side of the position (Long or Short)
/// @param _marginDelta The increase in margin, which can be 0
/// @param _sizeDelta The increase in size, which can be 0
/// @return tradePriceX96 The trade price at which the position is adjusted.
/// If only adding margin, it returns 0, as a Q64.96
function pluginIncreasePosition(
IPool _pool,
address _account,
Side _side,
uint128 _marginDelta,
uint128 _sizeDelta
) external returns (uint160 tradePriceX96) {
_onlyPluginApproved(_account);
return _pool.increasePosition(_account, _side, _marginDelta, _sizeDelta);
}
/// @notice Decrease the margin/liquidity (value) of a position
/// @param _pool The pool in which to decrease position
/// @param _account The owner of the position
/// @param _side The side of the position (Long or Short)
/// @param _marginDelta The decrease in margin, which can be 0
/// @param _sizeDelta The decrease in size, which can be 0
/// @param _receiver The address to receive the margin
/// @return tradePriceX96 The trade price at which the position is adjusted.
/// If only reducing margin, it returns 0, as a Q64.96
function pluginDecreasePosition(
IPool _pool,
address _account,
Side _side,
uint128 _marginDelta,
uint128 _sizeDelta,
address _receiver
) external returns (uint160 tradePriceX96) {
_onlyPluginApproved(_account);
return _pool.decreasePosition(_account, _side, _marginDelta, _sizeDelta, _receiver);
}
/// @notice Close a position by the liquidator
/// @param _pool The pool in which to close position
/// @param _account The owner of the position
/// @param _side The side of the position (Long or Short)
/// @param _sizeDelta The decrease in size
/// @param _receiver The address to receive the margin
function pluginClosePositionByLiquidator(
IPool _pool,
address _account,
Side _side,
uint128 _sizeDelta,
address _receiver
) external {
_onlyLiquidator();
_pool.decreasePosition(_account, _side, 0, _sizeDelta, _receiver);
}
/// @notice Collect the referral fee
/// @param _pool The pool in which to collect referral fee
/// @param _referralToken The id of the referral token
/// @param _receiver The address to receive the referral fee
/// @return The amount of referral fee received
function pluginCollectReferralFee(
IPool _pool,
uint256 _referralToken,
address _receiver
) external returns (uint256) {
_onlyPluginApproved(EFC.ownerOf(_referralToken));
return _pool.collectReferralFee(_referralToken, _receiver);
}
/// @notice Collect the liquidity reward
/// @param _pools The pools in which to collect farm liquidity reward
/// @param _owner The address of the reward owner
/// @param _receiver The address to receive the reward
/// @return rewardDebt The amount of liquidity reward received
function pluginCollectFarmLiquidityRewardBatch(
IPool[] calldata _pools,
address _owner,
address _receiver
) external returns (uint256 rewardDebt) {
_onlyPluginApproved(_owner);
return rewardFarm.collectLiquidityRewardBatch(_pools, _owner, _receiver);
}
/// @notice Collect the risk buffer fund reward
/// @param _pools The pools in which to collect farm risk buffer fund reward
/// @param _owner The address of the reward owner
/// @param _receiver The address to receive the reward
/// @return rewardDebt The amount of risk buffer fund reward received
function pluginCollectFarmRiskBufferFundRewardBatch(
IPool[] calldata _pools,
address _owner,
address _receiver
) external returns (uint256 rewardDebt) {
_onlyPluginApproved(_owner);
return rewardFarm.collectRiskBufferFundRewardBatch(_pools, _owner, _receiver);
}
/// @notice Collect the farm referral reward
/// @param _pools The pools in which to collect farm risk buffer fund reward
/// @param _referralTokens The IDs of the referral tokens
/// @param _receiver The address to receive the referral reward
/// @return rewardDebt The amount of the referral reward received
function pluginCollectFarmReferralRewardBatch(
IPool[] calldata _pools,
uint256[] calldata _referralTokens,
address _receiver
) external returns (uint256 rewardDebt) {
uint256 tokensLen = _referralTokens.length;
require(tokensLen > 0);
address owner = EFC.ownerOf(_referralTokens[0]);
_onlyPluginApproved(owner);
for (uint256 i = 1; i < tokensLen; ++i)
if (EFC.ownerOf(_referralTokens[i]) != owner) revert OwnerMismatch(EFC.ownerOf(_referralTokens[i]), owner);
return rewardFarm.collectReferralRewardBatch(_pools, _referralTokens, _receiver);
}
/// @notice Collect EQU staking reward tokens
/// @param _owner The staker
/// @param _receiver The address used to receive staking reward tokens
/// @param _ids Index of EQU tokens staking information that need to be collected
/// @return rewardDebt The amount of staking reward tokens received
function pluginCollectStakingRewardBatch(
address _owner,
address _receiver,
uint256[] calldata _ids
) external returns (uint256 rewardDebt) {
_onlyPluginApproved(_owner);
return feeDistributor.collectBatchByRouter(_owner, _receiver, _ids);
}
/// @notice Collect Uniswap V3 positions NFT staking reward tokens
/// @param _owner The Staker
/// @param _receiver The address used to receive staking reward tokens
/// @param _ids Index of Uniswap V3 positions NFTs staking information that need to be collected
/// @return rewardDebt The amount of staking reward tokens received
function pluginCollectV3PosStakingRewardBatch(
address _owner,
address _receiver,
uint256[] calldata _ids
) external returns (uint256 rewardDebt) {
_onlyPluginApproved(_owner);
return feeDistributor.collectV3PosBatchByRouter(_owner, _receiver, _ids);
}
/// @notice Collect the architect reward
/// @param _receiver The address used to receive rewards
/// @param _tokenIDs The IDs of the Architect-type NFT
/// @return rewardDebt The amount of architect rewards received
function pluginCollectArchitectRewardBatch(
address _receiver,
uint256[] calldata _tokenIDs
) external returns (uint256 rewardDebt) {
uint256 idsLen = _tokenIDs.length;
require(idsLen > 0);
address owner = EFC.ownerOf(_tokenIDs[0]);
_onlyPluginApproved(owner);
for (uint256 i = 1; i < idsLen; ++i)
if (EFC.ownerOf(_tokenIDs[i]) != owner) revert OwnerMismatch(EFC.ownerOf(_tokenIDs[i]), owner);
return feeDistributor.collectArchitectBatchByRouter(_receiver, _tokenIDs);
}
function _onlyPluginApproved(address _account) internal view {
if (!isPluginApproved(_account, msg.sender)) revert CallerUnauthorized();
}
function _onlyLiquidator() internal view {
if (!isRegisteredLiquidator(msg.sender)) revert CallerUnauthorized();
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IFeeDistributor {
/// @notice Emitted when EQU tokens are staked
/// @param sender The address to apply for staking
/// @param account Which address to stake to
/// @param id Index of EQU tokens staking information
/// @param amount The amount of EQU tokens that already staked
/// @param period Lockup period
event Staked(address indexed sender, address indexed account, uint256 indexed id, uint256 amount, uint16 period);
/// @notice Emitted when Uniswap V3 positions NFTs are staked
/// @param sender The address to apply for staking
/// @param account Which address to stake to
/// @param id Index of Uniswap V3 positions NFTs staking information
/// @param amount The amount of Uniswap V3 positions NFT converted into EQU tokens that already staked
/// @param period Lockup period
event V3PosStaked(
address indexed sender,
address indexed account,
uint256 indexed id,
uint256 amount,
uint16 period
);
/// @notice Emitted when EQU tokens are unstaked
/// @param owner The address to apply for unstaking
/// @param receiver The address used to receive the stake tokens
/// @param id Index of EQU tokens staking information
/// @param amount0 The amount of EQU tokens that already unstaked
/// @param amount1 The amount of staking rewards received
event Unstaked(
address indexed owner,
address indexed receiver,
uint256 indexed id,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when Uniswap V3 positions NFTs are unstaked
/// @param owner The address to apply for unstaking
/// @param receiver The address used to receive the Uniswap V3 positions NFT
/// @param id Index of Uniswap V3 positions NFTs staking information
/// @param amount0 The amount of Uniswap V3 positions NFT converted into EQU tokens that already unstaked
/// @param amount1 The amount of staking rewards received
event V3PosUnstaked(
address indexed owner,
address indexed receiver,
uint256 indexed id,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when claiming stake rewards
/// @param owner The address to apply for claiming staking rewards
/// @param receiver The address used to receive staking rewards
/// @param id Index of EQU tokens staking information
/// @param amount The amount of staking rewards received
event Collected(address indexed owner, address indexed receiver, uint256 indexed id, uint256 amount);
/// @notice Emitted when claiming stake rewards
/// @param owner The address to apply for claiming staking rewards
/// @param receiver The address used to receive staking rewards
/// @param id Index of Uniswap V3 positions NFTs staking information
/// @param amount The amount of staking rewards received
event V3PosCollected(address indexed owner, address indexed receiver, uint256 indexed id, uint256 amount);
/// @notice Emitted when claiming Architect-type NFT rewards
/// @param receiver The address used to receive rewards
/// @param tokenID The ID of the Architect-type NFT
/// @param amount The amount of rewards received
event ArchitectCollected(address indexed receiver, uint256 indexed tokenID, uint256 amount);
/// @notice Emitted when deposit staking reward tokens
/// @param amount The amount of staking reward tokens deposited
/// @param equFeeAmount The amount of reward tokens allocated to the EQU
/// @param architectFeeAmount The amount of reward tokens allocated to the Architect-type NFT pool
/// @param perShareGrowthAfterX64 The adjusted `perShareGrowthX64`, as a Q96.64
/// @param architectPerShareGrowthAfterX64 The adjusted `architectPerShareGrowthX64`, as a Q96.64
event FeeDeposited(
uint256 amount,
uint256 equFeeAmount,
uint256 architectFeeAmount,
uint160 perShareGrowthAfterX64,
uint160 architectPerShareGrowthAfterX64
);
/// @notice Emitted when the lockup periods and lockup multipliers are set
/// @param lockupRewardMultiplierParameters The list of LockupRewardMultiplierParameter
event LockupRewardMultipliersSet(LockupRewardMultiplierParameter[] lockupRewardMultiplierParameters);
/// @notice The number of Architect-type NFT that has been mined is 0
/// or the EQU tokens that Uniswap V3 positions NFTs converted into total staked amount is 0.
error DepositConditionNotMet();
/// @notice Invalid caller
error InvalidCaller(address caller);
/// @notice Invalid NFT owner
error InvalidNFTOwner(address owner, uint256 tokenID);
/// @notice Invalid lockup period
error InvalidLockupPeriod(uint16 period);
/// @notice Invalid StakeID
error InvalidStakeID(uint256 id);
/// @notice Not yet reached the unlocking time
error NotYetReachedTheUnlockingTime(uint256 id);
/// @notice Deposit amount is greater than the transfer amount
error DepositAmountIsGreaterThanTheTransfer(uint256 depositAmount, uint256 balance);
/// @notice The NFT is not part of the EQU-WETH pool
error InvalidUniswapV3PositionNFT(address token0, address token1);
/// @notice The exchangeable amount of EQU is 0
error ExchangeableEQUAmountIsZero();
/// @notice Invalid Uniswap V3 fee
error InvalidUniswapV3Fee(uint24 fee);
/// @notice The price range of the Uniswap V3 position is not full range
error RequireFullRangePosition(int24 tickLower, int24 tickUpper, int24 tickSpacing);
struct StakeInfo {
uint256 amount;
uint64 lockupStartTime;
uint16 multiplier;
uint16 period;
uint160 perShareGrowthX64;
}
struct LockupRewardMultiplierParameter {
uint16 period;
uint16 multiplier;
}
/// @notice Get the fee token balance
/// @return balance The balance of the fee token
function feeBalance() external view returns (uint96 balance);
/// @notice Get the fee token
/// @return token The fee token
function feeToken() external view returns (IERC20 token);
/// @notice Get the total amount with multiplier of staked EQU tokens
/// @return amount The total amount with multiplier of staked EQU tokens
function totalStakedWithMultiplier() external view returns (uint256 amount);
/// @notice Get the accumulated staking rewards growth per share
/// @return perShareGrowthX64 The accumulated staking rewards growth per share, as a Q96.64
function perShareGrowthX64() external view returns (uint160 perShareGrowthX64);
/// @notice Get EQU staking information
/// @param account The staker of EQU tokens
/// @param stakeID Index of EQU tokens staking information
/// @return amount The amount of EQU tokens that already staked
/// @return lockupStartTime Lockup start time
/// @return multiplier Lockup reward multiplier
/// @return period Lockup period
/// @return perShareGrowthX64 The accumulated staking rewards growth per share, as a Q96.64
function stakeInfos(
address account,
uint256 stakeID
)
external
view
returns (uint256 amount, uint64 lockupStartTime, uint16 multiplier, uint16 period, uint160 perShareGrowthX64);
/// @notice Get Uniswap V3 positions NFTs staking information
/// @param account The staker of Uniswap V3 positions NFTs
/// @param stakeID Index of Uniswap V3 positions NFTs staking information
/// @return amount The amount of EQU tokens that Uniswap V3 positions NFTs converted into that already staked
/// @return lockupStartTime Lockup start time
/// @return multiplier Lockup reward multiplier
/// @return period Lockup period
/// @return perShareGrowthX64 The accumulated staking rewards growth per share, as a Q96.64
function v3PosStakeInfos(
address account,
uint256 stakeID
)
external
view
returns (uint256 amount, uint64 lockupStartTime, uint16 multiplier, uint16 period, uint160 perShareGrowthX64);
/// @notice Get withdrawal time period
/// @return period Withdrawal time period
function withdrawalPeriod() external view returns (uint16 period);
/// @notice Get lockup multiplier based on lockup period
/// @param period Lockup period
/// @return multiplier Lockup multiplier
function lockupRewardMultipliers(uint16 period) external view returns (uint16 multiplier);
/// @notice The number of Architect-type NFTs minted
/// @return quantity The number of Architect-type NFTs minted
function mintedArchitects() external view returns (uint16 quantity);
/// @notice Get the accumulated reward growth for each Architect-type NFT
/// @return perShareGrowthX64 The accumulated reward growth for each Architect-type NFT, as a Q96.64
function architectPerShareGrowthX64() external view returns (uint160 perShareGrowthX64);
/// @notice Get the accumulated reward growth for each Architect-type NFT
/// @param tokenID The ID of the Architect-type NFT
/// @return perShareGrowthX64 The accumulated reward growth for each Architect-type NFT, as a Q96.64
function architectPerShareGrowthX64s(uint256 tokenID) external view returns (uint160 perShareGrowthX64);
/// @notice Set lockup reward multiplier
/// @param lockupRewardMultiplierParameters The list of LockupRewardMultiplierParameter
function setLockupRewardMultipliers(
LockupRewardMultiplierParameter[] calldata lockupRewardMultiplierParameters
) external;
/// @notice Deposite staking reward tokens
/// @param amount The amount of reward tokens deposited
function depositFee(uint256 amount) external;
/// @notice Stake EQU
/// @param amount The amount of EQU tokens that need to be staked
/// @param account Which address to stake to
/// @param period Lockup period
function stake(uint256 amount, address account, uint16 period) external;
/// @notice Stake Uniswap V3 positions NFT
/// @param id The ID of the Uniswap V3 positions NFT
/// @param account Which address to stake to
/// @param period Lockup period
function stakeV3Pos(uint256 id, address account, uint16 period) external;
/// @notice Unstake EQU
/// @param ids Indexs of EQU tokens staking information that need to be unstaked
/// @param receiver The address used to receive the staked tokens
/// @return rewardAmount The amount of staking reward tokens received
function unstake(uint256[] calldata ids, address receiver) external returns (uint256 rewardAmount);
/// @notice Unstake Uniswap V3 positions NFT
/// @param ids Indexs of Uniswap V3 positions NFTs staking information that need to be unstaked
/// @param receiver The address used to receive the Uniswap V3 positions NFTs
/// @return rewardAmount The amount of staking reward tokens received
function unstakeV3Pos(uint256[] calldata ids, address receiver) external returns (uint256 rewardAmount);
/// @notice Collect EQU staking rewards through router
/// @param owner The Staker
/// @param receiver The address used to receive staking rewards
/// @param ids Index of EQU tokens staking information that need to be collected
/// @return rewardAmount The amount of staking reward tokens received
function collectBatchByRouter(
address owner,
address receiver,
uint256[] calldata ids
) external returns (uint256 rewardAmount);
/// @notice Collect Uniswap V3 positions NFT staking rewards through router
/// @param owner The Staker
/// @param receiver The address used to receive staking reward tokens
/// @param ids Index of Uniswap V3 positions NFTs staking information that need to be collected
/// @return rewardAmount The amount of staking reward tokens received
function collectV3PosBatchByRouter(
address owner,
address receiver,
uint256[] calldata ids
) external returns (uint256 rewardAmount);
/// @notice Collect rewards for architect-type NFTs through router
/// @param receiver The address used to receive staking reward tokens
/// @param tokenIDs The IDs of the Architect-type NFT
/// @return rewardAmount The amount of staking reward tokens received
function collectArchitectBatchByRouter(
address receiver,
uint256[] calldata tokenIDs
) external returns (uint256 rewardAmount);
/// @notice Collect EQU staking rewards
/// @param receiver The address used to receive staking reward tokens
/// @param ids Index of EQU tokens staking information that need to be collected
/// @return rewardAmount The amount of staking reward tokens received
function collectBatch(address receiver, uint256[] calldata ids) external returns (uint256 rewardAmount);
/// @notice Collect Uniswap V3 positions NFT staking rewards
/// @param receiver The address used to receive staking reward tokens
/// @param ids Index of Uniswap V3 positions NFTs staking information that need to be collected
/// @return rewardAmount The amount of staking reward tokens received
function collectV3PosBatch(address receiver, uint256[] calldata ids) external returns (uint256 rewardAmount);
/// @notice Collect rewards for architect-type NFTs
/// @param receiver The address used to receive rewards
/// @param tokenIDs The IDs of the Architect-type NFT
/// @return rewardAmount The amount of staking reward tokens received
function collectArchitectBatch(
address receiver,
uint256[] calldata tokenIDs
) external returns (uint256 rewardAmount);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
/// @title Interface for the IEFC contract
/// @notice This contract is used to register referral codes and bind them to users
interface IEFC is IERC721 {
/// @notice Emitted when a referral code is registered
/// @param referrer The address of the user who registered the code
/// @param code The code to register
/// @param tokenId The id of the token to register the code for
event CodeRegistered(address indexed referrer, uint256 indexed tokenId, string code);
/// @notice Emitted when a referral code is bound
/// @param referee The address of the user who bound the code
/// @param code The code to bind
/// @param tokenIdBefore The id of the token before the code is bound
/// @param tokenIdAfter The id of the token after the code is bound
event CodeBound(address indexed referee, string code, uint256 tokenIdBefore, uint256 tokenIdAfter);
/// @notice Param cap is too large
/// @param capArchitect Cap of architect can be minted
/// @param capConnector Cap of connector can be minted
error CapTooLarge(uint256 capArchitect, uint256 capConnector);
/// @notice Token is not member
/// @param tokenId The tokenId
error NotMemberToken(uint256 tokenId);
/// @notice Token is not connector
/// @param tokenId The tokenId
error NotConnectorToken(uint256 tokenId);
/// @notice Cap exceeded
/// @param cap The cap
error CapExceeded(uint256 cap);
/// @notice Invalid code
error InvalidCode();
/// @notice Caller is not the owner
/// @param owner The owner
error CallerIsNotOwner(address owner);
/// @notice Code is already registered
/// @param code The code
error CodeAlreadyRegistered(string code);
/// @notice Code is not registered
/// @param code The code
error CodeNotRegistered(string code);
/// @notice Set the base URI of nft assets
/// @param baseURI Base URI for NFTs
function setBaseURI(string calldata baseURI) external;
/// @notice Get the token for a code
/// @param code The code to get the token for
/// @return tokenId The id of the token for the code
function codeTokens(string calldata code) external view returns (uint256 tokenId);
/// @notice Get the member and connector token id who referred the referee
/// @param referee The address of the referee
/// @return memberTokenId The token id of the member
/// @return connectorTokenId The token id of the connector
function referrerTokens(address referee) external view returns (uint256 memberTokenId, uint256 connectorTokenId);
/// @notice Register a referral code for the referrer
/// @param tokenId The id of the token to register the code for
/// @param code The code to register
function registerCode(uint256 tokenId, string calldata code) external;
/// @notice Bind a referral code for the referee
/// @param code The code to bind
function bindCode(string calldata code) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import "solady/src/utils/LibBit.sol";
type Bitmap is uint256;
using {flip, searchNextPosition} for Bitmap global;
/// @dev Flip the bit at the specified position in the given bitmap
/// @param self The original bitmap
/// @param position The position of the bit to be flipped
/// @return The updated bitmap after flipping the specified bit
function flip(Bitmap self, uint8 position) pure returns (Bitmap) {
return Bitmap.wrap(Bitmap.unwrap(self) ^ (1 << position));
}
/// @dev Search for the next position in a bitmap starting from a given index
/// @param self The bitmap to search within
/// @param startInclusive The index to start the search from (inclusive)
/// @return next The next position found in the bitmap
/// @return found A boolean indicating whether the next position was found or not
function searchNextPosition(Bitmap self, uint8 startInclusive) pure returns (uint8 next, bool found) {
uint256 mask = ~uint256(0) << startInclusive;
uint256 masked = Bitmap.unwrap(self) & mask;
return masked == 0 ? (0, false) : (uint8(LibBit.ffs(masked)), true);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
Side constant LONG = Side.wrap(1);
Side constant SHORT = Side.wrap(2);
type Side is uint8;
error InvalidSide(Side side);
using {requireValid, isLong, isShort, flip, eq as ==} for Side global;
function requireValid(Side self) pure {
if (!isLong(self) && !isShort(self)) revert InvalidSide(self);
}
function isLong(Side self) pure returns (bool) {
return Side.unwrap(self) == Side.unwrap(LONG);
}
function isShort(Side self) pure returns (bool) {
return Side.unwrap(self) == Side.unwrap(SHORT);
}
function eq(Side self, Side other) pure returns (bool) {
return Side.unwrap(self) == Side.unwrap(other);
}
function flip(Side self) pure returns (Side) {
return isLong(self) ? SHORT : LONG;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for bit twiddling and boolean operations.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBit.sol)
/// @author Inspired by (https://graphics.stanford.edu/~seander/bithacks.html)
library LibBit {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BIT TWIDDLING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Find last set.
/// Returns the index of the most significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
function fls(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, x)))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
r := or(r, shl(2, lt(0xf, shr(r, x))))
r := or(r, byte(shr(r, x), hex"00000101020202020303030303030303"))
}
}
/// @dev Count leading zeros.
/// Returns the number of zeros preceding the most significant one bit.
/// If `x` is zero, returns 256.
function clz(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
r := or(r, shl(2, lt(0xf, shr(r, x))))
// forgefmt: disable-next-item
r := add(iszero(x), xor(255,
or(r, byte(shr(r, x), hex"00000101020202020303030303030303"))))
}
}
/// @dev Find first set.
/// Returns the index of the least significant bit of `x`,
/// counting from the least significant bit position.
/// If `x` is zero, returns 256.
/// Equivalent to `ctz` (count trailing zeros), which gives
/// the number of zeros following the least significant one bit.
function ffs(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Isolate the least significant bit.
let b := and(x, add(not(x), 1))
r := or(shl(8, iszero(x)), shl(7, lt(0xffffffffffffffffffffffffffffffff, b)))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, b))))
r := or(r, shl(5, lt(0xffffffff, shr(r, b))))
// For the remaining 32 bits, use a De Bruijn lookup.
// forgefmt: disable-next-item
r := or(r, byte(and(div(0xd76453e0, shr(r, b)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
}
}
/// @dev Returns the number of set bits in `x`.
function popCount(uint256 x) internal pure returns (uint256 c) {
/// @solidity memory-safe-assembly
assembly {
let max := not(0)
let isMax := eq(x, max)
x := sub(x, and(shr(1, x), div(max, 3)))
x := add(and(x, div(max, 5)), and(shr(2, x), div(max, 5)))
x := and(add(x, shr(4, x)), div(max, 17))
c := or(shl(8, isMax), shr(248, mul(x, div(max, 255))))
}
}
/// @dev Returns whether `x` is a power of 2.
function isPo2(uint256 x) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `x && !(x & (x - 1))`.
result := iszero(add(and(x, sub(x, 1)), iszero(x)))
}
}
/// @dev Returns `x` reversed at the bit level.
function reverseBits(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Computing masks on-the-fly reduces bytecode size by about 500 bytes.
let m := not(0)
r := x
for { let s := 128 } 1 {} {
m := xor(m, shl(s, m))
r := or(and(shr(s, r), m), and(shl(s, r), not(m)))
s := shr(1, s)
if iszero(s) { break }
}
}
}
/// @dev Returns `x` reversed at the byte level.
function reverseBytes(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Computing masks on-the-fly reduces bytecode size by about 200 bytes.
let m := not(0)
r := x
for { let s := 128 } 1 {} {
m := xor(m, shl(s, m))
r := or(and(shr(s, r), m), and(shl(s, r), not(m)))
s := shr(1, s)
if eq(s, 4) { break }
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* BOOLEAN OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// A Solidity bool on the stack or memory is represented as a 256-bit word.
// Non-zero values are true, zero is false.
// A clean bool is either 0 (false) or 1 (true) under the hood.
// Usually, if not always, the bool result of a regular Solidity expression,
// or the argument of a public/external function will be a clean bool.
// You can usually use the raw variants for more performance.
// If uncertain, test (best with exact compiler settings).
// Or use the non-raw variants (compiler can sometimes optimize out the double `iszero`s).
/// @dev Returns `x & y`. Inputs must be clean.
function rawAnd(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := and(x, y)
}
}
/// @dev Returns `x & y`.
function and(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := and(iszero(iszero(x)), iszero(iszero(y)))
}
}
/// @dev Returns `x | y`. Inputs must be clean.
function rawOr(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := or(x, y)
}
}
/// @dev Returns `x | y`.
function or(bool x, bool y) internal pure returns (bool z) {
/// @solidity memory-safe-assembly
assembly {
z := or(iszero(iszero(x)), iszero(iszero(y)))
}
}
/// @dev Returns 1 if `b` is true, else 0. Input must be clean.
function rawToUint(bool b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := b
}
}
/// @dev Returns 1 if `b` is true, else 0.
function toUint(bool b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := iszero(iszero(b))
}
}
}{
"evmVersion": "paris",
"viaIR": true,
"optimizer": {
"enabled": true,
"runs": 100000000
},
"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":"contract IERC20","name":"_usd","type":"address"},{"internalType":"contract Router","name":"_router","type":"address"},{"internalType":"uint256","name":"_minExecutionFee","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Forbidden","type":"error"},{"inputs":[{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"InsufficientExecutionFee","type":"error"},{"inputs":[{"internalType":"uint160","name":"marketPriceX96","type":"uint160"},{"internalType":"uint160","name":"triggerMarketPriceX96","type":"uint160"}],"name":"InvalidMarketPriceToTrigger","type":"error"},{"inputs":[{"internalType":"Side","name":"side","type":"uint8"}],"name":"InvalidSide","type":"error"},{"inputs":[{"internalType":"uint160","name":"tradePriceX96","type":"uint160"},{"internalType":"uint160","name":"acceptableTradePriceX96","type":"uint160"}],"name":"InvalidTradePrice","type":"error"},{"inputs":[{"internalType":"uint256","name":"orderIndex","type":"uint256"}],"name":"OrderNotExists","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousGov","type":"address"},{"indexed":true,"internalType":"address","name":"newGov","type":"address"}],"name":"ChangeGovStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"address","name":"feeReceiver","type":"address"}],"name":"DecreaseOrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"contract IPool","name":"pool","type":"address"},{"indexed":false,"internalType":"Side","name":"side","type":"uint8"},{"indexed":false,"internalType":"uint128","name":"marginDelta","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"sizeDelta","type":"uint128"},{"indexed":false,"internalType":"uint160","name":"triggerMarketPriceX96","type":"uint160"},{"indexed":false,"internalType":"bool","name":"triggerAbove","type":"bool"},{"indexed":false,"internalType":"uint160","name":"acceptableTradePriceX96","type":"uint160"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"orderIndex","type":"uint256"}],"name":"DecreaseOrderCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"uint160","name":"marketPriceX96","type":"uint160"},{"indexed":false,"internalType":"address payable","name":"feeReceiver","type":"address"}],"name":"DecreaseOrderExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"uint160","name":"triggerMarketPriceX96","type":"uint160"},{"indexed":false,"internalType":"uint160","name":"acceptableTradePriceX96","type":"uint160"}],"name":"DecreaseOrderUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousGov","type":"address"},{"indexed":true,"internalType":"address","name":"newGov","type":"address"}],"name":"GovChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"address payable","name":"feeReceiver","type":"address"}],"name":"IncreaseOrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"contract IPool","name":"pool","type":"address"},{"indexed":false,"internalType":"Side","name":"side","type":"uint8"},{"indexed":false,"internalType":"uint128","name":"marginDelta","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"sizeDelta","type":"uint128"},{"indexed":false,"internalType":"uint160","name":"triggerMarketPriceX96","type":"uint160"},{"indexed":false,"internalType":"bool","name":"triggerAbove","type":"bool"},{"indexed":false,"internalType":"uint160","name":"acceptableTradePriceX96","type":"uint160"},{"indexed":false,"internalType":"uint256","name":"executionFee","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"orderIndex","type":"uint256"}],"name":"IncreaseOrderCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"uint160","name":"marketPriceX96","type":"uint160"},{"indexed":false,"internalType":"address payable","name":"feeReceiver","type":"address"}],"name":"IncreaseOrderExecuted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"orderIndex","type":"uint256"},{"indexed":false,"internalType":"uint160","name":"triggerMarketPriceX96","type":"uint160"},{"indexed":false,"internalType":"uint160","name":"acceptableTradePriceX96","type":"uint160"}],"name":"IncreaseOrderUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minExecutionFee","type":"uint256"}],"name":"MinExecutionFeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"active","type":"bool"}],"name":"OrderExecutorUpdated","type":"event"},{"inputs":[],"name":"acceptGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_orderIndex","type":"uint256"},{"internalType":"address payable","name":"_feeReceiver","type":"address"}],"name":"cancelDecreaseOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_orderIndex","type":"uint256"},{"internalType":"address payable","name":"_feeReceiver","type":"address"}],"name":"cancelIncreaseOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newGov","type":"address"}],"name":"changeGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPool","name":"_pool","type":"address"},{"internalType":"Side","name":"_side","type":"uint8"},{"internalType":"uint128","name":"_marginDelta","type":"uint128"},{"internalType":"uint128","name":"_sizeDelta","type":"uint128"},{"internalType":"uint160","name":"_triggerMarketPriceX96","type":"uint160"},{"internalType":"bool","name":"_triggerAbove","type":"bool"},{"internalType":"uint160","name":"_acceptableTradePriceX96","type":"uint160"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"createDecreaseOrder","outputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IPool","name":"_pool","type":"address"},{"internalType":"Side","name":"_side","type":"uint8"},{"internalType":"uint128","name":"_marginDelta","type":"uint128"},{"internalType":"uint128","name":"_sizeDelta","type":"uint128"},{"internalType":"uint160","name":"_triggerMarketPriceX96","type":"uint160"},{"internalType":"bool","name":"_triggerAbove","type":"bool"},{"internalType":"uint160","name":"_acceptableTradePriceX96","type":"uint160"}],"name":"createIncreaseOrder","outputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IPool","name":"_pool","type":"address"},{"internalType":"Side","name":"_side","type":"uint8"},{"internalType":"uint128[2]","name":"_marginDeltas","type":"uint128[2]"},{"internalType":"uint128[2]","name":"_sizeDeltas","type":"uint128[2]"},{"internalType":"uint160[2]","name":"_triggerMarketPriceX96s","type":"uint160[2]"},{"internalType":"uint160[2]","name":"_acceptableTradePriceX96s","type":"uint160[2]"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"createTakeProfitAndStopLossOrders","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"decreaseOrders","outputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"contract IPool","name":"pool","type":"address"},{"internalType":"Side","name":"side","type":"uint8"},{"internalType":"uint128","name":"marginDelta","type":"uint128"},{"internalType":"uint128","name":"sizeDelta","type":"uint128"},{"internalType":"uint160","name":"triggerMarketPriceX96","type":"uint160"},{"internalType":"bool","name":"triggerAbove","type":"bool"},{"internalType":"uint160","name":"acceptableTradePriceX96","type":"uint160"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"executionFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decreaseOrdersIndexNext","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_orderIndex","type":"uint256"},{"internalType":"address payable","name":"_feeReceiver","type":"address"}],"name":"executeDecreaseOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_orderIndex","type":"uint256"},{"internalType":"address payable","name":"_feeReceiver","type":"address"}],"name":"executeIncreaseOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"executionGasLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gov","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"increaseOrders","outputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"contract IPool","name":"pool","type":"address"},{"internalType":"Side","name":"side","type":"uint8"},{"internalType":"uint128","name":"marginDelta","type":"uint128"},{"internalType":"uint128","name":"sizeDelta","type":"uint128"},{"internalType":"uint160","name":"triggerMarketPriceX96","type":"uint160"},{"internalType":"bool","name":"triggerAbove","type":"bool"},{"internalType":"uint160","name":"acceptableTradePriceX96","type":"uint160"},{"internalType":"uint256","name":"executionFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"increaseOrdersIndexNext","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minExecutionFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"orderExecutors","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingGov","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"contract Router","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_orderIndex","type":"uint256"},{"internalType":"uint160","name":"_triggerMarketPriceX96","type":"uint160"},{"internalType":"uint160","name":"_acceptableTradePriceX96","type":"uint160"}],"name":"updateDecreaseOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_executionGasLimit","type":"uint256"}],"name":"updateExecutionGasLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_orderIndex","type":"uint256"},{"internalType":"uint160","name":"_triggerMarketPriceX96","type":"uint160"},{"internalType":"uint160","name":"_acceptableTradePriceX96","type":"uint160"}],"name":"updateIncreaseOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minExecutionFee","type":"uint256"}],"name":"updateMinExecutionFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_active","type":"bool"}],"name":"updateOrderExecutor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"usd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60c0346200013457601f62002e6c38819003918201601f19168301916001600160401b03831184841017620001395780849260609460405283398101031262000134578051906001600160a01b0380831683036200013457602082015192818416840362000134577face0e51322d77a7556ae0cf8f6528fdd3385e33d2951dc3c9cab391eb76b948c9360406020940151916000543360018060a01b0319821617600055604051943391167f3d1e4c3a68fed9f4f8315582b7297cf8fa264bc8e6704287603ba8c72bf05ac2600080a36001600255620f424060045560805260a052806003558152a1604051612d1c908162000150823960805181818161064101528181610f3b015281816118f30152611bb1015260a0518181816101c2015281816105d501528181610ad80152611ca40152f35b600080fd5b634e487b7160e01b600052604160045260246000fdfe608080604052600436101561001357600080fd5b600090813560e01c9081630f108862146121305750806312d43a51146120df578063252408101461208d5780632707b2d41461205157806339cb1f3e1461201557806363ae210314611fd9578063699735c014611f125780636a77d35f14611e495780636ffbbc5514611e0d57806371b40c9f146119d55780637bae8c5a1461180c5780637bc6729b146117615780638ec4ad8b146114ca5780638f5534ff146113f1578063a962ef1e14611344578063a9e5b11a14611177578063b65202bc146110a4578063c298d6b814610fcb578063d307f03f14610f5f578063d63a6ccd14610ef0578063e6467b69146107fb578063e9a6783c1461071a578063efe85323146101e6578063f887ea40146101775763f9a1dde91461013457600080fd5b346101745760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101745761016b612311565b60043560045580f35b80fd5b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b5060e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457610219612195565b906102226121bd565b61022a6121cd565b926102336121ec565b61023b61220b565b610243612251565b9061024c61222e565b92610255612438565b61025e86612c81565b6003548034106106e357506fffffffffffffffffffffffffffffffff88166105bd575b600654978897610290896123dc565b600655604051906102a082612332565b33825273ffffffffffffffffffffffffffffffffffffffff8816602083015260ff891660408301526fffffffffffffffffffffffffffffffff831660608301526fffffffffffffffffffffffffffffffff8416608083015273ffffffffffffffffffffffffffffffffffffffff851660a083015285151560c083015273ffffffffffffffffffffffffffffffffffffffff871660e08301526101008201903482528a815260086020526040902091805173ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000908185541617845560018401602083015173ffffffffffffffffffffffffffffffffffffffff1682825416178155604083015160ff1661040791907fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000083549260a01b169116179055565b6060820151608080840151901b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff9091161760028501556003840160a083015173ffffffffffffffffffffffffffffffffffffffff168282541617815560c083015115156104c991907fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff0000000000000000000000000000000000000000835492151560a01b169116179055565b600484019160e0015173ffffffffffffffffffffffffffffffffffffffff16908254161790555190600501556040519660ff1687526fffffffffffffffffffffffffffffffff1660208701526fffffffffffffffffffffffffffffffff16604086015273ffffffffffffffffffffffffffffffffffffffff1660608501521515608084015273ffffffffffffffffffffffffffffffffffffffff1660a08301523460c083015273ffffffffffffffffffffffffffffffffffffffff1690339060e07f63f3cc17cd3a0997a486628dcd61d39c2910a87ac8a30a06e902a5fa15b84f5291a46001600255604051908152602090f35b73ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000000000000000000000000000000000000000000016803b156106df578960846fffffffffffffffffffffffffffffffff938b809460405196879586947f1b8278780000000000000000000000000000000000000000000000000000000086527f00000000000000000000000000000000000000000000000000000000000000001660048601523360248601523060448601521660648401525af180156106d45761068b575b50610281565b67ffffffffffffffff81989298116106a7576040529538610685565b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b6040513d8a823e3d90fd5b8880fd5b604490604051907f5dac504d0000000000000000000000000000000000000000000000000000000082523460048301526024820152fd5b50346101745760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457604061014091600435815260096020522073ffffffffffffffffffffffffffffffffffffffff808254169160018101549160ff60028301549160038401549080600486015416938360068360058901541697015497604051998a5283811660208b015260a01c1660408901526fffffffffffffffffffffffffffffffff8116606089015260801c6080880152811660a087015260a01c16151560c085015260e0840152610100830152610120820152f35b50346101745761080a36612260565b610812612438565b338352600560205260ff60408420541615610ec65781835260096020526040832090604051916108418361237e565b600673ffffffffffffffffffffffffffffffffffffffff8254169182855260ff600182015473ffffffffffffffffffffffffffffffffffffffff8116602088015260a01c16604086015260028101546fffffffffffffffffffffffffffffffff8116606087015260801c608086015260ff600382015473ffffffffffffffffffffffffffffffffffffffff811660a088015260a01c16151560c086015273ffffffffffffffffffffffffffffffffffffffff60048201541660e086015273ffffffffffffffffffffffffffffffffffffffff600582015416610100860152015461012084015215610e955773ffffffffffffffffffffffffffffffffffffffff60208301511691602060ff602461095d82604086015116612cd2565b60405196879384927fd9fe726f0000000000000000000000000000000000000000000000000000000084521660048301525afa928315610e8a578593610e4a575b506109cb60c082015115158473ffffffffffffffffffffffffffffffffffffffff60a08501511691612b04565b6fffffffffffffffffffffffffffffffff608082015116906fffffffffffffffffffffffffffffffff6060820151168215610d58575b60209060c46004548973ffffffffffffffffffffffffffffffffffffffff85870151169173ffffffffffffffffffffffffffffffffffffffff875116946fffffffffffffffffffffffffffffffff60ff60408a015116998173ffffffffffffffffffffffffffffffffffffffff6101008c015116936040519c8d9a8b997ff86786f1000000000000000000000000000000000000000000000000000000008b5260048b015260248a0152604489015216606487015216608485015260a484015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690f18015610d4d578690610d0e575b61012092506fffffffffffffffffffffffffffffffff608083015116610cd2575b500151804710610c7457848080809373ffffffffffffffffffffffffffffffffffffffff86165af1610b586125ae565b5015610bf0577f16b4f79c5d270756b16267422b6507e69d649a8d0c3ea6a8c1c501108e563475918385526009602052610bb96040862060066000918281558260018201558260028201558260038201558260048201558260058201550155565b6040805173ffffffffffffffffffffffffffffffffffffffff9283168152929091166020830152819081015b0390a2600160025580f35b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152fd5b610d0890610ce660ff604085015116612cd2565b9073ffffffffffffffffffffffffffffffffffffffff60e08501511691612bbe565b38610b28565b506020823d602011610d45575b81610d286020938361239b565b81010312610d4157610d3c6101209261272e565b610b07565b8580fd5b3d9150610d1b565b6040513d88823e3d90fd5b50905073ffffffffffffffffffffffffffffffffffffffff602082015116608073ffffffffffffffffffffffffffffffffffffffff835116604460ff6040860151169160405194859384927fcfbf15e1000000000000000000000000000000000000000000000000000000008452600484015260248301525afa908115610d4d578691610de8575b509085610a01565b90506080813d608011610e42575b81610e036080938361239b565b81010312610d4157610e148161274f565b506060610e236020830161274f565b91610e306040820161272e565b5001518060170b03610d415738610de0565b3d9150610df6565b9092506020813d602011610e82575b81610e666020938361239b565b81010312610e7e57610e779061272e565b913861099e565b8480fd5b3d9150610e59565b6040513d87823e3d90fd5b602483604051907f6f2354130000000000000000000000000000000000000000000000000000000082526004820152fd5b60046040517fee90c468000000000000000000000000000000000000000000000000000000008152fd5b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346101745760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174577face0e51322d77a7556ae0cf8f6528fdd3385e33d2951dc3c9cab391eb76b948c6020600435610fbd612311565b80600355604051908152a180f35b503461017457610fda366122b1565b610fe2612438565b8284526008602052604084209173ffffffffffffffffffffffffffffffffffffffff808454163303610ec65783600460037f05813e22a64b0c6f66b71f2d867634beb3c66a5feb2cd379ca2437ea192c8e479601917fffffffffffffffffffffffff000000000000000000000000000000000000000092848616848254161790550191841690825416179055610be56040519283928390602090939293604083019473ffffffffffffffffffffffffffffffffffffffff809216845216910152565b50346101745760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457604061012091600435815260086020522073ffffffffffffffffffffffffffffffffffffffff808254169160ff600182015492600283015490600384015491836005836004880154169601549660405198895283811660208a015260a01c1660408801526fffffffffffffffffffffffffffffffff8116606088015260801c6080870152811660a086015260a01c16151560c084015260e0830152610100820152f35b506101607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576111ab612195565b906111b46121bd565b36608411611340573660c411611340576101049236841161133c5761014436811161133857359073ffffffffffffffffffffffffffffffffffffffff908183168303610e7e57611202612438565b61120b84612c81565b3460011c60035480821061130157506fffffffffffffffffffffffffffffffff60443581811681036112f55760843582811681036106df5760c4359086821682036112fd57600160ff8a16149a359287841684036112f957928b86938a93611275968d8b3361276c565b5060643581811681036112f55760a43591821682036112f55760e4359285841684036106df576101243595861686036106df573403963488116112c8576112bf989915943361276c565b50600160025580f35b6024897f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b8780fd5b8a80fd5b8980fd5b60449250604051917f5dac504d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b8380fd5b8280fd5b5080fd5b50346101745760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101745761137c612195565b611384612311565b73ffffffffffffffffffffffffffffffffffffffff80911690817fffffffffffffffffffffffff000000000000000000000000000000000000000060015416176001558254167fb9911e2099e372c216862258dc462bb65da46a2c854536c8b1acae619d0d62ed8380a380f35b503461017457611400366122b1565b611408612438565b8284526009602052604084209173ffffffffffffffffffffffffffffffffffffffff808454163303610ec65783600460037f457bb989db79f80ea810085c5e87dc3f6d398d1c357ef12fdf47ad2f8efbb1969601917fffffffffffffffffffffffff000000000000000000000000000000000000000092848616848254161790550191841690825416179055610be56040519283928390602090939293604083019473ffffffffffffffffffffffffffffffffffffffff809216845216910152565b5034610174576114d936612260565b6114e1612438565b8183526020906009825260408420604051906114fc8261237e565b73ffffffffffffffffffffffffffffffffffffffff916006838354169283835260ff60018201548681168986015260a01c16604084015260028101546fffffffffffffffffffffffffffffffff8116606085015260801c608084015260ff600382015486811660a086015260a01c16151560c08401528460048201541660e08401528460058201541661010084015201549161012082019283521561173057829051163314158061171a575b610ec65751918247106116bc578580809381931694855af16115c86125ae565b501561163857907f352426232b87a29640568b27e629c2baa8b3be397819d36fea1f1b2805251ace91838552600982526116296040862060066000918281558260018201558260028201558260038201558260048201558260058201550155565b604051908152a2600160025580f35b608482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152fd5b606484604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152fd5b503386526005845260ff604087205416156115a8565b602486604051907f6f2354130000000000000000000000000000000000000000000000000000000082526004820152fd5b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101745760015473ffffffffffffffffffffffffffffffffffffffff8082163303610ec6577fffffffffffffffffffffffff0000000000000000000000000000000000000000809216600155825491339083161783553391167f3d1e4c3a68fed9f4f8315582b7297cf8fa264bc8e6704287603ba8c72bf05ac28380a380f35b50346101745761181b36612260565b611823612438565b81835260209060088252604084206040519061183e82612332565b73ffffffffffffffffffffffffffffffffffffffff91828254169182825260ff60018201548581168885015260a01c16604083015260028101549060056fffffffffffffffffffffffffffffffff916060850193838116855260801c608086015260ff600382015488811660a088015260a01c16151560c08601528660048201541660e08601520154936101008401948552156119a457848351163314158061198e575b610ec65784611917935116915116907f0000000000000000000000000000000000000000000000000000000000000000612471565b51918247106116bc578580809381931694855af16119336125ae565b501561163857907f984fa73f12ae98c6efdbd872ad2d735a48bab444aca6db7f5b0a1efdffcbdce891838552600882526116296040862060056000918281558260018201558260028201558260038201558260048201550155565b503389526005875260ff60408a205416156118e2565b602488604051907f6f2354130000000000000000000000000000000000000000000000000000000082526004820152fd5b5034610174576119e436612260565b6119ec612438565b338352600560205260ff60408420541615610ec6578183526008602052604083209060405191611a1b83612332565b600573ffffffffffffffffffffffffffffffffffffffff8254169182855260ff600182015473ffffffffffffffffffffffffffffffffffffffff8116602088015260a01c16604086015260028101546fffffffffffffffffffffffffffffffff8116606087015260801c608086015260ff600382015473ffffffffffffffffffffffffffffffffffffffff811660a088015260a01c16151560c086015273ffffffffffffffffffffffffffffffffffffffff60048201541660e0860152015461010084015215610e955773ffffffffffffffffffffffffffffffffffffffff60208301511691602060ff6040830151166024604051809681937fd9fe726f00000000000000000000000000000000000000000000000000000000835260048301525afa928315610e8a578593611dd1575b50611b7960c082015115158473ffffffffffffffffffffffffffffffffffffffff60a08501511691612b04565b611bd573ffffffffffffffffffffffffffffffffffffffff6020830151166fffffffffffffffffffffffffffffffff606084015116907f0000000000000000000000000000000000000000000000000000000000000000612471565b60045490602073ffffffffffffffffffffffffffffffffffffffff818301511660a473ffffffffffffffffffffffffffffffffffffffff845116918960ff604087015116966fffffffffffffffffffffffffffffffff6060880151166fffffffffffffffffffffffffffffffff60808901511690604051998a9788967f2a5a2cca0000000000000000000000000000000000000000000000000000000088526004880152602487015260448601526064850152608484015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690f1918215610d4d578692611d90575b50611d066101009260ff6040840151169073ffffffffffffffffffffffffffffffffffffffff60e08501511691612bbe565b0151804710610c7457848080809373ffffffffffffffffffffffffffffffffffffffff86165af1611d356125ae565b5015610bf0577f32e1f893df12b36e043dac8c88d2207cf0adc4457187ff49529e74417bad8275918385526008602052610bb96040862060056000918281558260018201558260028201558260038201558260048201550155565b91506020823d602011611dc9575b81611dab6020938361239b565b81010312610d4157611d06611dc26101009361272e565b9250611cd4565b3d9150611d9e565b9092506020813d602011611e05575b81611ded6020938361239b565b81010312610e7e57611dfe9061272e565b9138611b4c565b3d9150611de0565b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576020600754604051908152f35b50346101745760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457611e81612195565b6024359081151580920361133c57602073ffffffffffffffffffffffffffffffffffffffff7f9e796433c770c70b207650b57cd6f20fdb8641d7c85d50ced2dd338cee4f158392611ed0612311565b169283855260058252604085207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541660ff8316179055604051908152a280f35b506101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457611f46612195565b90611f4f6121bd565b611f576121cd565b611f5f6121ec565b611f6761220b565b90611f70612251565b92611f7961222e565b9460e4359673ffffffffffffffffffffffffffffffffffffffff881688036101745750611fa4612438565b611fad81612c81565b6003548034106106e3576020611fcc8a8a8a8a8a8a8a8a34973361276c565b6001600255604051908152f35b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576020600354604051908152f35b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576020600454604051908152f35b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576020600654604051908152f35b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101745773ffffffffffffffffffffffffffffffffffffffff6020915416604051908152f35b9050346113405760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126113405760ff604060209373ffffffffffffffffffffffffffffffffffffffff612185612195565b1681526005855220541615158152f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036121b857565b600080fd5b6024359060ff821682036121b857565b604435906fffffffffffffffffffffffffffffffff821682036121b857565b606435906fffffffffffffffffffffffffffffffff821682036121b857565b6084359073ffffffffffffffffffffffffffffffffffffffff821682036121b857565b60c4359073ffffffffffffffffffffffffffffffffffffffff821682036121b857565b60a4359081151582036121b857565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60409101126121b8576004359060243573ffffffffffffffffffffffffffffffffffffffff811681036121b85790565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126121b8576004359073ffffffffffffffffffffffffffffffffffffffff9060243582811681036121b8579160443590811681036121b85790565b73ffffffffffffffffffffffffffffffffffffffff600054163303610ec657565b610120810190811067ffffffffffffffff82111761234f57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610140810190811067ffffffffffffffff82111761234f57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761234f57604052565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146124095760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60028054146124475760028055565b60046040517f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b9190916040519260208401927fa9059cbb00000000000000000000000000000000000000000000000000000000845273ffffffffffffffffffffffffffffffffffffffff8092166024860152604485015260448452608084019067ffffffffffffffff928583108484111761234f57169360c081019283118284101761234f5761253d92604052601e82527f416464726573733a206c6f772d6c6576656c2063616c6c206661696c6564000060a0820152600080948192519082885af16125366125ae565b908561260c565b908151918215159283612586575b5050506125555750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b819293509060209181010312611340576020015190811591821503610174575038808061254b565b3d15612607573d9067ffffffffffffffff821161234f57604051916125fb60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116018461239b565b82523d6000602084013e565b606090565b919290156126875750815115612620575090565b3b156126295790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b82519091501561269a5750805190602001fd5b604051907f08c379a000000000000000000000000000000000000000000000000000000000825281602080600483015282519283602484015260005b848110612717575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f836000604480968601015201168101030190fd5b8181018301518682016044015285935082016126d6565b519073ffffffffffffffffffffffffffffffffffffffff821682036121b857565b51906fffffffffffffffffffffffffffffffff821682036121b857565b94999893979299969196959095600754986127868a6123dc565b600755899b604051976127988961237e565b73ffffffffffffffffffffffffffffffffffffffff169889895273ffffffffffffffffffffffffffffffffffffffff16998a60208a015260ff16978860408201526fffffffffffffffffffffffffffffffff821660608201526fffffffffffffffffffffffffffffffff8316608082015273ffffffffffffffffffffffffffffffffffffffff841660a082015284151560c082015273ffffffffffffffffffffffffffffffffffffffff861660e082015273ffffffffffffffffffffffffffffffffffffffff871661010082015261012081018881528c6000526009602052604060002091805173ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000908185541617845560018401602083015173ffffffffffffffffffffffffffffffffffffffff1682825416178155604083015160ff1661293691907fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000083549260a01b169116179055565b6060820151608080840151901b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff9091161760028501556003840160a083015173ffffffffffffffffffffffffffffffffffffffff168282541617815560c083015115156129f891907fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff0000000000000000000000000000000000000000835492151560a01b169116179055565b6004840160e083015173ffffffffffffffffffffffffffffffffffffffff16828254161790556005840191610100015173ffffffffffffffffffffffffffffffffffffffff16908254161790555190600601556040519788526fffffffffffffffffffffffffffffffff1660208801526fffffffffffffffffffffffffffffffff16604087015273ffffffffffffffffffffffffffffffffffffffff1660608601521515608085015273ffffffffffffffffffffffffffffffffffffffff1660a084015273ffffffffffffffffffffffffffffffffffffffff1660c083015260e08201526101007f1c793446809d07ba2f6a48e1e634e6ecd28227b454018efd11da53b7432e103f91a4565b8080612b9b575b8115612b6a575b50612b1b575050565b6040517fe5d8c52e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015291166024820152604490fd5b90501580612b79575b38612b12565b5073ffffffffffffffffffffffffffffffffffffffff80831690821611612b73565b73ffffffffffffffffffffffffffffffffffffffff848116908416109150612b0b565b60ff16600181149081612c5e575b8115612c2b575b50612bdc575050565b6040517f7405791000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015291166024820152604490fd5b600291501480612c3c575b38612bd3565b5073ffffffffffffffffffffffffffffffffffffffff80831690821610612c36565b73ffffffffffffffffffffffffffffffffffffffff848116908416119150612bcc565b60ff16600181141580612cc7575b612c965750565b602490604051907f7006e8950000000000000000000000000000000000000000000000000000000082526004820152fd5b506002811415612c8f565b60ff16600103612ce157600290565b60019056fea26469706673582212207fca3bad7208dbbf1c6a5dfe98f07ee13bfa8b9aaea952dd68e8a887f195875864736f6c63430008150033000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9000000000000000000000000911a71dda951958913219f7cbd7e4a297ca52b3b000000000000000000000000000000000000000000000000000110d9316ec000
Deployed Bytecode
0x608080604052600436101561001357600080fd5b600090813560e01c9081630f108862146121305750806312d43a51146120df578063252408101461208d5780632707b2d41461205157806339cb1f3e1461201557806363ae210314611fd9578063699735c014611f125780636a77d35f14611e495780636ffbbc5514611e0d57806371b40c9f146119d55780637bae8c5a1461180c5780637bc6729b146117615780638ec4ad8b146114ca5780638f5534ff146113f1578063a962ef1e14611344578063a9e5b11a14611177578063b65202bc146110a4578063c298d6b814610fcb578063d307f03f14610f5f578063d63a6ccd14610ef0578063e6467b69146107fb578063e9a6783c1461071a578063efe85323146101e6578063f887ea40146101775763f9a1dde91461013457600080fd5b346101745760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101745761016b612311565b60043560045580f35b80fd5b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000911a71dda951958913219f7cbd7e4a297ca52b3b168152f35b5060e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457610219612195565b906102226121bd565b61022a6121cd565b926102336121ec565b61023b61220b565b610243612251565b9061024c61222e565b92610255612438565b61025e86612c81565b6003548034106106e357506fffffffffffffffffffffffffffffffff88166105bd575b600654978897610290896123dc565b600655604051906102a082612332565b33825273ffffffffffffffffffffffffffffffffffffffff8816602083015260ff891660408301526fffffffffffffffffffffffffffffffff831660608301526fffffffffffffffffffffffffffffffff8416608083015273ffffffffffffffffffffffffffffffffffffffff851660a083015285151560c083015273ffffffffffffffffffffffffffffffffffffffff871660e08301526101008201903482528a815260086020526040902091805173ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000908185541617845560018401602083015173ffffffffffffffffffffffffffffffffffffffff1682825416178155604083015160ff1661040791907fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000083549260a01b169116179055565b6060820151608080840151901b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff9091161760028501556003840160a083015173ffffffffffffffffffffffffffffffffffffffff168282541617815560c083015115156104c991907fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff0000000000000000000000000000000000000000835492151560a01b169116179055565b600484019160e0015173ffffffffffffffffffffffffffffffffffffffff16908254161790555190600501556040519660ff1687526fffffffffffffffffffffffffffffffff1660208701526fffffffffffffffffffffffffffffffff16604086015273ffffffffffffffffffffffffffffffffffffffff1660608501521515608084015273ffffffffffffffffffffffffffffffffffffffff1660a08301523460c083015273ffffffffffffffffffffffffffffffffffffffff1690339060e07f63f3cc17cd3a0997a486628dcd61d39c2910a87ac8a30a06e902a5fa15b84f5291a46001600255604051908152602090f35b73ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000911a71dda951958913219f7cbd7e4a297ca52b3b16803b156106df578960846fffffffffffffffffffffffffffffffff938b809460405196879586947f1b8278780000000000000000000000000000000000000000000000000000000086527f000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb91660048601523360248601523060448601521660648401525af180156106d45761068b575b50610281565b67ffffffffffffffff81989298116106a7576040529538610685565b6024827f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b6040513d8a823e3d90fd5b8880fd5b604490604051907f5dac504d0000000000000000000000000000000000000000000000000000000082523460048301526024820152fd5b50346101745760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457604061014091600435815260096020522073ffffffffffffffffffffffffffffffffffffffff808254169160018101549160ff60028301549160038401549080600486015416938360068360058901541697015497604051998a5283811660208b015260a01c1660408901526fffffffffffffffffffffffffffffffff8116606089015260801c6080880152811660a087015260a01c16151560c085015260e0840152610100830152610120820152f35b50346101745761080a36612260565b610812612438565b338352600560205260ff60408420541615610ec65781835260096020526040832090604051916108418361237e565b600673ffffffffffffffffffffffffffffffffffffffff8254169182855260ff600182015473ffffffffffffffffffffffffffffffffffffffff8116602088015260a01c16604086015260028101546fffffffffffffffffffffffffffffffff8116606087015260801c608086015260ff600382015473ffffffffffffffffffffffffffffffffffffffff811660a088015260a01c16151560c086015273ffffffffffffffffffffffffffffffffffffffff60048201541660e086015273ffffffffffffffffffffffffffffffffffffffff600582015416610100860152015461012084015215610e955773ffffffffffffffffffffffffffffffffffffffff60208301511691602060ff602461095d82604086015116612cd2565b60405196879384927fd9fe726f0000000000000000000000000000000000000000000000000000000084521660048301525afa928315610e8a578593610e4a575b506109cb60c082015115158473ffffffffffffffffffffffffffffffffffffffff60a08501511691612b04565b6fffffffffffffffffffffffffffffffff608082015116906fffffffffffffffffffffffffffffffff6060820151168215610d58575b60209060c46004548973ffffffffffffffffffffffffffffffffffffffff85870151169173ffffffffffffffffffffffffffffffffffffffff875116946fffffffffffffffffffffffffffffffff60ff60408a015116998173ffffffffffffffffffffffffffffffffffffffff6101008c015116936040519c8d9a8b997ff86786f1000000000000000000000000000000000000000000000000000000008b5260048b015260248a0152604489015216606487015216608485015260a484015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000911a71dda951958913219f7cbd7e4a297ca52b3b1690f18015610d4d578690610d0e575b61012092506fffffffffffffffffffffffffffffffff608083015116610cd2575b500151804710610c7457848080809373ffffffffffffffffffffffffffffffffffffffff86165af1610b586125ae565b5015610bf0577f16b4f79c5d270756b16267422b6507e69d649a8d0c3ea6a8c1c501108e563475918385526009602052610bb96040862060066000918281558260018201558260028201558260038201558260048201558260058201550155565b6040805173ffffffffffffffffffffffffffffffffffffffff9283168152929091166020830152819081015b0390a2600160025580f35b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152fd5b610d0890610ce660ff604085015116612cd2565b9073ffffffffffffffffffffffffffffffffffffffff60e08501511691612bbe565b38610b28565b506020823d602011610d45575b81610d286020938361239b565b81010312610d4157610d3c6101209261272e565b610b07565b8580fd5b3d9150610d1b565b6040513d88823e3d90fd5b50905073ffffffffffffffffffffffffffffffffffffffff602082015116608073ffffffffffffffffffffffffffffffffffffffff835116604460ff6040860151169160405194859384927fcfbf15e1000000000000000000000000000000000000000000000000000000008452600484015260248301525afa908115610d4d578691610de8575b509085610a01565b90506080813d608011610e42575b81610e036080938361239b565b81010312610d4157610e148161274f565b506060610e236020830161274f565b91610e306040820161272e565b5001518060170b03610d415738610de0565b3d9150610df6565b9092506020813d602011610e82575b81610e666020938361239b565b81010312610e7e57610e779061272e565b913861099e565b8480fd5b3d9150610e59565b6040513d87823e3d90fd5b602483604051907f6f2354130000000000000000000000000000000000000000000000000000000082526004820152fd5b60046040517fee90c468000000000000000000000000000000000000000000000000000000008152fd5b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9168152f35b50346101745760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174577face0e51322d77a7556ae0cf8f6528fdd3385e33d2951dc3c9cab391eb76b948c6020600435610fbd612311565b80600355604051908152a180f35b503461017457610fda366122b1565b610fe2612438565b8284526008602052604084209173ffffffffffffffffffffffffffffffffffffffff808454163303610ec65783600460037f05813e22a64b0c6f66b71f2d867634beb3c66a5feb2cd379ca2437ea192c8e479601917fffffffffffffffffffffffff000000000000000000000000000000000000000092848616848254161790550191841690825416179055610be56040519283928390602090939293604083019473ffffffffffffffffffffffffffffffffffffffff809216845216910152565b50346101745760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457604061012091600435815260086020522073ffffffffffffffffffffffffffffffffffffffff808254169160ff600182015492600283015490600384015491836005836004880154169601549660405198895283811660208a015260a01c1660408801526fffffffffffffffffffffffffffffffff8116606088015260801c6080870152811660a086015260a01c16151560c084015260e0830152610100820152f35b506101607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576111ab612195565b906111b46121bd565b36608411611340573660c411611340576101049236841161133c5761014436811161133857359073ffffffffffffffffffffffffffffffffffffffff908183168303610e7e57611202612438565b61120b84612c81565b3460011c60035480821061130157506fffffffffffffffffffffffffffffffff60443581811681036112f55760843582811681036106df5760c4359086821682036112fd57600160ff8a16149a359287841684036112f957928b86938a93611275968d8b3361276c565b5060643581811681036112f55760a43591821682036112f55760e4359285841684036106df576101243595861686036106df573403963488116112c8576112bf989915943361276c565b50600160025580f35b6024897f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b8780fd5b8a80fd5b8980fd5b60449250604051917f5dac504d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b8380fd5b8280fd5b5080fd5b50346101745760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101745761137c612195565b611384612311565b73ffffffffffffffffffffffffffffffffffffffff80911690817fffffffffffffffffffffffff000000000000000000000000000000000000000060015416176001558254167fb9911e2099e372c216862258dc462bb65da46a2c854536c8b1acae619d0d62ed8380a380f35b503461017457611400366122b1565b611408612438565b8284526009602052604084209173ffffffffffffffffffffffffffffffffffffffff808454163303610ec65783600460037f457bb989db79f80ea810085c5e87dc3f6d398d1c357ef12fdf47ad2f8efbb1969601917fffffffffffffffffffffffff000000000000000000000000000000000000000092848616848254161790550191841690825416179055610be56040519283928390602090939293604083019473ffffffffffffffffffffffffffffffffffffffff809216845216910152565b5034610174576114d936612260565b6114e1612438565b8183526020906009825260408420604051906114fc8261237e565b73ffffffffffffffffffffffffffffffffffffffff916006838354169283835260ff60018201548681168986015260a01c16604084015260028101546fffffffffffffffffffffffffffffffff8116606085015260801c608084015260ff600382015486811660a086015260a01c16151560c08401528460048201541660e08401528460058201541661010084015201549161012082019283521561173057829051163314158061171a575b610ec65751918247106116bc578580809381931694855af16115c86125ae565b501561163857907f352426232b87a29640568b27e629c2baa8b3be397819d36fea1f1b2805251ace91838552600982526116296040862060066000918281558260018201558260028201558260038201558260048201558260058201550155565b604051908152a2600160025580f35b608482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152fd5b606484604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152fd5b503386526005845260ff604087205416156115a8565b602486604051907f6f2354130000000000000000000000000000000000000000000000000000000082526004820152fd5b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101745760015473ffffffffffffffffffffffffffffffffffffffff8082163303610ec6577fffffffffffffffffffffffff0000000000000000000000000000000000000000809216600155825491339083161783553391167f3d1e4c3a68fed9f4f8315582b7297cf8fa264bc8e6704287603ba8c72bf05ac28380a380f35b50346101745761181b36612260565b611823612438565b81835260209060088252604084206040519061183e82612332565b73ffffffffffffffffffffffffffffffffffffffff91828254169182825260ff60018201548581168885015260a01c16604083015260028101549060056fffffffffffffffffffffffffffffffff916060850193838116855260801c608086015260ff600382015488811660a088015260a01c16151560c08601528660048201541660e08601520154936101008401948552156119a457848351163314158061198e575b610ec65784611917935116915116907f000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9612471565b51918247106116bc578580809381931694855af16119336125ae565b501561163857907f984fa73f12ae98c6efdbd872ad2d735a48bab444aca6db7f5b0a1efdffcbdce891838552600882526116296040862060056000918281558260018201558260028201558260038201558260048201550155565b503389526005875260ff60408a205416156118e2565b602488604051907f6f2354130000000000000000000000000000000000000000000000000000000082526004820152fd5b5034610174576119e436612260565b6119ec612438565b338352600560205260ff60408420541615610ec6578183526008602052604083209060405191611a1b83612332565b600573ffffffffffffffffffffffffffffffffffffffff8254169182855260ff600182015473ffffffffffffffffffffffffffffffffffffffff8116602088015260a01c16604086015260028101546fffffffffffffffffffffffffffffffff8116606087015260801c608086015260ff600382015473ffffffffffffffffffffffffffffffffffffffff811660a088015260a01c16151560c086015273ffffffffffffffffffffffffffffffffffffffff60048201541660e0860152015461010084015215610e955773ffffffffffffffffffffffffffffffffffffffff60208301511691602060ff6040830151166024604051809681937fd9fe726f00000000000000000000000000000000000000000000000000000000835260048301525afa928315610e8a578593611dd1575b50611b7960c082015115158473ffffffffffffffffffffffffffffffffffffffff60a08501511691612b04565b611bd573ffffffffffffffffffffffffffffffffffffffff6020830151166fffffffffffffffffffffffffffffffff606084015116907f000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9612471565b60045490602073ffffffffffffffffffffffffffffffffffffffff818301511660a473ffffffffffffffffffffffffffffffffffffffff845116918960ff604087015116966fffffffffffffffffffffffffffffffff6060880151166fffffffffffffffffffffffffffffffff60808901511690604051998a9788967f2a5a2cca0000000000000000000000000000000000000000000000000000000088526004880152602487015260448601526064850152608484015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000911a71dda951958913219f7cbd7e4a297ca52b3b1690f1918215610d4d578692611d90575b50611d066101009260ff6040840151169073ffffffffffffffffffffffffffffffffffffffff60e08501511691612bbe565b0151804710610c7457848080809373ffffffffffffffffffffffffffffffffffffffff86165af1611d356125ae565b5015610bf0577f32e1f893df12b36e043dac8c88d2207cf0adc4457187ff49529e74417bad8275918385526008602052610bb96040862060056000918281558260018201558260028201558260038201558260048201550155565b91506020823d602011611dc9575b81611dab6020938361239b565b81010312610d4157611d06611dc26101009361272e565b9250611cd4565b3d9150611d9e565b9092506020813d602011611e05575b81611ded6020938361239b565b81010312610e7e57611dfe9061272e565b9138611b4c565b3d9150611de0565b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576020600754604051908152f35b50346101745760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457611e81612195565b6024359081151580920361133c57602073ffffffffffffffffffffffffffffffffffffffff7f9e796433c770c70b207650b57cd6f20fdb8641d7c85d50ced2dd338cee4f158392611ed0612311565b169283855260058252604085207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541660ff8316179055604051908152a280f35b506101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457611f46612195565b90611f4f6121bd565b611f576121cd565b611f5f6121ec565b611f6761220b565b90611f70612251565b92611f7961222e565b9460e4359673ffffffffffffffffffffffffffffffffffffffff881688036101745750611fa4612438565b611fad81612c81565b6003548034106106e3576020611fcc8a8a8a8a8a8a8a8a34973361276c565b6001600255604051908152f35b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576020600354604051908152f35b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576020600454604051908152f35b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610174576020600654604051908152f35b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261017457602073ffffffffffffffffffffffffffffffffffffffff60015416604051908152f35b503461017457807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101745773ffffffffffffffffffffffffffffffffffffffff6020915416604051908152f35b9050346113405760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126113405760ff604060209373ffffffffffffffffffffffffffffffffffffffff612185612195565b1681526005855220541615158152f35b6004359073ffffffffffffffffffffffffffffffffffffffff821682036121b857565b600080fd5b6024359060ff821682036121b857565b604435906fffffffffffffffffffffffffffffffff821682036121b857565b606435906fffffffffffffffffffffffffffffffff821682036121b857565b6084359073ffffffffffffffffffffffffffffffffffffffff821682036121b857565b60c4359073ffffffffffffffffffffffffffffffffffffffff821682036121b857565b60a4359081151582036121b857565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60409101126121b8576004359060243573ffffffffffffffffffffffffffffffffffffffff811681036121b85790565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126121b8576004359073ffffffffffffffffffffffffffffffffffffffff9060243582811681036121b8579160443590811681036121b85790565b73ffffffffffffffffffffffffffffffffffffffff600054163303610ec657565b610120810190811067ffffffffffffffff82111761234f57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610140810190811067ffffffffffffffff82111761234f57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761234f57604052565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146124095760010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60028054146124475760028055565b60046040517f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b9190916040519260208401927fa9059cbb00000000000000000000000000000000000000000000000000000000845273ffffffffffffffffffffffffffffffffffffffff8092166024860152604485015260448452608084019067ffffffffffffffff928583108484111761234f57169360c081019283118284101761234f5761253d92604052601e82527f416464726573733a206c6f772d6c6576656c2063616c6c206661696c6564000060a0820152600080948192519082885af16125366125ae565b908561260c565b908151918215159283612586575b5050506125555750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b819293509060209181010312611340576020015190811591821503610174575038808061254b565b3d15612607573d9067ffffffffffffffff821161234f57604051916125fb60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116018461239b565b82523d6000602084013e565b606090565b919290156126875750815115612620575090565b3b156126295790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b82519091501561269a5750805190602001fd5b604051907f08c379a000000000000000000000000000000000000000000000000000000000825281602080600483015282519283602484015260005b848110612717575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f836000604480968601015201168101030190fd5b8181018301518682016044015285935082016126d6565b519073ffffffffffffffffffffffffffffffffffffffff821682036121b857565b51906fffffffffffffffffffffffffffffffff821682036121b857565b94999893979299969196959095600754986127868a6123dc565b600755899b604051976127988961237e565b73ffffffffffffffffffffffffffffffffffffffff169889895273ffffffffffffffffffffffffffffffffffffffff16998a60208a015260ff16978860408201526fffffffffffffffffffffffffffffffff821660608201526fffffffffffffffffffffffffffffffff8316608082015273ffffffffffffffffffffffffffffffffffffffff841660a082015284151560c082015273ffffffffffffffffffffffffffffffffffffffff861660e082015273ffffffffffffffffffffffffffffffffffffffff871661010082015261012081018881528c6000526009602052604060002091805173ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000908185541617845560018401602083015173ffffffffffffffffffffffffffffffffffffffff1682825416178155604083015160ff1661293691907fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000083549260a01b169116179055565b6060820151608080840151901b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff9091161760028501556003840160a083015173ffffffffffffffffffffffffffffffffffffffff168282541617815560c083015115156129f891907fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff0000000000000000000000000000000000000000835492151560a01b169116179055565b6004840160e083015173ffffffffffffffffffffffffffffffffffffffff16828254161790556005840191610100015173ffffffffffffffffffffffffffffffffffffffff16908254161790555190600601556040519788526fffffffffffffffffffffffffffffffff1660208801526fffffffffffffffffffffffffffffffff16604087015273ffffffffffffffffffffffffffffffffffffffff1660608601521515608085015273ffffffffffffffffffffffffffffffffffffffff1660a084015273ffffffffffffffffffffffffffffffffffffffff1660c083015260e08201526101007f1c793446809d07ba2f6a48e1e634e6ecd28227b454018efd11da53b7432e103f91a4565b8080612b9b575b8115612b6a575b50612b1b575050565b6040517fe5d8c52e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015291166024820152604490fd5b90501580612b79575b38612b12565b5073ffffffffffffffffffffffffffffffffffffffff80831690821611612b73565b73ffffffffffffffffffffffffffffffffffffffff848116908416109150612b0b565b60ff16600181149081612c5e575b8115612c2b575b50612bdc575050565b6040517f7405791000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015291166024820152604490fd5b600291501480612c3c575b38612bd3565b5073ffffffffffffffffffffffffffffffffffffffff80831690821610612c36565b73ffffffffffffffffffffffffffffffffffffffff848116908416119150612bcc565b60ff16600181141580612cc7575b612c965750565b602490604051907f7006e8950000000000000000000000000000000000000000000000000000000082526004820152fd5b506002811415612c8f565b60ff16600103612ce157600290565b60019056fea26469706673582212207fca3bad7208dbbf1c6a5dfe98f07ee13bfa8b9aaea952dd68e8a887f195875864736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9000000000000000000000000911a71dda951958913219f7cbd7e4a297ca52b3b000000000000000000000000000000000000000000000000000110d9316ec000
-----Decoded View---------------
Arg [0] : _usd (address): 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9
Arg [1] : _router (address): 0x911a71DDa951958913219f7cBD7e4a297ca52B3B
Arg [2] : _minExecutionFee (uint256): 300000000000000
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000fd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9
Arg [1] : 000000000000000000000000911a71dda951958913219f7cbd7e4a297ca52b3b
Arg [2] : 000000000000000000000000000000000000000000000000000110d9316ec000
Loading...
Loading
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.