Overview
ETH Balance
ETH Value
$0.00Latest 1 from a total of 1 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Initialize | 146170801 | 28 days ago | IN | 0 ETH | 0.000000001032 |
View more zero value Internal Transactions in Advanced View mode
Cross-Chain Transactions
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0xc4195a88...6d8d7989F The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { AddressUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import { SignedSafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol";
import { PerpMath } from "./lib/PerpMath.sol";
import { PerpSafeCast } from "./lib/PerpSafeCast.sol";
import { InsuranceFundStorageV1 } from "./storage/InsuranceFundStorage.sol";
import {
SafeERC20Upgradeable,
IERC20Upgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol";
import { ISurplusBeneficiary } from "@perp/voting-escrow/contracts/interface/ISurplusBeneficiary.sol";
import { PerpSafeCast } from "./lib/PerpSafeCast.sol";
import { InsuranceFundStorageV2 } from "./storage/InsuranceFundStorage.sol";
import { OwnerPausable } from "./base/OwnerPausable.sol";
import { IInsuranceFund } from "./interface/IInsuranceFund.sol";
import { IVault } from "./interface/IVault.sol";
// never inherit any new stateful contract. never change the orders of parent stateful contracts
contract InsuranceFund is IInsuranceFund, ReentrancyGuardUpgradeable, OwnerPausable, InsuranceFundStorageV2 {
using AddressUpgradeable for address;
using SignedSafeMathUpgradeable for int256;
using PerpMath for int256;
using PerpSafeCast for int256;
using PerpSafeCast for uint256;
//
// MODIFIER
//
function initialize(address tokenArg) external initializer {
// token address is not contract
require(tokenArg.isContract(), "IF_TNC");
__ReentrancyGuard_init();
__OwnerPausable_init();
_token = tokenArg;
}
function setVault(address vaultArg) external onlyOwner {
// vault is not a contract
require(vaultArg.isContract(), "IF_VNC");
_vault = vaultArg;
emit VaultChanged(vaultArg);
}
function setDistributionThreshold(uint256 distributionThreshold) external onlyOwner {
_distributionThreshold = distributionThreshold;
emit DistributionThresholdChanged(distributionThreshold);
}
function setSurplusBeneficiary(address surplusBeneficiary) external onlyOwner {
// IF_SNC: surplusBeneficiary is not a contract
require(surplusBeneficiary.isContract(), "IF_SNC");
// IF_TNM: token is not match
require(ISurplusBeneficiary(surplusBeneficiary).getToken() == _token, "IF_TNM");
_surplusBeneficiary = surplusBeneficiary;
emit SurplusBeneficiaryChanged(surplusBeneficiary);
}
/// @inheritdoc IInsuranceFund
function repay() external override nonReentrant whenNotPaused {
address vault = _vault;
address token = _token;
int256 insuranceFundSettlementValue = IVault(vault).getSettlementTokenValue(address(this));
// IF_RWNN: repay when non-negative
require(insuranceFundSettlementValue < 0, "IF_RWNN");
uint256 tokenBalance = IERC20Upgradeable(token).balanceOf(address(this));
uint256 insuranceFundSettlementValueAbs = insuranceFundSettlementValue.abs();
uint256 repaidAmount =
tokenBalance >= insuranceFundSettlementValueAbs ? insuranceFundSettlementValueAbs : tokenBalance;
IERC20Upgradeable(token).approve(vault, repaidAmount);
IVault(vault).deposit(token, repaidAmount);
uint256 tokenBalanceAfterRepaid = IERC20Upgradeable(token).balanceOf(address(this));
emit Repaid(repaidAmount, tokenBalanceAfterRepaid);
}
/// @inheritdoc IInsuranceFund
function distributeFee() external override nonReentrant whenNotPaused returns (uint256) {
address vault = _vault;
address token = _token;
address surplusBeneficiary = _surplusBeneficiary;
int256 distributionThreshold = _distributionThreshold.toInt256();
// IF_SNS: surplusBeneficiary not yet set
require(surplusBeneficiary != address(0), "IF_SNS");
// IF_DTEZ: distributionThreshold is equal to zero
require(distributionThreshold > 0, "IF_DTEZ");
int256 insuranceFundFreeCollateral = IVault(vault).getFreeCollateralByToken(address(this), token).toInt256();
int256 insuranceFundCapacity = getInsuranceFundCapacity();
// We check IF's capacity against the distribution threshold, which means if someone sends
// settlement collaterals to IF, it will increase IF's capacity and might help it meet the
// distribution threshold. However, since only settlement collateral can be counted as surplus,
// in certain circumstances you may find that IF cannot distribute fee because it has zero
// surplus even though its capacity has met the distribution threshold.
int256 overDistributionThreshold = PerpMath.max(insuranceFundCapacity.sub(distributionThreshold), 0);
uint256 surplus = PerpMath.min(overDistributionThreshold, insuranceFundFreeCollateral).toUint256();
if (surplus > 0) {
// this should always work since surplus <= insuranceFundFreeCollateral
IVault(vault).withdraw(token, surplus);
// this should always work since IF would have at least `surplus` USDC by now
SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(token), surplusBeneficiary, surplus);
emit FeeDistributed(
surplus,
insuranceFundCapacity.toUint256(),
insuranceFundFreeCollateral.toUint256(),
distributionThreshold.toUint256()
);
}
return surplus;
}
function deposit() external onlyOwner nonReentrant whenNotPaused {
address vault = _vault;
address token = _token;
uint256 insuranceFundWalletBalanceX10_S = IERC20Upgradeable(token).balanceOf(address(this));
IERC20Upgradeable(token).approve(vault, insuranceFundWalletBalanceX10_S);
IVault(vault).deposit(token, insuranceFundWalletBalanceX10_S);
}
//
// EXTERNAL VIEW
//
/// @inheritdoc IInsuranceFund
function getToken() external view override returns (address) {
return _token;
}
/// @inheritdoc IInsuranceFund
function getBorrower() external view override returns (address) {
return _vault;
}
/// @inheritdoc IInsuranceFund
function getVault() external view override returns (address) {
return _vault;
}
/// @inheritdoc IInsuranceFund
function getDistributionThreshold() external view override returns (uint256) {
return _distributionThreshold;
}
/// @inheritdoc IInsuranceFund
function getSurplusBeneficiary() external view override returns (address) {
return _surplusBeneficiary;
}
//
// PUBLIC VIEW
//
/// @inheritdoc IInsuranceFund
function getInsuranceFundCapacity() public view override returns (int256) {
address vault = _vault;
address token = _token;
int256 insuranceFundSettlementTokenValueX10_S = IVault(vault).getSettlementTokenValue(address(this));
int256 insuranceFundWalletBalanceX10_S = IERC20Upgradeable(token).balanceOf(address(this)).toInt256();
return insuranceFundSettlementTokenValueX10_S.add(insuranceFundWalletBalanceX10_S);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../utils/EnumerableSetUpgradeable.sol";
import "../utils/AddressUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../proxy/Initializable.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it.
*/
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable {
function __AccessControl_init() internal initializer {
__Context_init_unchained();
__AccessControl_init_unchained();
}
function __AccessControl_init_unchained() internal initializer {
}
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
using AddressUpgradeable for address;
struct RoleData {
EnumerableSetUpgradeable.AddressSet members;
bytes32 adminRole;
}
mapping (bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view returns (bool) {
return _roles[role].members.contains(account);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view returns (uint256) {
return _roles[role].members.length();
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
return _roles[role].members.at(index);
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) public virtual {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
_roles[role].adminRole = adminRole;
}
function _grantRole(bytes32 role, address account) private {
if (_roles[role].members.add(account)) {
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) private {
if (_roles[role].members.remove(account)) {
emit RoleRevoked(role, account, _msgSender());
}
}
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library MathUpgradeable {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow, so we distribute
return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMathUpgradeable {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @title SignedSafeMath
* @dev Signed math operations with safety checks that revert on error.
*/
library SignedSafeMathUpgradeable {
int256 constant private _INT256_MIN = -2**255;
/**
* @dev Returns the multiplication of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(int256 a, int256 b) internal pure returns (int256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow");
int256 c = a * b;
require(c / a == b, "SignedSafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two signed integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(int256 a, int256 b) internal pure returns (int256) {
require(b != 0, "SignedSafeMath: division by zero");
require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow");
int256 c = a / b;
return c;
}
/**
* @dev Returns the subtraction of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(int256 a, int256 b) internal pure returns (int256) {
int256 c = a - b;
require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow");
return c;
}
/**
* @dev Returns the addition of two signed integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(int256 a, int256 b) internal pure returns (int256) {
int256 c = a + b;
require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow");
return c;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../access/AccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../token/ERC20/ERC20Upgradeable.sol";
import "../token/ERC20/ERC20BurnableUpgradeable.sol";
import "../token/ERC20/ERC20PausableUpgradeable.sol";
import "../proxy/Initializable.sol";
/**
* @dev {ERC20} token, including:
*
* - ability for holders to burn (destroy) their tokens
* - a minter role that allows for token minting (creation)
* - a pauser role that allows to stop all token transfers
*
* This contract uses {AccessControl} to lock permissioned functions using the
* different roles - head to its documentation for details.
*
* The account that deploys the contract will be granted the minter and pauser
* roles, as well as the default admin role, which will let it grant both minter
* and pauser roles to other accounts.
*/
contract ERC20PresetMinterPauserUpgradeable is Initializable, ContextUpgradeable, AccessControlUpgradeable, ERC20BurnableUpgradeable, ERC20PausableUpgradeable {
function initialize(string memory name, string memory symbol) public virtual initializer {
__ERC20PresetMinterPauser_init(name, symbol);
}
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
/**
* @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the
* account that deploys the contract.
*
* See {ERC20-constructor}.
*/
function __ERC20PresetMinterPauser_init(string memory name, string memory symbol) internal initializer {
__Context_init_unchained();
__AccessControl_init_unchained();
__ERC20_init_unchained(name, symbol);
__ERC20Burnable_init_unchained();
__Pausable_init_unchained();
__ERC20Pausable_init_unchained();
__ERC20PresetMinterPauser_init_unchained(name, symbol);
}
function __ERC20PresetMinterPauser_init_unchained(string memory name, string memory symbol) internal initializer {
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
_setupRole(MINTER_ROLE, _msgSender());
_setupRole(PAUSER_ROLE, _msgSender());
}
/**
* @dev Creates `amount` new tokens for `to`.
*
* See {ERC20-_mint}.
*
* Requirements:
*
* - the caller must have the `MINTER_ROLE`.
*/
function mint(address to, uint256 amount) public virtual {
require(hasRole(MINTER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have minter role to mint");
_mint(to, amount);
}
/**
* @dev Pauses all token transfers.
*
* See {ERC20Pausable} and {Pausable-_pause}.
*
* Requirements:
*
* - the caller must have the `PAUSER_ROLE`.
*/
function pause() public virtual {
require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to pause");
_pause();
}
/**
* @dev Unpauses all token transfers.
*
* See {ERC20Pausable} and {Pausable-_unpause}.
*
* Requirements:
*
* - the caller must have the `PAUSER_ROLE`.
*/
function unpause() public virtual {
require(hasRole(PAUSER_ROLE, _msgSender()), "ERC20PresetMinterPauser: must have pauser role to unpause");
_unpause();
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override(ERC20Upgradeable, ERC20PausableUpgradeable) {
super._beforeTokenTransfer(from, to, amount);
}
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// solhint-disable-next-line compiler-version
pragma solidity >=0.4.24 <0.8.0;
import "../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
*/
bool private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Modifier to protect an initializer function from being invoked twice.
*/
modifier initializer() {
require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
/// @dev Returns true if and only if the function is running in the constructor
function _isConstructor() private view returns (bool) {
return !AddressUpgradeable.isContract(address(this));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../../utils/ContextUpgradeable.sol";
import "./ERC20Upgradeable.sol";
import "../../proxy/Initializable.sol";
/**
* @dev Extension of {ERC20} that allows token holders to destroy both their own
* tokens and those that they have an allowance for, in a way that can be
* recognized off-chain (via event analysis).
*/
abstract contract ERC20BurnableUpgradeable is Initializable, ContextUpgradeable, ERC20Upgradeable {
function __ERC20Burnable_init() internal initializer {
__Context_init_unchained();
__ERC20Burnable_init_unchained();
}
function __ERC20Burnable_init_unchained() internal initializer {
}
using SafeMathUpgradeable for uint256;
/**
* @dev Destroys `amount` tokens from the caller.
*
* See {ERC20-_burn}.
*/
function burn(uint256 amount) public virtual {
_burn(_msgSender(), amount);
}
/**
* @dev Destroys `amount` tokens from `account`, deducting from the caller's
* allowance.
*
* See {ERC20-_burn} and {ERC20-allowance}.
*
* Requirements:
*
* - the caller must have allowance for ``accounts``'s tokens of at least
* `amount`.
*/
function burnFrom(address account, uint256 amount) public virtual {
uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
_approve(account, _msgSender(), decreasedAllowance);
_burn(account, amount);
}
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "./ERC20Upgradeable.sol";
import "../../utils/PausableUpgradeable.sol";
import "../../proxy/Initializable.sol";
/**
* @dev ERC20 token with pausable token transfers, minting and burning.
*
* Useful for scenarios such as preventing trades until the end of an evaluation
* period, or having an emergency switch for freezing all token transfers in the
* event of a large bug.
*/
abstract contract ERC20PausableUpgradeable is Initializable, ERC20Upgradeable, PausableUpgradeable {
function __ERC20Pausable_init() internal initializer {
__Context_init_unchained();
__Pausable_init_unchained();
__ERC20Pausable_init_unchained();
}
function __ERC20Pausable_init_unchained() internal initializer {
}
/**
* @dev See {ERC20-_beforeTokenTransfer}.
*
* Requirements:
*
* - the contract must not be paused.
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override {
super._beforeTokenTransfer(from, to, amount);
require(!paused(), "ERC20Pausable: token transfer while paused");
}
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../../utils/ContextUpgradeable.sol";
import "./IERC20Upgradeable.sol";
import "../../math/SafeMathUpgradeable.sol";
import "../../proxy/Initializable.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable {
using SafeMathUpgradeable for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for {name} and {symbol}, initializes {decimals} with
* a default value of 18.
*
* To select a different value for {decimals}, use {_setupDecimals}.
*
* All three of these values are immutable: they can only be set once during
* construction.
*/
function __ERC20_init(string memory name_, string memory symbol_) internal initializer {
__Context_init_unchained();
__ERC20_init_unchained(name_, symbol_);
}
function __ERC20_init_unchained(string memory name_, string memory symbol_) internal initializer {
_name = name_;
_symbol = symbol_;
_decimals = 18;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
* called.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return _decimals;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* Requirements:
*
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Sets {decimals} to a value other than the default one of 18.
*
* WARNING: This function should only be called from the constructor. Most
* applications that interact with token contracts will not expect
* {decimals} to ever change, and may work incorrectly if it does.
*/
function _setupDecimals(uint8 decimals_) internal virtual {
_decimals = decimals_;
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be to transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
uint256[44] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Upgradeable {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "./IERC20Upgradeable.sol";
import "../../math/SafeMathUpgradeable.sol";
import "../../utils/AddressUpgradeable.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 SafeERC20Upgradeable {
using SafeMathUpgradeable for uint256;
using AddressUpgradeable for address;
function safeTransfer(IERC20Upgradeable token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20Upgradeable token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20Upgradeable token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20Upgradeable token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../proxy/Initializable.sol";
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal initializer {
__Context_init_unchained();
}
function __Context_init_unchained() internal initializer {
}
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*/
library EnumerableSetUpgradeable {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping (bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) { // Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
// When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "./ContextUpgradeable.sol";
import "../proxy/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal initializer {
__Context_init_unchained();
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal initializer {
_paused = false;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
require(!paused(), "Pausable: paused");
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
require(paused(), "Pausable: not paused");
_;
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
import "../proxy/Initializable.sol";
/**
* @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 ReentrancyGuardUpgradeable is Initializable {
// 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;
function __ReentrancyGuard_init() internal initializer {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal initializer {
_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 make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <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
pragma solidity ^0.7.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts may inherit from this and call {_registerInterface} to declare
* their support of an interface.
*/
abstract contract ERC165 is IERC165 {
/*
* bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7
*/
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/**
* @dev Mapping of interface ids to whether or not it's supported.
*/
mapping(bytes4 => bool) private _supportedInterfaces;
constructor () {
// Derived contracts need only register support for their own interfaces,
// we register support for ERC165 itself here
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev See {IERC165-supportsInterface}.
*
* Time complexity O(1), guaranteed to always use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev Registers the contract as an implementer of the interface defined by
* `interfaceId`. Support of the actual ERC165 interface is automatic and
* registering its interface id is not required.
*
* See {IERC165-supportsInterface}.
*
* Requirements:
*
* - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`).
*/
function _registerInterface(bytes4 interfaceId) internal virtual {
require(interfaceId != 0xffffffff, "ERC165: invalid interface id");
_supportedInterfaces[interfaceId] = true;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.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: MIT
pragma solidity ^0.7.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "../../utils/Context.sol";
import "./IERC721.sol";
import "./IERC721Metadata.sol";
import "./IERC721Enumerable.sol";
import "./IERC721Receiver.sol";
import "../../introspection/ERC165.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
import "../../utils/EnumerableSet.sol";
import "../../utils/EnumerableMap.sol";
import "../../utils/Strings.sol";
/**
* @title ERC721 Non-Fungible Token Standard basic implementation
* @dev see https://eips.ethereum.org/EIPS/eip-721
*/
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable {
using SafeMath for uint256;
using Address for address;
using EnumerableSet for EnumerableSet.UintSet;
using EnumerableMap for EnumerableMap.UintToAddressMap;
using Strings for uint256;
// Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
// which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
// Mapping from holder address to their (enumerable) set of owned tokens
mapping (address => EnumerableSet.UintSet) private _holderTokens;
// Enumerable mapping from token ids to their owners
EnumerableMap.UintToAddressMap private _tokenOwners;
// Mapping from token ID to approved address
mapping (uint256 => address) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping (address => mapping (address => bool)) private _operatorApprovals;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Optional mapping for token URIs
mapping (uint256 => string) private _tokenURIs;
// Base URI
string private _baseURI;
/*
* bytes4(keccak256('balanceOf(address)')) == 0x70a08231
* bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e
* bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3
* bytes4(keccak256('getApproved(uint256)')) == 0x081812fc
* bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465
* bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5
* bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde
*
* => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^
* 0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd
*/
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
/*
* bytes4(keccak256('name()')) == 0x06fdde03
* bytes4(keccak256('symbol()')) == 0x95d89b41
* bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd
*
* => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f
*/
bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
/*
* bytes4(keccak256('totalSupply()')) == 0x18160ddd
* bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59
* bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7
*
* => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63
*/
bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
/**
* @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
*/
constructor (string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721);
_registerInterface(_INTERFACE_ID_ERC721_METADATA);
_registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ERC721: balance query for the zero address");
return _holderTokens[owner].length();
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token");
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
}
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
}
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return string(abi.encodePacked(base, tokenId.toString()));
}
/**
* @dev Returns the base URI set via {_setBaseURI}. This will be
* automatically added as a prefix in {tokenURI} to each token's URI, or
* to the token ID if no specific URI is set for that token ID.
*/
function baseURI() public view virtual returns (string memory) {
return _baseURI;
}
/**
* @dev See {IERC721Enumerable-tokenOfOwnerByIndex}.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
return _holderTokens[owner].at(index);
}
/**
* @dev See {IERC721Enumerable-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
// _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds
return _tokenOwners.length();
}
/**
* @dev See {IERC721Enumerable-tokenByIndex}.
*/
function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
(uint256 tokenId, ) = _tokenOwners.at(index);
return tokenId;
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual override {
address owner = ERC721.ownerOf(tokenId);
require(to != owner, "ERC721: approval to current owner");
require(_msgSender() == owner || ERC721.isApprovedForAll(owner, _msgSender()),
"ERC721: approve caller is not owner nor approved for all"
);
_approve(to, tokenId);
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view virtual override returns (address) {
require(_exists(tokenId), "ERC721: approved query for nonexistent token");
return _tokenApprovals[tokenId];
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
require(operator != _msgSender(), "ERC721: approve to caller");
_operatorApprovals[_msgSender()][operator] = approved;
emit ApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(address from, address to, uint256 tokenId) public virtual override {
//solhint-disable-next-line max-line-length
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_transfer(from, to, tokenId);
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
_safeTransfer(from, to, tokenId, _data);
}
/**
* @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.
*
* `_data` is additional data, it has no specified format and it is sent in call to `to`.
*
* This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
* implement alternative mechanisms to perform token transfer, such as signature-based.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {
_transfer(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted (`_mint`),
* and stop existing when they are burned (`_burn`).
*/
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _tokenOwners.contains(tokenId);
}
/**
* @dev Returns whether `spender` is allowed to manage `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
require(_exists(tokenId), "ERC721: operator query for nonexistent token");
address owner = ERC721.ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || ERC721.isApprovedForAll(owner, spender));
}
/**
* @dev Safely mints `tokenId` and transfers it to `to`.
*
* Requirements:
d*
* - `tokenId` must not exist.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function _safeMint(address to, uint256 tokenId) internal virtual {
_safeMint(to, tokenId, "");
}
/**
* @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
* forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
*/
function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
_mint(to, tokenId);
require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
}
/**
* @dev Mints `tokenId` and transfers it to `to`.
*
* WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
*
* Requirements:
*
* - `tokenId` must not exist.
* - `to` cannot be the zero address.
*
* Emits a {Transfer} event.
*/
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId);
_holderTokens[to].add(tokenId);
_tokenOwners.set(tokenId, to);
emit Transfer(address(0), to, tokenId);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId) internal virtual {
address owner = ERC721.ownerOf(tokenId); // internal owner
_beforeTokenTransfer(owner, address(0), tokenId);
// Clear approvals
_approve(address(0), tokenId);
// Clear metadata (if any)
if (bytes(_tokenURIs[tokenId]).length != 0) {
delete _tokenURIs[tokenId];
}
_holderTokens[owner].remove(tokenId);
_tokenOwners.remove(tokenId);
emit Transfer(owner, address(0), tokenId);
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
* As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
function _transfer(address from, address to, uint256 tokenId) internal virtual {
require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); // internal owner
require(to != address(0), "ERC721: transfer to the zero address");
_beforeTokenTransfer(from, to, tokenId);
// Clear approvals from the previous owner
_approve(address(0), tokenId);
_holderTokens[from].remove(tokenId);
_holderTokens[to].add(tokenId);
_tokenOwners.set(tokenId, to);
emit Transfer(from, to, tokenId);
}
/**
* @dev Sets `_tokenURI` as the tokenURI of `tokenId`.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token");
_tokenURIs[tokenId] = _tokenURI;
}
/**
* @dev Internal function to set the base URI for all token IDs. It is
* automatically added as a prefix to the value returned in {tokenURI},
* or to the token ID if {tokenURI} is empty.
*/
function _setBaseURI(string memory baseURI_) internal virtual {
_baseURI = baseURI_;
}
/**
* @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
* The call is not executed if the target address is not a contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes optional data to send along with the call
* @return bool whether the call correctly returned the expected magic value
*/
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
private returns (bool)
{
if (!to.isContract()) {
return true;
}
bytes memory returndata = to.functionCall(abi.encodeWithSelector(
IERC721Receiver(to).onERC721Received.selector,
_msgSender(),
from,
tokenId,
_data
), "ERC721: transfer to non ERC721Receiver implementer");
bytes4 retval = abi.decode(returndata, (bytes4));
return (retval == _ERC721_RECEIVED);
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* Emits an {Approval} event.
*/
function _approve(address to, uint256 tokenId) internal virtual {
_tokenApprovals[tokenId] = to;
emit Approval(ERC721.ownerOf(tokenId), to, tokenId); // internal owner
}
/**
* @dev Hook that is called before any token transfer. This includes minting
* and burning.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, ``from``'s `tokenId` will be burned.
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { }
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "../../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`, 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 be 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: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
*
* 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 Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @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 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);
/**
* @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;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
import "./IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Metadata is IERC721 {
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
*/
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableMap for EnumerableMap.UintToAddressMap;
*
* // Declare a set state variable
* EnumerableMap.UintToAddressMap private myMap;
* }
* ```
*
* As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are
* supported.
*/
library EnumerableMap {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Map type with
// bytes32 keys and values.
// The Map implementation uses private functions, and user-facing
// implementations (such as Uint256ToAddressMap) are just wrappers around
// the underlying Map.
// This means that we can only create new EnumerableMaps for types that fit
// in bytes32.
struct MapEntry {
bytes32 _key;
bytes32 _value;
}
struct Map {
// Storage of map keys and values
MapEntry[] _entries;
// Position of the entry defined by a key in the `entries` array, plus 1
// because index 0 means a key is not in the map.
mapping (bytes32 => uint256) _indexes;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) {
// We read and store the key's index to prevent multiple reads from the same storage slot
uint256 keyIndex = map._indexes[key];
if (keyIndex == 0) { // Equivalent to !contains(map, key)
map._entries.push(MapEntry({ _key: key, _value: value }));
// The entry is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
map._indexes[key] = map._entries.length;
return true;
} else {
map._entries[keyIndex - 1]._value = value;
return false;
}
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function _remove(Map storage map, bytes32 key) private returns (bool) {
// We read and store the key's index to prevent multiple reads from the same storage slot
uint256 keyIndex = map._indexes[key];
if (keyIndex != 0) { // Equivalent to contains(map, key)
// To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one
// in the array, and then remove the last entry (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = keyIndex - 1;
uint256 lastIndex = map._entries.length - 1;
// When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
MapEntry storage lastEntry = map._entries[lastIndex];
// Move the last entry to the index where the entry to delete is
map._entries[toDeleteIndex] = lastEntry;
// Update the index for the moved entry
map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based
// Delete the slot where the moved entry was stored
map._entries.pop();
// Delete the index for the deleted slot
delete map._indexes[key];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function _contains(Map storage map, bytes32 key) private view returns (bool) {
return map._indexes[key] != 0;
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function _length(Map storage map) private view returns (uint256) {
return map._entries.length;
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) {
require(map._entries.length > index, "EnumerableMap: index out of bounds");
MapEntry storage entry = map._entries[index];
return (entry._key, entry._value);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) {
uint256 keyIndex = map._indexes[key];
if (keyIndex == 0) return (false, 0); // Equivalent to contains(map, key)
return (true, map._entries[keyIndex - 1]._value); // All indexes are 1-based
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function _get(Map storage map, bytes32 key) private view returns (bytes32) {
uint256 keyIndex = map._indexes[key];
require(keyIndex != 0, "EnumerableMap: nonexistent key"); // Equivalent to contains(map, key)
return map._entries[keyIndex - 1]._value; // All indexes are 1-based
}
/**
* @dev Same as {_get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {_tryGet}.
*/
function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) {
uint256 keyIndex = map._indexes[key];
require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key)
return map._entries[keyIndex - 1]._value; // All indexes are 1-based
}
// UintToAddressMap
struct UintToAddressMap {
Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
return _remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return _contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToAddressMap storage map) internal view returns (uint256) {
return _length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the set. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
(bytes32 key, bytes32 value) = _at(map._inner, index);
return (uint256(key), address(uint160(uint256(value))));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*
* _Available since v3.4._
*/
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
(bool success, bytes32 value) = _tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(value))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint160(uint256(_get(map._inner, bytes32(key)))));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) {
return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage))));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping (bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) { // Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
// When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
/**
* @dev String operations.
*/
library Strings {
/**
* @dev Converts a `uint256` to its ASCII `string` representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
uint256 index = digits - 1;
temp = value;
while (temp != 0) {
buffer[index--] = bytes1(uint8(48 + temp % 10));
temp /= 10;
}
return string(buffer);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
interface IPriceFeed {
function decimals() external view returns (uint8);
/// @dev Returns the index price of the token.
/// @param interval The interval represents twap interval.
function getPrice(uint256 interval) external view returns (uint256);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
interface IPriceFeedDispatcherEvent {
enum Status { Chainlink, UniswapV3 }
event StatusUpdated(Status status);
event UniswapV3PriceFeedUpdated(address uniswapV3PriceFeed);
}
interface IPriceFeedDispatcher is IPriceFeedDispatcherEvent {
/// @notice when Chainlink is down, switch priceFeed source to UniswapV3PriceFeed
/// @dev this method is called by every tx that settles funding in Exchange.settleFunding() -> BaseToken.cacheTwap()
/// @param interval only useful when using Chainlink; UniswapV3PriceFeed has its own fixed interval
function dispatchPrice(uint256 interval) external;
/// @notice return price from UniswapV3PriceFeed if _uniswapV3PriceFeed is ready to be switched to AND
/// 1. _status is already UniswapV3PriceFeed OR
/// 2. ChainlinkPriceFeedV3.isTimedOut()
/// else, return price from ChainlinkPriceFeedV3
/// @dev decimals of the return value is 18, which can be queried with the function decimals()
/// @param interval only useful when using Chainlink; UniswapV3PriceFeed has its own fixed interval
function getDispatchedPrice(uint256 interval) external view returns (uint256);
function getChainlinkPriceFeedV3() external view returns (address);
function getUniswapV3PriceFeed() external view returns (address);
function decimals() external pure returns (uint8);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
interface ISurplusBeneficiary {
/// @notice Emitted when dispatch function is trigger
/// @param amountToTreasury Distributed fee amount to Treasury
/// @param amountToFeeDistributor Distributed fee amount to Fee Distributor contract
event Dispatch(uint256 amountToTreasury, uint256 amountToFeeDistributor);
/// @notice Emitted when feeDistributor address is changed.
/// @param oldValue Old feeDistributor address
/// @param newValue New feeDistributor address
event FeeDistributorChanged(address oldValue, address newValue);
/// @notice Emitted when `Treasury` multiSig address is changed.
/// @param oldValue Old Treasury address
/// @param newValue New Treasury address
event TreasuryChanged(address oldValue, address newValue);
/// @notice Emitted when TreasuryPercentage value is changed.
/// @param oldValue Old TreasuryPercentage value
/// @param newValue New TreasuryPercentage value
event TreasuryPercentageChanged(uint24 oldValue, uint24 newValue);
/// @notice Will dispatch all balance to `Treasury` and `FeeDistributor`
function dispatch() external;
/// @notice Get token address
/// @return token The address of token
function getToken() external view returns (address token);
/// @notice Get Fee Distributor contract address
/// @return feeDistributor The address of Fee Distributor contract
function getFeeDistributor() external view returns (address feeDistributor);
/// @notice Get `Treasury` multisig address
/// @return treasury The address of `Treasury` multisig
function getTreasury() external view returns (address treasury);
/// @notice Get Treasury's fee share
/// @return percentage Treasury's fee share (6 decimals, 1000000 = 100%)
function getTreasuryPercentage() external view returns (uint24 percentage);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Callback for IUniswapV3PoolActions#flash
/// @notice Any contract that calls IUniswapV3PoolActions#flash must implement this interface
interface IUniswapV3FlashCallback {
/// @notice Called to `msg.sender` after transferring to the recipient from IUniswapV3Pool#flash.
/// @dev In the implementation you must repay the pool the tokens sent by flash plus the computed fee amounts.
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
/// @param fee0 The fee amount in token0 due to the pool by the end of the flash
/// @param fee1 The fee amount in token1 due to the pool by the end of the flash
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#flash call
function uniswapV3FlashCallback(
uint256 fee0,
uint256 fee1,
bytes calldata data
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Callback for IUniswapV3PoolActions#mint
/// @notice Any contract that calls IUniswapV3PoolActions#mint must implement this interface
interface IUniswapV3MintCallback {
/// @notice Called to `msg.sender` after minting liquidity to a position from IUniswapV3Pool#mint.
/// @dev In the implementation you must pay the pool tokens owed for the minted liquidity.
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
/// @param amount0Owed The amount of token0 due to the pool for the minted liquidity
/// @param amount1Owed The amount of token1 due to the pool for the minted liquidity
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#mint call
function uniswapV3MintCallback(
uint256 amount0Owed,
uint256 amount1Owed,
bytes calldata data
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
/// @dev In the implementation you must pay the pool tokens owed for the swap.
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Minimal ERC20 interface for Uniswap
/// @notice Contains a subset of the full ERC20 interface that is used in Uniswap V3
interface IERC20Minimal {
/// @notice Returns the balance of a token
/// @param account The account for which to look up the number of tokens it has, i.e. its balance
/// @return The number of tokens held by the account
function balanceOf(address account) external view returns (uint256);
/// @notice Transfers the amount of token from the `msg.sender` to the recipient
/// @param recipient The account that will receive the amount transferred
/// @param amount The number of tokens to send from the sender to the recipient
/// @return Returns true for a successful transfer, false for an unsuccessful transfer
function transfer(address recipient, uint256 amount) external returns (bool);
/// @notice Returns the current allowance given to a spender by an owner
/// @param owner The account of the token owner
/// @param spender The account of the token spender
/// @return The current allowance granted by `owner` to `spender`
function allowance(address owner, address spender) external view returns (uint256);
/// @notice Sets the allowance of a spender from the `msg.sender` to the value `amount`
/// @param spender The account which will be allowed to spend a given amount of the owners tokens
/// @param amount The amount of tokens allowed to be used by `spender`
/// @return Returns true for a successful approval, false for unsuccessful
function approve(address spender, uint256 amount) external returns (bool);
/// @notice Transfers `amount` tokens from `sender` to `recipient` up to the allowance given to the `msg.sender`
/// @param sender The account from which the transfer will be initiated
/// @param recipient The recipient of the transfer
/// @param amount The amount of the transfer
/// @return Returns true for a successful transfer, false for unsuccessful
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/// @notice Event emitted when tokens are transferred from one address to another, either via `#transfer` or `#transferFrom`.
/// @param from The account from which the tokens were sent, i.e. the balance decreased
/// @param to The account to which the tokens were sent, i.e. the balance increased
/// @param value The amount of tokens that were transferred
event Transfer(address indexed from, address indexed to, uint256 value);
/// @notice Event emitted when the approval amount for the spender of a given owner's tokens changes.
/// @param owner The account that approved spending of its tokens
/// @param spender The account for which the spending allowance was modified
/// @param value The new allowance from the owner to the spender
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title The interface for the Uniswap V3 Factory
/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees
interface IUniswapV3Factory {
/// @notice Emitted when the owner of the factory is changed
/// @param oldOwner The owner before the owner was changed
/// @param newOwner The owner after the owner was changed
event OwnerChanged(address indexed oldOwner, address indexed newOwner);
/// @notice Emitted when a pool is created
/// @param token0 The first token of the pool by address sort order
/// @param token1 The second token of the pool by address sort order
/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
/// @param tickSpacing The minimum number of ticks between initialized ticks
/// @param pool The address of the created pool
event PoolCreated(
address indexed token0,
address indexed token1,
uint24 indexed fee,
int24 tickSpacing,
address pool
);
/// @notice Emitted when a new fee amount is enabled for pool creation via the factory
/// @param fee The enabled fee, denominated in hundredths of a bip
/// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee
event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing);
/// @notice Returns the current owner of the factory
/// @dev Can be changed by the current owner via setOwner
/// @return The address of the factory owner
function owner() external view returns (address);
/// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled
/// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context
/// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee
/// @return The tick spacing
function feeAmountTickSpacing(uint24 fee) external view returns (int24);
/// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
/// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
/// @param tokenA The contract address of either token0 or token1
/// @param tokenB The contract address of the other token
/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
/// @return pool The pool address
function getPool(
address tokenA,
address tokenB,
uint24 fee
) external view returns (address pool);
/// @notice Creates a pool for the given two tokens and fee
/// @param tokenA One of the two tokens in the desired pool
/// @param tokenB The other of the two tokens in the desired pool
/// @param fee The desired fee for the pool
/// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved
/// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments
/// are invalid.
/// @return pool The address of the newly created pool
function createPool(
address tokenA,
address tokenB,
uint24 fee
) external returns (address pool);
/// @notice Updates the owner of the factory
/// @dev Must be called by the current owner
/// @param _owner The new owner of the factory
function setOwner(address _owner) external;
/// @notice Enables a fee amount with the given tickSpacing
/// @dev Fee amounts may never be removed once enabled
/// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6)
/// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount
function enableFeeAmount(uint24 fee, int24 tickSpacing) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import './pool/IUniswapV3PoolImmutables.sol';
import './pool/IUniswapV3PoolState.sol';
import './pool/IUniswapV3PoolDerivedState.sol';
import './pool/IUniswapV3PoolActions.sol';
import './pool/IUniswapV3PoolOwnerActions.sol';
import './pool/IUniswapV3PoolEvents.sol';
/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is
IUniswapV3PoolImmutables,
IUniswapV3PoolState,
IUniswapV3PoolDerivedState,
IUniswapV3PoolActions,
IUniswapV3PoolOwnerActions,
IUniswapV3PoolEvents
{
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title An interface for a contract that is capable of deploying Uniswap V3 Pools
/// @notice A contract that constructs a pool must implement this to pass arguments to the pool
/// @dev This is used to avoid having constructor arguments in the pool contract, which results in the init code hash
/// of the pool being constant allowing the CREATE2 address of the pool to be cheaply computed on-chain
interface IUniswapV3PoolDeployer {
/// @notice Get the parameters to be used in constructing the pool, set transiently during pool creation.
/// @dev Called by the pool constructor to fetch the parameters of the pool
/// Returns factory The factory address
/// Returns token0 The first token of the pool by address sort order
/// Returns token1 The second token of the pool by address sort order
/// Returns fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
/// Returns tickSpacing The minimum number of ticks between initialized ticks
function parameters()
external
view
returns (
address factory,
address token0,
address token1,
uint24 fee,
int24 tickSpacing
);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Permissionless pool actions
/// @notice Contains pool methods that can be called by anyone
interface IUniswapV3PoolActions {
/// @notice Sets the initial price for the pool
/// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
/// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96
function initialize(uint160 sqrtPriceX96) external;
/// @notice Adds liquidity for the given recipient/tickLower/tickUpper position
/// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback
/// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
/// on tickLower, tickUpper, the amount of liquidity, and the current price.
/// @param recipient The address for which the liquidity will be created
/// @param tickLower The lower tick of the position in which to add liquidity
/// @param tickUpper The upper tick of the position in which to add liquidity
/// @param amount The amount of liquidity to mint
/// @param data Any data that should be passed through to the callback
/// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback
/// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback
function mint(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount,
bytes calldata data
) external returns (uint256 amount0, uint256 amount1);
/// @notice Collects tokens owed to a position
/// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
/// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
/// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
/// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
/// @param recipient The address which should receive the fees collected
/// @param tickLower The lower tick of the position for which to collect fees
/// @param tickUpper The upper tick of the position for which to collect fees
/// @param amount0Requested How much token0 should be withdrawn from the fees owed
/// @param amount1Requested How much token1 should be withdrawn from the fees owed
/// @return amount0 The amount of fees collected in token0
/// @return amount1 The amount of fees collected in token1
function collect(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount0Requested,
uint128 amount1Requested
) external returns (uint128 amount0, uint128 amount1);
/// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
/// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
/// @dev Fees must be collected separately via a call to #collect
/// @param tickLower The lower tick of the position for which to burn liquidity
/// @param tickUpper The upper tick of the position for which to burn liquidity
/// @param amount How much liquidity to burn
/// @return amount0 The amount of token0 sent to the recipient
/// @return amount1 The amount of token1 sent to the recipient
function burn(
int24 tickLower,
int24 tickUpper,
uint128 amount
) external returns (uint256 amount0, uint256 amount1);
/// @notice Swap token0 for token1, or token1 for token0
/// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback
/// @param recipient The address to receive the output of the swap
/// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0
/// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
/// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
/// value after the swap. If one for zero, the price cannot be greater than this value after the swap
/// @param data Any data to be passed through to the callback
/// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
/// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
function swap(
address recipient,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
bytes calldata data
) external returns (int256 amount0, int256 amount1);
/// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback
/// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback
/// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling
/// with 0 amount{0,1} and sending the donation amount(s) from the callback
/// @param recipient The address which will receive the token0 and token1 amounts
/// @param amount0 The amount of token0 to send
/// @param amount1 The amount of token1 to send
/// @param data Any data to be passed through to the callback
function flash(
address recipient,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external;
/// @notice Increase the maximum number of price and liquidity observations that this pool will store
/// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to
/// the input observationCardinalityNext.
/// @param observationCardinalityNext The desired minimum number of observations for the pool to store
function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Pool state that is not stored
/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the
/// blockchain. The functions here may have variable gas costs.
interface IUniswapV3PoolDerivedState {
/// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp
/// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing
/// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,
/// you must call it with secondsAgos = [3600, 0].
/// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in
/// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.
/// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned
/// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp
/// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block
/// timestamp
function observe(uint32[] calldata secondsAgos)
external
view
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);
/// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range
/// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.
/// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first
/// snapshot is taken and the second snapshot is taken.
/// @param tickLower The lower tick of the range
/// @param tickUpper The upper tick of the range
/// @return tickCumulativeInside The snapshot of the tick accumulator for the range
/// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range
/// @return secondsInside The snapshot of seconds per liquidity for the range
function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
external
view
returns (
int56 tickCumulativeInside,
uint160 secondsPerLiquidityInsideX128,
uint32 secondsInside
);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Events emitted by a pool
/// @notice Contains all events emitted by the pool
interface IUniswapV3PoolEvents {
/// @notice Emitted exactly once by a pool when #initialize is first called on the pool
/// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize
/// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96
/// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool
event Initialize(uint160 sqrtPriceX96, int24 tick);
/// @notice Emitted when liquidity is minted for a given position
/// @param sender The address that minted the liquidity
/// @param owner The owner of the position and recipient of any minted liquidity
/// @param tickLower The lower tick of the position
/// @param tickUpper The upper tick of the position
/// @param amount The amount of liquidity minted to the position range
/// @param amount0 How much token0 was required for the minted liquidity
/// @param amount1 How much token1 was required for the minted liquidity
event Mint(
address sender,
address indexed owner,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted when fees are collected by the owner of a position
/// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees
/// @param owner The owner of the position for which fees are collected
/// @param tickLower The lower tick of the position
/// @param tickUpper The upper tick of the position
/// @param amount0 The amount of token0 fees collected
/// @param amount1 The amount of token1 fees collected
event Collect(
address indexed owner,
address recipient,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount0,
uint128 amount1
);
/// @notice Emitted when a position's liquidity is removed
/// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect
/// @param owner The owner of the position for which liquidity is removed
/// @param tickLower The lower tick of the position
/// @param tickUpper The upper tick of the position
/// @param amount The amount of liquidity to remove
/// @param amount0 The amount of token0 withdrawn
/// @param amount1 The amount of token1 withdrawn
event Burn(
address indexed owner,
int24 indexed tickLower,
int24 indexed tickUpper,
uint128 amount,
uint256 amount0,
uint256 amount1
);
/// @notice Emitted by the pool for any swaps between token0 and token1
/// @param sender The address that initiated the swap call, and that received the callback
/// @param recipient The address that received the output of the swap
/// @param amount0 The delta of the token0 balance of the pool
/// @param amount1 The delta of the token1 balance of the pool
/// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96
/// @param liquidity The liquidity of the pool after the swap
/// @param tick The log base 1.0001 of price of the pool after the swap
event Swap(
address indexed sender,
address indexed recipient,
int256 amount0,
int256 amount1,
uint160 sqrtPriceX96,
uint128 liquidity,
int24 tick
);
/// @notice Emitted by the pool for any flashes of token0/token1
/// @param sender The address that initiated the swap call, and that received the callback
/// @param recipient The address that received the tokens from flash
/// @param amount0 The amount of token0 that was flashed
/// @param amount1 The amount of token1 that was flashed
/// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee
/// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee
event Flash(
address indexed sender,
address indexed recipient,
uint256 amount0,
uint256 amount1,
uint256 paid0,
uint256 paid1
);
/// @notice Emitted by the pool for increases to the number of observations that can be stored
/// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index
/// just before a mint/swap/burn.
/// @param observationCardinalityNextOld The previous value of the next observation cardinality
/// @param observationCardinalityNextNew The updated value of the next observation cardinality
event IncreaseObservationCardinalityNext(
uint16 observationCardinalityNextOld,
uint16 observationCardinalityNextNew
);
/// @notice Emitted when the protocol fee is changed by the pool
/// @param feeProtocol0Old The previous value of the token0 protocol fee
/// @param feeProtocol1Old The previous value of the token1 protocol fee
/// @param feeProtocol0New The updated value of the token0 protocol fee
/// @param feeProtocol1New The updated value of the token1 protocol fee
event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);
/// @notice Emitted when the collected protocol fees are withdrawn by the factory owner
/// @param sender The address that collects the protocol fees
/// @param recipient The address that receives the collected protocol fees
/// @param amount0 The amount of token0 protocol fees that is withdrawn
/// @param amount0 The amount of token1 protocol fees that is withdrawn
event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Pool state that never changes
/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values
interface IUniswapV3PoolImmutables {
/// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface
/// @return The contract address
function factory() external view returns (address);
/// @notice The first of the two tokens of the pool, sorted by address
/// @return The token contract address
function token0() external view returns (address);
/// @notice The second of the two tokens of the pool, sorted by address
/// @return The token contract address
function token1() external view returns (address);
/// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
/// @return The fee
function fee() external view returns (uint24);
/// @notice The pool tick spacing
/// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
/// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
/// This value is an int24 to avoid casting even though it is always positive.
/// @return The tick spacing
function tickSpacing() external view returns (int24);
/// @notice The maximum amount of position liquidity that can use any tick in the range
/// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
/// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
/// @return The max amount of liquidity per tick
function maxLiquidityPerTick() external view returns (uint128);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Permissioned pool actions
/// @notice Contains pool methods that may only be called by the factory owner
interface IUniswapV3PoolOwnerActions {
/// @notice Set the denominator of the protocol's % share of the fees
/// @param feeProtocol0 new protocol fee for token0 of the pool
/// @param feeProtocol1 new protocol fee for token1 of the pool
function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;
/// @notice Collect the protocol fee accrued to the pool
/// @param recipient The address to which collected protocol fees should be sent
/// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1
/// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0
/// @return amount0 The protocol fee collected in token0
/// @return amount1 The protocol fee collected in token1
function collectProtocol(
address recipient,
uint128 amount0Requested,
uint128 amount1Requested
) external returns (uint128 amount0, uint128 amount1);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Pool state that can change
/// @notice These methods compose the pool's state, and can change with any frequency including multiple times
/// per transaction
interface IUniswapV3PoolState {
/// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
/// when accessed externally.
/// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
/// tick The current tick of the pool, i.e. according to the last tick transition that was run.
/// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
/// boundary.
/// observationIndex The index of the last oracle observation that was written,
/// observationCardinality The current maximum number of observations stored in the pool,
/// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
/// feeProtocol The protocol fee for both tokens of the pool.
/// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
/// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
/// unlocked Whether the pool is currently locked to reentrancy
function slot0()
external
view
returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
uint8 feeProtocol,
bool unlocked
);
/// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
/// @dev This value can overflow the uint256
function feeGrowthGlobal0X128() external view returns (uint256);
/// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
/// @dev This value can overflow the uint256
function feeGrowthGlobal1X128() external view returns (uint256);
/// @notice The amounts of token0 and token1 that are owed to the protocol
/// @dev Protocol fees will never exceed uint128 max in either token
function protocolFees() external view returns (uint128 token0, uint128 token1);
/// @notice The currently in range liquidity available to the pool
/// @dev This value has no relationship to the total liquidity across all ticks
function liquidity() external view returns (uint128);
/// @notice Look up information about a specific tick in the pool
/// @param tick The tick to look up
/// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
/// tick upper,
/// liquidityNet how much liquidity changes when the pool price crosses the tick,
/// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
/// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
/// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
/// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,
/// secondsOutside the seconds spent on the other side of the tick from the current tick,
/// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.
/// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
/// In addition, these values are only relative and must be used only in comparison to previous snapshots for
/// a specific position.
function ticks(int24 tick)
external
view
returns (
uint128 liquidityGross,
int128 liquidityNet,
uint256 feeGrowthOutside0X128,
uint256 feeGrowthOutside1X128,
int56 tickCumulativeOutside,
uint160 secondsPerLiquidityOutsideX128,
uint32 secondsOutside,
bool initialized
);
/// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
function tickBitmap(int16 wordPosition) external view returns (uint256);
/// @notice Returns the information about a position by the position's key
/// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
/// @return _liquidity The amount of liquidity in the position,
/// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
/// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
/// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
/// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
function positions(bytes32 key)
external
view
returns (
uint128 _liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
/// @notice Returns data about a specific observation index
/// @param index The element of the observations array to fetch
/// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time
/// ago, rather than at a specific index in the array.
/// @return blockTimestamp The timestamp of the observation,
/// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,
/// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,
/// Returns initialized whether the observation has been initialized and the values are safe to use
function observations(uint256 index)
external
view
returns (
uint32 blockTimestamp,
int56 tickCumulative,
uint160 secondsPerLiquidityCumulativeX128,
bool initialized
);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title BitMath
/// @dev This library provides functionality for computing bit properties of an unsigned integer
library BitMath {
/// @notice Returns the index of the most significant bit of the number,
/// where the least significant bit is at index 0 and the most significant bit is at index 255
/// @dev The function satisfies the property:
/// x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1)
/// @param x the value for which to compute the most significant bit, must be greater than 0
/// @return r the index of the most significant bit
function mostSignificantBit(uint256 x) internal pure returns (uint8 r) {
require(x > 0);
if (x >= 0x100000000000000000000000000000000) {
x >>= 128;
r += 128;
}
if (x >= 0x10000000000000000) {
x >>= 64;
r += 64;
}
if (x >= 0x100000000) {
x >>= 32;
r += 32;
}
if (x >= 0x10000) {
x >>= 16;
r += 16;
}
if (x >= 0x100) {
x >>= 8;
r += 8;
}
if (x >= 0x10) {
x >>= 4;
r += 4;
}
if (x >= 0x4) {
x >>= 2;
r += 2;
}
if (x >= 0x2) r += 1;
}
/// @notice Returns the index of the least significant bit of the number,
/// where the least significant bit is at index 0 and the most significant bit is at index 255
/// @dev The function satisfies the property:
/// (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0)
/// @param x the value for which to compute the least significant bit, must be greater than 0
/// @return r the index of the least significant bit
function leastSignificantBit(uint256 x) internal pure returns (uint8 r) {
require(x > 0);
r = 255;
if (x & type(uint128).max > 0) {
r -= 128;
} else {
x >>= 128;
}
if (x & type(uint64).max > 0) {
r -= 64;
} else {
x >>= 64;
}
if (x & type(uint32).max > 0) {
r -= 32;
} else {
x >>= 32;
}
if (x & type(uint16).max > 0) {
r -= 16;
} else {
x >>= 16;
}
if (x & type(uint8).max > 0) {
r -= 8;
} else {
x >>= 8;
}
if (x & 0xf > 0) {
r -= 4;
} else {
x >>= 4;
}
if (x & 0x3 > 0) {
r -= 2;
} else {
x >>= 2;
}
if (x & 0x1 > 0) r -= 1;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.4.0;
/// @title FixedPoint128
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
library FixedPoint128 {
uint256 internal constant Q128 = 0x100000000000000000000000000000000;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.4.0;
/// @title FixedPoint96
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
/// @dev Used in SqrtPriceMath.sol
library FixedPoint96 {
uint8 internal constant RESOLUTION = 96;
uint256 internal constant Q96 = 0x1000000000000000000000000;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;
/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
function mulDiv(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
// 512-bit multiply [prod1 prod0] = a * b
// Compute the product mod 2**256 and mod 2**256 - 1
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2**256 + prod0
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
require(denominator > 0);
assembly {
result := div(prod0, denominator)
}
return result;
}
// Make sure the result is less than 2**256.
// Also prevents denominator == 0
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0]
// Compute remainder using mulmod
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
}
// Subtract 256 bit number from 512 bit number
assembly {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator
// Compute largest power of two divisor of denominator.
// Always >= 1.
uint256 twos = -denominator & denominator;
// Divide denominator by power of two
assembly {
denominator := div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of two
assembly {
prod0 := div(prod0, twos)
}
// Shift in bits from prod1 into prod0. For this we need
// to flip `twos` such that it is 2**256 / twos.
// If twos is zero, then it becomes one
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
// Invert denominator mod 2**256
// Now that denominator is an odd number, it has an inverse
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, denominator * inv = 1 mod 2**4
uint256 inv = (3 * denominator) ^ 2;
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv *= 2 - denominator * inv; // inverse mod 2**8
inv *= 2 - denominator * inv; // inverse mod 2**16
inv *= 2 - denominator * inv; // inverse mod 2**32
inv *= 2 - denominator * inv; // inverse mod 2**64
inv *= 2 - denominator * inv; // inverse mod 2**128
inv *= 2 - denominator * inv; // inverse mod 2**256
// Because the division is now exact we can divide by multiplying
// with the modular inverse of denominator. This will give us the
// correct result modulo 2**256. Since the precoditions guarantee
// that the outcome is less than 2**256, this is the final result.
// We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inv;
return result;
}
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
function mulDivRoundingUp(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
result = mulDiv(a, b, denominator);
if (mulmod(a, b, denominator) > 0) {
require(result < type(uint256).max);
result++;
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Math library for liquidity
library LiquidityMath {
/// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows
/// @param x The liquidity before change
/// @param y The delta by which liquidity should be changed
/// @return z The liquidity delta
function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) {
if (y < 0) {
require((z = x - uint128(-y)) < x, 'LS');
} else {
require((z = x + uint128(y)) >= x, 'LA');
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.0;
/// @title Optimized overflow and underflow safe math operations
/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost
library LowGasSafeMath {
/// @notice Returns x + y, reverts if sum overflows uint256
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
/// @notice Returns x - y, reverts if underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
/// @notice Returns x * y, reverts if overflows
/// @param x The multiplicand
/// @param y The multiplier
/// @return z The product of x and y
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(x == 0 || (z = x * y) / x == y);
}
/// @notice Returns x + y, reverts if overflows or underflows
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x + y) >= x == (y >= 0));
}
/// @notice Returns x - y, reverts if overflows or underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x - y) <= x == (y >= 0));
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
/// @title Oracle
/// @notice Provides price and liquidity data useful for a wide variety of system designs
/// @dev Instances of stored oracle data, "observations", are collected in the oracle array
/// Every pool is initialized with an oracle array length of 1. Anyone can pay the SSTOREs to increase the
/// maximum length of the oracle array. New slots will be added when the array is fully populated.
/// Observations are overwritten when the full length of the oracle array is populated.
/// The most recent observation is available, independent of the length of the oracle array, by passing 0 to observe()
library Oracle {
struct Observation {
// the block timestamp of the observation
uint32 blockTimestamp;
// the tick accumulator, i.e. tick * time elapsed since the pool was first initialized
int56 tickCumulative;
// the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized
uint160 secondsPerLiquidityCumulativeX128;
// whether or not the observation is initialized
bool initialized;
}
/// @notice Transforms a previous observation into a new observation, given the passage of time and the current tick and liquidity values
/// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows
/// @param last The specified observation to be transformed
/// @param blockTimestamp The timestamp of the new observation
/// @param tick The active tick at the time of the new observation
/// @param liquidity The total in-range liquidity at the time of the new observation
/// @return Observation The newly populated observation
function transform(
Observation memory last,
uint32 blockTimestamp,
int24 tick,
uint128 liquidity
) private pure returns (Observation memory) {
uint32 delta = blockTimestamp - last.blockTimestamp;
return
Observation({
blockTimestamp: blockTimestamp,
tickCumulative: last.tickCumulative + int56(tick) * delta,
secondsPerLiquidityCumulativeX128: last.secondsPerLiquidityCumulativeX128 +
((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)),
initialized: true
});
}
/// @notice Initialize the oracle array by writing the first slot. Called once for the lifecycle of the observations array
/// @param self The stored oracle array
/// @param time The time of the oracle initialization, via block.timestamp truncated to uint32
/// @return cardinality The number of populated elements in the oracle array
/// @return cardinalityNext The new length of the oracle array, independent of population
function initialize(Observation[65535] storage self, uint32 time)
internal
returns (uint16 cardinality, uint16 cardinalityNext)
{
self[0] = Observation({
blockTimestamp: time,
tickCumulative: 0,
secondsPerLiquidityCumulativeX128: 0,
initialized: true
});
return (1, 1);
}
/// @notice Writes an oracle observation to the array
/// @dev Writable at most once per block. Index represents the most recently written element. cardinality and index must be tracked externally.
/// If the index is at the end of the allowable array length (according to cardinality), and the next cardinality
/// is greater than the current one, cardinality may be increased. This restriction is created to preserve ordering.
/// @param self The stored oracle array
/// @param index The index of the observation that was most recently written to the observations array
/// @param blockTimestamp The timestamp of the new observation
/// @param tick The active tick at the time of the new observation
/// @param liquidity The total in-range liquidity at the time of the new observation
/// @param cardinality The number of populated elements in the oracle array
/// @param cardinalityNext The new length of the oracle array, independent of population
/// @return indexUpdated The new index of the most recently written element in the oracle array
/// @return cardinalityUpdated The new cardinality of the oracle array
function write(
Observation[65535] storage self,
uint16 index,
uint32 blockTimestamp,
int24 tick,
uint128 liquidity,
uint16 cardinality,
uint16 cardinalityNext
) internal returns (uint16 indexUpdated, uint16 cardinalityUpdated) {
Observation memory last = self[index];
// early return if we've already written an observation this block
if (last.blockTimestamp == blockTimestamp) return (index, cardinality);
// if the conditions are right, we can bump the cardinality
if (cardinalityNext > cardinality && index == (cardinality - 1)) {
cardinalityUpdated = cardinalityNext;
} else {
cardinalityUpdated = cardinality;
}
indexUpdated = (index + 1) % cardinalityUpdated;
self[indexUpdated] = transform(last, blockTimestamp, tick, liquidity);
}
/// @notice Prepares the oracle array to store up to `next` observations
/// @param self The stored oracle array
/// @param current The current next cardinality of the oracle array
/// @param next The proposed next cardinality which will be populated in the oracle array
/// @return next The next cardinality which will be populated in the oracle array
function grow(
Observation[65535] storage self,
uint16 current,
uint16 next
) internal returns (uint16) {
require(current > 0, 'I');
// no-op if the passed next value isn't greater than the current next value
if (next <= current) return current;
// store in each slot to prevent fresh SSTOREs in swaps
// this data will not be used because the initialized boolean is still false
for (uint16 i = current; i < next; i++) self[i].blockTimestamp = 1;
return next;
}
/// @notice comparator for 32-bit timestamps
/// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to time
/// @param time A timestamp truncated to 32 bits
/// @param a A comparison timestamp from which to determine the relative position of `time`
/// @param b From which to determine the relative position of `time`
/// @return bool Whether `a` is chronologically <= `b`
function lte(
uint32 time,
uint32 a,
uint32 b
) private pure returns (bool) {
// if there hasn't been overflow, no need to adjust
if (a <= time && b <= time) return a <= b;
uint256 aAdjusted = a > time ? a : a + 2**32;
uint256 bAdjusted = b > time ? b : b + 2**32;
return aAdjusted <= bAdjusted;
}
/// @notice Fetches the observations beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied.
/// The result may be the same observation, or adjacent observations.
/// @dev The answer must be contained in the array, used when the target is located within the stored observation
/// boundaries: older than the most recent observation and younger, or the same age as, the oldest observation
/// @param self The stored oracle array
/// @param time The current block.timestamp
/// @param target The timestamp at which the reserved observation should be for
/// @param index The index of the observation that was most recently written to the observations array
/// @param cardinality The number of populated elements in the oracle array
/// @return beforeOrAt The observation recorded before, or at, the target
/// @return atOrAfter The observation recorded at, or after, the target
function binarySearch(
Observation[65535] storage self,
uint32 time,
uint32 target,
uint16 index,
uint16 cardinality
) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
uint256 l = (index + 1) % cardinality; // oldest observation
uint256 r = l + cardinality - 1; // newest observation
uint256 i;
while (true) {
i = (l + r) / 2;
beforeOrAt = self[i % cardinality];
// we've landed on an uninitialized tick, keep searching higher (more recently)
if (!beforeOrAt.initialized) {
l = i + 1;
continue;
}
atOrAfter = self[(i + 1) % cardinality];
bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target);
// check if we've found the answer!
if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break;
if (!targetAtOrAfter) r = i - 1;
else l = i + 1;
}
}
/// @notice Fetches the observations beforeOrAt and atOrAfter a given target, i.e. where [beforeOrAt, atOrAfter] is satisfied
/// @dev Assumes there is at least 1 initialized observation.
/// Used by observeSingle() to compute the counterfactual accumulator values as of a given block timestamp.
/// @param self The stored oracle array
/// @param time The current block.timestamp
/// @param target The timestamp at which the reserved observation should be for
/// @param tick The active tick at the time of the returned or simulated observation
/// @param index The index of the observation that was most recently written to the observations array
/// @param liquidity The total pool liquidity at the time of the call
/// @param cardinality The number of populated elements in the oracle array
/// @return beforeOrAt The observation which occurred at, or before, the given timestamp
/// @return atOrAfter The observation which occurred at, or after, the given timestamp
function getSurroundingObservations(
Observation[65535] storage self,
uint32 time,
uint32 target,
int24 tick,
uint16 index,
uint128 liquidity,
uint16 cardinality
) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
// optimistically set before to the newest observation
beforeOrAt = self[index];
// if the target is chronologically at or after the newest observation, we can early return
if (lte(time, beforeOrAt.blockTimestamp, target)) {
if (beforeOrAt.blockTimestamp == target) {
// if newest observation equals target, we're in the same block, so we can ignore atOrAfter
return (beforeOrAt, atOrAfter);
} else {
// otherwise, we need to transform
return (beforeOrAt, transform(beforeOrAt, target, tick, liquidity));
}
}
// now, set before to the oldest observation
beforeOrAt = self[(index + 1) % cardinality];
if (!beforeOrAt.initialized) beforeOrAt = self[0];
// ensure that the target is chronologically at or after the oldest observation
require(lte(time, beforeOrAt.blockTimestamp, target), 'OLD');
// if we've reached this point, we have to binary search
return binarySearch(self, time, target, index, cardinality);
}
/// @dev Reverts if an observation at or before the desired observation timestamp does not exist.
/// 0 may be passed as `secondsAgo' to return the current cumulative values.
/// If called with a timestamp falling between two observations, returns the counterfactual accumulator values
/// at exactly the timestamp between the two observations.
/// @param self The stored oracle array
/// @param time The current block timestamp
/// @param secondsAgo The amount of time to look back, in seconds, at which point to return an observation
/// @param tick The current tick
/// @param index The index of the observation that was most recently written to the observations array
/// @param liquidity The current in-range pool liquidity
/// @param cardinality The number of populated elements in the oracle array
/// @return tickCumulative The tick * time elapsed since the pool was first initialized, as of `secondsAgo`
/// @return secondsPerLiquidityCumulativeX128 The time elapsed / max(1, liquidity) since the pool was first initialized, as of `secondsAgo`
function observeSingle(
Observation[65535] storage self,
uint32 time,
uint32 secondsAgo,
int24 tick,
uint16 index,
uint128 liquidity,
uint16 cardinality
) internal view returns (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) {
if (secondsAgo == 0) {
Observation memory last = self[index];
if (last.blockTimestamp != time) last = transform(last, time, tick, liquidity);
return (last.tickCumulative, last.secondsPerLiquidityCumulativeX128);
}
uint32 target = time - secondsAgo;
(Observation memory beforeOrAt, Observation memory atOrAfter) =
getSurroundingObservations(self, time, target, tick, index, liquidity, cardinality);
if (target == beforeOrAt.blockTimestamp) {
// we're at the left boundary
return (beforeOrAt.tickCumulative, beforeOrAt.secondsPerLiquidityCumulativeX128);
} else if (target == atOrAfter.blockTimestamp) {
// we're at the right boundary
return (atOrAfter.tickCumulative, atOrAfter.secondsPerLiquidityCumulativeX128);
} else {
// we're in the middle
uint32 observationTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp;
uint32 targetDelta = target - beforeOrAt.blockTimestamp;
return (
beforeOrAt.tickCumulative +
((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / observationTimeDelta) *
targetDelta,
beforeOrAt.secondsPerLiquidityCumulativeX128 +
uint160(
(uint256(
atOrAfter.secondsPerLiquidityCumulativeX128 - beforeOrAt.secondsPerLiquidityCumulativeX128
) * targetDelta) / observationTimeDelta
)
);
}
}
/// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos`
/// @dev Reverts if `secondsAgos` > oldest observation
/// @param self The stored oracle array
/// @param time The current block.timestamp
/// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an observation
/// @param tick The current tick
/// @param index The index of the observation that was most recently written to the observations array
/// @param liquidity The current in-range pool liquidity
/// @param cardinality The number of populated elements in the oracle array
/// @return tickCumulatives The tick * time elapsed since the pool was first initialized, as of each `secondsAgo`
/// @return secondsPerLiquidityCumulativeX128s The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo`
function observe(
Observation[65535] storage self,
uint32 time,
uint32[] memory secondsAgos,
int24 tick,
uint16 index,
uint128 liquidity,
uint16 cardinality
) internal view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) {
require(cardinality > 0, 'I');
tickCumulatives = new int56[](secondsAgos.length);
secondsPerLiquidityCumulativeX128s = new uint160[](secondsAgos.length);
for (uint256 i = 0; i < secondsAgos.length; i++) {
(tickCumulatives[i], secondsPerLiquidityCumulativeX128s[i]) = observeSingle(
self,
time,
secondsAgos[i],
tick,
index,
liquidity,
cardinality
);
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import './FullMath.sol';
import './FixedPoint128.sol';
import './LiquidityMath.sol';
/// @title Position
/// @notice Positions represent an owner address' liquidity between a lower and upper tick boundary
/// @dev Positions store additional state for tracking fees owed to the position
library Position {
// info stored for each user's position
struct Info {
// the amount of liquidity owned by this position
uint128 liquidity;
// fee growth per unit of liquidity as of the last update to liquidity or fees owed
uint256 feeGrowthInside0LastX128;
uint256 feeGrowthInside1LastX128;
// the fees owed to the position owner in token0/token1
uint128 tokensOwed0;
uint128 tokensOwed1;
}
/// @notice Returns the Info struct of a position, given an owner and position boundaries
/// @param self The mapping containing all user positions
/// @param owner The address of the position owner
/// @param tickLower The lower tick boundary of the position
/// @param tickUpper The upper tick boundary of the position
/// @return position The position info struct of the given owners' position
function get(
mapping(bytes32 => Info) storage self,
address owner,
int24 tickLower,
int24 tickUpper
) internal view returns (Position.Info storage position) {
position = self[keccak256(abi.encodePacked(owner, tickLower, tickUpper))];
}
/// @notice Credits accumulated fees to a user's position
/// @param self The individual position to update
/// @param liquidityDelta The change in pool liquidity as a result of the position update
/// @param feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries
/// @param feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries
function update(
Info storage self,
int128 liquidityDelta,
uint256 feeGrowthInside0X128,
uint256 feeGrowthInside1X128
) internal {
Info memory _self = self;
uint128 liquidityNext;
if (liquidityDelta == 0) {
require(_self.liquidity > 0, 'NP'); // disallow pokes for 0 liquidity positions
liquidityNext = _self.liquidity;
} else {
liquidityNext = LiquidityMath.addDelta(_self.liquidity, liquidityDelta);
}
// calculate accumulated fees
uint128 tokensOwed0 =
uint128(
FullMath.mulDiv(
feeGrowthInside0X128 - _self.feeGrowthInside0LastX128,
_self.liquidity,
FixedPoint128.Q128
)
);
uint128 tokensOwed1 =
uint128(
FullMath.mulDiv(
feeGrowthInside1X128 - _self.feeGrowthInside1LastX128,
_self.liquidity,
FixedPoint128.Q128
)
);
// update the position
if (liquidityDelta != 0) self.liquidity = liquidityNext;
self.feeGrowthInside0LastX128 = feeGrowthInside0X128;
self.feeGrowthInside1LastX128 = feeGrowthInside1X128;
if (tokensOwed0 > 0 || tokensOwed1 > 0) {
// overflow is acceptable, have to withdraw before you hit type(uint128).max fees
self.tokensOwed0 += tokensOwed0;
self.tokensOwed1 += tokensOwed1;
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Safe casting methods
/// @notice Contains methods for safely casting between types
library SafeCast {
/// @notice Cast a uint256 to a uint160, revert on overflow
/// @param y The uint256 to be downcasted
/// @return z The downcasted integer, now type uint160
function toUint160(uint256 y) internal pure returns (uint160 z) {
require((z = uint160(y)) == y);
}
/// @notice Cast a int256 to a int128, revert on overflow or underflow
/// @param y The int256 to be downcasted
/// @return z The downcasted integer, now type int128
function toInt128(int256 y) internal pure returns (int128 z) {
require((z = int128(y)) == y);
}
/// @notice Cast a uint256 to a int256, revert on overflow
/// @param y The uint256 to be casted
/// @return z The casted integer, now type int256
function toInt256(uint256 y) internal pure returns (int256 z) {
require(y < 2**255);
z = int256(y);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import './LowGasSafeMath.sol';
import './SafeCast.sol';
import './FullMath.sol';
import './UnsafeMath.sol';
import './FixedPoint96.sol';
/// @title Functions based on Q64.96 sqrt price and liquidity
/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas
library SqrtPriceMath {
using LowGasSafeMath for uint256;
using SafeCast for uint256;
/// @notice Gets the next sqrt price given a delta of token0
/// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least
/// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the
/// price less in order to not send too much output.
/// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96),
/// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount).
/// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta
/// @param liquidity The amount of usable liquidity
/// @param amount How much of token0 to add or remove from virtual reserves
/// @param add Whether to add or remove the amount of token0
/// @return The price after adding or removing amount, depending on add
function getNextSqrtPriceFromAmount0RoundingUp(
uint160 sqrtPX96,
uint128 liquidity,
uint256 amount,
bool add
) internal pure returns (uint160) {
// we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price
if (amount == 0) return sqrtPX96;
uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;
if (add) {
uint256 product;
if ((product = amount * sqrtPX96) / amount == sqrtPX96) {
uint256 denominator = numerator1 + product;
if (denominator >= numerator1)
// always fits in 160 bits
return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator));
}
return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount)));
} else {
uint256 product;
// if the product overflows, we know the denominator underflows
// in addition, we must check that the denominator does not underflow
require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product);
uint256 denominator = numerator1 - product;
return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160();
}
}
/// @notice Gets the next sqrt price given a delta of token1
/// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least
/// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the
/// price less in order to not send too much output.
/// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity
/// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta
/// @param liquidity The amount of usable liquidity
/// @param amount How much of token1 to add, or remove, from virtual reserves
/// @param add Whether to add, or remove, the amount of token1
/// @return The price after adding or removing `amount`
function getNextSqrtPriceFromAmount1RoundingDown(
uint160 sqrtPX96,
uint128 liquidity,
uint256 amount,
bool add
) internal pure returns (uint160) {
// if we're adding (subtracting), rounding down requires rounding the quotient down (up)
// in both cases, avoid a mulDiv for most inputs
if (add) {
uint256 quotient =
(
amount <= type(uint160).max
? (amount << FixedPoint96.RESOLUTION) / liquidity
: FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity)
);
return uint256(sqrtPX96).add(quotient).toUint160();
} else {
uint256 quotient =
(
amount <= type(uint160).max
? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity)
: FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity)
);
require(sqrtPX96 > quotient);
// always fits 160 bits
return uint160(sqrtPX96 - quotient);
}
}
/// @notice Gets the next sqrt price given an input amount of token0 or token1
/// @dev Throws if price or liquidity are 0, or if the next price is out of bounds
/// @param sqrtPX96 The starting price, i.e., before accounting for the input amount
/// @param liquidity The amount of usable liquidity
/// @param amountIn How much of token0, or token1, is being swapped in
/// @param zeroForOne Whether the amount in is token0 or token1
/// @return sqrtQX96 The price after adding the input amount to token0 or token1
function getNextSqrtPriceFromInput(
uint160 sqrtPX96,
uint128 liquidity,
uint256 amountIn,
bool zeroForOne
) internal pure returns (uint160 sqrtQX96) {
require(sqrtPX96 > 0);
require(liquidity > 0);
// round to make sure that we don't pass the target price
return
zeroForOne
? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true)
: getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true);
}
/// @notice Gets the next sqrt price given an output amount of token0 or token1
/// @dev Throws if price or liquidity are 0 or the next price is out of bounds
/// @param sqrtPX96 The starting price before accounting for the output amount
/// @param liquidity The amount of usable liquidity
/// @param amountOut How much of token0, or token1, is being swapped out
/// @param zeroForOne Whether the amount out is token0 or token1
/// @return sqrtQX96 The price after removing the output amount of token0 or token1
function getNextSqrtPriceFromOutput(
uint160 sqrtPX96,
uint128 liquidity,
uint256 amountOut,
bool zeroForOne
) internal pure returns (uint160 sqrtQX96) {
require(sqrtPX96 > 0);
require(liquidity > 0);
// round to make sure that we pass the target price
return
zeroForOne
? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false)
: getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false);
}
/// @notice Gets the amount0 delta between two prices
/// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper),
/// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))
/// @param sqrtRatioAX96 A sqrt price
/// @param sqrtRatioBX96 Another sqrt price
/// @param liquidity The amount of usable liquidity
/// @param roundUp Whether to round the amount up or down
/// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices
function getAmount0Delta(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity,
bool roundUp
) internal pure returns (uint256 amount0) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;
uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96;
require(sqrtRatioAX96 > 0);
return
roundUp
? UnsafeMath.divRoundingUp(
FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96),
sqrtRatioAX96
)
: FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96;
}
/// @notice Gets the amount1 delta between two prices
/// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower))
/// @param sqrtRatioAX96 A sqrt price
/// @param sqrtRatioBX96 Another sqrt price
/// @param liquidity The amount of usable liquidity
/// @param roundUp Whether to round the amount up, or down
/// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices
function getAmount1Delta(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity,
bool roundUp
) internal pure returns (uint256 amount1) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return
roundUp
? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96)
: FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96);
}
/// @notice Helper that gets signed token0 delta
/// @param sqrtRatioAX96 A sqrt price
/// @param sqrtRatioBX96 Another sqrt price
/// @param liquidity The change in liquidity for which to compute the amount0 delta
/// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices
function getAmount0Delta(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
int128 liquidity
) internal pure returns (int256 amount0) {
return
liquidity < 0
? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()
: getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();
}
/// @notice Helper that gets signed token1 delta
/// @param sqrtRatioAX96 A sqrt price
/// @param sqrtRatioBX96 Another sqrt price
/// @param liquidity The change in liquidity for which to compute the amount1 delta
/// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices
function getAmount1Delta(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
int128 liquidity
) internal pure returns (int256 amount1) {
return
liquidity < 0
? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()
: getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import './FullMath.sol';
import './SqrtPriceMath.sol';
/// @title Computes the result of a swap within ticks
/// @notice Contains methods for computing the result of a swap within a single tick price range, i.e., a single tick.
library SwapMath {
/// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap
/// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive
/// @param sqrtRatioCurrentX96 The current sqrt price of the pool
/// @param sqrtRatioTargetX96 The price that cannot be exceeded, from which the direction of the swap is inferred
/// @param liquidity The usable liquidity
/// @param amountRemaining How much input or output amount is remaining to be swapped in/out
/// @param feePips The fee taken from the input amount, expressed in hundredths of a bip
/// @return sqrtRatioNextX96 The price after swapping the amount in/out, not to exceed the price target
/// @return amountIn The amount to be swapped in, of either token0 or token1, based on the direction of the swap
/// @return amountOut The amount to be received, of either token0 or token1, based on the direction of the swap
/// @return feeAmount The amount of input that will be taken as a fee
function computeSwapStep(
uint160 sqrtRatioCurrentX96,
uint160 sqrtRatioTargetX96,
uint128 liquidity,
int256 amountRemaining,
uint24 feePips
)
internal
pure
returns (
uint160 sqrtRatioNextX96,
uint256 amountIn,
uint256 amountOut,
uint256 feeAmount
)
{
bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96;
bool exactIn = amountRemaining >= 0;
if (exactIn) {
uint256 amountRemainingLessFee = FullMath.mulDiv(uint256(amountRemaining), 1e6 - feePips, 1e6);
amountIn = zeroForOne
? SqrtPriceMath.getAmount0Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true)
: SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true);
if (amountRemainingLessFee >= amountIn) sqrtRatioNextX96 = sqrtRatioTargetX96;
else
sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromInput(
sqrtRatioCurrentX96,
liquidity,
amountRemainingLessFee,
zeroForOne
);
} else {
amountOut = zeroForOne
? SqrtPriceMath.getAmount1Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false)
: SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false);
if (uint256(-amountRemaining) >= amountOut) sqrtRatioNextX96 = sqrtRatioTargetX96;
else
sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromOutput(
sqrtRatioCurrentX96,
liquidity,
uint256(-amountRemaining),
zeroForOne
);
}
bool max = sqrtRatioTargetX96 == sqrtRatioNextX96;
// get the input/output amounts
if (zeroForOne) {
amountIn = max && exactIn
? amountIn
: SqrtPriceMath.getAmount0Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true);
amountOut = max && !exactIn
? amountOut
: SqrtPriceMath.getAmount1Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false);
} else {
amountIn = max && exactIn
? amountIn
: SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true);
amountOut = max && !exactIn
? amountOut
: SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false);
}
// cap the output amount to not exceed the remaining output amount
if (!exactIn && amountOut > uint256(-amountRemaining)) {
amountOut = uint256(-amountRemaining);
}
if (exactIn && sqrtRatioNextX96 != sqrtRatioTargetX96) {
// we didn't reach the target, so take the remainder of the maximum input as fee
feeAmount = uint256(amountRemaining) - amountIn;
} else {
feeAmount = FullMath.mulDivRoundingUp(amountIn, feePips, 1e6 - feePips);
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import './LowGasSafeMath.sol';
import './SafeCast.sol';
import './TickMath.sol';
import './LiquidityMath.sol';
/// @title Tick
/// @notice Contains functions for managing tick processes and relevant calculations
library Tick {
using LowGasSafeMath for int256;
using SafeCast for int256;
// info stored for each initialized individual tick
struct Info {
// the total position liquidity that references this tick
uint128 liquidityGross;
// amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left),
int128 liquidityNet;
// fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)
// only has relative meaning, not absolute — the value depends on when the tick is initialized
uint256 feeGrowthOutside0X128;
uint256 feeGrowthOutside1X128;
// the cumulative tick value on the other side of the tick
int56 tickCumulativeOutside;
// the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick)
// only has relative meaning, not absolute — the value depends on when the tick is initialized
uint160 secondsPerLiquidityOutsideX128;
// the seconds spent on the other side of the tick (relative to the current tick)
// only has relative meaning, not absolute — the value depends on when the tick is initialized
uint32 secondsOutside;
// true iff the tick is initialized, i.e. the value is exactly equivalent to the expression liquidityGross != 0
// these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks
bool initialized;
}
/// @notice Derives max liquidity per tick from given tick spacing
/// @dev Executed within the pool constructor
/// @param tickSpacing The amount of required tick separation, realized in multiples of `tickSpacing`
/// e.g., a tickSpacing of 3 requires ticks to be initialized every 3rd tick i.e., ..., -6, -3, 0, 3, 6, ...
/// @return The max liquidity per tick
function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) {
int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing;
int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing;
uint24 numTicks = uint24((maxTick - minTick) / tickSpacing) + 1;
return type(uint128).max / numTicks;
}
/// @notice Retrieves fee growth data
/// @param self The mapping containing all tick information for initialized ticks
/// @param tickLower The lower tick boundary of the position
/// @param tickUpper The upper tick boundary of the position
/// @param tickCurrent The current tick
/// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0
/// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1
/// @return feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries
/// @return feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries
function getFeeGrowthInside(
mapping(int24 => Tick.Info) storage self,
int24 tickLower,
int24 tickUpper,
int24 tickCurrent,
uint256 feeGrowthGlobal0X128,
uint256 feeGrowthGlobal1X128
) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) {
Info storage lower = self[tickLower];
Info storage upper = self[tickUpper];
// calculate fee growth below
uint256 feeGrowthBelow0X128;
uint256 feeGrowthBelow1X128;
if (tickCurrent >= tickLower) {
feeGrowthBelow0X128 = lower.feeGrowthOutside0X128;
feeGrowthBelow1X128 = lower.feeGrowthOutside1X128;
} else {
feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128;
feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128;
}
// calculate fee growth above
uint256 feeGrowthAbove0X128;
uint256 feeGrowthAbove1X128;
if (tickCurrent < tickUpper) {
feeGrowthAbove0X128 = upper.feeGrowthOutside0X128;
feeGrowthAbove1X128 = upper.feeGrowthOutside1X128;
} else {
feeGrowthAbove0X128 = feeGrowthGlobal0X128 - upper.feeGrowthOutside0X128;
feeGrowthAbove1X128 = feeGrowthGlobal1X128 - upper.feeGrowthOutside1X128;
}
feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128;
feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128;
}
/// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa
/// @param self The mapping containing all tick information for initialized ticks
/// @param tick The tick that will be updated
/// @param tickCurrent The current tick
/// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left)
/// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0
/// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1
/// @param secondsPerLiquidityCumulativeX128 The all-time seconds per max(1, liquidity) of the pool
/// @param time The current block timestamp cast to a uint32
/// @param upper true for updating a position's upper tick, or false for updating a position's lower tick
/// @param maxLiquidity The maximum liquidity allocation for a single tick
/// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa
function update(
mapping(int24 => Tick.Info) storage self,
int24 tick,
int24 tickCurrent,
int128 liquidityDelta,
uint256 feeGrowthGlobal0X128,
uint256 feeGrowthGlobal1X128,
uint160 secondsPerLiquidityCumulativeX128,
int56 tickCumulative,
uint32 time,
bool upper,
uint128 maxLiquidity
) internal returns (bool flipped) {
Tick.Info storage info = self[tick];
uint128 liquidityGrossBefore = info.liquidityGross;
uint128 liquidityGrossAfter = LiquidityMath.addDelta(liquidityGrossBefore, liquidityDelta);
require(liquidityGrossAfter <= maxLiquidity, 'LO');
flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0);
if (liquidityGrossBefore == 0) {
// by convention, we assume that all growth before a tick was initialized happened _below_ the tick
if (tick <= tickCurrent) {
info.feeGrowthOutside0X128 = feeGrowthGlobal0X128;
info.feeGrowthOutside1X128 = feeGrowthGlobal1X128;
info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128;
info.tickCumulativeOutside = tickCumulative;
info.secondsOutside = time;
}
info.initialized = true;
}
info.liquidityGross = liquidityGrossAfter;
// when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed)
info.liquidityNet = upper
? int256(info.liquidityNet).sub(liquidityDelta).toInt128()
: int256(info.liquidityNet).add(liquidityDelta).toInt128();
}
/// @notice Clears tick data
/// @param self The mapping containing all initialized tick information for initialized ticks
/// @param tick The tick that will be cleared
function clear(mapping(int24 => Tick.Info) storage self, int24 tick) internal {
delete self[tick];
}
/// @notice Transitions to next tick as needed by price movement
/// @param self The mapping containing all tick information for initialized ticks
/// @param tick The destination tick of the transition
/// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0
/// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1
/// @param secondsPerLiquidityCumulativeX128 The current seconds per liquidity
/// @param time The current block.timestamp
/// @return liquidityNet The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left)
function cross(
mapping(int24 => Tick.Info) storage self,
int24 tick,
uint256 feeGrowthGlobal0X128,
uint256 feeGrowthGlobal1X128,
uint160 secondsPerLiquidityCumulativeX128,
int56 tickCumulative,
uint32 time
) internal returns (int128 liquidityNet) {
Tick.Info storage info = self[tick];
info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128;
info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128;
info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128 - info.secondsPerLiquidityOutsideX128;
info.tickCumulativeOutside = tickCumulative - info.tickCumulativeOutside;
info.secondsOutside = time - info.secondsOutside;
liquidityNet = info.liquidityNet;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import './BitMath.sol';
/// @title Packed tick initialized state library
/// @notice Stores a packed mapping of tick index to its initialized state
/// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word.
library TickBitmap {
/// @notice Computes the position in the mapping where the initialized bit for a tick lives
/// @param tick The tick for which to compute the position
/// @return wordPos The key in the mapping containing the word in which the bit is stored
/// @return bitPos The bit position in the word where the flag is stored
function position(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) {
wordPos = int16(tick >> 8);
bitPos = uint8(tick % 256);
}
/// @notice Flips the initialized state for a given tick from false to true, or vice versa
/// @param self The mapping in which to flip the tick
/// @param tick The tick to flip
/// @param tickSpacing The spacing between usable ticks
function flipTick(
mapping(int16 => uint256) storage self,
int24 tick,
int24 tickSpacing
) internal {
require(tick % tickSpacing == 0); // ensure that the tick is spaced
(int16 wordPos, uint8 bitPos) = position(tick / tickSpacing);
uint256 mask = 1 << bitPos;
self[wordPos] ^= mask;
}
/// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either
/// to the left (less than or equal to) or right (greater than) of the given tick
/// @param self The mapping in which to compute the next initialized tick
/// @param tick The starting tick
/// @param tickSpacing The spacing between usable ticks
/// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick)
/// @return next The next initialized or uninitialized tick up to 256 ticks away from the current tick
/// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks
function nextInitializedTickWithinOneWord(
mapping(int16 => uint256) storage self,
int24 tick,
int24 tickSpacing,
bool lte
) internal view returns (int24 next, bool initialized) {
int24 compressed = tick / tickSpacing;
if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity
if (lte) {
(int16 wordPos, uint8 bitPos) = position(compressed);
// all the 1s at or to the right of the current bitPos
uint256 mask = (1 << bitPos) - 1 + (1 << bitPos);
uint256 masked = self[wordPos] & mask;
// if there are no initialized ticks to the right of or at the current tick, return rightmost in the word
initialized = masked != 0;
// overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick
next = initialized
? (compressed - int24(bitPos - BitMath.mostSignificantBit(masked))) * tickSpacing
: (compressed - int24(bitPos)) * tickSpacing;
} else {
// start from the word of the next tick, since the current tick state doesn't matter
(int16 wordPos, uint8 bitPos) = position(compressed + 1);
// all the 1s at or to the left of the bitPos
uint256 mask = ~((1 << bitPos) - 1);
uint256 masked = self[wordPos] & mask;
// if there are no initialized ticks to the left of the current tick, return leftmost in the word
initialized = masked != 0;
// overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick
next = initialized
? (compressed + 1 + int24(BitMath.leastSignificantBit(masked) - bitPos)) * tickSpacing
: (compressed + 1 + int24(type(uint8).max - bitPos)) * tickSpacing;
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {
/// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
int24 internal constant MIN_TICK = -887272;
/// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
int24 internal constant MAX_TICK = -MIN_TICK;
/// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
uint160 internal constant MIN_SQRT_RATIO = 4295128739;
/// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;
/// @notice Calculates sqrt(1.0001^tick) * 2^96
/// @dev Throws if |tick| > max tick
/// @param tick The input tick for the above formula
/// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
/// at the given tick
function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));
require(absTick <= uint256(MAX_TICK), 'T');
uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;
if (tick > 0) ratio = type(uint256).max / ratio;
// this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
// we then downcast because we know the result always fits within 160 bits due to our tick input constraint
// we round up in the division so getTickAtSqrtRatio of the output price is always consistent
sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
}
/// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
/// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
/// ever return.
/// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96
/// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
// second inequality must be < because the price can never reach the price at the max tick
require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, 'R');
uint256 ratio = uint256(sqrtPriceX96) << 32;
uint256 r = ratio;
uint256 msb = 0;
assembly {
let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(5, gt(r, 0xFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(4, gt(r, 0xFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(3, gt(r, 0xFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(2, gt(r, 0xF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(1, gt(r, 0x3))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := gt(r, 0x1)
msb := or(msb, f)
}
if (msb >= 128) r = ratio >> (msb - 127);
else r = ratio << (127 - msb);
int256 log_2 = (int256(msb) - 128) << 64;
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(63, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(62, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(61, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(60, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(59, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(58, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(57, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(56, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(55, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(54, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(53, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(52, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(51, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(50, f))
}
int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number
int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);
tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.0;
import '../interfaces/IERC20Minimal.sol';
/// @title TransferHelper
/// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false
library TransferHelper {
/// @notice Transfers tokens from msg.sender to a recipient
/// @dev Calls transfer on token contract, errors with TF if transfer fails
/// @param token The contract address of the token which will be transferred
/// @param to The recipient of the transfer
/// @param value The value of the transfer
function safeTransfer(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) =
token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF');
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Math functions that do not check inputs or outputs
/// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks
library UnsafeMath {
/// @notice Returns ceil(x / y)
/// @dev division by 0 has unspecified behavior, and must be checked externally
/// @param x The dividend
/// @param y The divisor
/// @return z The quotient, ceil(x / y)
function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := add(div(x, y), gt(mod(x, y), 0))
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
/// @title Prevents delegatecall to a contract
/// @notice Base contract that provides a modifier for preventing delegatecall to methods in a child contract
abstract contract NoDelegateCall {
/// @dev The original address of this contract
address private immutable original;
constructor() {
// Immutables are computed in the init code of the contract, and then inlined into the deployed bytecode.
// In other words, this variable won't change when it's checked at runtime.
original = address(this);
}
/// @dev Private method is used instead of inlining into modifier because modifiers are copied into each method,
/// and the use of immutable means the address bytes are copied in every place the modifier is used.
function checkNotDelegateCall() private view {
require(address(this) == original);
}
/// @notice Prevents delegatecall into the modified method
modifier noDelegateCall() {
checkNotDelegateCall();
_;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import './interfaces/IUniswapV3Factory.sol';
import './UniswapV3PoolDeployer.sol';
import './NoDelegateCall.sol';
import './UniswapV3Pool.sol';
/// @title Canonical Uniswap V3 factory
/// @notice Deploys Uniswap V3 pools and manages ownership and control over pool protocol fees
contract UniswapV3Factory is IUniswapV3Factory, UniswapV3PoolDeployer, NoDelegateCall {
/// @inheritdoc IUniswapV3Factory
address public override owner;
/// @inheritdoc IUniswapV3Factory
mapping(uint24 => int24) public override feeAmountTickSpacing;
/// @inheritdoc IUniswapV3Factory
mapping(address => mapping(address => mapping(uint24 => address))) public override getPool;
constructor() {
owner = msg.sender;
emit OwnerChanged(address(0), msg.sender);
feeAmountTickSpacing[500] = 10;
emit FeeAmountEnabled(500, 10);
feeAmountTickSpacing[3000] = 60;
emit FeeAmountEnabled(3000, 60);
feeAmountTickSpacing[10000] = 200;
emit FeeAmountEnabled(10000, 200);
}
/// @inheritdoc IUniswapV3Factory
function createPool(
address tokenA,
address tokenB,
uint24 fee
) external override noDelegateCall returns (address pool) {
require(tokenA != tokenB);
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0));
int24 tickSpacing = feeAmountTickSpacing[fee];
require(tickSpacing != 0);
require(getPool[token0][token1][fee] == address(0));
pool = deploy(address(this), token0, token1, fee, tickSpacing);
getPool[token0][token1][fee] = pool;
// populate mapping in the reverse direction, deliberate choice to avoid the cost of comparing addresses
getPool[token1][token0][fee] = pool;
emit PoolCreated(token0, token1, fee, tickSpacing, pool);
}
/// @inheritdoc IUniswapV3Factory
function setOwner(address _owner) external override {
require(msg.sender == owner);
emit OwnerChanged(owner, _owner);
owner = _owner;
}
/// @inheritdoc IUniswapV3Factory
function enableFeeAmount(uint24 fee, int24 tickSpacing) public override {
require(msg.sender == owner);
require(fee < 1000000);
// tick spacing is capped at 16384 to prevent the situation where tickSpacing is so large that
// TickBitmap#nextInitializedTickWithinOneWord overflows int24 container from a valid tick
// 16384 ticks represents a >5x price change with ticks of 1 bips
require(tickSpacing > 0 && tickSpacing < 16384);
require(feeAmountTickSpacing[fee] == 0);
feeAmountTickSpacing[fee] = tickSpacing;
emit FeeAmountEnabled(fee, tickSpacing);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import './interfaces/IUniswapV3Pool.sol';
import './NoDelegateCall.sol';
import './libraries/LowGasSafeMath.sol';
import './libraries/SafeCast.sol';
import './libraries/Tick.sol';
import './libraries/TickBitmap.sol';
import './libraries/Position.sol';
import './libraries/Oracle.sol';
import './libraries/FullMath.sol';
import './libraries/FixedPoint128.sol';
import './libraries/TransferHelper.sol';
import './libraries/TickMath.sol';
import './libraries/LiquidityMath.sol';
import './libraries/SqrtPriceMath.sol';
import './libraries/SwapMath.sol';
import './interfaces/IUniswapV3PoolDeployer.sol';
import './interfaces/IUniswapV3Factory.sol';
import './interfaces/IERC20Minimal.sol';
import './interfaces/callback/IUniswapV3MintCallback.sol';
import './interfaces/callback/IUniswapV3SwapCallback.sol';
import './interfaces/callback/IUniswapV3FlashCallback.sol';
contract UniswapV3Pool is IUniswapV3Pool, NoDelegateCall {
using LowGasSafeMath for uint256;
using LowGasSafeMath for int256;
using SafeCast for uint256;
using SafeCast for int256;
using Tick for mapping(int24 => Tick.Info);
using TickBitmap for mapping(int16 => uint256);
using Position for mapping(bytes32 => Position.Info);
using Position for Position.Info;
using Oracle for Oracle.Observation[65535];
/// @inheritdoc IUniswapV3PoolImmutables
address public immutable override factory;
/// @inheritdoc IUniswapV3PoolImmutables
address public immutable override token0;
/// @inheritdoc IUniswapV3PoolImmutables
address public immutable override token1;
/// @inheritdoc IUniswapV3PoolImmutables
uint24 public immutable override fee;
/// @inheritdoc IUniswapV3PoolImmutables
int24 public immutable override tickSpacing;
/// @inheritdoc IUniswapV3PoolImmutables
uint128 public immutable override maxLiquidityPerTick;
struct Slot0 {
// the current price
uint160 sqrtPriceX96;
// the current tick
int24 tick;
// the most-recently updated index of the observations array
uint16 observationIndex;
// the current maximum number of observations that are being stored
uint16 observationCardinality;
// the next maximum number of observations to store, triggered in observations.write
uint16 observationCardinalityNext;
// the current protocol fee as a percentage of the swap fee taken on withdrawal
// represented as an integer denominator (1/x)%
uint8 feeProtocol;
// whether the pool is locked
bool unlocked;
}
/// @inheritdoc IUniswapV3PoolState
Slot0 public override slot0;
/// @inheritdoc IUniswapV3PoolState
uint256 public override feeGrowthGlobal0X128;
/// @inheritdoc IUniswapV3PoolState
uint256 public override feeGrowthGlobal1X128;
// accumulated protocol fees in token0/token1 units
struct ProtocolFees {
uint128 token0;
uint128 token1;
}
/// @inheritdoc IUniswapV3PoolState
ProtocolFees public override protocolFees;
/// @inheritdoc IUniswapV3PoolState
uint128 public override liquidity;
/// @inheritdoc IUniswapV3PoolState
mapping(int24 => Tick.Info) public override ticks;
/// @inheritdoc IUniswapV3PoolState
mapping(int16 => uint256) public override tickBitmap;
/// @inheritdoc IUniswapV3PoolState
mapping(bytes32 => Position.Info) public override positions;
/// @inheritdoc IUniswapV3PoolState
Oracle.Observation[65535] public override observations;
/// @dev Mutually exclusive reentrancy protection into the pool to/from a method. This method also prevents entrance
/// to a function before the pool is initialized. The reentrancy guard is required throughout the contract because
/// we use balance checks to determine the payment status of interactions such as mint, swap and flash.
modifier lock() {
require(slot0.unlocked, 'LOK');
slot0.unlocked = false;
_;
slot0.unlocked = true;
}
/// @dev Prevents calling a function from anyone except the address returned by IUniswapV3Factory#owner()
modifier onlyFactoryOwner() {
require(msg.sender == IUniswapV3Factory(factory).owner());
_;
}
constructor() {
int24 _tickSpacing;
(factory, token0, token1, fee, _tickSpacing) = IUniswapV3PoolDeployer(msg.sender).parameters();
tickSpacing = _tickSpacing;
maxLiquidityPerTick = Tick.tickSpacingToMaxLiquidityPerTick(_tickSpacing);
}
/// @dev Common checks for valid tick inputs.
function checkTicks(int24 tickLower, int24 tickUpper) private pure {
require(tickLower < tickUpper, 'TLU');
require(tickLower >= TickMath.MIN_TICK, 'TLM');
require(tickUpper <= TickMath.MAX_TICK, 'TUM');
}
/// @dev Returns the block timestamp truncated to 32 bits, i.e. mod 2**32. This method is overridden in tests.
function _blockTimestamp() internal view virtual returns (uint32) {
return uint32(block.timestamp); // truncation is desired
}
/// @dev Get the pool's balance of token0
/// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize
/// check
function balance0() private view returns (uint256) {
(bool success, bytes memory data) =
token0.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)));
require(success && data.length >= 32);
return abi.decode(data, (uint256));
}
/// @dev Get the pool's balance of token1
/// @dev This function is gas optimized to avoid a redundant extcodesize check in addition to the returndatasize
/// check
function balance1() private view returns (uint256) {
(bool success, bytes memory data) =
token1.staticcall(abi.encodeWithSelector(IERC20Minimal.balanceOf.selector, address(this)));
require(success && data.length >= 32);
return abi.decode(data, (uint256));
}
/// @inheritdoc IUniswapV3PoolDerivedState
function snapshotCumulativesInside(int24 tickLower, int24 tickUpper)
external
view
override
noDelegateCall
returns (
int56 tickCumulativeInside,
uint160 secondsPerLiquidityInsideX128,
uint32 secondsInside
)
{
checkTicks(tickLower, tickUpper);
int56 tickCumulativeLower;
int56 tickCumulativeUpper;
uint160 secondsPerLiquidityOutsideLowerX128;
uint160 secondsPerLiquidityOutsideUpperX128;
uint32 secondsOutsideLower;
uint32 secondsOutsideUpper;
{
Tick.Info storage lower = ticks[tickLower];
Tick.Info storage upper = ticks[tickUpper];
bool initializedLower;
(tickCumulativeLower, secondsPerLiquidityOutsideLowerX128, secondsOutsideLower, initializedLower) = (
lower.tickCumulativeOutside,
lower.secondsPerLiquidityOutsideX128,
lower.secondsOutside,
lower.initialized
);
require(initializedLower);
bool initializedUpper;
(tickCumulativeUpper, secondsPerLiquidityOutsideUpperX128, secondsOutsideUpper, initializedUpper) = (
upper.tickCumulativeOutside,
upper.secondsPerLiquidityOutsideX128,
upper.secondsOutside,
upper.initialized
);
require(initializedUpper);
}
Slot0 memory _slot0 = slot0;
if (_slot0.tick < tickLower) {
return (
tickCumulativeLower - tickCumulativeUpper,
secondsPerLiquidityOutsideLowerX128 - secondsPerLiquidityOutsideUpperX128,
secondsOutsideLower - secondsOutsideUpper
);
} else if (_slot0.tick < tickUpper) {
uint32 time = _blockTimestamp();
(int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) =
observations.observeSingle(
time,
0,
_slot0.tick,
_slot0.observationIndex,
liquidity,
_slot0.observationCardinality
);
return (
tickCumulative - tickCumulativeLower - tickCumulativeUpper,
secondsPerLiquidityCumulativeX128 -
secondsPerLiquidityOutsideLowerX128 -
secondsPerLiquidityOutsideUpperX128,
time - secondsOutsideLower - secondsOutsideUpper
);
} else {
return (
tickCumulativeUpper - tickCumulativeLower,
secondsPerLiquidityOutsideUpperX128 - secondsPerLiquidityOutsideLowerX128,
secondsOutsideUpper - secondsOutsideLower
);
}
}
/// @inheritdoc IUniswapV3PoolDerivedState
function observe(uint32[] calldata secondsAgos)
external
view
override
noDelegateCall
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s)
{
return
observations.observe(
_blockTimestamp(),
secondsAgos,
slot0.tick,
slot0.observationIndex,
liquidity,
slot0.observationCardinality
);
}
/// @inheritdoc IUniswapV3PoolActions
function increaseObservationCardinalityNext(uint16 observationCardinalityNext)
external
override
lock
noDelegateCall
{
uint16 observationCardinalityNextOld = slot0.observationCardinalityNext; // for the event
uint16 observationCardinalityNextNew =
observations.grow(observationCardinalityNextOld, observationCardinalityNext);
slot0.observationCardinalityNext = observationCardinalityNextNew;
if (observationCardinalityNextOld != observationCardinalityNextNew)
emit IncreaseObservationCardinalityNext(observationCardinalityNextOld, observationCardinalityNextNew);
}
/// @inheritdoc IUniswapV3PoolActions
/// @dev not locked because it initializes unlocked
function initialize(uint160 sqrtPriceX96) external override {
require(slot0.sqrtPriceX96 == 0, 'AI');
int24 tick = TickMath.getTickAtSqrtRatio(sqrtPriceX96);
(uint16 cardinality, uint16 cardinalityNext) = observations.initialize(_blockTimestamp());
slot0 = Slot0({
sqrtPriceX96: sqrtPriceX96,
tick: tick,
observationIndex: 0,
observationCardinality: cardinality,
observationCardinalityNext: cardinalityNext,
feeProtocol: 0,
unlocked: true
});
emit Initialize(sqrtPriceX96, tick);
}
struct ModifyPositionParams {
// the address that owns the position
address owner;
// the lower and upper tick of the position
int24 tickLower;
int24 tickUpper;
// any change in liquidity
int128 liquidityDelta;
}
/// @dev Effect some changes to a position
/// @param params the position details and the change to the position's liquidity to effect
/// @return position a storage pointer referencing the position with the given owner and tick range
/// @return amount0 the amount of token0 owed to the pool, negative if the pool should pay the recipient
/// @return amount1 the amount of token1 owed to the pool, negative if the pool should pay the recipient
function _modifyPosition(ModifyPositionParams memory params)
private
noDelegateCall
returns (
Position.Info storage position,
int256 amount0,
int256 amount1
)
{
checkTicks(params.tickLower, params.tickUpper);
Slot0 memory _slot0 = slot0; // SLOAD for gas optimization
position = _updatePosition(
params.owner,
params.tickLower,
params.tickUpper,
params.liquidityDelta,
_slot0.tick
);
if (params.liquidityDelta != 0) {
if (_slot0.tick < params.tickLower) {
// current tick is below the passed range; liquidity can only become in range by crossing from left to
// right, when we'll need _more_ token0 (it's becoming more valuable) so user must provide it
amount0 = SqrtPriceMath.getAmount0Delta(
TickMath.getSqrtRatioAtTick(params.tickLower),
TickMath.getSqrtRatioAtTick(params.tickUpper),
params.liquidityDelta
);
} else if (_slot0.tick < params.tickUpper) {
// current tick is inside the passed range
uint128 liquidityBefore = liquidity; // SLOAD for gas optimization
// write an oracle entry
(slot0.observationIndex, slot0.observationCardinality) = observations.write(
_slot0.observationIndex,
_blockTimestamp(),
_slot0.tick,
liquidityBefore,
_slot0.observationCardinality,
_slot0.observationCardinalityNext
);
amount0 = SqrtPriceMath.getAmount0Delta(
_slot0.sqrtPriceX96,
TickMath.getSqrtRatioAtTick(params.tickUpper),
params.liquidityDelta
);
amount1 = SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(params.tickLower),
_slot0.sqrtPriceX96,
params.liquidityDelta
);
liquidity = LiquidityMath.addDelta(liquidityBefore, params.liquidityDelta);
} else {
// current tick is above the passed range; liquidity can only become in range by crossing from right to
// left, when we'll need _more_ token1 (it's becoming more valuable) so user must provide it
amount1 = SqrtPriceMath.getAmount1Delta(
TickMath.getSqrtRatioAtTick(params.tickLower),
TickMath.getSqrtRatioAtTick(params.tickUpper),
params.liquidityDelta
);
}
}
}
/// @dev Gets and updates a position with the given liquidity delta
/// @param owner the owner of the position
/// @param tickLower the lower tick of the position's tick range
/// @param tickUpper the upper tick of the position's tick range
/// @param tick the current tick, passed to avoid sloads
function _updatePosition(
address owner,
int24 tickLower,
int24 tickUpper,
int128 liquidityDelta,
int24 tick
) private returns (Position.Info storage position) {
position = positions.get(owner, tickLower, tickUpper);
uint256 _feeGrowthGlobal0X128 = feeGrowthGlobal0X128; // SLOAD for gas optimization
uint256 _feeGrowthGlobal1X128 = feeGrowthGlobal1X128; // SLOAD for gas optimization
// if we need to update the ticks, do it
bool flippedLower;
bool flippedUpper;
if (liquidityDelta != 0) {
uint32 time = _blockTimestamp();
(int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) =
observations.observeSingle(
time,
0,
slot0.tick,
slot0.observationIndex,
liquidity,
slot0.observationCardinality
);
flippedLower = ticks.update(
tickLower,
tick,
liquidityDelta,
_feeGrowthGlobal0X128,
_feeGrowthGlobal1X128,
secondsPerLiquidityCumulativeX128,
tickCumulative,
time,
false,
maxLiquidityPerTick
);
flippedUpper = ticks.update(
tickUpper,
tick,
liquidityDelta,
_feeGrowthGlobal0X128,
_feeGrowthGlobal1X128,
secondsPerLiquidityCumulativeX128,
tickCumulative,
time,
true,
maxLiquidityPerTick
);
if (flippedLower) {
tickBitmap.flipTick(tickLower, tickSpacing);
}
if (flippedUpper) {
tickBitmap.flipTick(tickUpper, tickSpacing);
}
}
(uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) =
ticks.getFeeGrowthInside(tickLower, tickUpper, tick, _feeGrowthGlobal0X128, _feeGrowthGlobal1X128);
position.update(liquidityDelta, feeGrowthInside0X128, feeGrowthInside1X128);
// clear any tick data that is no longer needed
if (liquidityDelta < 0) {
if (flippedLower) {
ticks.clear(tickLower);
}
if (flippedUpper) {
ticks.clear(tickUpper);
}
}
}
/// @inheritdoc IUniswapV3PoolActions
/// @dev noDelegateCall is applied indirectly via _modifyPosition
function mint(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount,
bytes calldata data
) external override lock returns (uint256 amount0, uint256 amount1) {
require(amount > 0);
(, int256 amount0Int, int256 amount1Int) =
_modifyPosition(
ModifyPositionParams({
owner: recipient,
tickLower: tickLower,
tickUpper: tickUpper,
liquidityDelta: int256(amount).toInt128()
})
);
amount0 = uint256(amount0Int);
amount1 = uint256(amount1Int);
uint256 balance0Before;
uint256 balance1Before;
if (amount0 > 0) balance0Before = balance0();
if (amount1 > 0) balance1Before = balance1();
IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data);
if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), 'M0');
if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), 'M1');
emit Mint(msg.sender, recipient, tickLower, tickUpper, amount, amount0, amount1);
}
/// @inheritdoc IUniswapV3PoolActions
function collect(
address recipient,
int24 tickLower,
int24 tickUpper,
uint128 amount0Requested,
uint128 amount1Requested
) external override lock returns (uint128 amount0, uint128 amount1) {
// we don't need to checkTicks here, because invalid positions will never have non-zero tokensOwed{0,1}
Position.Info storage position = positions.get(msg.sender, tickLower, tickUpper);
amount0 = amount0Requested > position.tokensOwed0 ? position.tokensOwed0 : amount0Requested;
amount1 = amount1Requested > position.tokensOwed1 ? position.tokensOwed1 : amount1Requested;
if (amount0 > 0) {
position.tokensOwed0 -= amount0;
TransferHelper.safeTransfer(token0, recipient, amount0);
}
if (amount1 > 0) {
position.tokensOwed1 -= amount1;
TransferHelper.safeTransfer(token1, recipient, amount1);
}
emit Collect(msg.sender, recipient, tickLower, tickUpper, amount0, amount1);
}
/// @inheritdoc IUniswapV3PoolActions
/// @dev noDelegateCall is applied indirectly via _modifyPosition
function burn(
int24 tickLower,
int24 tickUpper,
uint128 amount
) external override lock returns (uint256 amount0, uint256 amount1) {
(Position.Info storage position, int256 amount0Int, int256 amount1Int) =
_modifyPosition(
ModifyPositionParams({
owner: msg.sender,
tickLower: tickLower,
tickUpper: tickUpper,
liquidityDelta: -int256(amount).toInt128()
})
);
amount0 = uint256(-amount0Int);
amount1 = uint256(-amount1Int);
if (amount0 > 0 || amount1 > 0) {
(position.tokensOwed0, position.tokensOwed1) = (
position.tokensOwed0 + uint128(amount0),
position.tokensOwed1 + uint128(amount1)
);
}
emit Burn(msg.sender, tickLower, tickUpper, amount, amount0, amount1);
}
struct SwapCache {
// the protocol fee for the input token
uint8 feeProtocol;
// liquidity at the beginning of the swap
uint128 liquidityStart;
// the timestamp of the current block
uint32 blockTimestamp;
// the current value of the tick accumulator, computed only if we cross an initialized tick
int56 tickCumulative;
// the current value of seconds per liquidity accumulator, computed only if we cross an initialized tick
uint160 secondsPerLiquidityCumulativeX128;
// whether we've computed and cached the above two accumulators
bool computedLatestObservation;
}
// the top level state of the swap, the results of which are recorded in storage at the end
struct SwapState {
// the amount remaining to be swapped in/out of the input/output asset
int256 amountSpecifiedRemaining;
// the amount already swapped out/in of the output/input asset
int256 amountCalculated;
// current sqrt(price)
uint160 sqrtPriceX96;
// the tick associated with the current price
int24 tick;
// the global fee growth of the input token
uint256 feeGrowthGlobalX128;
// amount of input token paid as protocol fee
uint128 protocolFee;
// the current liquidity in range
uint128 liquidity;
}
struct StepComputations {
// the price at the beginning of the step
uint160 sqrtPriceStartX96;
// the next tick to swap to from the current tick in the swap direction
int24 tickNext;
// whether tickNext is initialized or not
bool initialized;
// sqrt(price) for the next tick (1/0)
uint160 sqrtPriceNextX96;
// how much is being swapped in in this step
uint256 amountIn;
// how much is being swapped out
uint256 amountOut;
// how much fee is being paid in
uint256 feeAmount;
}
/// @inheritdoc IUniswapV3PoolActions
function swap(
address recipient,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96,
bytes calldata data
) external override noDelegateCall returns (int256 amount0, int256 amount1) {
require(amountSpecified != 0, 'AS');
Slot0 memory slot0Start = slot0;
require(slot0Start.unlocked, 'LOK');
require(
zeroForOne
? sqrtPriceLimitX96 < slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO
: sqrtPriceLimitX96 > slot0Start.sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO,
'SPL'
);
slot0.unlocked = false;
SwapCache memory cache =
SwapCache({
liquidityStart: liquidity,
blockTimestamp: _blockTimestamp(),
feeProtocol: zeroForOne ? (slot0Start.feeProtocol % 16) : (slot0Start.feeProtocol >> 4),
secondsPerLiquidityCumulativeX128: 0,
tickCumulative: 0,
computedLatestObservation: false
});
bool exactInput = amountSpecified > 0;
SwapState memory state =
SwapState({
amountSpecifiedRemaining: amountSpecified,
amountCalculated: 0,
sqrtPriceX96: slot0Start.sqrtPriceX96,
tick: slot0Start.tick,
feeGrowthGlobalX128: zeroForOne ? feeGrowthGlobal0X128 : feeGrowthGlobal1X128,
protocolFee: 0,
liquidity: cache.liquidityStart
});
// continue swapping as long as we haven't used the entire input/output and haven't reached the price limit
while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96) {
StepComputations memory step;
step.sqrtPriceStartX96 = state.sqrtPriceX96;
(step.tickNext, step.initialized) = tickBitmap.nextInitializedTickWithinOneWord(
state.tick,
tickSpacing,
zeroForOne
);
// ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds
if (step.tickNext < TickMath.MIN_TICK) {
step.tickNext = TickMath.MIN_TICK;
} else if (step.tickNext > TickMath.MAX_TICK) {
step.tickNext = TickMath.MAX_TICK;
}
// get the price for the next tick
step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext);
// compute values to swap to the target tick, price limit, or point where input/output amount is exhausted
(state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath.computeSwapStep(
state.sqrtPriceX96,
(zeroForOne ? step.sqrtPriceNextX96 < sqrtPriceLimitX96 : step.sqrtPriceNextX96 > sqrtPriceLimitX96)
? sqrtPriceLimitX96
: step.sqrtPriceNextX96,
state.liquidity,
state.amountSpecifiedRemaining,
fee
);
if (exactInput) {
state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256();
state.amountCalculated = state.amountCalculated.sub(step.amountOut.toInt256());
} else {
state.amountSpecifiedRemaining += step.amountOut.toInt256();
state.amountCalculated = state.amountCalculated.add((step.amountIn + step.feeAmount).toInt256());
}
// if the protocol fee is on, calculate how much is owed, decrement feeAmount, and increment protocolFee
if (cache.feeProtocol > 0) {
uint256 delta = step.feeAmount / cache.feeProtocol;
step.feeAmount -= delta;
state.protocolFee += uint128(delta);
}
// update global fee tracker
if (state.liquidity > 0)
state.feeGrowthGlobalX128 += FullMath.mulDiv(step.feeAmount, FixedPoint128.Q128, state.liquidity);
// shift tick if we reached the next price
if (state.sqrtPriceX96 == step.sqrtPriceNextX96) {
// if the tick is initialized, run the tick transition
if (step.initialized) {
// check for the placeholder value, which we replace with the actual value the first time the swap
// crosses an initialized tick
if (!cache.computedLatestObservation) {
(cache.tickCumulative, cache.secondsPerLiquidityCumulativeX128) = observations.observeSingle(
cache.blockTimestamp,
0,
slot0Start.tick,
slot0Start.observationIndex,
cache.liquidityStart,
slot0Start.observationCardinality
);
cache.computedLatestObservation = true;
}
int128 liquidityNet =
ticks.cross(
step.tickNext,
(zeroForOne ? state.feeGrowthGlobalX128 : feeGrowthGlobal0X128),
(zeroForOne ? feeGrowthGlobal1X128 : state.feeGrowthGlobalX128),
cache.secondsPerLiquidityCumulativeX128,
cache.tickCumulative,
cache.blockTimestamp
);
// if we're moving leftward, we interpret liquidityNet as the opposite sign
// safe because liquidityNet cannot be type(int128).min
if (zeroForOne) liquidityNet = -liquidityNet;
state.liquidity = LiquidityMath.addDelta(state.liquidity, liquidityNet);
}
state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext;
} else if (state.sqrtPriceX96 != step.sqrtPriceStartX96) {
// recompute unless we're on a lower tick boundary (i.e. already transitioned ticks), and haven't moved
state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96);
}
}
// update tick and write an oracle entry if the tick change
if (state.tick != slot0Start.tick) {
(uint16 observationIndex, uint16 observationCardinality) =
observations.write(
slot0Start.observationIndex,
cache.blockTimestamp,
slot0Start.tick,
cache.liquidityStart,
slot0Start.observationCardinality,
slot0Start.observationCardinalityNext
);
(slot0.sqrtPriceX96, slot0.tick, slot0.observationIndex, slot0.observationCardinality) = (
state.sqrtPriceX96,
state.tick,
observationIndex,
observationCardinality
);
} else {
// otherwise just update the price
slot0.sqrtPriceX96 = state.sqrtPriceX96;
}
// update liquidity if it changed
if (cache.liquidityStart != state.liquidity) liquidity = state.liquidity;
// update fee growth global and, if necessary, protocol fees
// overflow is acceptable, protocol has to withdraw before it hits type(uint128).max fees
if (zeroForOne) {
feeGrowthGlobal0X128 = state.feeGrowthGlobalX128;
if (state.protocolFee > 0) protocolFees.token0 += state.protocolFee;
} else {
feeGrowthGlobal1X128 = state.feeGrowthGlobalX128;
if (state.protocolFee > 0) protocolFees.token1 += state.protocolFee;
}
(amount0, amount1) = zeroForOne == exactInput
? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated)
: (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining);
// do the transfers and collect payment
if (zeroForOne) {
if (amount1 < 0) TransferHelper.safeTransfer(token1, recipient, uint256(-amount1));
uint256 balance0Before = balance0();
IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data);
require(balance0Before.add(uint256(amount0)) <= balance0(), 'IIA');
} else {
if (amount0 < 0) TransferHelper.safeTransfer(token0, recipient, uint256(-amount0));
uint256 balance1Before = balance1();
IUniswapV3SwapCallback(msg.sender).uniswapV3SwapCallback(amount0, amount1, data);
require(balance1Before.add(uint256(amount1)) <= balance1(), 'IIA');
}
emit Swap(msg.sender, recipient, amount0, amount1, state.sqrtPriceX96, state.liquidity, state.tick);
slot0.unlocked = true;
}
/// @inheritdoc IUniswapV3PoolActions
function flash(
address recipient,
uint256 amount0,
uint256 amount1,
bytes calldata data
) external override lock noDelegateCall {
uint128 _liquidity = liquidity;
require(_liquidity > 0, 'L');
uint256 fee0 = FullMath.mulDivRoundingUp(amount0, fee, 1e6);
uint256 fee1 = FullMath.mulDivRoundingUp(amount1, fee, 1e6);
uint256 balance0Before = balance0();
uint256 balance1Before = balance1();
if (amount0 > 0) TransferHelper.safeTransfer(token0, recipient, amount0);
if (amount1 > 0) TransferHelper.safeTransfer(token1, recipient, amount1);
IUniswapV3FlashCallback(msg.sender).uniswapV3FlashCallback(fee0, fee1, data);
uint256 balance0After = balance0();
uint256 balance1After = balance1();
require(balance0Before.add(fee0) <= balance0After, 'F0');
require(balance1Before.add(fee1) <= balance1After, 'F1');
// sub is safe because we know balanceAfter is gt balanceBefore by at least fee
uint256 paid0 = balance0After - balance0Before;
uint256 paid1 = balance1After - balance1Before;
if (paid0 > 0) {
uint8 feeProtocol0 = slot0.feeProtocol % 16;
uint256 fees0 = feeProtocol0 == 0 ? 0 : paid0 / feeProtocol0;
if (uint128(fees0) > 0) protocolFees.token0 += uint128(fees0);
feeGrowthGlobal0X128 += FullMath.mulDiv(paid0 - fees0, FixedPoint128.Q128, _liquidity);
}
if (paid1 > 0) {
uint8 feeProtocol1 = slot0.feeProtocol >> 4;
uint256 fees1 = feeProtocol1 == 0 ? 0 : paid1 / feeProtocol1;
if (uint128(fees1) > 0) protocolFees.token1 += uint128(fees1);
feeGrowthGlobal1X128 += FullMath.mulDiv(paid1 - fees1, FixedPoint128.Q128, _liquidity);
}
emit Flash(msg.sender, recipient, amount0, amount1, paid0, paid1);
}
/// @inheritdoc IUniswapV3PoolOwnerActions
function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external override lock onlyFactoryOwner {
require(
(feeProtocol0 == 0 || (feeProtocol0 >= 4 && feeProtocol0 <= 10)) &&
(feeProtocol1 == 0 || (feeProtocol1 >= 4 && feeProtocol1 <= 10))
);
uint8 feeProtocolOld = slot0.feeProtocol;
slot0.feeProtocol = feeProtocol0 + (feeProtocol1 << 4);
emit SetFeeProtocol(feeProtocolOld % 16, feeProtocolOld >> 4, feeProtocol0, feeProtocol1);
}
/// @inheritdoc IUniswapV3PoolOwnerActions
function collectProtocol(
address recipient,
uint128 amount0Requested,
uint128 amount1Requested
) external override lock onlyFactoryOwner returns (uint128 amount0, uint128 amount1) {
amount0 = amount0Requested > protocolFees.token0 ? protocolFees.token0 : amount0Requested;
amount1 = amount1Requested > protocolFees.token1 ? protocolFees.token1 : amount1Requested;
if (amount0 > 0) {
if (amount0 == protocolFees.token0) amount0--; // ensure that the slot is not cleared, for gas savings
protocolFees.token0 -= amount0;
TransferHelper.safeTransfer(token0, recipient, amount0);
}
if (amount1 > 0) {
if (amount1 == protocolFees.token1) amount1--; // ensure that the slot is not cleared, for gas savings
protocolFees.token1 -= amount1;
TransferHelper.safeTransfer(token1, recipient, amount1);
}
emit CollectProtocol(msg.sender, recipient, amount0, amount1);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.7.6;
import './interfaces/IUniswapV3PoolDeployer.sol';
import './UniswapV3Pool.sol';
contract UniswapV3PoolDeployer is IUniswapV3PoolDeployer {
struct Parameters {
address factory;
address token0;
address token1;
uint24 fee;
int24 tickSpacing;
}
/// @inheritdoc IUniswapV3PoolDeployer
Parameters public override parameters;
/// @dev Deploys a pool with the given parameters by transiently setting the parameters storage slot and then
/// clearing it after deploying the pool.
/// @param factory The contract address of the Uniswap V3 factory
/// @param token0 The first token of the pool by address sort order
/// @param token1 The second token of the pool by address sort order
/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
/// @param tickSpacing The spacing between usable ticks
function deploy(
address factory,
address token0,
address token1,
uint24 fee,
int24 tickSpacing
) internal returns (address pool) {
parameters = Parameters({factory: factory, token0: token0, token1: token1, fee: fee, tickSpacing: tickSpacing});
pool = address(new UniswapV3Pool{salt: keccak256(abi.encode(token0, token1, fee))}());
delete parameters;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
/// @title Function for getting block timestamp
/// @dev Base contract that is overridden for tests
abstract contract BlockTimestamp {
/// @dev Method that exists purely to be overridden for tests
/// @return The current block timestamp
function _blockTimestamp() internal view virtual returns (uint256) {
return block.timestamp;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
import '@openzeppelin/contracts/token/ERC721/ERC721.sol';
import '@openzeppelin/contracts/utils/Address.sol';
import '../libraries/ChainId.sol';
import '../interfaces/external/IERC1271.sol';
import '../interfaces/IERC721Permit.sol';
import './BlockTimestamp.sol';
/// @title ERC721 with permit
/// @notice Nonfungible tokens that support an approve via signature, i.e. permit
abstract contract ERC721Permit is BlockTimestamp, ERC721, IERC721Permit {
/// @dev Gets the current nonce for a token ID and then increments it, returning the original value
function _getAndIncrementNonce(uint256 tokenId) internal virtual returns (uint256);
/// @dev The hash of the name used in the permit signature verification
bytes32 private immutable nameHash;
/// @dev The hash of the version string used in the permit signature verification
bytes32 private immutable versionHash;
/// @notice Computes the nameHash and versionHash
constructor(
string memory name_,
string memory symbol_,
string memory version_
) ERC721(name_, symbol_) {
nameHash = keccak256(bytes(name_));
versionHash = keccak256(bytes(version_));
}
/// @inheritdoc IERC721Permit
function DOMAIN_SEPARATOR() public view override returns (bytes32) {
return
keccak256(
abi.encode(
// keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)')
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f,
nameHash,
versionHash,
ChainId.get(),
address(this)
)
);
}
/// @inheritdoc IERC721Permit
/// @dev Value is equal to keccak256("Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)");
bytes32 public constant override PERMIT_TYPEHASH =
0x49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad;
/// @inheritdoc IERC721Permit
function permit(
address spender,
uint256 tokenId,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable override {
require(_blockTimestamp() <= deadline, 'Permit expired');
bytes32 digest =
keccak256(
abi.encodePacked(
'\x19\x01',
DOMAIN_SEPARATOR(),
keccak256(abi.encode(PERMIT_TYPEHASH, spender, tokenId, _getAndIncrementNonce(tokenId), deadline))
)
);
address owner = ownerOf(tokenId);
require(spender != owner, 'ERC721Permit: approval to current owner');
if (Address.isContract(owner)) {
require(IERC1271(owner).isValidSignature(digest, abi.encodePacked(r, s, v)) == 0x1626ba7e, 'Unauthorized');
} else {
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0), 'Invalid signature');
require(recoveredAddress == owner, 'Unauthorized');
}
_approve(spender, tokenId);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol';
import '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3MintCallback.sol';
import '@uniswap/v3-core/contracts/libraries/TickMath.sol';
import '../libraries/PoolAddress.sol';
import '../libraries/CallbackValidation.sol';
import '../libraries/LiquidityAmounts.sol';
import './PeripheryPayments.sol';
import './PeripheryImmutableState.sol';
/// @title Liquidity management functions
/// @notice Internal functions for safely managing liquidity in Uniswap V3
abstract contract LiquidityManagement is IUniswapV3MintCallback, PeripheryImmutableState, PeripheryPayments {
struct MintCallbackData {
PoolAddress.PoolKey poolKey;
address payer;
}
/// @inheritdoc IUniswapV3MintCallback
function uniswapV3MintCallback(
uint256 amount0Owed,
uint256 amount1Owed,
bytes calldata data
) external override {
MintCallbackData memory decoded = abi.decode(data, (MintCallbackData));
CallbackValidation.verifyCallback(factory, decoded.poolKey);
if (amount0Owed > 0) pay(decoded.poolKey.token0, decoded.payer, msg.sender, amount0Owed);
if (amount1Owed > 0) pay(decoded.poolKey.token1, decoded.payer, msg.sender, amount1Owed);
}
struct AddLiquidityParams {
address token0;
address token1;
uint24 fee;
address recipient;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
}
/// @notice Add liquidity to an initialized pool
function addLiquidity(AddLiquidityParams memory params)
internal
returns (
uint128 liquidity,
uint256 amount0,
uint256 amount1,
IUniswapV3Pool pool
)
{
PoolAddress.PoolKey memory poolKey =
PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee});
pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey));
// compute the liquidity amount
{
(uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(params.tickLower);
uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(params.tickUpper);
liquidity = LiquidityAmounts.getLiquidityForAmounts(
sqrtPriceX96,
sqrtRatioAX96,
sqrtRatioBX96,
params.amount0Desired,
params.amount1Desired
);
}
(amount0, amount1) = pool.mint(
params.recipient,
params.tickLower,
params.tickUpper,
liquidity,
abi.encode(MintCallbackData({poolKey: poolKey, payer: msg.sender}))
);
require(amount0 >= params.amount0Min && amount1 >= params.amount1Min, 'Price slippage check');
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import '../interfaces/IMulticall.sol';
/// @title Multicall
/// @notice Enables calling multiple methods in a single call to the contract
abstract contract Multicall is IMulticall {
/// @inheritdoc IMulticall
function multicall(bytes[] calldata data) external payable override returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
(bool success, bytes memory result) = address(this).delegatecall(data[i]);
if (!success) {
// Next 5 lines from https://ethereum.stackexchange.com/a/83577
if (result.length < 68) revert();
assembly {
result := add(result, 0x04)
}
revert(abi.decode(result, (string)));
}
results[i] = result;
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
import '../interfaces/IPeripheryImmutableState.sol';
/// @title Immutable state
/// @notice Immutable state used by periphery contracts
abstract contract PeripheryImmutableState is IPeripheryImmutableState {
/// @inheritdoc IPeripheryImmutableState
address public immutable override factory;
/// @inheritdoc IPeripheryImmutableState
address public immutable override WETH9;
constructor(address _factory, address _WETH9) {
factory = _factory;
WETH9 = _WETH9;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '../interfaces/IPeripheryPayments.sol';
import '../interfaces/external/IWETH9.sol';
import '../libraries/TransferHelper.sol';
import './PeripheryImmutableState.sol';
abstract contract PeripheryPayments is IPeripheryPayments, PeripheryImmutableState {
receive() external payable {
require(msg.sender == WETH9, 'Not WETH9');
}
/// @inheritdoc IPeripheryPayments
function unwrapWETH9(uint256 amountMinimum, address recipient) external payable override {
uint256 balanceWETH9 = IWETH9(WETH9).balanceOf(address(this));
require(balanceWETH9 >= amountMinimum, 'Insufficient WETH9');
if (balanceWETH9 > 0) {
IWETH9(WETH9).withdraw(balanceWETH9);
TransferHelper.safeTransferETH(recipient, balanceWETH9);
}
}
/// @inheritdoc IPeripheryPayments
function sweepToken(
address token,
uint256 amountMinimum,
address recipient
) external payable override {
uint256 balanceToken = IERC20(token).balanceOf(address(this));
require(balanceToken >= amountMinimum, 'Insufficient token');
if (balanceToken > 0) {
TransferHelper.safeTransfer(token, recipient, balanceToken);
}
}
/// @inheritdoc IPeripheryPayments
function refundETH() external payable override {
if (address(this).balance > 0) TransferHelper.safeTransferETH(msg.sender, address(this).balance);
}
/// @param token The token to pay
/// @param payer The entity that must pay
/// @param recipient The entity that will receive payment
/// @param value The amount to pay
function pay(
address token,
address payer,
address recipient,
uint256 value
) internal {
if (token == WETH9 && address(this).balance >= value) {
// pay with WETH9
IWETH9(WETH9).deposit{value: value}(); // wrap only what is needed to pay
IWETH9(WETH9).transfer(recipient, value);
} else if (payer == address(this)) {
// pay with tokens already in the contract (for the exact input multihop case)
TransferHelper.safeTransfer(token, recipient, value);
} else {
// pull payment
TransferHelper.safeTransferFrom(token, payer, recipient, value);
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
import './BlockTimestamp.sol';
abstract contract PeripheryValidation is BlockTimestamp {
modifier checkDeadline(uint256 deadline) {
require(_blockTimestamp() <= deadline, 'Transaction too old');
_;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol';
import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol';
import './PeripheryImmutableState.sol';
import '../interfaces/IPoolInitializer.sol';
/// @title Creates and initializes V3 Pools
abstract contract PoolInitializer is IPoolInitializer, PeripheryImmutableState {
/// @inheritdoc IPoolInitializer
function createAndInitializePoolIfNecessary(
address token0,
address token1,
uint24 fee,
uint160 sqrtPriceX96
) external payable override returns (address pool) {
require(token0 < token1);
pool = IUniswapV3Factory(factory).getPool(token0, token1, fee);
if (pool == address(0)) {
pool = IUniswapV3Factory(factory).createPool(token0, token1, fee);
IUniswapV3Pool(pool).initialize(sqrtPriceX96);
} else {
(uint160 sqrtPriceX96Existing, , , , , , ) = IUniswapV3Pool(pool).slot0();
if (sqrtPriceX96Existing == 0) {
IUniswapV3Pool(pool).initialize(sqrtPriceX96);
}
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/drafts/IERC20Permit.sol';
import '../interfaces/ISelfPermit.sol';
import '../interfaces/external/IERC20PermitAllowed.sol';
/// @title Self Permit
/// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route
/// @dev These functions are expected to be embedded in multicalls to allow EOAs to approve a contract and call a function
/// that requires an approval in a single transaction.
abstract contract SelfPermit is ISelfPermit {
/// @inheritdoc ISelfPermit
function selfPermit(
address token,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public payable override {
IERC20Permit(token).permit(msg.sender, address(this), value, deadline, v, r, s);
}
/// @inheritdoc ISelfPermit
function selfPermitIfNecessary(
address token,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable override {
if (IERC20(token).allowance(msg.sender, address(this)) < value) selfPermit(token, value, deadline, v, r, s);
}
/// @inheritdoc ISelfPermit
function selfPermitAllowed(
address token,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) public payable override {
IERC20PermitAllowed(token).permit(msg.sender, address(this), nonce, expiry, true, v, r, s);
}
/// @inheritdoc ISelfPermit
function selfPermitAllowedIfNecessary(
address token,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external payable override {
if (IERC20(token).allowance(msg.sender, address(this)) < type(uint256).max)
selfPermitAllowed(token, nonce, expiry, v, r, s);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Interface for verifying contract-based account signatures
/// @notice Interface that verifies provided signature for the data
/// @dev Interface defined by EIP-1271
interface IERC1271 {
/// @notice Returns whether the provided signature is valid for the provided data
/// @dev MUST return the bytes4 magic value 0x1626ba7e when function passes.
/// MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5).
/// MUST allow external calls.
/// @param hash Hash of the data to be signed
/// @param signature Signature byte array associated with _data
/// @return magicValue The bytes4 magic value 0x1626ba7e
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Interface for permit
/// @notice Interface used by DAI/CHAI for permit
interface IERC20PermitAllowed {
/// @notice Approve the spender to spend some tokens via the holder signature
/// @dev This is the permit interface used by DAI and CHAI
/// @param holder The address of the token holder, the token owner
/// @param spender The address of the token spender
/// @param nonce The holder's nonce, increases at each call to permit
/// @param expiry The timestamp at which the permit is no longer valid
/// @param allowed Boolean that sets approval amount, true for type(uint256).max and false for 0
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function permit(
address holder,
address spender,
uint256 nonce,
uint256 expiry,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
/// @title Interface for WETH9
interface IWETH9 is IERC20 {
/// @notice Deposit ether to get wrapped ether
function deposit() external payable;
/// @notice Withdraw wrapped ether to get ether
function withdraw(uint256) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
import '@openzeppelin/contracts/token/ERC721/IERC721.sol';
/// @title ERC721 with permit
/// @notice Extension to ERC721 that includes a permit function for signature based approvals
interface IERC721Permit is IERC721 {
/// @notice The permit typehash used in the permit signature
/// @return The typehash for the permit
function PERMIT_TYPEHASH() external pure returns (bytes32);
/// @notice The domain separator used in the permit signature
/// @return The domain seperator used in encoding of permit signature
function DOMAIN_SEPARATOR() external view returns (bytes32);
/// @notice Approve of a specific token ID for spending by spender via signature
/// @param spender The account that is being approved
/// @param tokenId The ID of the token that is being approved for spending
/// @param deadline The deadline timestamp by which the call must be mined for the approve to work
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function permit(
address spender,
uint256 tokenId,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
/// @title Multicall interface
/// @notice Enables calling multiple methods in a single call to the contract
interface IMulticall {
/// @notice Call multiple functions in the current contract and return the data from all of them if they all succeed
/// @dev The `msg.value` should not be trusted for any method callable from multicall.
/// @param data The encoded function data for each of the calls to make to this contract
/// @return results The results from each of the calls passed in via data
function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
import '@openzeppelin/contracts/token/ERC721/IERC721Metadata.sol';
import '@openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol';
import './IPoolInitializer.sol';
import './IERC721Permit.sol';
import './IPeripheryPayments.sol';
import './IPeripheryImmutableState.sol';
import '../libraries/PoolAddress.sol';
/// @title Non-fungible token for positions
/// @notice Wraps Uniswap V3 positions in a non-fungible token interface which allows for them to be transferred
/// and authorized.
interface INonfungiblePositionManager is
IPoolInitializer,
IPeripheryPayments,
IPeripheryImmutableState,
IERC721Metadata,
IERC721Enumerable,
IERC721Permit
{
/// @notice Emitted when liquidity is increased for a position NFT
/// @dev Also emitted when a token is minted
/// @param tokenId The ID of the token for which liquidity was increased
/// @param liquidity The amount by which liquidity for the NFT position was increased
/// @param amount0 The amount of token0 that was paid for the increase in liquidity
/// @param amount1 The amount of token1 that was paid for the increase in liquidity
event IncreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
/// @notice Emitted when liquidity is decreased for a position NFT
/// @param tokenId The ID of the token for which liquidity was decreased
/// @param liquidity The amount by which liquidity for the NFT position was decreased
/// @param amount0 The amount of token0 that was accounted for the decrease in liquidity
/// @param amount1 The amount of token1 that was accounted for the decrease in liquidity
event DecreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
/// @notice Emitted when tokens are collected for a position NFT
/// @dev The amounts reported may not be exactly equivalent to the amounts transferred, due to rounding behavior
/// @param tokenId The ID of the token for which underlying tokens were collected
/// @param recipient The address of the account that received the collected tokens
/// @param amount0 The amount of token0 owed to the position that was collected
/// @param amount1 The amount of token1 owed to the position that was collected
event Collect(uint256 indexed tokenId, address recipient, uint256 amount0, uint256 amount1);
/// @notice Returns the position information associated with a given token ID.
/// @dev Throws if the token ID is not valid.
/// @param tokenId The ID of the token that represents the position
/// @return nonce The nonce for permits
/// @return operator The address that is approved for spending
/// @return token0 The address of the token0 for a specific pool
/// @return token1 The address of the token1 for a specific pool
/// @return fee The fee associated with the pool
/// @return tickLower The lower end of the tick range for the position
/// @return tickUpper The higher end of the tick range for the position
/// @return liquidity The liquidity of the position
/// @return feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position
/// @return feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position
/// @return tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation
/// @return tokensOwed1 The uncollected amount of token1 owed to the position as of the last computation
function positions(uint256 tokenId)
external
view
returns (
uint96 nonce,
address operator,
address token0,
address token1,
uint24 fee,
int24 tickLower,
int24 tickUpper,
uint128 liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
);
struct MintParams {
address token0;
address token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
/// @notice Creates a new position wrapped in a NFT
/// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized
/// a method does not exist, i.e. the pool is assumed to be initialized.
/// @param params The params necessary to mint a position, encoded as `MintParams` in calldata
/// @return tokenId The ID of the token that represents the minted position
/// @return liquidity The amount of liquidity for this position
/// @return amount0 The amount of token0
/// @return amount1 The amount of token1
function mint(MintParams calldata params)
external
payable
returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
struct IncreaseLiquidityParams {
uint256 tokenId;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
/// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender`
/// @param params tokenId The ID of the token for which liquidity is being increased,
/// amount0Desired The desired amount of token0 to be spent,
/// amount1Desired The desired amount of token1 to be spent,
/// amount0Min The minimum amount of token0 to spend, which serves as a slippage check,
/// amount1Min The minimum amount of token1 to spend, which serves as a slippage check,
/// deadline The time by which the transaction must be included to effect the change
/// @return liquidity The new liquidity amount as a result of the increase
/// @return amount0 The amount of token0 to acheive resulting liquidity
/// @return amount1 The amount of token1 to acheive resulting liquidity
function increaseLiquidity(IncreaseLiquidityParams calldata params)
external
payable
returns (
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
struct DecreaseLiquidityParams {
uint256 tokenId;
uint128 liquidity;
uint256 amount0Min;
uint256 amount1Min;
uint256 deadline;
}
/// @notice Decreases the amount of liquidity in a position and accounts it to the position
/// @param params tokenId The ID of the token for which liquidity is being decreased,
/// amount The amount by which liquidity will be decreased,
/// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity,
/// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity,
/// deadline The time by which the transaction must be included to effect the change
/// @return amount0 The amount of token0 accounted to the position's tokens owed
/// @return amount1 The amount of token1 accounted to the position's tokens owed
function decreaseLiquidity(DecreaseLiquidityParams calldata params)
external
payable
returns (uint256 amount0, uint256 amount1);
struct CollectParams {
uint256 tokenId;
address recipient;
uint128 amount0Max;
uint128 amount1Max;
}
/// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient
/// @param params tokenId The ID of the NFT for which tokens are being collected,
/// recipient The account that should receive the tokens,
/// amount0Max The maximum amount of token0 to collect,
/// amount1Max The maximum amount of token1 to collect
/// @return amount0 The amount of fees collected in token0
/// @return amount1 The amount of fees collected in token1
function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);
/// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens
/// must be collected first.
/// @param tokenId The ID of the token that is being burned
function burn(uint256 tokenId) external payable;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import './INonfungiblePositionManager.sol';
/// @title Describes position NFT tokens via URI
interface INonfungibleTokenPositionDescriptor {
/// @notice Produces the URI describing a particular token ID for a position manager
/// @dev Note this URI may be a data: URI with the JSON contents directly inlined
/// @param positionManager The position manager for which to describe the token
/// @param tokenId The ID of the token for which to produce a description, which may not be valid
/// @return The URI of the ERC721-compliant metadata
function tokenURI(INonfungiblePositionManager positionManager, uint256 tokenId)
external
view
returns (string memory);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Immutable state
/// @notice Functions that return immutable state of the router
interface IPeripheryImmutableState {
/// @return Returns the address of the Uniswap V3 factory
function factory() external view returns (address);
/// @return Returns the address of WETH9
function WETH9() external view returns (address);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
/// @title Periphery Payments
/// @notice Functions to ease deposits and withdrawals of ETH
interface IPeripheryPayments {
/// @notice Unwraps the contract's WETH9 balance and sends it to recipient as ETH.
/// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users.
/// @param amountMinimum The minimum amount of WETH9 to unwrap
/// @param recipient The address receiving ETH
function unwrapWETH9(uint256 amountMinimum, address recipient) external payable;
/// @notice Refunds any ETH balance held by this contract to the `msg.sender`
/// @dev Useful for bundling with mint or increase liquidity that uses ether, or exact output swaps
/// that use ether for the input amount
function refundETH() external payable;
/// @notice Transfers the full amount of a token held by this contract to recipient
/// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users
/// @param token The contract address of the token which will be transferred to `recipient`
/// @param amountMinimum The minimum amount of token required for a transfer
/// @param recipient The destination address of the token
function sweepToken(
address token,
uint256 amountMinimum,
address recipient
) external payable;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
/// @title Creates and initializes V3 Pools
/// @notice Provides a method for creating and initializing a pool, if necessary, for bundling with other methods that
/// require the pool to exist.
interface IPoolInitializer {
/// @notice Creates a new pool if it does not exist, then initializes if not initialized
/// @dev This method can be bundled with others via IMulticall for the first action (e.g. mint) performed against a pool
/// @param token0 The contract address of token0 of the pool
/// @param token1 The contract address of token1 of the pool
/// @param fee The fee amount of the v3 pool for the specified token pair
/// @param sqrtPriceX96 The initial square root price of the pool as a Q64.96 value
/// @return pool Returns the pool address based on the pair of tokens and fee, will return the newly created pool address if necessary
function createAndInitializePoolIfNecessary(
address token0,
address token1,
uint24 fee,
uint160 sqrtPriceX96
) external payable returns (address pool);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
/// @title Self Permit
/// @notice Functionality to call permit on any EIP-2612-compliant token for use in the route
interface ISelfPermit {
/// @notice Permits this contract to spend a given token from `msg.sender`
/// @dev The `owner` is always msg.sender and the `spender` is always address(this).
/// @param token The address of the token spent
/// @param value The amount that can be spent of token
/// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermit(
address token,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
/// @notice Permits this contract to spend a given token from `msg.sender`
/// @dev The `owner` is always msg.sender and the `spender` is always address(this).
/// Can be used instead of #selfPermit to prevent calls from failing due to a frontrun of a call to #selfPermit
/// @param token The address of the token spent
/// @param value The amount that can be spent of token
/// @param deadline A timestamp, the current blocktime must be less than or equal to this timestamp
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermitIfNecessary(
address token,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
/// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter
/// @dev The `owner` is always msg.sender and the `spender` is always address(this)
/// @param token The address of the token spent
/// @param nonce The current nonce of the owner
/// @param expiry The timestamp at which the permit is no longer valid
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermitAllowed(
address token,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
/// @notice Permits this contract to spend the sender's tokens for permit signatures that have the `allowed` parameter
/// @dev The `owner` is always msg.sender and the `spender` is always address(this)
/// Can be used instead of #selfPermitAllowed to prevent calls from failing due to a frontrun of a call to #selfPermitAllowed.
/// @param token The address of the token spent
/// @param nonce The current nonce of the owner
/// @param expiry The timestamp at which the permit is no longer valid
/// @param v Must produce valid secp256k1 signature from the holder along with `r` and `s`
/// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s`
/// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v`
function selfPermitAllowedIfNecessary(
address token,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external payable;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol';
import './PoolAddress.sol';
/// @notice Provides validation for callbacks from Uniswap V3 Pools
library CallbackValidation {
/// @notice Returns the address of a valid Uniswap V3 Pool
/// @param factory The contract address of the Uniswap V3 factory
/// @param tokenA The contract address of either token0 or token1
/// @param tokenB The contract address of the other token
/// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
/// @return pool The V3 pool contract address
function verifyCallback(
address factory,
address tokenA,
address tokenB,
uint24 fee
) internal view returns (IUniswapV3Pool pool) {
return verifyCallback(factory, PoolAddress.getPoolKey(tokenA, tokenB, fee));
}
/// @notice Returns the address of a valid Uniswap V3 Pool
/// @param factory The contract address of the Uniswap V3 factory
/// @param poolKey The identifying key of the V3 pool
/// @return pool The V3 pool contract address
function verifyCallback(address factory, PoolAddress.PoolKey memory poolKey)
internal
view
returns (IUniswapV3Pool pool)
{
pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey));
require(msg.sender == address(pool));
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.0;
/// @title Function for getting the current chain ID
library ChainId {
/// @dev Gets the current chain ID
/// @return chainId The current chain ID
function get() internal pure returns (uint256 chainId) {
assembly {
chainId := chainid()
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import '@uniswap/v3-core/contracts/libraries/FullMath.sol';
import '@uniswap/v3-core/contracts/libraries/FixedPoint96.sol';
/// @title Liquidity amount functions
/// @notice Provides functions for computing liquidity amounts from token amounts and prices
library LiquidityAmounts {
/// @notice Downcasts uint256 to uint128
/// @param x The uint258 to be downcasted
/// @return y The passed value, downcasted to uint128
function toUint128(uint256 x) private pure returns (uint128 y) {
require((y = uint128(x)) == x);
}
/// @notice Computes the amount of liquidity received for a given amount of token0 and price range
/// @dev Calculates amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower))
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param amount0 The amount0 being sent in
/// @return liquidity The amount of returned liquidity
function getLiquidityForAmount0(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount0
) internal pure returns (uint128 liquidity) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
uint256 intermediate = FullMath.mulDiv(sqrtRatioAX96, sqrtRatioBX96, FixedPoint96.Q96);
return toUint128(FullMath.mulDiv(amount0, intermediate, sqrtRatioBX96 - sqrtRatioAX96));
}
/// @notice Computes the amount of liquidity received for a given amount of token1 and price range
/// @dev Calculates amount1 / (sqrt(upper) - sqrt(lower)).
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param amount1 The amount1 being sent in
/// @return liquidity The amount of returned liquidity
function getLiquidityForAmount1(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount1
) internal pure returns (uint128 liquidity) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return toUint128(FullMath.mulDiv(amount1, FixedPoint96.Q96, sqrtRatioBX96 - sqrtRatioAX96));
}
/// @notice Computes the maximum amount of liquidity received for a given amount of token0, token1, the current
/// pool prices and the prices at the tick boundaries
/// @param sqrtRatioX96 A sqrt price representing the current pool prices
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param amount0 The amount of token0 being sent in
/// @param amount1 The amount of token1 being sent in
/// @return liquidity The maximum amount of liquidity received
function getLiquidityForAmounts(
uint160 sqrtRatioX96,
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint256 amount0,
uint256 amount1
) internal pure returns (uint128 liquidity) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
if (sqrtRatioX96 <= sqrtRatioAX96) {
liquidity = getLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0);
} else if (sqrtRatioX96 < sqrtRatioBX96) {
uint128 liquidity0 = getLiquidityForAmount0(sqrtRatioX96, sqrtRatioBX96, amount0);
uint128 liquidity1 = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioX96, amount1);
liquidity = liquidity0 < liquidity1 ? liquidity0 : liquidity1;
} else {
liquidity = getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, amount1);
}
}
/// @notice Computes the amount of token0 for a given amount of liquidity and a price range
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount0 The amount of token0
function getAmount0ForLiquidity(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 amount0) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return
FullMath.mulDiv(
uint256(liquidity) << FixedPoint96.RESOLUTION,
sqrtRatioBX96 - sqrtRatioAX96,
sqrtRatioBX96
) / sqrtRatioAX96;
}
/// @notice Computes the amount of token1 for a given amount of liquidity and a price range
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount1 The amount of token1
function getAmount1ForLiquidity(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 amount1) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96);
}
/// @notice Computes the token0 and token1 value for a given amount of liquidity, the current
/// pool prices and the prices at the tick boundaries
/// @param sqrtRatioX96 A sqrt price representing the current pool prices
/// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
/// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
/// @param liquidity The liquidity being valued
/// @return amount0 The amount of token0
/// @return amount1 The amount of token1
function getAmountsForLiquidity(
uint160 sqrtRatioX96,
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) internal pure returns (uint256 amount0, uint256 amount1) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
if (sqrtRatioX96 <= sqrtRatioAX96) {
amount0 = getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
} else if (sqrtRatioX96 < sqrtRatioBX96) {
amount0 = getAmount0ForLiquidity(sqrtRatioX96, sqrtRatioBX96, liquidity);
amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioX96, liquidity);
} else {
amount1 = getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Provides functions for deriving a pool address from the factory, tokens, and the fee
library PoolAddress {
bytes32 internal constant POOL_INIT_CODE_HASH = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54;
/// @notice The identifying key of the pool
struct PoolKey {
address token0;
address token1;
uint24 fee;
}
/// @notice Returns PoolKey: the ordered tokens with the matched fee levels
/// @param tokenA The first token of a pool, unsorted
/// @param tokenB The second token of a pool, unsorted
/// @param fee The fee level of the pool
/// @return Poolkey The pool details with ordered token0 and token1 assignments
function getPoolKey(
address tokenA,
address tokenB,
uint24 fee
) internal pure returns (PoolKey memory) {
if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA);
return PoolKey({token0: tokenA, token1: tokenB, fee: fee});
}
/// @notice Deterministically computes the pool address given the factory and PoolKey
/// @param factory The Uniswap V3 factory contract address
/// @param key The PoolKey
/// @return pool The contract address of the V3 pool
function computeAddress(address factory, PoolKey memory key) internal pure returns (address pool) {
require(key.token0 < key.token1);
pool = address(
uint256(
keccak256(
abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encode(key.token0, key.token1, key.fee)),
POOL_INIT_CODE_HASH
)
)
)
);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
library PositionKey {
/// @dev Returns the key of the position in the core library
function compute(
address owner,
int24 tickLower,
int24 tickUpper
) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(owner, tickLower, tickUpper));
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.0;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
library TransferHelper {
/// @notice Transfers tokens from the targeted address to the given destination
/// @notice Errors with 'STF' if transfer fails
/// @param token The contract address of the token to be transferred
/// @param from The originating address from which the tokens will be transferred
/// @param to The destination address of the transfer
/// @param value The amount to be transferred
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) =
token.call(abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'STF');
}
/// @notice Transfers tokens from msg.sender to a recipient
/// @dev Errors with ST if transfer fails
/// @param token The contract address of the token which will be transferred
/// @param to The recipient of the transfer
/// @param value The value of the transfer
function safeTransfer(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'ST');
}
/// @notice Approves the stipulated contract to spend the given allowance in the given token
/// @dev Errors with 'SA' if transfer fails
/// @param token The contract address of the token to be approved
/// @param to The target of the approval
/// @param value The amount of the given token the target will be allowed to spend
function safeApprove(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'SA');
}
/// @notice Transfers ETH to the recipient address
/// @dev Fails with `STE`
/// @param to The destination of the transfer
/// @param value The value to be transferred
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, 'STE');
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity =0.7.6;
pragma abicoder v2;
import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol';
import '@uniswap/v3-core/contracts/libraries/FixedPoint128.sol';
import '@uniswap/v3-core/contracts/libraries/FullMath.sol';
import './interfaces/INonfungiblePositionManager.sol';
import './interfaces/INonfungibleTokenPositionDescriptor.sol';
import './libraries/PositionKey.sol';
import './libraries/PoolAddress.sol';
import './base/LiquidityManagement.sol';
import './base/PeripheryImmutableState.sol';
import './base/Multicall.sol';
import './base/ERC721Permit.sol';
import './base/PeripheryValidation.sol';
import './base/SelfPermit.sol';
import './base/PoolInitializer.sol';
/// @title NFT positions
/// @notice Wraps Uniswap V3 positions in the ERC721 non-fungible token interface
contract NonfungiblePositionManager is
INonfungiblePositionManager,
Multicall,
ERC721Permit,
PeripheryImmutableState,
PoolInitializer,
LiquidityManagement,
PeripheryValidation,
SelfPermit
{
// details about the uniswap position
struct Position {
// the nonce for permits
uint96 nonce;
// the address that is approved for spending this token
address operator;
// the ID of the pool with which this token is connected
uint80 poolId;
// the tick range of the position
int24 tickLower;
int24 tickUpper;
// the liquidity of the position
uint128 liquidity;
// the fee growth of the aggregate position as of the last action on the individual position
uint256 feeGrowthInside0LastX128;
uint256 feeGrowthInside1LastX128;
// how many uncollected tokens are owed to the position, as of the last computation
uint128 tokensOwed0;
uint128 tokensOwed1;
}
/// @dev IDs of pools assigned by this contract
mapping(address => uint80) private _poolIds;
/// @dev Pool keys by pool ID, to save on SSTOREs for position data
mapping(uint80 => PoolAddress.PoolKey) private _poolIdToPoolKey;
/// @dev The token ID position data
mapping(uint256 => Position) private _positions;
/// @dev The ID of the next token that will be minted. Skips 0
uint176 private _nextId = 1;
/// @dev The ID of the next pool that is used for the first time. Skips 0
uint80 private _nextPoolId = 1;
/// @dev The address of the token descriptor contract, which handles generating token URIs for position tokens
address private immutable _tokenDescriptor;
constructor(
address _factory,
address _WETH9,
address _tokenDescriptor_
) ERC721Permit('Uniswap V3 Positions NFT-V1', 'UNI-V3-POS', '1') PeripheryImmutableState(_factory, _WETH9) {
_tokenDescriptor = _tokenDescriptor_;
}
/// @inheritdoc INonfungiblePositionManager
function positions(uint256 tokenId)
external
view
override
returns (
uint96 nonce,
address operator,
address token0,
address token1,
uint24 fee,
int24 tickLower,
int24 tickUpper,
uint128 liquidity,
uint256 feeGrowthInside0LastX128,
uint256 feeGrowthInside1LastX128,
uint128 tokensOwed0,
uint128 tokensOwed1
)
{
Position memory position = _positions[tokenId];
require(position.poolId != 0, 'Invalid token ID');
PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId];
return (
position.nonce,
position.operator,
poolKey.token0,
poolKey.token1,
poolKey.fee,
position.tickLower,
position.tickUpper,
position.liquidity,
position.feeGrowthInside0LastX128,
position.feeGrowthInside1LastX128,
position.tokensOwed0,
position.tokensOwed1
);
}
/// @dev Caches a pool key
function cachePoolKey(address pool, PoolAddress.PoolKey memory poolKey) private returns (uint80 poolId) {
poolId = _poolIds[pool];
if (poolId == 0) {
_poolIds[pool] = (poolId = _nextPoolId++);
_poolIdToPoolKey[poolId] = poolKey;
}
}
/// @inheritdoc INonfungiblePositionManager
function mint(MintParams calldata params)
external
payable
override
checkDeadline(params.deadline)
returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
)
{
IUniswapV3Pool pool;
(liquidity, amount0, amount1, pool) = addLiquidity(
AddLiquidityParams({
token0: params.token0,
token1: params.token1,
fee: params.fee,
recipient: address(this),
tickLower: params.tickLower,
tickUpper: params.tickUpper,
amount0Desired: params.amount0Desired,
amount1Desired: params.amount1Desired,
amount0Min: params.amount0Min,
amount1Min: params.amount1Min
})
);
_mint(params.recipient, (tokenId = _nextId++));
bytes32 positionKey = PositionKey.compute(address(this), params.tickLower, params.tickUpper);
(, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey);
// idempotent set
uint80 poolId =
cachePoolKey(
address(pool),
PoolAddress.PoolKey({token0: params.token0, token1: params.token1, fee: params.fee})
);
_positions[tokenId] = Position({
nonce: 0,
operator: address(0),
poolId: poolId,
tickLower: params.tickLower,
tickUpper: params.tickUpper,
liquidity: liquidity,
feeGrowthInside0LastX128: feeGrowthInside0LastX128,
feeGrowthInside1LastX128: feeGrowthInside1LastX128,
tokensOwed0: 0,
tokensOwed1: 0
});
emit IncreaseLiquidity(tokenId, liquidity, amount0, amount1);
}
modifier isAuthorizedForToken(uint256 tokenId) {
require(_isApprovedOrOwner(msg.sender, tokenId), 'Not approved');
_;
}
function tokenURI(uint256 tokenId) public view override(ERC721, IERC721Metadata) returns (string memory) {
require(_exists(tokenId));
return INonfungibleTokenPositionDescriptor(_tokenDescriptor).tokenURI(this, tokenId);
}
// save bytecode by removing implementation of unused method
function baseURI() public pure override returns (string memory) {}
/// @inheritdoc INonfungiblePositionManager
function increaseLiquidity(IncreaseLiquidityParams calldata params)
external
payable
override
checkDeadline(params.deadline)
returns (
uint128 liquidity,
uint256 amount0,
uint256 amount1
)
{
Position storage position = _positions[params.tokenId];
PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId];
IUniswapV3Pool pool;
(liquidity, amount0, amount1, pool) = addLiquidity(
AddLiquidityParams({
token0: poolKey.token0,
token1: poolKey.token1,
fee: poolKey.fee,
tickLower: position.tickLower,
tickUpper: position.tickUpper,
amount0Desired: params.amount0Desired,
amount1Desired: params.amount1Desired,
amount0Min: params.amount0Min,
amount1Min: params.amount1Min,
recipient: address(this)
})
);
bytes32 positionKey = PositionKey.compute(address(this), position.tickLower, position.tickUpper);
// this is now updated to the current transaction
(, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey);
position.tokensOwed0 += uint128(
FullMath.mulDiv(
feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128,
position.liquidity,
FixedPoint128.Q128
)
);
position.tokensOwed1 += uint128(
FullMath.mulDiv(
feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128,
position.liquidity,
FixedPoint128.Q128
)
);
position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128;
position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128;
position.liquidity += liquidity;
emit IncreaseLiquidity(params.tokenId, liquidity, amount0, amount1);
}
/// @inheritdoc INonfungiblePositionManager
function decreaseLiquidity(DecreaseLiquidityParams calldata params)
external
payable
override
isAuthorizedForToken(params.tokenId)
checkDeadline(params.deadline)
returns (uint256 amount0, uint256 amount1)
{
require(params.liquidity > 0);
Position storage position = _positions[params.tokenId];
uint128 positionLiquidity = position.liquidity;
require(positionLiquidity >= params.liquidity);
PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId];
IUniswapV3Pool pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey));
(amount0, amount1) = pool.burn(position.tickLower, position.tickUpper, params.liquidity);
require(amount0 >= params.amount0Min && amount1 >= params.amount1Min, 'Price slippage check');
bytes32 positionKey = PositionKey.compute(address(this), position.tickLower, position.tickUpper);
// this is now updated to the current transaction
(, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) = pool.positions(positionKey);
position.tokensOwed0 +=
uint128(amount0) +
uint128(
FullMath.mulDiv(
feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128,
positionLiquidity,
FixedPoint128.Q128
)
);
position.tokensOwed1 +=
uint128(amount1) +
uint128(
FullMath.mulDiv(
feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128,
positionLiquidity,
FixedPoint128.Q128
)
);
position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128;
position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128;
// subtraction is safe because we checked positionLiquidity is gte params.liquidity
position.liquidity = positionLiquidity - params.liquidity;
emit DecreaseLiquidity(params.tokenId, params.liquidity, amount0, amount1);
}
/// @inheritdoc INonfungiblePositionManager
function collect(CollectParams calldata params)
external
payable
override
isAuthorizedForToken(params.tokenId)
returns (uint256 amount0, uint256 amount1)
{
require(params.amount0Max > 0 || params.amount1Max > 0);
// allow collecting to the nft position manager address with address 0
address recipient = params.recipient == address(0) ? address(this) : params.recipient;
Position storage position = _positions[params.tokenId];
PoolAddress.PoolKey memory poolKey = _poolIdToPoolKey[position.poolId];
IUniswapV3Pool pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, poolKey));
(uint128 tokensOwed0, uint128 tokensOwed1) = (position.tokensOwed0, position.tokensOwed1);
// trigger an update of the position fees owed and fee growth snapshots if it has any liquidity
if (position.liquidity > 0) {
pool.burn(position.tickLower, position.tickUpper, 0);
(, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, , ) =
pool.positions(PositionKey.compute(address(this), position.tickLower, position.tickUpper));
tokensOwed0 += uint128(
FullMath.mulDiv(
feeGrowthInside0LastX128 - position.feeGrowthInside0LastX128,
position.liquidity,
FixedPoint128.Q128
)
);
tokensOwed1 += uint128(
FullMath.mulDiv(
feeGrowthInside1LastX128 - position.feeGrowthInside1LastX128,
position.liquidity,
FixedPoint128.Q128
)
);
position.feeGrowthInside0LastX128 = feeGrowthInside0LastX128;
position.feeGrowthInside1LastX128 = feeGrowthInside1LastX128;
}
// compute the arguments to give to the pool#collect method
(uint128 amount0Collect, uint128 amount1Collect) =
(
params.amount0Max > tokensOwed0 ? tokensOwed0 : params.amount0Max,
params.amount1Max > tokensOwed1 ? tokensOwed1 : params.amount1Max
);
// the actual amounts collected are returned
(amount0, amount1) = pool.collect(
recipient,
position.tickLower,
position.tickUpper,
amount0Collect,
amount1Collect
);
// sometimes there will be a few less wei than expected due to rounding down in core, but we just subtract the full amount expected
// instead of the actual amount so we can burn the token
(position.tokensOwed0, position.tokensOwed1) = (tokensOwed0 - amount0Collect, tokensOwed1 - amount1Collect);
emit Collect(params.tokenId, recipient, amount0Collect, amount1Collect);
}
/// @inheritdoc INonfungiblePositionManager
function burn(uint256 tokenId) external payable override isAuthorizedForToken(tokenId) {
Position storage position = _positions[tokenId];
require(position.liquidity == 0 && position.tokensOwed0 == 0 && position.tokensOwed1 == 0, 'Not cleared');
delete _positions[tokenId];
_burn(tokenId);
}
function _getAndIncrementNonce(uint256 tokenId) internal override returns (uint256) {
return uint256(_positions[tokenId].nonce++);
}
/// @inheritdoc IERC721
function getApproved(uint256 tokenId) public view override(ERC721, IERC721) returns (address) {
require(_exists(tokenId), 'ERC721: approved query for nonexistent token');
return _positions[tokenId].operator;
}
/// @dev Overrides _approve to use the operator in the position, which is packed with the position permit nonce
function _approve(address to, uint256 tokenId) internal override(ERC721) {
_positions[tokenId].operator = to;
emit Approval(ownerOf(tokenId), to, tokenId);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { AddressUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import { SignedSafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import { FullMath } from "@uniswap/v3-core/contracts/libraries/FullMath.sol";
import { BlockContext } from "./base/BlockContext.sol";
import { ClearingHouseCallee } from "./base/ClearingHouseCallee.sol";
import { IAccountBalance } from "./interface/IAccountBalance.sol";
import { IBaseToken } from "./interface/IBaseToken.sol";
import { IClearingHouseConfig } from "./interface/IClearingHouseConfig.sol";
import { IExchange } from "./interface/IExchange.sol";
import { IIndexPrice } from "./interface/IIndexPrice.sol";
import { IOrderBook } from "./interface/IOrderBook.sol";
import { PerpSafeCast } from "./lib/PerpSafeCast.sol";
import { PerpMath } from "./lib/PerpMath.sol";
import { AccountBalanceStorageV1, AccountMarket } from "./storage/AccountBalanceStorage.sol";
// never inherit any new stateful contract. never change the orders of parent stateful contracts
contract AccountBalance is IAccountBalance, BlockContext, ClearingHouseCallee, AccountBalanceStorageV1 {
using AddressUpgradeable for address;
using SafeMathUpgradeable for uint256;
using SignedSafeMathUpgradeable for int256;
using PerpSafeCast for uint256;
using PerpSafeCast for int256;
using PerpMath for uint256;
using PerpMath for int256;
using PerpMath for uint160;
using AccountMarket for AccountMarket.Info;
//
// CONSTANT
//
uint256 internal constant _DUST = 10 wei;
uint256 internal constant _MIN_PARTIAL_LIQUIDATE_POSITION_VALUE = 100e18 wei; // 100 USD in decimal 18
//
// EXTERNAL NON-VIEW
//
function initialize(address clearingHouseConfigArg, address orderBookArg) external initializer {
// IClearingHouseConfig address is not contract
require(clearingHouseConfigArg.isContract(), "AB_CHCNC");
// IOrderBook is not contract
require(orderBookArg.isContract(), "AB_OBNC");
__ClearingHouseCallee_init();
_clearingHouseConfig = clearingHouseConfigArg;
_orderBook = orderBookArg;
}
function setVault(address vaultArg) external onlyOwner {
// vault address is not contract
require(vaultArg.isContract(), "AB_VNC");
_vault = vaultArg;
emit VaultChanged(vaultArg);
}
/// @inheritdoc IAccountBalance
function modifyTakerBalance(
address trader,
address baseToken,
int256 base,
int256 quote
) external override returns (int256, int256) {
_requireOnlyClearingHouse();
return _modifyTakerBalance(trader, baseToken, base, quote);
}
/// @inheritdoc IAccountBalance
function modifyOwedRealizedPnl(address trader, int256 amount) external override {
_requireOnlyClearingHouse();
_modifyOwedRealizedPnl(trader, amount);
}
/// @inheritdoc IAccountBalance
function settleQuoteToOwedRealizedPnl(
address trader,
address baseToken,
int256 amount
) external override {
_requireOnlyClearingHouse();
_settleQuoteToOwedRealizedPnl(trader, baseToken, amount);
}
/// @inheritdoc IAccountBalance
function settleOwedRealizedPnl(address trader) external override returns (int256) {
// only vault
require(_msgSender() == _vault, "AB_OV");
int256 owedRealizedPnl = _owedRealizedPnlMap[trader];
_owedRealizedPnlMap[trader] = 0;
return owedRealizedPnl;
}
/// @inheritdoc IAccountBalance
function settleBalanceAndDeregister(
address trader,
address baseToken,
int256 takerBase,
int256 takerQuote,
int256 realizedPnl,
int256 makerFee
) external override {
_requireOnlyClearingHouse();
_modifyTakerBalance(trader, baseToken, takerBase, takerQuote);
_modifyOwedRealizedPnl(trader, makerFee);
// @audit should merge _addOwedRealizedPnl and settleQuoteToOwedRealizedPnl in some way.
// PnlRealized will be emitted three times when removing trader's liquidity
_settleQuoteToOwedRealizedPnl(trader, baseToken, realizedPnl);
_deregisterBaseToken(trader, baseToken);
}
/// @inheritdoc IAccountBalance
function registerBaseToken(address trader, address baseToken) external override {
_requireOnlyClearingHouse();
address[] storage tokensStorage = _baseTokensMap[trader];
if (_hasBaseToken(tokensStorage, baseToken)) {
return;
}
tokensStorage.push(baseToken);
// AB_MNE: markets number exceeds
require(tokensStorage.length <= IClearingHouseConfig(_clearingHouseConfig).getMaxMarketsPerAccount(), "AB_MNE");
}
/// @inheritdoc IAccountBalance
function deregisterBaseToken(address trader, address baseToken) external override {
_requireOnlyClearingHouse();
_deregisterBaseToken(trader, baseToken);
}
/// @inheritdoc IAccountBalance
function updateTwPremiumGrowthGlobal(
address trader,
address baseToken,
int256 lastTwPremiumGrowthGlobalX96
) external override {
_requireOnlyClearingHouse();
_accountMarketMap[trader][baseToken].lastTwPremiumGrowthGlobalX96 = lastTwPremiumGrowthGlobalX96;
}
/// @inheritdoc IAccountBalance
/// @dev we don't do swap to get position notional here.
/// we define the position notional in a closed market is `closed price * position size`
function settlePositionInClosedMarket(address trader, address baseToken)
external
override
returns (
int256 positionNotional,
int256 openNotional,
int256 realizedPnl,
uint256 closedPrice
)
{
_requireOnlyClearingHouse();
int256 positionSize = getTakerPositionSize(trader, baseToken);
closedPrice = IBaseToken(baseToken).getClosedPrice();
positionNotional = positionSize.mulDiv(closedPrice.toInt256(), 1e18);
openNotional = _accountMarketMap[trader][baseToken].takerOpenNotional;
realizedPnl = positionNotional.add(openNotional);
_deleteBaseToken(trader, baseToken);
_modifyOwedRealizedPnl(trader, realizedPnl);
return (positionNotional, openNotional, realizedPnl, closedPrice);
}
//
// EXTERNAL VIEW
//
/// @inheritdoc IAccountBalance
function getClearingHouseConfig() external view override returns (address) {
return _clearingHouseConfig;
}
/// @inheritdoc IAccountBalance
function getOrderBook() external view override returns (address) {
return _orderBook;
}
/// @inheritdoc IAccountBalance
function getVault() external view override returns (address) {
return _vault;
}
/// @inheritdoc IAccountBalance
function getBaseTokens(address trader) external view override returns (address[] memory) {
return _baseTokensMap[trader];
}
/// @inheritdoc IAccountBalance
function getAccountInfo(address trader, address baseToken)
external
view
override
returns (AccountMarket.Info memory)
{
return _accountMarketMap[trader][baseToken];
}
// @inheritdoc IAccountBalance
function getTakerOpenNotional(address trader, address baseToken) external view override returns (int256) {
return _accountMarketMap[trader][baseToken].takerOpenNotional;
}
// @inheritdoc IAccountBalance
function getTotalOpenNotional(address trader, address baseToken) external view override returns (int256) {
// quote.pool[baseToken] + quoteBalance[baseToken]
(uint256 quoteInPool, ) =
IOrderBook(_orderBook).getTotalTokenAmountInPoolAndPendingFee(trader, baseToken, false);
int256 quoteBalance = getQuote(trader, baseToken);
return quoteInPool.toInt256().add(quoteBalance);
}
/// @inheritdoc IAccountBalance
function getTotalDebtValue(address trader) external view override returns (uint256) {
int256 totalQuoteBalance;
int256 totalBaseDebtValue;
uint256 tokenLen = _baseTokensMap[trader].length;
for (uint256 i = 0; i < tokenLen; i++) {
address baseToken = _baseTokensMap[trader][i];
int256 baseBalance = getBase(trader, baseToken);
int256 baseDebtValue;
// baseDebt = baseBalance when it's negative
if (baseBalance < 0) {
// baseDebtValue = baseDebt * indexPrice
baseDebtValue = baseBalance.mulDiv(_getReferencePrice(baseToken).toInt256(), 1e18);
}
totalBaseDebtValue = totalBaseDebtValue.add(baseDebtValue);
// we can't calculate totalQuoteDebtValue until we have totalQuoteBalance
totalQuoteBalance = totalQuoteBalance.add(getQuote(trader, baseToken));
}
int256 totalQuoteDebtValue = totalQuoteBalance >= 0 ? 0 : totalQuoteBalance;
// both values are negative due to the above condition checks
return totalQuoteDebtValue.add(totalBaseDebtValue).abs();
}
/// @inheritdoc IAccountBalance
function getPnlAndPendingFee(address trader)
external
view
override
returns (
int256,
int256,
uint256
)
{
int256 totalPositionValue;
uint256 tokenLen = _baseTokensMap[trader].length;
for (uint256 i = 0; i < tokenLen; i++) {
address baseToken = _baseTokensMap[trader][i];
totalPositionValue = totalPositionValue.add(getTotalPositionValue(trader, baseToken));
}
(int256 netQuoteBalance, uint256 pendingFee) = _getNetQuoteBalanceAndPendingFee(trader);
int256 unrealizedPnl = totalPositionValue.add(netQuoteBalance);
return (_owedRealizedPnlMap[trader], unrealizedPnl, pendingFee);
}
/// @inheritdoc IAccountBalance
function hasOrder(address trader) external view override returns (bool) {
uint256 tokenLen = _baseTokensMap[trader].length;
address[] memory tokens = new address[](tokenLen);
uint256 skipped = 0;
for (uint256 i = 0; i < tokenLen; i++) {
address baseToken = _baseTokensMap[trader][i];
if (!IBaseToken(baseToken).isOpen()) {
skipped++;
continue;
}
tokens[i - skipped] = baseToken;
}
return IOrderBook(_orderBook).hasOrder(trader, tokens);
}
/// @inheritdoc IAccountBalance
function getLiquidatablePositionSize(
address trader,
address baseToken,
int256 accountValue
) external view override returns (int256) {
int256 marginRequirement = getMarginRequirementForLiquidation(trader);
int256 positionSize = getTotalPositionSize(trader, baseToken);
// No liquidatable position
if (accountValue >= marginRequirement || positionSize == 0) {
return 0;
}
// Liquidate the entire position if its value is small enough
// to prevent tiny positions left in the system
uint256 positionValueAbs = _getPositionValue(baseToken, positionSize).abs();
if (positionValueAbs <= _MIN_PARTIAL_LIQUIDATE_POSITION_VALUE) {
return positionSize;
}
// https://www.notion.so/perp/Backstop-LP-Spec-614b42798d4943768c2837bfe659524d#968996cadaec4c00ac60bd1da02ea8bb
// Liquidator can only take over partial position if margin ratio is ≥ 3.125% (aka the half of mmRatio).
// If margin ratio < 3.125%, liquidator can take over the entire position.
//
// threshold = mmRatio / 2 = 3.125%
// if marginRatio >= threshold, then
// maxLiquidateRatio = MIN(1, 0.5 * totalAbsPositionValue / absPositionValue)
// if marginRatio < threshold, then
// maxLiquidateRatio = 1
uint24 maxLiquidateRatio = 1e6; // 100%
if (accountValue >= marginRequirement.div(2)) {
// maxLiquidateRatio = getTotalAbsPositionValue / ( getTotalPositionValueInMarket.abs * 2 )
maxLiquidateRatio = FullMath
.mulDiv(getTotalAbsPositionValue(trader), 1e6, positionValueAbs.mul(2))
.toUint24();
if (maxLiquidateRatio > 1e6) {
maxLiquidateRatio = 1e6;
}
}
return positionSize.mulRatio(maxLiquidateRatio);
}
/// @inheritdoc IAccountBalance
function getMarkPrice(address baseToken) external view override returns (uint256) {
return _getMarkPrice(baseToken);
}
//
// PUBLIC VIEW
//
/// @inheritdoc IAccountBalance
function getBase(address trader, address baseToken) public view override returns (int256) {
uint256 orderDebt = IOrderBook(_orderBook).getTotalOrderDebt(trader, baseToken, true);
// base = takerPositionSize - orderBaseDebt
return _accountMarketMap[trader][baseToken].takerPositionSize.sub(orderDebt.toInt256());
}
/// @inheritdoc IAccountBalance
function getQuote(address trader, address baseToken) public view override returns (int256) {
uint256 orderDebt = IOrderBook(_orderBook).getTotalOrderDebt(trader, baseToken, false);
// quote = takerOpenNotional - orderQuoteDebt
return _accountMarketMap[trader][baseToken].takerOpenNotional.sub(orderDebt.toInt256());
}
/// @inheritdoc IAccountBalance
function getTakerPositionSize(address trader, address baseToken) public view override returns (int256) {
int256 positionSize = _accountMarketMap[trader][baseToken].takerPositionSize;
return positionSize.abs() < _DUST ? 0 : positionSize;
}
/// @inheritdoc IAccountBalance
function getTotalPositionSize(address trader, address baseToken) public view override returns (int256) {
// NOTE: when a token goes into UniswapV3 pool (addLiquidity or swap), there would be 1 wei rounding error
// for instance, maker adds liquidity with 2 base (2000000000000000000),
// the actual base amount in pool would be 1999999999999999999
// makerBalance = totalTokenAmountInPool - totalOrderDebt
(uint256 totalBaseBalanceFromOrders, ) =
IOrderBook(_orderBook).getTotalTokenAmountInPoolAndPendingFee(trader, baseToken, true);
uint256 totalBaseDebtFromOrder = IOrderBook(_orderBook).getTotalOrderDebt(trader, baseToken, true);
int256 makerBaseBalance = totalBaseBalanceFromOrders.toInt256().sub(totalBaseDebtFromOrder.toInt256());
int256 takerPositionSize = _accountMarketMap[trader][baseToken].takerPositionSize;
int256 totalPositionSize = makerBaseBalance.add(takerPositionSize);
return totalPositionSize.abs() < _DUST ? 0 : totalPositionSize;
}
/// @inheritdoc IAccountBalance
function getTotalPositionValue(address trader, address baseToken) public view override returns (int256) {
int256 positionSize = getTotalPositionSize(trader, baseToken);
return _getPositionValue(baseToken, positionSize);
}
/// @inheritdoc IAccountBalance
function getTotalAbsPositionValue(address trader) public view override returns (uint256) {
address[] memory tokens = _baseTokensMap[trader];
uint256 totalPositionValue;
uint256 tokenLen = tokens.length;
for (uint256 i = 0; i < tokenLen; i++) {
address baseToken = tokens[i];
// will not use negative value in this case
uint256 positionValue = getTotalPositionValue(trader, baseToken).abs();
totalPositionValue = totalPositionValue.add(positionValue);
}
return totalPositionValue;
}
/// @inheritdoc IAccountBalance
function getMarginRequirementForLiquidation(address trader) public view override returns (int256) {
return
getTotalAbsPositionValue(trader)
.mulRatio(IClearingHouseConfig(_clearingHouseConfig).getMmRatio())
.toInt256();
}
//
// INTERNAL NON-VIEW
//
function _modifyTakerBalance(
address trader,
address baseToken,
int256 base,
int256 quote
) internal returns (int256, int256) {
AccountMarket.Info storage accountInfo = _accountMarketMap[trader][baseToken];
accountInfo.takerPositionSize = accountInfo.takerPositionSize.add(base);
accountInfo.takerOpenNotional = accountInfo.takerOpenNotional.add(quote);
return (accountInfo.takerPositionSize, accountInfo.takerOpenNotional);
}
function _modifyOwedRealizedPnl(address trader, int256 amount) internal {
if (amount != 0) {
_owedRealizedPnlMap[trader] = _owedRealizedPnlMap[trader].add(amount);
emit PnlRealized(trader, amount);
}
}
function _settleQuoteToOwedRealizedPnl(
address trader,
address baseToken,
int256 amount
) internal {
if (amount != 0) {
AccountMarket.Info storage accountInfo = _accountMarketMap[trader][baseToken];
accountInfo.takerOpenNotional = accountInfo.takerOpenNotional.sub(amount);
_modifyOwedRealizedPnl(trader, amount);
}
}
/// @dev this function is expensive
function _deregisterBaseToken(address trader, address baseToken) internal {
AccountMarket.Info memory info = _accountMarketMap[trader][baseToken];
if (info.takerPositionSize.abs() >= _DUST || info.takerOpenNotional.abs() >= _DUST) {
return;
}
if (IOrderBook(_orderBook).getOpenOrderIds(trader, baseToken).length > 0) {
return;
}
_deleteBaseToken(trader, baseToken);
}
function _deleteBaseToken(address trader, address baseToken) internal {
delete _accountMarketMap[trader][baseToken];
address[] storage tokensStorage = _baseTokensMap[trader];
uint256 tokenLen = tokensStorage.length;
for (uint256 i; i < tokenLen; i++) {
if (tokensStorage[i] == baseToken) {
// if the target to be removed is the last one, pop it directly;
// else, replace it with the last one and pop the last one instead
if (i != tokenLen - 1) {
tokensStorage[i] = tokensStorage[tokenLen - 1];
}
tokensStorage.pop();
break;
}
}
}
//
// INTERNAL VIEW
//
function _getPositionValue(address baseToken, int256 positionSize) internal view returns (int256) {
if (positionSize == 0) return 0;
uint256 price = _getReferencePrice(baseToken);
// both positionSize & price are in 10^18 already
// overflow inspection:
// only overflow when position value in USD(18 decimals) > 2^255 / 10^18
return positionSize.mulDiv(price.toInt256(), 1e18);
}
function _getReferencePrice(address baseToken) internal view returns (uint256) {
if (IBaseToken(baseToken).isOpen()) {
return _getMarkPrice(baseToken);
}
return
IBaseToken(baseToken).isClosed()
? IBaseToken(baseToken).getClosedPrice()
: IBaseToken(baseToken).getPausedIndexPrice();
}
/// @return netQuoteBalance = quote.balance + totalQuoteInPools
function _getNetQuoteBalanceAndPendingFee(address trader)
internal
view
returns (int256 netQuoteBalance, uint256 pendingFee)
{
int256 totalTakerQuoteBalance;
uint256 tokenLen = _baseTokensMap[trader].length;
for (uint256 i = 0; i < tokenLen; i++) {
address baseToken = _baseTokensMap[trader][i];
totalTakerQuoteBalance = totalTakerQuoteBalance.add(_accountMarketMap[trader][baseToken].takerOpenNotional);
}
// pendingFee is included
int256 totalMakerQuoteBalance;
(totalMakerQuoteBalance, pendingFee) = IOrderBook(_orderBook).getTotalQuoteBalanceAndPendingFee(
trader,
_baseTokensMap[trader]
);
netQuoteBalance = totalTakerQuoteBalance.add(totalMakerQuoteBalance);
return (netQuoteBalance, pendingFee);
}
function _getMarkPrice(address baseToken) internal view virtual returns (uint256) {
IClearingHouseConfig clearingHouseConfig = IClearingHouseConfig(_clearingHouseConfig);
(uint32 marketTwapInterval, uint32 premiumInterval) = clearingHouseConfig.getMarkPriceConfig();
// previously, marketPrice is _getMarketPrice(baseToken, 0) which is relatively easy to be manipulated,
// so we change to 15-second twap
uint256 marketPrice = _getMarketPrice(baseToken, 15);
uint256 marketTwap = _getMarketPrice(baseToken, marketTwapInterval);
int256 premium =
_getMarketPrice(baseToken, premiumInterval).toInt256().sub(
_getIndexPrice(baseToken, premiumInterval).toInt256()
);
uint256 indexWithPremium = _getIndexPrice(baseToken, 0).toInt256().add(premium).toUint256();
return PerpMath.findMedianOfThree(marketPrice, marketTwap, indexWithPremium);
}
function _getMarketPrice(address baseToken, uint32 twapInterval) internal view returns (uint256) {
return
IExchange(IOrderBook(_orderBook).getExchange())
.getSqrtMarketTwapX96(baseToken, twapInterval)
.formatSqrtPriceX96ToPriceX96()
.formatX96ToX10_18();
}
function _getIndexPrice(address baseToken, uint32 twapInterval) internal view returns (uint256) {
return IIndexPrice(baseToken).getIndexPrice(twapInterval);
}
//
// INTERNAL PURE
//
function _hasBaseToken(address[] memory baseTokens, address baseToken) internal pure returns (bool) {
for (uint256 i = 0; i < baseTokens.length; i++) {
if (baseTokens[i] == baseToken) {
return true;
}
}
return false;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
abstract contract BlockContext {
function _blockTimestamp() internal view virtual returns (uint256) {
// Reply from Arbitrum
// block.timestamp returns timestamp at the time at which the sequencer receives the tx.
// It may not actually correspond to a particular L1 block
return block.timestamp;
}
function _blockNumber() internal view virtual returns (uint256) {
return block.number;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { SafeOwnable } from "./SafeOwnable.sol";
abstract contract ClearingHouseCallee is SafeOwnable {
//
// STATE
//
address internal _clearingHouse;
// __gap is reserved storage
uint256[50] private __gap;
//
// EVENT
//
event ClearingHouseChanged(address indexed clearingHouse);
//
// CONSTRUCTOR
//
// solhint-disable-next-line func-order
function __ClearingHouseCallee_init() internal initializer {
__SafeOwnable_init();
}
function setClearingHouse(address clearingHouseArg) external onlyOwner {
_clearingHouse = clearingHouseArg;
emit ClearingHouseChanged(clearingHouseArg);
}
function getClearingHouse() external view returns (address) {
return _clearingHouse;
}
function _requireOnlyClearingHouse() internal view {
// only ClearingHouse
require(_msgSender() == _clearingHouse, "CHD_OCH");
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import { SafeOwnable } from "./SafeOwnable.sol";
abstract contract OwnerPausable is SafeOwnable, PausableUpgradeable {
// __gap is reserved storage
uint256[50] private __gap;
// solhint-disable-next-line func-order
function __OwnerPausable_init() internal initializer {
__SafeOwnable_init();
__Pausable_init();
}
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
function _msgSender() internal view virtual override returns (address payable) {
return super._msgSender();
}
function _msgData() internal view virtual override returns (bytes memory) {
return super._msgData();
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { ContextUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
abstract contract SafeOwnable is ContextUpgradeable {
address private _owner;
address private _candidate;
// __gap is reserved storage
uint256[50] private __gap;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
// caller not owner
require(owner() == _msgSender(), "SO_CNO");
_;
}
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __SafeOwnable_init() internal initializer {
__Context_init();
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() external virtual onlyOwner {
// emitting event first to avoid caching values
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
_candidate = address(0);
}
/**
* @dev Set ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function setOwner(address newOwner) external onlyOwner {
// newOwner is 0
require(newOwner != address(0), "SO_NW0");
// same as original
require(newOwner != _owner, "SO_SAO");
// same as candidate
require(newOwner != _candidate, "SO_SAC");
_candidate = newOwner;
}
/**
* @dev Transfers ownership of the contract to a new account (`_candidate`).
* Can only be called by the new owner.
*/
function updateOwner() external {
// candidate is zero
require(_candidate != address(0), "SO_C0");
// caller is not candidate
require(_candidate == _msgSender(), "SO_CNC");
// emitting event first to avoid caching values
emit OwnershipTransferred(_owner, _candidate);
_owner = _candidate;
_candidate = address(0);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Returns the candidate that can become the owner.
*/
function candidate() external view returns (address) {
return _candidate;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { ContextUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import { IMarketRegistry } from "../interface/IMarketRegistry.sol";
abstract contract UniswapV3CallbackBridge is ContextUpgradeable {
//
// STATE
//
address internal _marketRegistry;
// __gap is reserved storage
uint256[50] private __gap;
//
// MODIFIER
//
modifier checkCallback() {
address pool = _msgSender();
address baseToken = IUniswapV3Pool(pool).token0();
// UCB_FCV: failed callback validation
require(pool == IMarketRegistry(_marketRegistry).getPool(baseToken), "UCB_FCV");
_;
}
//
// CONSTRUCTOR
//
// solhint-disable-next-line func-order
function __UniswapV3CallbackBridge_init(address marketRegistryArg) internal initializer {
__Context_init();
_marketRegistry = marketRegistryArg;
}
function getMarketRegistry() external view returns (address) {
return _marketRegistry;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import { IPriceFeedDispatcher } from "@perp/perp-oracle-contract/contracts/interface/IPriceFeedDispatcher.sol";
import { IIndexPrice } from "./interface/IIndexPrice.sol";
import { VirtualToken } from "./VirtualToken.sol";
import { BaseTokenStorageV2 } from "./storage/BaseTokenStorage.sol";
import { IBaseToken } from "./interface/IBaseToken.sol";
import { BlockContext } from "./base/BlockContext.sol";
// never inherit any new stateful contract. never change the orders of parent stateful contracts
contract BaseToken is IBaseToken, IIndexPrice, VirtualToken, BlockContext, BaseTokenStorageV2 {
using SafeMathUpgradeable for uint256;
using SafeMathUpgradeable for uint8;
//
// CONSTANT
//
uint256 internal constant _TWAP_INTERVAL_FOR_PAUSE = 15 * 60; // 15 minutes
uint256 internal constant _MAX_WAITING_PERIOD = 5 days;
//
// EXTERNAL NON-VIEW
//
function initialize(
string memory nameArg,
string memory symbolArg,
address priceFeedArg
) external initializer {
__VirtualToken_init(nameArg, symbolArg);
uint8 priceFeedDecimals = IPriceFeedDispatcher(priceFeedArg).decimals();
// invalid price feed decimals
require(priceFeedDecimals <= decimals(), "BT_IPFD");
_priceFeed = priceFeedArg;
_priceFeedDecimals = priceFeedDecimals;
}
function pause() external onlyOwner {
// BT_NO: Not open
require(_status == IBaseToken.Status.Open, "BT_NO");
_pausedIndexPrice = getIndexPrice(_TWAP_INTERVAL_FOR_PAUSE);
_status = IBaseToken.Status.Paused;
_pausedTimestamp = _blockTimestamp();
emit StatusUpdated(_status);
}
function close(uint256 closedPrice) external onlyOwner {
// BT_NP: Not paused
require(_status == IBaseToken.Status.Paused, "BT_NP");
_close(closedPrice);
}
function close() external override {
// BT_NP: Not paused
require(_status == IBaseToken.Status.Paused, "BT_NP");
// BT_WPNE: Waiting period not expired
require(_blockTimestamp() > _pausedTimestamp + _MAX_WAITING_PERIOD, "BT_WPNE");
_close(_pausedIndexPrice);
}
/// @dev priceFeed is now priceFeedDispatcher, which dispatches either Chainlink or UniswapV3 price
function setPriceFeed(address priceFeedArg) external onlyOwner {
// For all USD pairs, ChainlinkPriceFeed uses 8 decimals
uint8 priceFeedDecimals = IPriceFeedDispatcher(priceFeedArg).decimals();
// BT_IPFD: Invalid price feed decimals
require(priceFeedDecimals <= decimals(), "BT_IPFD");
_priceFeed = priceFeedArg;
_priceFeedDecimals = priceFeedDecimals;
emit PriceFeedChanged(_priceFeed);
}
function cacheTwap(uint256 interval) external override {
IPriceFeedDispatcher(_priceFeed).dispatchPrice(interval);
}
//
// EXTERNAL VIEW
//
/// @inheritdoc IBaseToken
function getPriceFeed() external view override returns (address) {
return _priceFeed;
}
function isOpen() external view override returns (bool) {
return _status == IBaseToken.Status.Open;
}
function isPaused() external view override returns (bool) {
return _status == IBaseToken.Status.Paused;
}
function isClosed() external view override returns (bool) {
return _status == IBaseToken.Status.Closed;
}
function getPausedTimestamp() external view override returns (uint256) {
return _pausedTimestamp;
}
function getPausedIndexPrice() external view override returns (uint256) {
return _pausedIndexPrice;
}
/// @inheritdoc IBaseToken
function getClosedPrice() external view override returns (uint256) {
// not closed
require(_status == IBaseToken.Status.Closed, "BT_NC");
return _closedPrice;
}
/// @inheritdoc IIndexPrice
/// @dev we overwrite the index price in BaseToken depending on the status
/// 1. Open: the price is from the price feed
/// 2. Paused or Closed: the price is twap when the token was paused
function getIndexPrice(uint256 interval) public view override returns (uint256) {
if (_status == IBaseToken.Status.Open) {
return _formatDecimals(IPriceFeedDispatcher(_priceFeed).getDispatchedPrice(interval));
}
return _pausedIndexPrice;
}
//
// INTERNAL
//
function _close(uint256 closedPrice) internal {
_status = IBaseToken.Status.Closed;
_closedPrice = closedPrice;
emit StatusUpdated(_status);
}
function _formatDecimals(uint256 _price) internal view returns (uint256) {
if (_priceFeedDecimals == decimals()) {
return _price;
}
return _price.mul(10**(decimals().sub(_priceFeedDecimals)));
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { AddressUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import { SignedSafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import { IUniswapV3MintCallback } from "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3MintCallback.sol";
import { IUniswapV3SwapCallback } from "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol";
import { FullMath } from "@uniswap/v3-core/contracts/libraries/FullMath.sol";
import { PerpSafeCast } from "./lib/PerpSafeCast.sol";
import { PerpMath } from "./lib/PerpMath.sol";
import { SettlementTokenMath } from "./lib/SettlementTokenMath.sol";
import { Funding } from "./lib/Funding.sol";
import { AccountMarket } from "./lib/AccountMarket.sol";
import { OpenOrder } from "./lib/OpenOrder.sol";
import { OwnerPausable } from "./base/OwnerPausable.sol";
import { BlockContext } from "./base/BlockContext.sol";
import { IERC20Metadata } from "./interface/IERC20Metadata.sol";
import { IVault } from "./interface/IVault.sol";
import { IExchange } from "./interface/IExchange.sol";
import { IOrderBook } from "./interface/IOrderBook.sol";
import { IBaseToken } from "./interface/IBaseToken.sol";
import { IClearingHouseConfig } from "./interface/IClearingHouseConfig.sol";
import { IAccountBalance } from "./interface/IAccountBalance.sol";
import { IInsuranceFund } from "./interface/IInsuranceFund.sol";
import { IDelegateApproval } from "./interface/IDelegateApproval.sol";
import { IClearingHouse } from "./interface/IClearingHouse.sol";
import { BaseRelayRecipient } from "./gsn/BaseRelayRecipient.sol";
import { ClearingHouseStorageV2 } from "./storage/ClearingHouseStorage.sol";
// never inherit any new stateful contract. never change the orders of parent stateful contracts
contract ClearingHouse is
IUniswapV3MintCallback,
IUniswapV3SwapCallback,
IClearingHouse,
BlockContext,
ReentrancyGuardUpgradeable,
OwnerPausable,
BaseRelayRecipient,
ClearingHouseStorageV2
{
using AddressUpgradeable for address;
using SafeMathUpgradeable for uint256;
using SignedSafeMathUpgradeable for int256;
using PerpSafeCast for uint256;
using PerpSafeCast for uint128;
using PerpSafeCast for int256;
using PerpMath for uint256;
using PerpMath for uint160;
using PerpMath for uint128;
using PerpMath for int256;
using SettlementTokenMath for int256;
//
// STRUCT
//
/// @param sqrtPriceLimitX96 tx will fill until it reaches this price but WON'T REVERT
struct InternalOpenPositionParams {
address trader;
address baseToken;
bool isBaseToQuote;
bool isExactInput;
bool isClose;
uint256 amount;
uint160 sqrtPriceLimitX96;
}
struct InternalCheckSlippageParams {
bool isBaseToQuote;
bool isExactInput;
uint256 base;
uint256 quote;
uint256 oppositeAmountBound;
}
//
// MODIFIER
//
modifier checkDeadline(uint256 deadline) {
// transaction expires
require(_blockTimestamp() <= deadline, "CH_TE");
_;
}
//
// EXTERNAL NON-VIEW
//
/// @dev this function is public for testing
// solhint-disable-next-line func-order
function initialize(
address clearingHouseConfigArg,
address vaultArg,
address quoteTokenArg,
address uniV3FactoryArg,
address exchangeArg,
address accountBalanceArg,
address insuranceFundArg
) public initializer {
// CH_VANC: Vault address is not contract
_isContract(vaultArg, "CH_VANC");
// CH_QANC: QuoteToken address is not contract
_isContract(quoteTokenArg, "CH_QANC");
// CH_QDN18: QuoteToken decimals is not 18
require(IERC20Metadata(quoteTokenArg).decimals() == 18, "CH_QDN18");
// CH_UANC: UniV3Factory address is not contract
_isContract(uniV3FactoryArg, "CH_UANC");
// ClearingHouseConfig address is not contract
_isContract(clearingHouseConfigArg, "CH_CCNC");
// AccountBalance is not contract
_isContract(accountBalanceArg, "CH_ABNC");
// CH_ENC: Exchange is not contract
_isContract(exchangeArg, "CH_ENC");
// CH_IFANC: InsuranceFund address is not contract
_isContract(insuranceFundArg, "CH_IFANC");
address orderBookArg = IExchange(exchangeArg).getOrderBook();
// orderBook is not contract
_isContract(orderBookArg, "CH_OBNC");
__ReentrancyGuard_init();
__OwnerPausable_init();
_clearingHouseConfig = clearingHouseConfigArg;
_vault = vaultArg;
_quoteToken = quoteTokenArg;
_uniswapV3Factory = uniV3FactoryArg;
_exchange = exchangeArg;
_orderBook = orderBookArg;
_accountBalance = accountBalanceArg;
_insuranceFund = insuranceFundArg;
_settlementTokenDecimals = IVault(_vault).decimals();
}
/// @dev remove to reduce bytecode size, might add back when we need it
// // solhint-disable-next-line func-order
// function setTrustedForwarder(address trustedForwarderArg) external onlyOwner {
// // CH_TFNC: TrustedForwarder is not contract
// require(trustedForwarderArg.isContract(), "CH_TFNC");
// // TrustedForwarderUpdated event is emitted in BaseRelayRecipient
// _setTrustedForwarder(trustedForwarderArg);
// }
function setDelegateApproval(address delegateApprovalArg) external onlyOwner {
// CH_DANC: DelegateApproval is not contract
require(delegateApprovalArg.isContract(), "CH_DANC");
_delegateApproval = delegateApprovalArg;
emit DelegateApprovalChanged(delegateApprovalArg);
}
/// @inheritdoc IClearingHouse
function addLiquidity(AddLiquidityParams calldata params)
external
override
whenNotPaused
nonReentrant
checkDeadline(params.deadline)
returns (AddLiquidityResponse memory)
{
// input requirement checks:
// baseToken: in Exchange.settleFunding()
// base & quote: in LiquidityAmounts.getLiquidityForAmounts() -> FullMath.mulDiv()
// lowerTick & upperTick: in UniswapV3Pool._modifyPosition()
// minBase, minQuote & deadline: here
_checkMarketOpen(params.baseToken);
// This condition is to prevent the intentional bad debt attack through price manipulation.
// CH_OMPS: Over the maximum price spread
require(!IExchange(_exchange).isOverPriceSpread(params.baseToken), "CH_OMPS");
// CH_DUTB: Disable useTakerBalance
require(!params.useTakerBalance, "CH_DUTB");
address trader = _msgSender();
// register token if it's the first time
_registerBaseToken(trader, params.baseToken);
// must settle funding first
Funding.Growth memory fundingGrowthGlobal = _settleFunding(trader, params.baseToken);
// note that we no longer check available tokens here because CH will always auto-mint in UniswapV3MintCallback
IOrderBook.AddLiquidityResponse memory response =
IOrderBook(_orderBook).addLiquidity(
IOrderBook.AddLiquidityParams({
trader: trader,
baseToken: params.baseToken,
base: params.base,
quote: params.quote,
lowerTick: params.lowerTick,
upperTick: params.upperTick,
fundingGrowthGlobal: fundingGrowthGlobal
})
);
_checkSlippageAfterLiquidityChange(response.base, params.minBase, response.quote, params.minQuote);
// fees always have to be collected to owedRealizedPnl, as long as there is a change in liquidity
_modifyOwedRealizedPnl(trader, response.fee.toInt256());
// after token balances are updated, we can check if there is enough free collateral
_requireEnoughFreeCollateral(trader);
_emitLiquidityChanged(
trader,
params.baseToken,
_quoteToken,
params.lowerTick,
params.upperTick,
response.base.toInt256(),
response.quote.toInt256(),
response.liquidity.toInt128(),
response.fee
);
return
AddLiquidityResponse({
base: response.base,
quote: response.quote,
fee: response.fee,
liquidity: response.liquidity
});
}
/// @inheritdoc IClearingHouse
function removeLiquidity(RemoveLiquidityParams calldata params)
external
override
whenNotPaused
nonReentrant
checkDeadline(params.deadline)
returns (RemoveLiquidityResponse memory)
{
// input requirement checks:
// baseToken: in Exchange.settleFunding()
// lowerTick & upperTick: in UniswapV3Pool._modifyPosition()
// liquidity: in LiquidityMath.addDelta()
// minBase, minQuote & deadline: here
// CH_MP: Market paused
require(!IBaseToken(params.baseToken).isPaused(), "CH_MP");
address trader = _msgSender();
// must settle funding first
_settleFunding(trader, params.baseToken);
IOrderBook.RemoveLiquidityResponse memory response =
_removeLiquidity(
IOrderBook.RemoveLiquidityParams({
maker: trader,
baseToken: params.baseToken,
lowerTick: params.lowerTick,
upperTick: params.upperTick,
liquidity: params.liquidity
})
);
_checkSlippageAfterLiquidityChange(response.base, params.minBase, response.quote, params.minQuote);
_modifyPositionAndRealizePnl(
trader,
params.baseToken,
response.takerBase, // exchangedPositionSize
response.takerQuote, // exchangedPositionNotional
response.fee, // makerFee
0 //takerFee
);
_emitLiquidityChanged(
trader,
params.baseToken,
_quoteToken,
params.lowerTick,
params.upperTick,
response.base.neg256(),
response.quote.neg256(),
params.liquidity.neg128(),
response.fee
);
return RemoveLiquidityResponse({ quote: response.quote, base: response.base, fee: response.fee });
}
/// @inheritdoc IClearingHouse
function settleAllFunding(address trader) external override {
// only vault or trader
// vault must check msg.sender == trader when calling settleAllFunding
require(_msgSender() == _vault || _msgSender() == trader, "CH_OVOT");
address[] memory baseTokens = IAccountBalance(_accountBalance).getBaseTokens(trader);
uint256 baseTokenLength = baseTokens.length;
for (uint256 i = 0; i < baseTokenLength; i++) {
_settleFunding(trader, baseTokens[i]);
}
}
/// @inheritdoc IClearingHouse
function openPosition(OpenPositionParams memory params)
external
override
whenNotPaused
nonReentrant
checkDeadline(params.deadline)
returns (uint256 base, uint256 quote)
{
// openPosition() is already published, returned types remain the same (without fee)
(base, quote, ) = _openPositionFor(_msgSender(), params);
return (base, quote);
}
/// @inheritdoc IClearingHouse
function openPositionFor(address trader, OpenPositionParams memory params)
external
override
whenNotPaused
nonReentrant
checkDeadline(params.deadline)
returns (
uint256 base,
uint256 quote,
uint256 fee
)
{
// CH_SHNAOPT: Sender Has No Approval to Open Position for Trader
require(IDelegateApproval(_delegateApproval).canOpenPositionFor(trader, _msgSender()), "CH_SHNAOPT");
return _openPositionFor(trader, params);
}
/// @inheritdoc IClearingHouse
function closePosition(ClosePositionParams calldata params)
external
override
whenNotPaused
nonReentrant
checkDeadline(params.deadline)
returns (uint256 base, uint256 quote)
{
// input requirement checks:
// baseToken: in Exchange.settleFunding()
// sqrtPriceLimitX96: X (this is not for slippage protection)
// oppositeAmountBound: in _checkSlippage()
// deadline: here
// referralCode: X
_checkMarketOpen(params.baseToken);
address trader = _msgSender();
// must settle funding first
_settleFunding(trader, params.baseToken);
int256 positionSize = _getTakerPositionSafe(trader, params.baseToken);
uint256 positionSizeAbs = positionSize.abs();
// old position is long. when closing, it's baseToQuote && exactInput (sell exact base)
// old position is short. when closing, it's quoteToBase && exactOutput (buy exact base back)
bool isBaseToQuote = positionSize > 0;
IExchange.SwapResponse memory response =
_openPosition(
InternalOpenPositionParams({
trader: trader,
baseToken: params.baseToken,
isBaseToQuote: isBaseToQuote,
isExactInput: isBaseToQuote,
isClose: true,
amount: positionSizeAbs,
sqrtPriceLimitX96: params.sqrtPriceLimitX96
})
);
_checkSlippage(
InternalCheckSlippageParams({
isBaseToQuote: isBaseToQuote,
isExactInput: isBaseToQuote,
base: response.base,
quote: response.quote,
oppositeAmountBound: response.isPartialClose
? params.oppositeAmountBound.mulRatio(response.closedRatio)
: params.oppositeAmountBound
})
);
_referredPositionChanged(params.referralCode);
return (response.base, response.quote);
}
/// @inheritdoc IClearingHouse
function liquidate(
address trader,
address baseToken,
int256 positionSize
) external override whenNotPaused nonReentrant {
_liquidate(trader, baseToken, positionSize);
}
/// @inheritdoc IClearingHouse
function liquidate(address trader, address baseToken) external override whenNotPaused nonReentrant {
// positionSizeToBeLiquidated = 0 means liquidating as much as possible
_liquidate(trader, baseToken, 0);
}
/// @inheritdoc IClearingHouse
function cancelExcessOrders(
address maker,
address baseToken,
bytes32[] calldata orderIds
) external override whenNotPaused nonReentrant {
// input requirement checks:
// maker: in _cancelExcessOrders()
// baseToken: in Exchange.settleFunding()
// orderIds: in OrderBook.removeLiquidityByIds()
_cancelExcessOrders(maker, baseToken, orderIds);
}
/// @inheritdoc IClearingHouse
function cancelAllExcessOrders(address maker, address baseToken) external override whenNotPaused nonReentrant {
// input requirement checks:
// maker: in _cancelExcessOrders()
// baseToken: in Exchange.settleFunding()
// orderIds: in OrderBook.removeLiquidityByIds()
_cancelExcessOrders(maker, baseToken, _getOpenOrderIds(maker, baseToken));
}
/// @inheritdoc IClearingHouse
function quitMarket(address trader, address baseToken)
external
override
nonReentrant
returns (uint256 base, uint256 quote)
{
// CH_MNC: Market not closed
require(IBaseToken(baseToken).isClosed(), "CH_MNC");
_settleFunding(trader, baseToken);
bytes32[] memory orderIds = _getOpenOrderIds(trader, baseToken);
// remove all orders in internal function
if (orderIds.length > 0) {
_removeAllLiquidity(trader, baseToken, orderIds);
}
int256 positionSize = _getTakerPosition(trader, baseToken);
// if position is 0, no need to do settlement accounting
if (positionSize == 0) {
return (0, 0);
}
(int256 positionNotional, int256 openNotional, int256 realizedPnl, uint256 closedPrice) =
IAccountBalance(_accountBalance).settlePositionInClosedMarket(trader, baseToken);
emit PositionClosed(trader, baseToken, positionSize, positionNotional, openNotional, realizedPnl, closedPrice);
_settleBadDebt(trader);
return (positionSize.abs(), positionNotional.abs());
}
/// @inheritdoc IUniswapV3MintCallback
/// @dev namings here follow Uniswap's convention
function uniswapV3MintCallback(
uint256 amount0Owed,
uint256 amount1Owed,
bytes calldata data
) external override {
// input requirement checks:
// amount0Owed: here
// amount1Owed: here
// data: X
// For caller validation purposes it would be more efficient and more reliable to use
// "msg.sender" instead of "_msgSender()" as contracts never call each other through GSN.
// not orderbook
require(msg.sender == _orderBook, "CH_NOB");
IOrderBook.MintCallbackData memory callbackData = abi.decode(data, (IOrderBook.MintCallbackData));
if (amount0Owed > 0) {
address token = IUniswapV3Pool(callbackData.pool).token0();
_requireTransfer(token, callbackData.pool, amount0Owed);
}
if (amount1Owed > 0) {
address token = IUniswapV3Pool(callbackData.pool).token1();
_requireTransfer(token, callbackData.pool, amount1Owed);
}
}
/// @inheritdoc IUniswapV3SwapCallback
/// @dev namings here follow Uniswap's convention
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external override {
// input requirement checks:
// amount0Delta: here
// amount1Delta: here
// data: X
// For caller validation purposes it would be more efficient and more reliable to use
// "msg.sender" instead of "_msgSender()" as contracts never call each other through GSN.
require(msg.sender == _exchange, "CH_OE");
// swaps entirely within 0-liquidity regions are not supported -> 0 swap is forbidden
// CH_F0S: forbidden 0 swap
require((amount0Delta > 0 && amount1Delta < 0) || (amount0Delta < 0 && amount1Delta > 0), "CH_F0S");
IExchange.SwapCallbackData memory callbackData = abi.decode(data, (IExchange.SwapCallbackData));
IUniswapV3Pool uniswapV3Pool = IUniswapV3Pool(callbackData.pool);
// amount0Delta & amount1Delta are guaranteed to be positive when being the amount to be paid
(address token, uint256 amountToPay) =
amount0Delta > 0
? (uniswapV3Pool.token0(), uint256(amount0Delta))
: (uniswapV3Pool.token1(), uint256(amount1Delta));
// swap
_requireTransfer(token, callbackData.pool, amountToPay);
}
//
// EXTERNAL VIEW
//
/// @inheritdoc IClearingHouse
function getQuoteToken() external view override returns (address) {
return _quoteToken;
}
/// @inheritdoc IClearingHouse
function getUniswapV3Factory() external view override returns (address) {
return _uniswapV3Factory;
}
/// @inheritdoc IClearingHouse
function getClearingHouseConfig() external view override returns (address) {
return _clearingHouseConfig;
}
/// @inheritdoc IClearingHouse
function getVault() external view override returns (address) {
return _vault;
}
/// @inheritdoc IClearingHouse
function getExchange() external view override returns (address) {
return _exchange;
}
/// @inheritdoc IClearingHouse
function getOrderBook() external view override returns (address) {
return _orderBook;
}
/// @inheritdoc IClearingHouse
function getAccountBalance() external view override returns (address) {
return _accountBalance;
}
/// @inheritdoc IClearingHouse
function getInsuranceFund() external view override returns (address) {
return _insuranceFund;
}
/// @inheritdoc IClearingHouse
function getDelegateApproval() external view override returns (address) {
return _delegateApproval;
}
/// @inheritdoc IClearingHouse
function getAccountValue(address trader) public view override returns (int256) {
return IVault(_vault).getAccountValue(trader).parseSettlementToken(_settlementTokenDecimals);
}
//
// INTERNAL NON-VIEW
//
function _requireTransfer(
address token,
address to,
uint256 amount
) internal {
// CH_TF: Transfer failed
require(IERC20Metadata(token).transfer(to, amount), "CH_TF");
}
function _liquidate(
address trader,
address baseToken,
int256 positionSizeToBeLiquidated
) internal {
_checkMarketOpen(baseToken);
// CH_CLWTISO: cannot liquidate when there is still order
require(!IAccountBalance(_accountBalance).hasOrder(trader), "CH_CLWTISO");
// CH_EAV: enough account value
require(_isLiquidatable(trader), "CH_EAV");
int256 positionSize = _getTakerPositionSafe(trader, baseToken);
// CH_WLD: wrong liquidation direction
require(positionSize.mul(positionSizeToBeLiquidated) >= 0, "CH_WLD");
address liquidator = _msgSender();
_registerBaseToken(liquidator, baseToken);
// must settle funding first
_settleFunding(trader, baseToken);
_settleFunding(liquidator, baseToken);
int256 accountValue = getAccountValue(trader);
// trader's position is closed at index price and pnl realized
(int256 liquidatedPositionSize, int256 liquidatedPositionNotional) =
_getLiquidatedPositionSizeAndNotional(trader, baseToken, accountValue, positionSizeToBeLiquidated);
_modifyPositionAndRealizePnl(trader, baseToken, liquidatedPositionSize, liquidatedPositionNotional, 0, 0);
// trader pays liquidation penalty
uint256 liquidationPenalty = liquidatedPositionNotional.abs().mulRatio(_getLiquidationPenaltyRatio());
_modifyOwedRealizedPnl(trader, liquidationPenalty.neg256());
address insuranceFund = _insuranceFund;
// if there is bad debt, liquidation fees all go to liquidator; otherwise, split between liquidator & IF
uint256 liquidationFeeToLiquidator = liquidationPenalty.div(2);
uint256 liquidationFeeToIF;
if (accountValue < 0) {
liquidationFeeToLiquidator = liquidationPenalty;
} else {
liquidationFeeToIF = liquidationPenalty.sub(liquidationFeeToLiquidator);
_modifyOwedRealizedPnl(insuranceFund, liquidationFeeToIF.toInt256());
}
// assume there is no longer any unsettled bad debt in the system
// (so that true IF capacity = accountValue(IF) + USDC.balanceOf(IF))
// if trader's account value becomes negative, the amount is the bad debt IF must have enough capacity to cover
{
int256 accountValueAfterLiquidationX10_18 = getAccountValue(trader);
if (accountValueAfterLiquidationX10_18 < 0) {
int256 insuranceFundCapacityX10_18 =
IInsuranceFund(insuranceFund).getInsuranceFundCapacity().parseSettlementToken(
_settlementTokenDecimals
);
// CH_IIC: insufficient insuranceFund capacity
require(insuranceFundCapacityX10_18 >= accountValueAfterLiquidationX10_18.neg256(), "CH_IIC");
}
}
// liquidator opens a position with liquidationFeeToLiquidator as a discount
// liquidator's openNotional = -liquidatedPositionNotional + liquidationFeeToLiquidator
int256 liquidatorExchangedPositionSize = liquidatedPositionSize.neg256();
int256 liquidatorExchangedPositionNotional =
liquidatedPositionNotional.neg256().add(liquidationFeeToLiquidator.toInt256());
// note that this function will realize pnl if it's reducing liquidator's existing position size
_modifyPositionAndRealizePnl(
liquidator,
baseToken,
liquidatorExchangedPositionSize, // exchangedPositionSize
liquidatorExchangedPositionNotional, // exchangedPositionNotional
0, // makerFee
0 // takerFee
);
_requireEnoughFreeCollateral(liquidator);
emit PositionLiquidated(
trader,
baseToken,
liquidatedPositionNotional.abs(), // positionNotional
liquidatedPositionSize.abs(), // positionSize
liquidationPenalty,
liquidator
);
_settleBadDebt(trader);
}
/// @dev Calculate how much profit/loss we should realize,
/// The profit/loss is calculated by exchangedPositionSize/exchangedPositionNotional amount
/// and existing taker's base/quote amount.
function _modifyPositionAndRealizePnl(
address trader,
address baseToken,
int256 exchangedPositionSize,
int256 exchangedPositionNotional,
uint256 makerFee,
uint256 takerFee
) internal {
int256 realizedPnl;
if (exchangedPositionSize != 0) {
realizedPnl = IExchange(_exchange).getPnlToBeRealized(
IExchange.RealizePnlParams({
trader: trader,
baseToken: baseToken,
base: exchangedPositionSize,
quote: exchangedPositionNotional
})
);
}
// realizedPnl is realized here
// will deregister baseToken if there is no position
_settleBalanceAndDeregister(
trader,
baseToken,
exchangedPositionSize, // takerBase
exchangedPositionNotional, // takerQuote
realizedPnl,
makerFee.toInt256()
);
_emitPositionChanged(
trader,
baseToken,
exchangedPositionSize,
exchangedPositionNotional,
takerFee, // fee
_getTakerOpenNotional(trader, baseToken), // openNotional
realizedPnl,
_getSqrtMarketTwapX96(baseToken) // sqrtPriceAfterX96: no swap, so market price didn't change
);
}
/// @dev only cancel open orders if there are not enough free collateral with mmRatio
/// or account is able to being liquidated.
function _cancelExcessOrders(
address maker,
address baseToken,
bytes32[] memory orderIds
) internal {
_checkMarketOpen(baseToken);
if (orderIds.length == 0) {
return;
}
// CH_NEXO: not excess orders
require(
(_getFreeCollateralByRatio(maker, IClearingHouseConfig(_clearingHouseConfig).getMmRatio()) < 0) ||
_isLiquidatable(maker),
"CH_NEXO"
);
// must settle funding first
_settleFunding(maker, baseToken);
// remove all orders in internal function
_removeAllLiquidity(maker, baseToken, orderIds);
}
function _removeAllLiquidity(
address maker,
address baseToken,
bytes32[] memory orderIds
) internal {
IOrderBook.RemoveLiquidityResponse memory removeLiquidityResponse;
uint256 length = orderIds.length;
for (uint256 i = 0; i < length; i++) {
OpenOrder.Info memory order = IOrderBook(_orderBook).getOpenOrderById(orderIds[i]);
// CH_ONBM: order is not belongs to this maker
require(
OpenOrder.calcOrderKey(maker, baseToken, order.lowerTick, order.upperTick) == orderIds[i],
"CH_ONBM"
);
IOrderBook.RemoveLiquidityResponse memory response =
_removeLiquidity(
IOrderBook.RemoveLiquidityParams({
maker: maker,
baseToken: baseToken,
lowerTick: order.lowerTick,
upperTick: order.upperTick,
liquidity: order.liquidity
})
);
removeLiquidityResponse.base = removeLiquidityResponse.base.add(response.base);
removeLiquidityResponse.quote = removeLiquidityResponse.quote.add(response.quote);
removeLiquidityResponse.fee = removeLiquidityResponse.fee.add(response.fee);
removeLiquidityResponse.takerBase = removeLiquidityResponse.takerBase.add(response.takerBase);
removeLiquidityResponse.takerQuote = removeLiquidityResponse.takerQuote.add(response.takerQuote);
_emitLiquidityChanged(
maker,
baseToken,
_quoteToken,
order.lowerTick,
order.upperTick,
response.base.neg256(),
response.quote.neg256(),
order.liquidity.neg128(),
response.fee
);
}
_modifyPositionAndRealizePnl(
maker,
baseToken,
removeLiquidityResponse.takerBase,
removeLiquidityResponse.takerQuote,
removeLiquidityResponse.fee,
0
);
}
/// @dev explainer diagram for the relationship between exchangedPositionNotional, fee and openNotional:
/// https://www.figma.com/file/xuue5qGH4RalX7uAbbzgP3/swap-accounting-and-events
function _openPosition(InternalOpenPositionParams memory params) internal returns (IExchange.SwapResponse memory) {
int256 takerPositionSizeBeforeSwap =
IAccountBalance(_accountBalance).getTakerPositionSize(params.trader, params.baseToken);
IExchange.SwapResponse memory response =
IExchange(_exchange).swap(
IExchange.SwapParams({
trader: params.trader,
baseToken: params.baseToken,
isBaseToQuote: params.isBaseToQuote,
isExactInput: params.isExactInput,
isClose: params.isClose,
amount: params.amount,
sqrtPriceLimitX96: params.sqrtPriceLimitX96
})
);
_modifyOwedRealizedPnl(_insuranceFund, response.insuranceFundFee.toInt256());
// examples:
// https://www.figma.com/file/xuue5qGH4RalX7uAbbzgP3/swap-accounting-and-events?node-id=0%3A1
_settleBalanceAndDeregister(
params.trader,
params.baseToken,
response.exchangedPositionSize,
response.exchangedPositionNotional.sub(response.fee.toInt256()),
response.pnlToBeRealized,
0
);
if (takerPositionSizeBeforeSwap != 0) {
int256 takerPositionSizeAfterSwap =
IAccountBalance(_accountBalance).getTakerPositionSize(params.trader, params.baseToken);
bool hasBecameInversePosition =
_isReversingPosition(takerPositionSizeBeforeSwap, takerPositionSizeAfterSwap);
bool isReducingPosition = takerPositionSizeBeforeSwap < 0 != params.isBaseToQuote;
if (isReducingPosition && !hasBecameInversePosition) {
// check margin free collateral by mmRatio after swap (reducing and closing position)
// trader cannot reduce/close position if the free collateral by mmRatio is not enough
// for preventing bad debt and not enough liquidation penalty fee
// only liquidator can take over this position
// CH_NEFCM: not enough free collateral by mmRatio
require(
(_getFreeCollateralByRatio(
params.trader,
IClearingHouseConfig(_clearingHouseConfig).getMmRatio()
) >= 0),
"CH_NEFCM"
);
} else {
// check margin free collateral by imRatio after swap (increasing and reversing position)
_requireEnoughFreeCollateral(params.trader);
}
} else {
// check margin free collateral by imRatio after swap (opening a position)
_requireEnoughFreeCollateral(params.trader);
}
// openNotional will be zero if baseToken is deregistered from trader's token list.
int256 openNotional = _getTakerOpenNotional(params.trader, params.baseToken);
_emitPositionChanged(
params.trader,
params.baseToken,
response.exchangedPositionSize,
response.exchangedPositionNotional,
response.fee,
openNotional,
response.pnlToBeRealized, // realizedPnl
response.sqrtPriceAfterX96
);
return response;
}
function _openPositionFor(address trader, OpenPositionParams memory params)
internal
returns (
uint256 base,
uint256 quote,
uint256 fee
)
{
// input requirement checks:
// baseToken: in Exchange.settleFunding()
// isBaseToQuote & isExactInput: X
// amount: in UniswapV3Pool.swap()
// oppositeAmountBound: in _checkSlippage()
// deadline: here
// sqrtPriceLimitX96: X (this is not for slippage protection)
// referralCode: X
_checkMarketOpen(params.baseToken);
// register token if it's the first time
_registerBaseToken(trader, params.baseToken);
// must settle funding first
_settleFunding(trader, params.baseToken);
IExchange.SwapResponse memory response =
_openPosition(
InternalOpenPositionParams({
trader: trader,
baseToken: params.baseToken,
isBaseToQuote: params.isBaseToQuote,
isExactInput: params.isExactInput,
amount: params.amount,
isClose: false,
sqrtPriceLimitX96: params.sqrtPriceLimitX96
})
);
_checkSlippage(
InternalCheckSlippageParams({
isBaseToQuote: params.isBaseToQuote,
isExactInput: params.isExactInput,
base: response.base,
quote: response.quote,
oppositeAmountBound: params.oppositeAmountBound
})
);
_referredPositionChanged(params.referralCode);
return (response.base, response.quote, response.fee);
}
/// @dev Remove maker's liquidity.
function _removeLiquidity(IOrderBook.RemoveLiquidityParams memory params)
internal
returns (IOrderBook.RemoveLiquidityResponse memory)
{
return IOrderBook(_orderBook).removeLiquidity(params);
}
/// @dev Settle trader's funding payment to his/her realized pnl.
function _settleFunding(address trader, address baseToken)
internal
returns (Funding.Growth memory fundingGrowthGlobal)
{
int256 fundingPayment;
(fundingPayment, fundingGrowthGlobal) = IExchange(_exchange).settleFunding(trader, baseToken);
if (fundingPayment != 0) {
_modifyOwedRealizedPnl(trader, fundingPayment.neg256());
emit FundingPaymentSettled(trader, baseToken, fundingPayment);
}
IAccountBalance(_accountBalance).updateTwPremiumGrowthGlobal(
trader,
baseToken,
fundingGrowthGlobal.twPremiumX96
);
return fundingGrowthGlobal;
}
function _registerBaseToken(address trader, address baseToken) internal {
IAccountBalance(_accountBalance).registerBaseToken(trader, baseToken);
}
function _modifyOwedRealizedPnl(address trader, int256 amount) internal {
IAccountBalance(_accountBalance).modifyOwedRealizedPnl(trader, amount);
}
function _settleBalanceAndDeregister(
address trader,
address baseToken,
int256 takerBase,
int256 takerQuote,
int256 realizedPnl,
int256 makerFee
) internal {
IAccountBalance(_accountBalance).settleBalanceAndDeregister(
trader,
baseToken,
takerBase,
takerQuote,
realizedPnl,
makerFee
);
}
function _emitPositionChanged(
address trader,
address baseToken,
int256 exchangedPositionSize,
int256 exchangedPositionNotional,
uint256 fee,
int256 openNotional,
int256 realizedPnl,
uint256 sqrtPriceAfterX96
) internal {
emit PositionChanged(
trader,
baseToken,
exchangedPositionSize,
exchangedPositionNotional,
fee,
openNotional,
realizedPnl,
sqrtPriceAfterX96
);
}
function _emitLiquidityChanged(
address maker,
address baseToken,
address quoteToken,
int24 lowerTick,
int24 upperTick,
int256 base,
int256 quote,
int128 liquidity,
uint256 quoteFee
) internal {
emit LiquidityChanged(maker, baseToken, quoteToken, lowerTick, upperTick, base, quote, liquidity, quoteFee);
}
function _referredPositionChanged(bytes32 referralCode) internal {
if (referralCode != 0) {
emit ReferredPositionChanged(referralCode);
}
}
function _settleBadDebt(address trader) internal {
IVault(_vault).settleBadDebt(trader);
}
//
// INTERNAL VIEW
//
/// @inheritdoc BaseRelayRecipient
function _msgSender() internal view override(BaseRelayRecipient, OwnerPausable) returns (address payable) {
return super._msgSender();
}
/// @inheritdoc BaseRelayRecipient
function _msgData() internal view override(BaseRelayRecipient, OwnerPausable) returns (bytes memory) {
return super._msgData();
}
function _getTakerOpenNotional(address trader, address baseToken) internal view returns (int256) {
return IAccountBalance(_accountBalance).getTakerOpenNotional(trader, baseToken);
}
function _getTakerPositionSafe(address trader, address baseToken) internal view returns (int256) {
int256 takerPositionSize = _getTakerPosition(trader, baseToken);
// CH_PSZ: position size is zero
require(takerPositionSize != 0, "CH_PSZ");
return takerPositionSize;
}
function _getTakerPosition(address trader, address baseToken) internal view returns (int256) {
return IAccountBalance(_accountBalance).getTakerPositionSize(trader, baseToken);
}
function _getFreeCollateralByRatio(address trader, uint24 ratio) internal view returns (int256) {
return IVault(_vault).getFreeCollateralByRatio(trader, ratio);
}
function _getSqrtMarketTwapX96(address baseToken) internal view returns (uint160) {
return IExchange(_exchange).getSqrtMarketTwapX96(baseToken, 0);
}
function _getMarginRequirementForLiquidation(address trader) internal view returns (int256) {
return IAccountBalance(_accountBalance).getMarginRequirementForLiquidation(trader);
}
function _getLiquidationPenaltyRatio() internal view returns (uint24) {
return IClearingHouseConfig(_clearingHouseConfig).getLiquidationPenaltyRatio();
}
function _getTotalAbsPositionValue(address trader) internal view returns (uint256) {
return IAccountBalance(_accountBalance).getTotalAbsPositionValue(trader);
}
function _getOpenOrderIds(address maker, address baseToken) internal view returns (bytes32[] memory) {
return IOrderBook(_orderBook).getOpenOrderIds(maker, baseToken);
}
/// @dev liquidation condition:
/// accountValue < sum(abs(positionValue_by_market)) * mmRatio = totalMinimumMarginRequirement
function _isLiquidatable(address trader) internal view returns (bool) {
return getAccountValue(trader) < _getMarginRequirementForLiquidation(trader);
}
/// @param positionSizeToBeLiquidated its direction should be the same as taker's existing position
function _getLiquidatedPositionSizeAndNotional(
address trader,
address baseToken,
int256 accountValue,
int256 positionSizeToBeLiquidated
) internal view returns (int256, int256) {
int256 maxLiquidatablePositionSize =
IAccountBalance(_accountBalance).getLiquidatablePositionSize(trader, baseToken, accountValue);
if (positionSizeToBeLiquidated.abs() > maxLiquidatablePositionSize.abs() || positionSizeToBeLiquidated == 0) {
positionSizeToBeLiquidated = maxLiquidatablePositionSize;
}
int256 liquidatedPositionSize = positionSizeToBeLiquidated.neg256();
int256 liquidatedPositionNotional =
positionSizeToBeLiquidated.mulDiv(
IAccountBalance(_accountBalance).getMarkPrice(baseToken).toInt256(),
1e18
);
return (liquidatedPositionSize, liquidatedPositionNotional);
}
function _requireEnoughFreeCollateral(address trader) internal view {
// CH_NEFCI: not enough free collateral by imRatio
require(
_getFreeCollateralByRatio(trader, IClearingHouseConfig(_clearingHouseConfig).getImRatio()) >= 0,
"CH_NEFCI"
);
}
function _checkMarketOpen(address baseToken) internal view {
// CH_MNO: Market not opened
require(IBaseToken(baseToken).isOpen(), "CH_MNO");
}
function _isContract(address contractArg, string memory errorMsg) internal view {
require(contractArg.isContract(), errorMsg);
}
//
// INTERNAL PURE
//
function _checkSlippage(InternalCheckSlippageParams memory params) internal pure {
// skip when params.oppositeAmountBound is zero
if (params.oppositeAmountBound == 0) {
return;
}
// B2Q + exact input, want more output quote as possible, so we set a lower bound of output quote
// B2Q + exact output, want less input base as possible, so we set a upper bound of input base
// Q2B + exact input, want more output base as possible, so we set a lower bound of output base
// Q2B + exact output, want less input quote as possible, so we set a upper bound of input quote
if (params.isBaseToQuote) {
if (params.isExactInput) {
// too little received when short
require(params.quote >= params.oppositeAmountBound, "CH_TLRS");
} else {
// too much requested when short
require(params.base <= params.oppositeAmountBound, "CH_TMRS");
}
} else {
if (params.isExactInput) {
// too little received when long
require(params.base >= params.oppositeAmountBound, "CH_TLRL");
} else {
// too much requested when long
require(params.quote <= params.oppositeAmountBound, "CH_TMRL");
}
}
}
function _checkSlippageAfterLiquidityChange(
uint256 base,
uint256 minBase,
uint256 quote,
uint256 minQuote
) internal pure {
// CH_PSCF: price slippage check fails
require(base >= minBase && quote >= minQuote, "CH_PSCF");
}
function _isReversingPosition(int256 sizeBefore, int256 sizeAfter) internal pure returns (bool) {
return !(sizeAfter == 0 || sizeBefore == 0) && sizeBefore ^ sizeAfter < 0;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { SafeOwnable } from "./base/SafeOwnable.sol";
import { ClearingHouseConfigStorageV3 } from "./storage/ClearingHouseConfigStorage.sol";
import { IClearingHouseConfig } from "./interface/IClearingHouseConfig.sol";
// never inherit any new stateful contract. never change the orders of parent stateful contracts
contract ClearingHouseConfig is IClearingHouseConfig, SafeOwnable, ClearingHouseConfigStorageV3 {
//
// MODIFIER
//
modifier checkRatio(uint24 ratio) {
// CHC_RO: ratio overflow
require(ratio <= 1e6, "CHC_RO");
_;
}
//
// EXTERNAL NON-VIEW
//
function initialize() external initializer {
__SafeOwnable_init();
_maxMarketsPerAccount = type(uint8).max;
_imRatio = 0.1e6; // initial-margin ratio, 10% in decimal 6
_mmRatio = 0.0625e6; // minimum-margin ratio, 6.25% in decimal 6
_liquidationPenaltyRatio = 0.025e6; // initial penalty ratio, 2.5% in decimal 6
_maxFundingRate = 0.1e6; // max funding rate, 10% in decimal 6
_twapInterval = 15 minutes;
_settlementTokenBalanceCap = 0;
_markPriceMarketTwapInterval = 30 minutes;
_markPricePremiumInterval = 15 minutes;
}
function setLiquidationPenaltyRatio(uint24 liquidationPenaltyRatioArg)
external
checkRatio(liquidationPenaltyRatioArg)
onlyOwner
{
_liquidationPenaltyRatio = liquidationPenaltyRatioArg;
emit LiquidationPenaltyRatioChanged(liquidationPenaltyRatioArg);
}
function setTwapInterval(uint32 twapIntervalArg) external onlyOwner {
_twapInterval = twapIntervalArg;
emit TwapIntervalChanged(twapIntervalArg);
}
function setMaxMarketsPerAccount(uint8 maxMarketsPerAccountArg) external onlyOwner {
_maxMarketsPerAccount = maxMarketsPerAccountArg;
emit MaxMarketsPerAccountChanged(maxMarketsPerAccountArg);
}
function setSettlementTokenBalanceCap(uint256 cap) external onlyOwner {
_settlementTokenBalanceCap = cap;
emit SettlementTokenBalanceCapChanged(cap);
}
function setMaxFundingRate(uint24 rate) external onlyOwner {
_maxFundingRate = rate;
emit MaxFundingRateChanged(rate);
}
function setMarkPriceMarketTwapInterval(uint32 twapIntervalArg) external onlyOwner {
// CHC_IMPMTI: invalid mark price market twap interval
require(twapIntervalArg != 0, "CHC_IMPMTI");
_markPriceMarketTwapInterval = twapIntervalArg;
emit MarkPriceMarketTwapIntervalChanged(twapIntervalArg);
}
function setMarkPricePremiumInterval(uint32 premiumIntervalArg) external onlyOwner {
// CHC_IMPPI: invalid mark price premium interval
require(premiumIntervalArg != 0, "CHC_IMPPI");
_markPricePremiumInterval = premiumIntervalArg;
emit MarkPricePremiumIntervalChanged(premiumIntervalArg);
}
//
// EXTERNAL VIEW
//
/// @inheritdoc IClearingHouseConfig
function getMaxMarketsPerAccount() external view override returns (uint8) {
return _maxMarketsPerAccount;
}
/// @inheritdoc IClearingHouseConfig
function getImRatio() external view override returns (uint24) {
return _imRatio;
}
/// @inheritdoc IClearingHouseConfig
function getMmRatio() external view override returns (uint24) {
return _mmRatio;
}
/// @inheritdoc IClearingHouseConfig
function getLiquidationPenaltyRatio() external view override returns (uint24) {
return _liquidationPenaltyRatio;
}
/// @inheritdoc IClearingHouseConfig
function getPartialCloseRatio() external view override returns (uint24) {
return _partialCloseRatio;
}
/// @inheritdoc IClearingHouseConfig
function getTwapInterval() external view override returns (uint32) {
return _twapInterval;
}
/// @inheritdoc IClearingHouseConfig
function getSettlementTokenBalanceCap() external view override returns (uint256) {
return _settlementTokenBalanceCap;
}
/// @inheritdoc IClearingHouseConfig
function getMaxFundingRate() external view override returns (uint24) {
return _maxFundingRate;
}
/// @inheritdoc IClearingHouseConfig
function getMarkPriceConfig() external view override returns (uint32, uint32) {
return (_markPriceMarketTwapInterval, _markPricePremiumInterval);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { OwnerPausable } from "./base/OwnerPausable.sol";
import { CollateralManagerStorageV2 } from "./storage/CollateralManagerStorage.sol";
import { AddressUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import { IPriceFeed } from "@perp/perp-oracle-contract/contracts/interface/IPriceFeed.sol";
import { Collateral } from "./lib/Collateral.sol";
import { ICollateralManager } from "./interface/ICollateralManager.sol";
import { IClearingHouseConfig } from "./interface/IClearingHouseConfig.sol";
import { IVault } from "./interface/IVault.sol";
import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
contract CollateralManager is ICollateralManager, OwnerPausable, CollateralManagerStorageV2 {
using AddressUpgradeable for address;
using SafeMathUpgradeable for uint256;
uint24 private constant _ONE_HUNDRED_PERCENT_RATIO = 1e6;
//
// MODIFIER
//
modifier checkRatio(uint24 ratio) {
// CM_IR: invalid ratio, should be in [0, 1]
require(ratio <= _ONE_HUNDRED_PERCENT_RATIO, "CM_IR");
_;
}
//
// EXTERNAL NON-VIEW
//
function initialize(
address clearingHouseConfigArg,
address vaultArg,
uint8 maxCollateralTokensPerAccountArg,
uint24 debtNonSettlementTokenValueRatioArg,
uint24 liquidationRatioArg,
uint24 mmRatioBufferArg,
uint24 clInsuranceFundFeeRatioArg,
uint256 debtThresholdArg,
uint256 collateralValueDustArg
)
external
initializer
checkRatio(debtNonSettlementTokenValueRatioArg)
checkRatio(liquidationRatioArg)
checkRatio(clInsuranceFundFeeRatioArg)
{
// CM_CHCNC: clearing house config is not contract
require(clearingHouseConfigArg.isContract(), "CM_CHCNC");
// CM_VNC: vault is not contract
require(vaultArg.isContract(), "CM_VNC");
__OwnerPausable_init();
_clearingHouseConfig = clearingHouseConfigArg;
_vault = vaultArg;
_maxCollateralTokensPerAccount = maxCollateralTokensPerAccountArg;
_debtNonSettlementTokenValueRatio = debtNonSettlementTokenValueRatioArg;
_liquidationRatio = liquidationRatioArg;
requireValidCollateralMmRatio(mmRatioBufferArg);
_mmRatioBuffer = mmRatioBufferArg;
_clInsuranceFundFeeRatio = clInsuranceFundFeeRatioArg;
_debtThreshold = debtThresholdArg;
_collateralValueDust = collateralValueDustArg;
emit ClearingHouseConfigChanged(clearingHouseConfigArg);
emit VaultChanged(vaultArg);
emit MaxCollateralTokensPerAccountChanged(maxCollateralTokensPerAccountArg);
emit MmRatioBufferChanged(mmRatioBufferArg);
emit DebtNonSettlementTokenValueRatioChanged(debtNonSettlementTokenValueRatioArg);
emit LiquidationRatioChanged(liquidationRatioArg);
emit CLInsuranceFundFeeRatioChanged(clInsuranceFundFeeRatioArg);
emit DebtThresholdChanged(debtThresholdArg);
emit CollateralValueDustChanged(collateralValueDustArg);
}
function addCollateral(address token, Collateral.Config memory config)
external
checkRatio(config.collateralRatio)
checkRatio(config.discountRatio)
onlyOwner
{
// CM_CTE: collateral token already exists
require(!isCollateral(token), "CM_CTE");
// CM_CTNC: collateral token is not contract
require(token.isContract(), "CM_CTNC");
// CM_PFNC: price feed is not contract
require(config.priceFeed.isContract(), "CM_PFNC");
// CM_CIS: collateral token is settlement token
require(IVault(_vault).getSettlementToken() != token, "CM_CIS");
_collateralConfigMap[token] = config;
emit CollateralAdded(token, config.priceFeed, config.collateralRatio, config.discountRatio, config.depositCap);
}
function setPriceFeed(address token, address priceFeed) external onlyOwner {
_requireIsCollateral(token);
// CM_PFNC: price feed is not contract
require(priceFeed.isContract(), "CM_PFNC");
_collateralConfigMap[token].priceFeed = priceFeed;
emit PriceFeedChanged(token, priceFeed);
}
function setCollateralRatio(address token, uint24 collateralRatio) external checkRatio(collateralRatio) onlyOwner {
_requireIsCollateral(token);
_collateralConfigMap[token].collateralRatio = collateralRatio;
emit CollateralRatioChanged(token, collateralRatio);
}
function setDiscountRatio(address token, uint24 discountRatio) external checkRatio(discountRatio) onlyOwner {
_requireIsCollateral(token);
_collateralConfigMap[token].discountRatio = discountRatio;
emit DiscountRatioChanged(token, discountRatio);
}
function setDepositCap(address token, uint256 depositCap) external onlyOwner {
_requireIsCollateral(token);
_collateralConfigMap[token].depositCap = depositCap;
emit DepositCapChanged(token, depositCap);
}
function setMaxCollateralTokensPerAccount(uint8 maxCollateralTokensPerAccount) external onlyOwner {
_maxCollateralTokensPerAccount = maxCollateralTokensPerAccount;
emit MaxCollateralTokensPerAccountChanged(maxCollateralTokensPerAccount);
}
function setMmRatioBuffer(uint24 mmRatioBuffer) external onlyOwner {
requireValidCollateralMmRatio(mmRatioBuffer);
_mmRatioBuffer = mmRatioBuffer;
emit MmRatioBufferChanged(mmRatioBuffer);
}
function setDebtNonSettlementTokenValueRatio(uint24 debtNonSettlementTokenValueRatio)
external
checkRatio(debtNonSettlementTokenValueRatio)
onlyOwner
{
_debtNonSettlementTokenValueRatio = debtNonSettlementTokenValueRatio;
emit DebtNonSettlementTokenValueRatioChanged(debtNonSettlementTokenValueRatio);
}
function setLiquidationRatio(uint24 liquidationRatio) external checkRatio(liquidationRatio) onlyOwner {
_liquidationRatio = liquidationRatio;
emit LiquidationRatioChanged(liquidationRatio);
}
function setCLInsuranceFundFeeRatio(uint24 clInsuranceFundFeeRatio)
external
checkRatio(clInsuranceFundFeeRatio)
onlyOwner
{
_clInsuranceFundFeeRatio = clInsuranceFundFeeRatio;
emit CLInsuranceFundFeeRatioChanged(clInsuranceFundFeeRatio);
}
function setDebtThreshold(uint256 debtThreshold) external onlyOwner {
// CM_ZDT: zero debt threshold
require(debtThreshold != 0, "CM_ZDT");
_debtThreshold = debtThreshold;
emit DebtThresholdChanged(debtThreshold);
}
function setWhitelistedDebtThreshold(address trader, uint256 whitelistedDebtThreshold) external onlyOwner {
uint256 whitelistedDebtThresholdBefore = _whitelistedDebtThresholdMap[trader];
_whitelistedDebtThresholdMap[trader] = whitelistedDebtThreshold;
_totalWhitelistedDebtThreshold = whitelistedDebtThresholdBefore > whitelistedDebtThreshold
? _totalWhitelistedDebtThreshold.sub(whitelistedDebtThresholdBefore - whitelistedDebtThreshold)
: _totalWhitelistedDebtThreshold.add(whitelistedDebtThreshold - whitelistedDebtThresholdBefore);
emit WhitelistedDebtThresholdChanged(trader, whitelistedDebtThreshold);
}
/// @dev Same decimals as the settlement token
function setCollateralValueDust(uint256 collateralValueDust) external onlyOwner {
_collateralValueDust = collateralValueDust;
emit CollateralValueDustChanged(collateralValueDust);
}
//
// EXTERNAL VIEW
//
/// @inheritdoc ICollateralManager
function getClearingHouseConfig() external view override returns (address) {
return _clearingHouseConfig;
}
/// @inheritdoc ICollateralManager
function getVault() external view override returns (address) {
return _vault;
}
/// @inheritdoc ICollateralManager
function getCollateralConfig(address token) external view override returns (Collateral.Config memory) {
return _collateralConfigMap[token];
}
/// @inheritdoc ICollateralManager
function getPriceFeedDecimals(address token) external view override returns (uint8) {
_requireIsCollateral(token);
return IPriceFeed(_collateralConfigMap[token].priceFeed).decimals();
}
/// @inheritdoc ICollateralManager
function getPrice(address token, uint256 interval) external view override returns (uint256) {
_requireIsCollateral(token);
return IPriceFeed(_collateralConfigMap[token].priceFeed).getPrice(interval);
}
function getMaxCollateralTokensPerAccount() external view override returns (uint8) {
return _maxCollateralTokensPerAccount;
}
/// @inheritdoc ICollateralManager
function getMmRatioBuffer() external view override returns (uint24) {
return _mmRatioBuffer;
}
/// @inheritdoc ICollateralManager
function getDebtNonSettlementTokenValueRatio() external view override returns (uint24) {
return _debtNonSettlementTokenValueRatio;
}
/// @inheritdoc ICollateralManager
function getLiquidationRatio() external view override returns (uint24) {
return _liquidationRatio;
}
/// @inheritdoc ICollateralManager
function getCLInsuranceFundFeeRatio() external view override returns (uint24) {
return _clInsuranceFundFeeRatio;
}
/// @inheritdoc ICollateralManager
function getDebtThreshold() external view override returns (uint256) {
return _debtThreshold;
}
/// @inheritdoc ICollateralManager
function getDebtThresholdByTrader(address trader) external view override returns (uint256) {
return _whitelistedDebtThresholdMap[trader] == 0 ? _debtThreshold : _whitelistedDebtThresholdMap[trader];
}
/// @inheritdoc ICollateralManager
function getTotalWhitelistedDebtThreshold() external view override returns (uint256) {
return _totalWhitelistedDebtThreshold;
}
/// @inheritdoc ICollateralManager
function getCollateralValueDust() external view override returns (uint256) {
return _collateralValueDust;
}
//
// PUBLIC VIEW
//
/// @inheritdoc ICollateralManager
function isCollateral(address token) public view override returns (bool) {
return _collateralConfigMap[token].priceFeed != address(0);
}
/// @inheritdoc ICollateralManager
function requireValidCollateralMmRatio(uint24 mmRatioBuffer) public view override returns (uint24) {
uint24 collateralMmRatio = IClearingHouseConfig(_clearingHouseConfig).getMmRatio() + mmRatioBuffer;
// CM_ICMR : invalid collateralMmRatio
require(collateralMmRatio <= _ONE_HUNDRED_PERCENT_RATIO, "CM_ICMR");
return collateralMmRatio;
}
//
// INTERNAL VIEW
//
function _requireIsCollateral(address token) internal view {
// CM_TINAC: token is not a collateral
require(isCollateral(token), "CM_TINAC");
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { IDelegateApproval } from "./interface/IDelegateApproval.sol";
import { BlockContext } from "./base/BlockContext.sol";
import { SafeOwnable } from "./base/SafeOwnable.sol";
import { DelegateApprovalStorageV1 } from "./storage/DelegateApprovalStorage.sol";
contract DelegateApproval is IDelegateApproval, BlockContext, SafeOwnable, DelegateApprovalStorageV1 {
//
// CONSTANTS
//
/// @dev remember to update checkActions() if we add new actions
/// the rule for action constants is `<<`, 2^n, n starts from 0
/// so actions will be 1, 2, 4, 8, 16, 32, 64, 128
uint8 internal constant _CLEARINGHOUSE_OPENPOSITION = 1; // 00000001
uint8 internal constant _CLEARINGHOUSE_ADDLIQUIDITY = 2; // 00000010, not used for now
uint8 internal constant _CLEARINGHOUSE_REMOVELIQUIDITY = 4; // 00000100, not used for now
//
// MODIFIER
//
/// @dev prevent user from approving/revoking non-existed actions
/// we only have 3 actions now, so actions cannot be greater than 7 (00000111)
modifier checkActions(uint8 actions) {
// DA_IA: Invalid Actions
require(actions > 0 && actions <= 7, "DA_IA");
_;
}
//
// EXTERNAL NON-VIEW
//
function initialize() external initializer {
__SafeOwnable_init();
}
/// @inheritdoc IDelegateApproval
function approve(address delegate, uint8 actions) external override checkActions(actions) {
address trader = _msgSender();
bytes32 key = _getApprovalKey(trader, delegate);
// Examples:
// oldApprovedActions: 001
// actions: 010
// newApprovedActions: 011
// oldApprovedActions: 010
// actions: 110
// newApprovedActions: 110
// oldApprovedActions: 010
// actions: 100
// newApprovedActions: 110
_approvalMap[key] = _approvalMap[key] | actions;
emit DelegationApproved(trader, delegate, actions);
}
/// @inheritdoc IDelegateApproval
function revoke(address delegate, uint8 actions) external override checkActions(actions) {
address trader = _msgSender();
bytes32 key = _getApprovalKey(trader, delegate);
// oldApprovedActions: 010
// actions: 010
// newApprovedActions: 000
// oldApprovedActions: 010
// actions: 110
// newApprovedActions: 000
// oldApprovedActions: 010
// actions: 100
// newApprovedActions: 010
_approvalMap[key] = _approvalMap[key] & (~actions);
emit DelegationRevoked(trader, delegate, actions);
}
//
// EXTERNAL VIEW
//
/// @inheritdoc IDelegateApproval
function getClearingHouseOpenPositionAction() external pure override returns (uint8) {
return _CLEARINGHOUSE_OPENPOSITION;
}
/// @inheritdoc IDelegateApproval
function getClearingHouseAddLiquidityAction() external pure override returns (uint8) {
return _CLEARINGHOUSE_ADDLIQUIDITY;
}
/// @inheritdoc IDelegateApproval
function getClearingHouseRemoveLiquidityAction() external pure override returns (uint8) {
return _CLEARINGHOUSE_REMOVELIQUIDITY;
}
/// @inheritdoc IDelegateApproval
function getApprovedActions(address trader, address delegate) external view override returns (uint8) {
bytes32 key = _getApprovalKey(trader, delegate);
return _approvalMap[key];
}
/// @inheritdoc IDelegateApproval
function hasApprovalFor(
address trader,
address delegate,
uint8 actions
) external view override checkActions(actions) returns (bool) {
return _hasApprovalFor(trader, delegate, actions);
}
/// @inheritdoc IDelegateApproval
function canOpenPositionFor(address trader, address delegate) external view override returns (bool) {
return _hasApprovalFor(trader, delegate, _CLEARINGHOUSE_OPENPOSITION);
}
/// @inheritdoc IDelegateApproval
function canAddLiquidityFor(address trader, address delegate) external view override returns (bool) {
return _hasApprovalFor(trader, delegate, _CLEARINGHOUSE_ADDLIQUIDITY);
}
/// @inheritdoc IDelegateApproval
function canRemoveLiquidityFor(address trader, address delegate) external view override returns (bool) {
return _hasApprovalFor(trader, delegate, _CLEARINGHOUSE_REMOVELIQUIDITY);
}
//
// INTERNAL VIEW
//
function _getApprovalKey(address trader, address delegate) internal pure returns (bytes32) {
return keccak256(abi.encode(trader, delegate));
}
function _hasApprovalFor(
address trader,
address delegate,
uint8 actions
) internal view checkActions(actions) returns (bool) {
bytes32 key = _getApprovalKey(trader, delegate);
// approvedActions: 010
// actions: 110
// 010 & 110 = 010 != 110 => false
// approvedActions: 000
// actions: 010
// 000 & 010 = 000 != 010 => false
// approvedActions: 110
// actions: 110
// 110 & 110 = 110 == 110 => true
return (_approvalMap[key] & actions) == actions;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { AddressUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import { SignedSafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import { FullMath } from "@uniswap/v3-core/contracts/libraries/FullMath.sol";
import { TickMath } from "@uniswap/v3-core/contracts/libraries/TickMath.sol";
import { IUniswapV3SwapCallback } from "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol";
import { BlockContext } from "./base/BlockContext.sol";
import { UniswapV3Broker } from "./lib/UniswapV3Broker.sol";
import { PerpSafeCast } from "./lib/PerpSafeCast.sol";
import { SwapMath } from "./lib/SwapMath.sol";
import { PerpFixedPoint96 } from "./lib/PerpFixedPoint96.sol";
import { Funding } from "./lib/Funding.sol";
import { PerpMath } from "./lib/PerpMath.sol";
import { AccountMarket } from "./lib/AccountMarket.sol";
import { ClearingHouseCallee } from "./base/ClearingHouseCallee.sol";
import { UniswapV3CallbackBridge } from "./base/UniswapV3CallbackBridge.sol";
import { IOrderBook } from "./interface/IOrderBook.sol";
import { IMarketRegistry } from "./interface/IMarketRegistry.sol";
import { IAccountBalance } from "./interface/IAccountBalance.sol";
import { IClearingHouseConfig } from "./interface/IClearingHouseConfig.sol";
import { IIndexPrice } from "./interface/IIndexPrice.sol";
import { IBaseToken } from "./interface/IBaseToken.sol";
import { ExchangeStorageV2 } from "./storage/ExchangeStorage.sol";
import { IExchange } from "./interface/IExchange.sol";
import { OpenOrder } from "./lib/OpenOrder.sol";
// never inherit any new stateful contract. never change the orders of parent stateful contracts
contract Exchange is
IUniswapV3SwapCallback,
IExchange,
BlockContext,
ClearingHouseCallee,
UniswapV3CallbackBridge,
ExchangeStorageV2
{
using AddressUpgradeable for address;
using SafeMathUpgradeable for uint256;
using SignedSafeMathUpgradeable for int256;
using SignedSafeMathUpgradeable for int24;
using PerpMath for uint256;
using PerpMath for uint160;
using PerpMath for int256;
using PerpSafeCast for uint24;
using PerpSafeCast for uint256;
using PerpSafeCast for int256;
//
// STRUCT
//
struct InternalSwapResponse {
int256 base;
int256 quote;
int256 exchangedPositionSize;
int256 exchangedPositionNotional;
uint256 fee;
uint256 insuranceFundFee;
int24 tick;
}
struct InternalRealizePnlParams {
address trader;
address baseToken;
int256 takerPositionSize;
int256 takerOpenNotional;
int256 base;
int256 quote;
}
//
// CONSTANT
//
uint256 internal constant _FULLY_CLOSED_RATIO = 1e18;
uint24 internal constant _MAX_TICK_CROSSED_WITHIN_BLOCK_CAP = 1000; // 10%
uint24 internal constant _MAX_PRICE_SPREAD_RATIO = 0.1e6; // 10% in decimal 6
uint256 internal constant _PRICE_LIMIT_INTERVAL = 15; // 15 sec
//
// EXTERNAL NON-VIEW
//
function initialize(
address marketRegistryArg,
address orderBookArg,
address clearingHouseConfigArg
) external initializer {
__ClearingHouseCallee_init();
__UniswapV3CallbackBridge_init(marketRegistryArg);
// E_OBNC: OrderBook is not contract
require(orderBookArg.isContract(), "E_OBNC");
// E_CHNC: CH is not contract
require(clearingHouseConfigArg.isContract(), "E_CHNC");
// update states
_orderBook = orderBookArg;
_clearingHouseConfig = clearingHouseConfigArg;
}
/// @param accountBalanceArg: AccountBalance contract address
function setAccountBalance(address accountBalanceArg) external onlyOwner {
// accountBalance is 0
require(accountBalanceArg != address(0), "E_AB0");
_accountBalance = accountBalanceArg;
emit AccountBalanceChanged(accountBalanceArg);
}
/// @dev Restrict the price impact by setting the ticks can be crossed within a block when
/// trader reducing liquidity. It is used to prevent the malicious behavior of the malicious traders.
/// The restriction is applied in _isOverPriceLimitWithTick()
/// @param baseToken The base token address
/// @param maxTickCrossedWithinBlock The maximum ticks can be crossed within a block
function setMaxTickCrossedWithinBlock(address baseToken, uint24 maxTickCrossedWithinBlock) external onlyOwner {
// EX_BNC: baseToken is not contract
require(baseToken.isContract(), "EX_BNC");
// EX_BTNE: base token does not exists
require(IMarketRegistry(_marketRegistry).hasPool(baseToken), "EX_BTNE");
// tick range is [MIN_TICK, MAX_TICK], maxTickCrossedWithinBlock should be in [0, MAX_TICK - MIN_TICK]
// EX_MTCLOOR: max tick crossed limit out of range
require(maxTickCrossedWithinBlock <= _getMaxTickCrossedWithinBlockCap(), "EX_MTCLOOR");
_maxTickCrossedWithinBlockMap[baseToken] = maxTickCrossedWithinBlock;
emit MaxTickCrossedWithinBlockChanged(baseToken, maxTickCrossedWithinBlock);
}
/// @inheritdoc IUniswapV3SwapCallback
/// @dev This callback is forwarded to ClearingHouse.uniswapV3SwapCallback() because all the tokens
/// are stored in there.
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external override checkCallback {
IUniswapV3SwapCallback(_clearingHouse).uniswapV3SwapCallback(amount0Delta, amount1Delta, data);
}
/// @param params The parameters of the swap
/// @return The result of the swap
/// @dev can only be called from ClearingHouse
/// @inheritdoc IExchange
function swap(SwapParams memory params) external override returns (SwapResponse memory) {
_requireOnlyClearingHouse();
// EX_MIP: market is paused
require(_maxTickCrossedWithinBlockMap[params.baseToken] > 0, "EX_MIP");
// get account info before swap
int256 takerPositionSize =
IAccountBalance(_accountBalance).getTakerPositionSize(params.trader, params.baseToken);
int256 takerOpenNotional =
IAccountBalance(_accountBalance).getTakerOpenNotional(params.trader, params.baseToken);
bool isBaseToQuote = takerPositionSize < 0;
if (params.isClose && takerPositionSize != 0) {
// open reverse position when closing position
params.sqrtPriceLimitX96 = _getSqrtPriceLimitForClosingPosition(
params.baseToken,
isBaseToQuote,
params.sqrtPriceLimitX96
);
}
InternalSwapResponse memory response = _swap(params);
// EX_OPLAS: over price limit after swap
require(!_isOverPriceLimitWithTick(params.baseToken, response.tick), "EX_OPLAS");
// when takerPositionSize < 0, it's a short position
bool isReducingPosition = takerPositionSize == 0 ? false : isBaseToQuote != params.isBaseToQuote;
// when reducing/not increasing the position size, it's necessary to realize pnl
int256 pnlToBeRealized;
if (isReducingPosition) {
pnlToBeRealized = _getPnlToBeRealized(
InternalRealizePnlParams({
trader: params.trader,
baseToken: params.baseToken,
takerPositionSize: takerPositionSize,
takerOpenNotional: takerOpenNotional,
base: response.base,
quote: response.quote
})
);
}
(uint256 sqrtPriceX96, , , , , , ) =
UniswapV3Broker.getSlot0(IMarketRegistry(_marketRegistry).getPool(params.baseToken));
uint256 baseAbs = response.base.abs();
return
SwapResponse({
base: baseAbs,
quote: response.quote.abs(),
exchangedPositionSize: response.exchangedPositionSize,
exchangedPositionNotional: response.exchangedPositionNotional,
fee: response.fee,
insuranceFundFee: response.insuranceFundFee,
pnlToBeRealized: pnlToBeRealized,
sqrtPriceAfterX96: sqrtPriceX96,
tick: response.tick,
isPartialClose: params.isClose ? baseAbs < params.amount : false,
closedRatio: params.isClose ? FullMath.mulDiv(baseAbs, 1e6, params.amount).toUint24() : 0
});
}
/// @inheritdoc IExchange
function settleFunding(address trader, address baseToken)
external
override
returns (int256 fundingPayment, Funding.Growth memory fundingGrowthGlobal)
{
_requireOnlyClearingHouse();
// EX_BTNE: base token does not exists
require(IMarketRegistry(_marketRegistry).hasPool(baseToken), "EX_BTNE");
// The purpose of caching index twap here is to save the gas consumption of calculating mark price,
// if updating TWAP fails, this call will be reverted and thus using try-catch.
// NOTE: the cached index twap is used for AccountBalance.MarkPrice calculation,
// not for funding rate calculation.
(, uint32 premiumInterval) = IClearingHouseConfig(_clearingHouseConfig).getMarkPriceConfig();
try IBaseToken(baseToken).cacheTwap(premiumInterval) {} catch {}
uint256 marketTwap;
uint256 indexTwap;
(fundingGrowthGlobal, marketTwap, indexTwap) = _getFundingGrowthGlobalAndTwaps(baseToken);
fundingPayment = _updateFundingGrowth(
trader,
baseToken,
IAccountBalance(_accountBalance).getBase(trader, baseToken),
IAccountBalance(_accountBalance).getAccountInfo(trader, baseToken).lastTwPremiumGrowthGlobalX96,
fundingGrowthGlobal
);
// funding will be stopped once the market is being paused
uint256 timestamp =
IBaseToken(baseToken).isOpen() ? _blockTimestamp() : IBaseToken(baseToken).getPausedTimestamp();
// update states before further actions in this block; once per block
if (timestamp != _lastSettledTimestampMap[baseToken]) {
// update fundingGrowthGlobal and _lastSettledTimestamp
Funding.Growth storage lastFundingGrowthGlobal = _globalFundingGrowthX96Map[baseToken];
(
_lastSettledTimestampMap[baseToken],
lastFundingGrowthGlobal.twPremiumX96,
lastFundingGrowthGlobal.twPremiumDivBySqrtPriceX96
) = (timestamp, fundingGrowthGlobal.twPremiumX96, fundingGrowthGlobal.twPremiumDivBySqrtPriceX96);
emit FundingUpdated(baseToken, marketTwap, indexTwap);
}
// update tick & timestamp for price limit check
// if timestamp diff < _PRICE_LIMIT_INTERVAL, including when the market is paused, they won't get updated
uint256 lastTickUpdatedTimestamp = _lastTickUpdatedTimestampMap[baseToken];
if (timestamp >= lastTickUpdatedTimestamp.add(_PRICE_LIMIT_INTERVAL)) {
_lastTickUpdatedTimestampMap[baseToken] = timestamp;
_lastUpdatedTickMap[baseToken] = _getTick(baseToken);
}
return (fundingPayment, fundingGrowthGlobal);
}
//
// EXTERNAL VIEW
//
/// @inheritdoc IExchange
function getOrderBook() external view override returns (address) {
return _orderBook;
}
/// @inheritdoc IExchange
function getAccountBalance() external view override returns (address) {
return _accountBalance;
}
/// @inheritdoc IExchange
function getClearingHouseConfig() external view override returns (address) {
return _clearingHouseConfig;
}
/// @inheritdoc IExchange
function getMaxTickCrossedWithinBlock(address baseToken) external view override returns (uint24) {
return _maxTickCrossedWithinBlockMap[baseToken];
}
/// @inheritdoc IExchange
function getPnlToBeRealized(RealizePnlParams memory params) external view override returns (int256) {
AccountMarket.Info memory info =
IAccountBalance(_accountBalance).getAccountInfo(params.trader, params.baseToken);
int256 takerOpenNotional = info.takerOpenNotional;
int256 takerPositionSize = info.takerPositionSize;
// when takerPositionSize < 0, it's a short position; when base < 0, isBaseToQuote(shorting)
bool isReducingPosition = takerPositionSize == 0 ? false : takerPositionSize < 0 != params.base < 0;
return
isReducingPosition
? _getPnlToBeRealized(
InternalRealizePnlParams({
trader: params.trader,
baseToken: params.baseToken,
takerPositionSize: takerPositionSize,
takerOpenNotional: takerOpenNotional,
base: params.base,
quote: params.quote
})
)
: 0;
}
/// @inheritdoc IExchange
function getAllPendingFundingPayment(address trader) external view override returns (int256 pendingFundingPayment) {
address[] memory baseTokens = IAccountBalance(_accountBalance).getBaseTokens(trader);
uint256 baseTokenLength = baseTokens.length;
for (uint256 i = 0; i < baseTokenLength; i++) {
pendingFundingPayment = pendingFundingPayment.add(getPendingFundingPayment(trader, baseTokens[i]));
}
return pendingFundingPayment;
}
/// @inheritdoc IExchange
function isOverPriceSpread(address baseToken) external view override returns (bool) {
return
_getPriceSpreadRatio(baseToken, IClearingHouseConfig(_clearingHouseConfig).getTwapInterval()).abs() >
_MAX_PRICE_SPREAD_RATIO;
}
/// @inheritdoc IExchange
// **Deprecated function, will be removed in the next release, use `getSqrtMarketTwapX96()` instead**
function getSqrtMarkTwapX96(address baseToken, uint32 twapInterval) external view override returns (uint160) {
return _getSqrtMarketTwapX96(baseToken, twapInterval);
}
/// @inheritdoc IExchange
function getSqrtMarketTwapX96(address baseToken, uint32 twapInterval) external view override returns (uint160) {
return _getSqrtMarketTwapX96(baseToken, twapInterval);
}
//
// PUBLIC VIEW
//
/// @inheritdoc IExchange
function getPendingFundingPayment(address trader, address baseToken) public view override returns (int256) {
(Funding.Growth memory fundingGrowthGlobal, , ) = _getFundingGrowthGlobalAndTwaps(baseToken);
int256 liquidityCoefficientInFundingPayment =
IOrderBook(_orderBook).getLiquidityCoefficientInFundingPayment(trader, baseToken, fundingGrowthGlobal);
return
Funding.calcPendingFundingPaymentWithLiquidityCoefficient(
IAccountBalance(_accountBalance).getBase(trader, baseToken),
IAccountBalance(_accountBalance).getAccountInfo(trader, baseToken).lastTwPremiumGrowthGlobalX96,
fundingGrowthGlobal,
liquidityCoefficientInFundingPayment
);
}
//
// INTERNAL NON-VIEW
//
/// @dev customized fee: https://www.notion.so/perp/Customise-fee-tier-on-B2QFee-1b7244e1db63416c8651e8fa04128cdb
function _swap(SwapParams memory params) internal returns (InternalSwapResponse memory) {
IMarketRegistry.MarketInfo memory marketInfo =
IMarketRegistry(_marketRegistry).getMarketInfoByTrader(params.trader, params.baseToken);
(uint256 scaledAmountForUniswapV3PoolSwap, int256 signedScaledAmountForReplaySwap) =
SwapMath.calcScaledAmountForSwaps(
params.isBaseToQuote,
params.isExactInput,
params.amount,
marketInfo.exchangeFeeRatio,
marketInfo.uniswapFeeRatio
);
(Funding.Growth memory fundingGrowthGlobal, , ) = _getFundingGrowthGlobalAndTwaps(params.baseToken);
// simulate the swap to calculate the fees charged in exchange
IOrderBook.ReplaySwapResponse memory replayResponse =
IOrderBook(_orderBook).replaySwap(
IOrderBook.ReplaySwapParams({
baseToken: params.baseToken,
pool: marketInfo.pool,
isBaseToQuote: params.isBaseToQuote,
shouldUpdateState: true,
amount: signedScaledAmountForReplaySwap,
sqrtPriceLimitX96: params.sqrtPriceLimitX96,
exchangeFeeRatio: marketInfo.exchangeFeeRatio,
uniswapFeeRatio: marketInfo.uniswapFeeRatio,
insuranceFundFeeRatio: marketInfo.insuranceFundFeeRatio,
globalFundingGrowth: fundingGrowthGlobal
})
);
int256 priceSpreadRatioBeforeSwap = _getPriceSpreadRatio(params.baseToken, 0);
UniswapV3Broker.SwapResponse memory response =
UniswapV3Broker.swap(
UniswapV3Broker.SwapParams(
marketInfo.pool,
_clearingHouse,
params.isBaseToQuote,
params.isExactInput,
// mint extra base token before swap
scaledAmountForUniswapV3PoolSwap,
params.sqrtPriceLimitX96,
abi.encode(
SwapCallbackData({
trader: params.trader,
baseToken: params.baseToken,
pool: marketInfo.pool,
fee: replayResponse.fee,
uniswapFeeRatio: marketInfo.uniswapFeeRatio
})
)
)
);
int24 tick = UniswapV3Broker.getTick(marketInfo.pool);
// tick mismatch
require(tick == replayResponse.tick, "EX_TKMM");
// avoid stack too deep
{
// check price band after swap
int256 priceSpreadRatioAfterSwap = _getPriceSpreadRatio(params.baseToken, 0);
int256 maxPriceSpreadRatio = marketInfo.maxPriceSpreadRatio.toInt256();
require(
PerpMath.min(priceSpreadRatioBeforeSwap, maxPriceSpreadRatio.neg256()) <= priceSpreadRatioAfterSwap &&
priceSpreadRatioAfterSwap <= PerpMath.max(priceSpreadRatioBeforeSwap, maxPriceSpreadRatio),
"EX_OPB"
);
}
// as we charge fees in ClearingHouse instead of in Uniswap pools,
// we need to scale up base or quote amounts to get the exact exchanged position size and notional
int256 exchangedPositionSize;
int256 exchangedPositionNotional;
if (params.isBaseToQuote) {
// short: exchangedPositionSize <= 0 && exchangedPositionNotional >= 0
exchangedPositionSize = SwapMath
.calcAmountScaledByFeeRatio(response.base, marketInfo.uniswapFeeRatio, false)
.neg256();
// due to base to quote fee, exchangedPositionNotional contains the fee
// s.t. we can take the fee away from exchangedPositionNotional
exchangedPositionNotional = response.quote.toInt256();
} else {
// long: exchangedPositionSize >= 0 && exchangedPositionNotional <= 0
exchangedPositionSize = response.base.toInt256();
// scaledAmountForUniswapV3PoolSwap is the amount of quote token to swap (input),
// response.quote is the actual amount of quote token swapped (output).
// as long as liquidity is enough, they would be equal.
// otherwise, response.quote < scaledAmountForUniswapV3PoolSwap
// which also means response.quote < exact input amount.
if (params.isExactInput && response.quote == scaledAmountForUniswapV3PoolSwap) {
// NOTE: replayResponse.fee might have an extra charge of 1 wei, for instance:
// Q2B exact input amount 1000000000000000000000 with fee ratio 1%,
// replayResponse.fee is actually 10000000000000000001 (1000 * 1% + 1 wei),
// and quote = exchangedPositionNotional - replayResponse.fee = -1000000000000000000001
// which is not matched with exact input 1000000000000000000000
// we modify exchangedPositionNotional here to make sure
// quote = exchangedPositionNotional - replayResponse.fee = exact input
exchangedPositionNotional = params.amount.sub(replayResponse.fee).toInt256().neg256();
} else {
exchangedPositionNotional = SwapMath
.calcAmountScaledByFeeRatio(response.quote, marketInfo.uniswapFeeRatio, false)
.neg256();
}
}
// update the timestamp of the first tx in this market
if (_firstTradedTimestampMap[params.baseToken] == 0) {
_firstTradedTimestampMap[params.baseToken] = _blockTimestamp();
}
return
InternalSwapResponse({
base: exchangedPositionSize,
quote: exchangedPositionNotional.sub(replayResponse.fee.toInt256()),
exchangedPositionSize: exchangedPositionSize,
exchangedPositionNotional: exchangedPositionNotional,
fee: replayResponse.fee,
insuranceFundFee: replayResponse.insuranceFundFee,
tick: replayResponse.tick
});
}
/// @dev this is the non-view version of getPendingFundingPayment()
/// @return pendingFundingPayment the pending funding payment of a trader in one market,
/// including liquidity & balance coefficients
function _updateFundingGrowth(
address trader,
address baseToken,
int256 baseBalance,
int256 twPremiumGrowthGlobalX96,
Funding.Growth memory fundingGrowthGlobal
) internal returns (int256 pendingFundingPayment) {
int256 liquidityCoefficientInFundingPayment =
IOrderBook(_orderBook).updateFundingGrowthAndLiquidityCoefficientInFundingPayment(
trader,
baseToken,
fundingGrowthGlobal
);
return
Funding.calcPendingFundingPaymentWithLiquidityCoefficient(
baseBalance,
twPremiumGrowthGlobalX96,
fundingGrowthGlobal,
liquidityCoefficientInFundingPayment
);
}
//
// INTERNAL VIEW
//
function _getSqrtMarketTwapX96(address baseToken, uint32 twapInterval) internal view returns (uint160) {
return UniswapV3Broker.getSqrtMarketTwapX96(IMarketRegistry(_marketRegistry).getPool(baseToken), twapInterval);
}
function _isOverPriceLimitWithTick(address baseToken, int24 tick) internal view returns (bool) {
uint24 maxDeltaTick = _maxTickCrossedWithinBlockMap[baseToken];
int24 lastUpdatedTick = _lastUpdatedTickMap[baseToken];
// no overflow/underflow issue because there are range limits for tick and maxDeltaTick
int24 upperTickBound = lastUpdatedTick.add(maxDeltaTick).toInt24();
int24 lowerTickBound = lastUpdatedTick.sub(maxDeltaTick).toInt24();
return (tick < lowerTickBound || tick > upperTickBound);
}
function _getTick(address baseToken) internal view returns (int24) {
(, int24 tick, , , , , ) = UniswapV3Broker.getSlot0(IMarketRegistry(_marketRegistry).getPool(baseToken));
return tick;
}
/// @dev this function calculates the up-to-date globalFundingGrowth and twaps and pass them out
/// @return fundingGrowthGlobal the up-to-date globalFundingGrowth
/// @return marketTwap only for settleFunding()
/// @return indexTwap only for settleFunding()
function _getFundingGrowthGlobalAndTwaps(address baseToken)
internal
view
returns (
Funding.Growth memory fundingGrowthGlobal,
uint256 marketTwap,
uint256 indexTwap
)
{
bool marketOpen = IBaseToken(baseToken).isOpen();
uint256 timestamp = marketOpen ? _blockTimestamp() : IBaseToken(baseToken).getPausedTimestamp();
// shorten twapInterval if prior observations are not enough
uint32 twapInterval;
if (_firstTradedTimestampMap[baseToken] != 0) {
twapInterval = IClearingHouseConfig(_clearingHouseConfig).getTwapInterval();
// overflow inspection:
// 2 ^ 32 = 4,294,967,296 > 100 years = 60 * 60 * 24 * 365 * 100 = 3,153,600,000
uint32 deltaTimestamp = timestamp.sub(_firstTradedTimestampMap[baseToken]).toUint32();
twapInterval = twapInterval > deltaTimestamp ? deltaTimestamp : twapInterval;
}
uint256 marketTwapX96;
if (marketOpen) {
marketTwapX96 = _getSqrtMarketTwapX96(baseToken, twapInterval).formatSqrtPriceX96ToPriceX96();
indexTwap = IIndexPrice(baseToken).getIndexPrice(twapInterval);
} else {
// if a market is paused/closed, we use the last known index price which is getPausedIndexPrice
//
// -----+--- twap interval ---+--- secondsAgo ---+
// pausedTime now
// timestamp is pausedTime when the market is not open
uint32 secondsAgo = _blockTimestamp().sub(timestamp).toUint32();
marketTwapX96 = UniswapV3Broker
.getSqrtMarketTwapX96From(IMarketRegistry(_marketRegistry).getPool(baseToken), secondsAgo, twapInterval)
.formatSqrtPriceX96ToPriceX96();
indexTwap = IBaseToken(baseToken).getPausedIndexPrice();
}
marketTwap = marketTwapX96.formatX96ToX10_18();
uint256 lastSettledTimestamp = _lastSettledTimestampMap[baseToken];
Funding.Growth storage lastFundingGrowthGlobal = _globalFundingGrowthX96Map[baseToken];
if (timestamp == lastSettledTimestamp || lastSettledTimestamp == 0) {
// if this is the latest updated timestamp, values in _globalFundingGrowthX96Map are up-to-date already
fundingGrowthGlobal = lastFundingGrowthGlobal;
} else {
// deltaTwPremium = (marketTwap - indexTwap) * (now - lastSettledTimestamp)
int256 deltaTwPremiumX96 =
_getDeltaTwapX96(marketTwapX96, indexTwap.formatX10_18ToX96()).mul(
timestamp.sub(lastSettledTimestamp).toInt256()
);
fundingGrowthGlobal.twPremiumX96 = lastFundingGrowthGlobal.twPremiumX96.add(deltaTwPremiumX96);
// overflow inspection:
// assuming premium = 1 billion (1e9), time diff = 1 year (3600 * 24 * 365)
// log(1e9 * 2^96 * (3600 * 24 * 365) * 2^96) / log(2) = 246.8078491997 < 255
// twPremiumDivBySqrtPrice += deltaTwPremium / getSqrtMarketTwap(baseToken)
fundingGrowthGlobal.twPremiumDivBySqrtPriceX96 = lastFundingGrowthGlobal.twPremiumDivBySqrtPriceX96.add(
PerpMath.mulDiv(deltaTwPremiumX96, PerpFixedPoint96._IQ96, _getSqrtMarketTwapX96(baseToken, 0))
);
}
return (fundingGrowthGlobal, marketTwap, indexTwap);
}
/// @dev get a sqrt price limit for closing position s.t. it can stop when reaching the limit to save gas
function _getSqrtPriceLimitForClosingPosition(
address baseToken,
bool isBaseToQuote,
uint160 inputSqrtPriceLimitX96
) internal view returns (uint160) {
int24 lastUpdatedTick = _lastUpdatedTickMap[baseToken];
uint24 maxDeltaTick = _maxTickCrossedWithinBlockMap[baseToken];
// price limit = upper tick boundary or lower tick boundary depending on which direction
int24 tickBoundary =
isBaseToQuote ? lastUpdatedTick + int24(maxDeltaTick) : lastUpdatedTick - int24(maxDeltaTick);
// tickBoundary should be in (MIN_TICK, MAX_TICK)
// ref: https://github.com/Uniswap/v3-core/blob/main/contracts/UniswapV3Pool.sol#L608
tickBoundary = tickBoundary > TickMath.MAX_TICK ? TickMath.MAX_TICK - 1 : tickBoundary;
tickBoundary = tickBoundary < TickMath.MIN_TICK ? TickMath.MIN_TICK + 1 : tickBoundary;
uint160 targetSqrtPriceLimitX96 = TickMath.getSqrtRatioAtTick(tickBoundary);
if (inputSqrtPriceLimitX96 == 0) {
return targetSqrtPriceLimitX96;
}
if (isBaseToQuote) {
return targetSqrtPriceLimitX96 > inputSqrtPriceLimitX96 ? inputSqrtPriceLimitX96 : targetSqrtPriceLimitX96;
}
return targetSqrtPriceLimitX96 < inputSqrtPriceLimitX96 ? inputSqrtPriceLimitX96 : targetSqrtPriceLimitX96;
}
function _getDeltaTwapX96(uint256 marketTwapX96, uint256 indexTwapX96) internal view returns (int256 deltaTwapX96) {
uint24 maxFundingRate = IClearingHouseConfig(_clearingHouseConfig).getMaxFundingRate();
uint256 maxDeltaTwapX96 = indexTwapX96.mulRatio(maxFundingRate);
uint256 absDeltaTwapX96;
if (marketTwapX96 > indexTwapX96) {
absDeltaTwapX96 = marketTwapX96.sub(indexTwapX96);
deltaTwapX96 = absDeltaTwapX96 > maxDeltaTwapX96 ? maxDeltaTwapX96.toInt256() : absDeltaTwapX96.toInt256();
} else {
absDeltaTwapX96 = indexTwapX96.sub(marketTwapX96);
deltaTwapX96 = absDeltaTwapX96 > maxDeltaTwapX96 ? maxDeltaTwapX96.neg256() : absDeltaTwapX96.neg256();
}
}
/// @dev ratio will return in int256
function _getPriceSpreadRatio(address baseToken, uint32 twapInterval) internal view returns (int256) {
uint256 marketPrice = _getSqrtMarketTwapX96(baseToken, 0).formatSqrtPriceX96ToPriceX96().formatX96ToX10_18();
uint256 indexPrice = IIndexPrice(baseToken).getIndexPrice(twapInterval);
int256 spread =
marketPrice > indexPrice ? marketPrice.sub(indexPrice).toInt256() : indexPrice.sub(marketPrice).neg256();
return spread.mulDiv(1e6, indexPrice);
}
function _getPnlToBeRealized(InternalRealizePnlParams memory params) internal pure returns (int256) {
// closedRatio is based on the position size
uint256 closedRatio = FullMath.mulDiv(params.base.abs(), _FULLY_CLOSED_RATIO, params.takerPositionSize.abs());
int256 pnlToBeRealized;
// if closedRatio <= 1, it's reducing or closing a position; else, it's opening a larger reverse position
if (closedRatio <= _FULLY_CLOSED_RATIO) {
// https://docs.google.com/spreadsheets/d/1QwN_UZOiASv3dPBP7bNVdLR_GTaZGUrHW3-29ttMbLs/edit#gid=148137350
// taker:
// step 1: long 20 base
// openNotionalFraction = 252.53
// openNotional = -252.53
// step 2: short 10 base (reduce half of the position)
// quote = 137.5
// closeRatio = 10/20 = 0.5
// reducedOpenNotional = openNotional * closedRatio = -252.53 * 0.5 = -126.265
// realizedPnl = quote + reducedOpenNotional = 137.5 + -126.265 = 11.235
// openNotionalFraction = openNotionalFraction - quote + realizedPnl
// = 252.53 - 137.5 + 11.235 = 126.265
// openNotional = -openNotionalFraction = 126.265
// overflow inspection:
// max closedRatio = 1e18; range of oldOpenNotional = (-2 ^ 255, 2 ^ 255)
// only overflow when oldOpenNotional < -2 ^ 255 / 1e18 or oldOpenNotional > 2 ^ 255 / 1e18
int256 reducedOpenNotional = params.takerOpenNotional.mulDiv(closedRatio.toInt256(), _FULLY_CLOSED_RATIO);
pnlToBeRealized = params.quote.add(reducedOpenNotional);
} else {
// https://docs.google.com/spreadsheets/d/1QwN_UZOiASv3dPBP7bNVdLR_GTaZGUrHW3-29ttMbLs/edit#gid=668982944
// taker:
// step 1: long 20 base
// openNotionalFraction = 252.53
// openNotional = -252.53
// step 2: short 30 base (open a larger reverse position)
// quote = 337.5
// closeRatio = 30/20 = 1.5
// closedPositionNotional = quote / closeRatio = 337.5 / 1.5 = 225
// remainsPositionNotional = quote - closedPositionNotional = 337.5 - 225 = 112.5
// realizedPnl = closedPositionNotional + openNotional = -252.53 + 225 = -27.53
// openNotionalFraction = openNotionalFraction - quote + realizedPnl
// = 252.53 - 337.5 + -27.53 = -112.5
// openNotional = -openNotionalFraction = remainsPositionNotional = 112.5
// overflow inspection:
// max & min tick = 887272, -887272; max liquidity = 2 ^ 128
// max quote = 2^128 * (sqrt(1.0001^887272) - sqrt(1.0001^-887272)) = 6.276865796e57 < 2^255 / 1e18
int256 closedPositionNotional = params.quote.mulDiv(int256(_FULLY_CLOSED_RATIO), closedRatio);
pnlToBeRealized = params.takerOpenNotional.add(closedPositionNotional);
}
return pnlToBeRealized;
}
// @dev use virtual for testing
function _getMaxTickCrossedWithinBlockCap() internal pure virtual returns (uint24) {
return _MAX_TICK_CROSSED_WITHIN_BLOCK_CAP;
}
}// copied from @opengsn/provider-2.2.4,
// https://github.com/opengsn/gsn/blob/master/packages/contracts/src/BaseRelayRecipient.sol
// for adding `payable` property at the return value of _msgSender()
// SPDX-License-Identifier: MIT
// solhint-disable no-inline-assembly
pragma solidity 0.7.6;
import "./IRelayRecipient.sol";
/**
* A base contract to be inherited by any contract that want to receive relayed transactions
* A subclass must use "_msgSender()" instead of "msg.sender"
*/
abstract contract BaseRelayRecipient is IRelayRecipient {
/*
* Forwarder singleton we accept calls from
*/
address internal _trustedForwarder;
// __gap is reserved storage
uint256[50] private __gap;
event TrustedForwarderUpdated(address trustedForwarder);
function getTrustedForwarder() external view returns (address) {
return _trustedForwarder;
}
/// @inheritdoc IRelayRecipient
function versionRecipient() external pure override returns (string memory) {
return "2.0.0";
}
/// @inheritdoc IRelayRecipient
function isTrustedForwarder(address forwarder) public view virtual override returns (bool) {
return forwarder == _trustedForwarder;
}
function _setTrustedForwarder(address trustedForwarderArg) internal {
_trustedForwarder = trustedForwarderArg;
emit TrustedForwarderUpdated(trustedForwarderArg);
}
/**
* return the sender of this call.
* if the call came through our trusted forwarder, return the original sender.
* otherwise, return `msg.sender`.
* should be used in the contract anywhere instead of msg.sender
*/
/// @inheritdoc IRelayRecipient
function _msgSender() internal view virtual override returns (address payable ret) {
if (msg.data.length >= 20 && isTrustedForwarder(msg.sender)) {
// At this point we know that the sender is a trusted forwarder,
// so we trust that the last bytes of msg.data are the verified sender address.
// extract sender address from the end of msg.data
assembly {
ret := shr(96, calldataload(sub(calldatasize(), 20)))
}
} else {
ret = msg.sender;
}
}
/**
* return the msg.data of this call.
* if the call came through our trusted forwarder, then the real sender was appended as the last 20 bytes
* of the msg.data - so this method will strip those 20 bytes off.
* otherwise (if the call was made directly and not through the forwarder), return `msg.data`
* should be used in the contract instead of msg.data, where this difference matters.
*/
/// @inheritdoc IRelayRecipient
function _msgData() internal view virtual override returns (bytes calldata ret) {
if (msg.data.length >= 20 && isTrustedForwarder(msg.sender)) {
return msg.data[0:msg.data.length - 20];
} else {
return msg.data;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
/**
* a contract must implement this interface in order to support relayed transaction.
* It is better to inherit the BaseRelayRecipient as its implementation.
*/
abstract contract IRelayRecipient {
/**
* return if the forwarder is trusted to forward relayed transactions to us.
* the forwarder is required to verify the sender's signature, and verify
* the call is not a replay.
*/
function isTrustedForwarder(address forwarder) public view virtual returns (bool);
/**
* return the sender of this call.
* if the call came through our trusted forwarder, then the real sender is appended as the last 20 bytes
* of the msg.data.
* otherwise, return `msg.sender`
* should be used in the contract anywhere instead of msg.sender
*/
function _msgSender() internal view virtual returns (address payable);
/**
* return the msg.data of this call.
* if the call came through our trusted forwarder, then the real sender was appended as the last 20 bytes
* of the msg.data - so this method will strip those 20 bytes off.
* otherwise (if the call was made directly and not through the forwarder), return `msg.data`
* should be used in the contract instead of msg.data, where this difference matters.
*/
function _msgData() internal view virtual returns (bytes calldata);
function versionRecipient() external view virtual returns (string memory);
}// SPDX-License-Identifier: UNLICENSED pragma solidity >0.0.0; import '@uniswap/v3-core/contracts/UniswapV3Factory.sol';
// SPDX-License-Identifier: UNLICENSED pragma solidity >0.0.0; import '@uniswap/v3-core/contracts/UniswapV3Pool.sol';
// SPDX-License-Identifier: UNLICENSED pragma solidity >0.0.0; import '@uniswap/v3-periphery/contracts/NonfungiblePositionManager.sol';
// SPDX-License-Identifier: UNLICENSED pragma solidity >0.0.0; import 'externalContracts/TestERC20.sol';
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.6;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title Interface for WETH9
interface IWETH9 is IERC20 {
/// @notice Deposit ether to get wrapped ether
function deposit() external payable;
/// @notice Withdraw wrapped ether to get ether
function withdraw(uint256) external;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { AccountMarket } from "../lib/AccountMarket.sol";
interface IAccountBalance {
/// @param vault The address of the vault contract
event VaultChanged(address indexed vault);
/// @dev Emit whenever a trader's `owedRealizedPnl` is updated
/// @param trader The address of the trader
/// @param amount The amount changed
event PnlRealized(address indexed trader, int256 amount);
/// @notice Modify trader account balance
/// @dev Only used by `ClearingHouse` contract
/// @param trader The address of the trader
/// @param baseToken The address of the baseToken
/// @param base Modified amount of base
/// @param quote Modified amount of quote
/// @return takerPositionSize Taker position size after modified
/// @return takerOpenNotional Taker open notional after modified
function modifyTakerBalance(
address trader,
address baseToken,
int256 base,
int256 quote
) external returns (int256 takerPositionSize, int256 takerOpenNotional);
/// @notice Modify trader owedRealizedPnl
/// @dev Only used by `ClearingHouse` contract
/// @param trader The address of the trader
/// @param amount Modified amount of owedRealizedPnl
function modifyOwedRealizedPnl(address trader, int256 amount) external;
/// @notice Settle owedRealizedPnl
/// @dev Only used by `Vault.withdraw()`
/// @param trader The address of the trader
/// @return pnl Settled owedRealizedPnl
function settleOwedRealizedPnl(address trader) external returns (int256 pnl);
/// @notice Modify trader owedRealizedPnl
/// @dev Only used by `ClearingHouse` contract
/// @param trader The address of the trader
/// @param baseToken The address of the baseToken
/// @param amount Settled quote amount
function settleQuoteToOwedRealizedPnl(
address trader,
address baseToken,
int256 amount
) external;
/// @notice Settle account balance and deregister base token
/// @dev Only used by `ClearingHouse` contract
/// @param trader The address of the trader
/// @param baseToken The address of the baseToken
/// @param takerBase Modified amount of taker base
/// @param takerQuote Modified amount of taker quote
/// @param realizedPnl Amount of pnl realized
/// @param makerFee Amount of maker fee collected from pool
function settleBalanceAndDeregister(
address trader,
address baseToken,
int256 takerBase,
int256 takerQuote,
int256 realizedPnl,
int256 makerFee
) external;
/// @notice Every time a trader's position value is checked, the base token list of this trader will be traversed;
/// thus, this list should be kept as short as possible
/// @dev Only used by `ClearingHouse` contract
/// @param trader The address of the trader
/// @param baseToken The address of the trader's base token
function registerBaseToken(address trader, address baseToken) external;
/// @notice Deregister baseToken from trader accountInfo
/// @dev Only used by `ClearingHouse` contract, this function is expensive, due to for loop
/// @param trader The address of the trader
/// @param baseToken The address of the trader's base token
function deregisterBaseToken(address trader, address baseToken) external;
/// @notice Update trader Twap premium info
/// @dev Only used by `ClearingHouse` contract
/// @param trader The address of trader
/// @param baseToken The address of baseToken
/// @param lastTwPremiumGrowthGlobalX96 The last Twap Premium
function updateTwPremiumGrowthGlobal(
address trader,
address baseToken,
int256 lastTwPremiumGrowthGlobalX96
) external;
/// @notice Settle trader's PnL in closed market
/// @dev Only used by `ClearingHouse`
/// @param trader The address of the trader
/// @param baseToken The address of the trader's base token
/// @return positionNotional Taker's position notional settled with closed price
/// @return openNotional Taker's open notional
/// @return realizedPnl Settled realized pnl
/// @return closedPrice The closed price of the closed market
function settlePositionInClosedMarket(address trader, address baseToken)
external
returns (
int256 positionNotional,
int256 openNotional,
int256 realizedPnl,
uint256 closedPrice
);
/// @notice Get `ClearingHouseConfig` address
/// @return clearingHouseConfig The address of ClearingHouseConfig
function getClearingHouseConfig() external view returns (address clearingHouseConfig);
/// @notice Get `OrderBook` address
/// @return orderBook The address of OrderBook
function getOrderBook() external view returns (address orderBook);
/// @notice Get `Vault` address
/// @return vault The address of Vault
function getVault() external view returns (address vault);
/// @notice Get trader registered baseTokens
/// @param trader The address of trader
/// @return baseTokens The array of baseToken address
function getBaseTokens(address trader) external view returns (address[] memory baseTokens);
/// @notice Get trader account info
/// @param trader The address of trader
/// @param baseToken The address of baseToken
/// @return traderAccountInfo The baseToken account info of trader
function getAccountInfo(address trader, address baseToken)
external
view
returns (AccountMarket.Info memory traderAccountInfo);
/// @notice Get taker cost of trader's baseToken
/// @param trader The address of trader
/// @param baseToken The address of baseToken
/// @return openNotional The taker cost of trader's baseToken
function getTakerOpenNotional(address trader, address baseToken) external view returns (int256 openNotional);
/// @notice Get total cost of trader's baseToken
/// @param trader The address of trader
/// @param baseToken The address of baseToken
/// @return totalOpenNotional the amount of quote token paid for a position when opening
function getTotalOpenNotional(address trader, address baseToken) external view returns (int256 totalOpenNotional);
/// @notice Get total debt value of trader
/// @param trader The address of trader
/// @dev Total debt value will relate to `Vault.getFreeCollateral()`
/// @return totalDebtValue The debt value of trader
function getTotalDebtValue(address trader) external view returns (uint256 totalDebtValue);
/// @notice Get margin requirement to check whether trader will be able to liquidate
/// @dev This is different from `Vault._getTotalMarginRequirement()`, which is for freeCollateral calculation
/// @param trader The address of trader
/// @return marginRequirementForLiquidation It is compared with `ClearingHouse.getAccountValue` which is also an int
function getMarginRequirementForLiquidation(address trader)
external
view
returns (int256 marginRequirementForLiquidation);
/// @notice Get owedRealizedPnl, unrealizedPnl and pending fee
/// @param trader The address of trader
/// @return owedRealizedPnl the pnl realized already but stored temporarily in AccountBalance
/// @return unrealizedPnl the pnl not yet realized
/// @return pendingFee the pending fee of maker earned
function getPnlAndPendingFee(address trader)
external
view
returns (
int256 owedRealizedPnl,
int256 unrealizedPnl,
uint256 pendingFee
);
/// @notice Check trader has open order in open/closed market.
/// @param trader The address of trader
/// @return True of false
function hasOrder(address trader) external view returns (bool);
/// @notice Get trader base amount
/// @dev `base amount = takerPositionSize - orderBaseDebt`
/// @param trader The address of trader
/// @param baseToken The address of baseToken
/// @return baseAmount The base amount of trader's baseToken market
function getBase(address trader, address baseToken) external view returns (int256 baseAmount);
/// @notice Get trader quote amount
/// @dev `quote amount = takerOpenNotional - orderQuoteDebt`
/// @param trader The address of trader
/// @param baseToken The address of baseToken
/// @return quoteAmount The quote amount of trader's baseToken market
function getQuote(address trader, address baseToken) external view returns (int256 quoteAmount);
/// @notice Get taker position size of trader's baseToken market
/// @dev This will only has taker position, can get maker impermanent position through `getTotalPositionSize`
/// @param trader The address of trader
/// @param baseToken The address of baseToken
/// @return takerPositionSize The taker position size of trader's baseToken market
function getTakerPositionSize(address trader, address baseToken) external view returns (int256 takerPositionSize);
/// @notice Get total position size of trader's baseToken market
/// @dev `total position size = taker position size + maker impermanent position size`
/// @param trader The address of trader
/// @param baseToken The address of baseToken
/// @return totalPositionSize The total position size of trader's baseToken market
function getTotalPositionSize(address trader, address baseToken) external view returns (int256 totalPositionSize);
/// @notice Get total position value of trader's baseToken market
/// @dev A negative returned value is only be used when calculating pnl,
/// @dev we use mark price to calc position value
/// @param trader The address of trader
/// @param baseToken The address of baseToken
/// @return totalPositionValue Total position value of trader's baseToken market
function getTotalPositionValue(address trader, address baseToken) external view returns (int256 totalPositionValue);
/// @notice Get all market position abs value of trader
/// @param trader The address of trader
/// @return totalAbsPositionValue Sum up positions value of every market
function getTotalAbsPositionValue(address trader) external view returns (uint256 totalAbsPositionValue);
/// @notice Get liquidatable position size of trader's baseToken market
/// @param trader The address of trader
/// @param baseToken The address of baseToken
/// @param accountValue The account value of trader
/// @return liquidatablePositionSize The liquidatable position size of trader's baseToken market
function getLiquidatablePositionSize(
address trader,
address baseToken,
int256 accountValue
) external view returns (int256);
/// @notice Get mark price of baseToken market
/// @dev Mark price is the median of three prices as below.
/// 1. current market price
/// 2. market twap with 30 mins
/// 3. index price + premium with 15 mins
/// @dev If the parameters to calculate mark price are not set, returns index twap instead for backward compatible
/// @dev If the market is paused, returns index twap instead, that will be the index twap while pausing market
/// @param baseToken The address of baseToken
/// @return price The mark price of baseToken market
function getMarkPrice(address baseToken) external view returns (uint256);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
interface IBaseToken {
// Do NOT change the order of enum values because it will break backwards compatibility
enum Status { Open, Paused, Closed }
event PriceFeedChanged(address indexed priceFeed);
event StatusUpdated(Status indexed status);
function close() external;
/// @notice Update the cached index price of the token.
/// @param interval The twap interval in seconds.
function cacheTwap(uint256 interval) external;
/// @notice Get the price feed address
/// @dev priceFeed is now priceFeedDispatcher, which dispatches either Chainlink or UniswapV3 price
/// @return priceFeed the current price feed
function getPriceFeed() external view returns (address priceFeed);
function getPausedTimestamp() external view returns (uint256);
function getPausedIndexPrice() external view returns (uint256);
function getClosedPrice() external view returns (uint256);
function isOpen() external view returns (bool);
function isPaused() external view returns (bool);
function isClosed() external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
interface IClearingHouse {
/// @param useTakerBalance only accept false now
struct AddLiquidityParams {
address baseToken;
uint256 base;
uint256 quote;
int24 lowerTick;
int24 upperTick;
uint256 minBase;
uint256 minQuote;
bool useTakerBalance;
uint256 deadline;
}
/// @param liquidity collect fee when 0
struct RemoveLiquidityParams {
address baseToken;
int24 lowerTick;
int24 upperTick;
uint128 liquidity;
uint256 minBase;
uint256 minQuote;
uint256 deadline;
}
struct AddLiquidityResponse {
uint256 base;
uint256 quote;
uint256 fee;
uint256 liquidity;
}
struct RemoveLiquidityResponse {
uint256 base;
uint256 quote;
uint256 fee;
}
/// @param oppositeAmountBound
// B2Q + exact input, want more output quote as possible, so we set a lower bound of output quote
// B2Q + exact output, want less input base as possible, so we set a upper bound of input base
// Q2B + exact input, want more output base as possible, so we set a lower bound of output base
// Q2B + exact output, want less input quote as possible, so we set a upper bound of input quote
// when it's set to 0, it will disable slippage protection entirely regardless of exact input or output
// when it's over or under the bound, it will be reverted
/// @param sqrtPriceLimitX96
// B2Q: the price cannot be less than this value after the swap
// Q2B: the price cannot be greater than this value after the swap
// it will fill the trade until it reaches the price limit but WON'T REVERT
// when it's set to 0, it will disable price limit;
// when it's 0 and exact output, the output amount is required to be identical to the param amount
struct OpenPositionParams {
address baseToken;
bool isBaseToQuote;
bool isExactInput;
uint256 amount;
uint256 oppositeAmountBound;
uint256 deadline;
uint160 sqrtPriceLimitX96;
bytes32 referralCode;
}
struct ClosePositionParams {
address baseToken;
uint160 sqrtPriceLimitX96;
uint256 oppositeAmountBound;
uint256 deadline;
bytes32 referralCode;
}
struct CollectPendingFeeParams {
address trader;
address baseToken;
int24 lowerTick;
int24 upperTick;
}
/// @notice Emitted when open position with non-zero referral code
/// @param referralCode The referral code by partners
event ReferredPositionChanged(bytes32 indexed referralCode);
/// @notice Emitted when taker position is being liquidated
/// @param trader The trader who has been liquidated
/// @param baseToken Virtual base token(ETH, BTC, etc...) address
/// @param positionNotional The cost of position
/// @param positionSize The size of position
/// @param liquidationFee The fee of liquidate
/// @param liquidator The address of liquidator
event PositionLiquidated(
address indexed trader,
address indexed baseToken,
uint256 positionNotional,
uint256 positionSize,
uint256 liquidationFee,
address liquidator
);
/// @notice Emitted when maker's liquidity of a order changed
/// @param maker The one who provide liquidity
/// @param baseToken The address of virtual base token(ETH, BTC, etc...)
/// @param quoteToken The address of virtual USD token
/// @param lowerTick The lower tick of the position in which to add liquidity
/// @param upperTick The upper tick of the position in which to add liquidity
/// @param base The amount of base token added (> 0) / removed (< 0) as liquidity; fees not included
/// @param quote The amount of quote token added ... (same as the above)
/// @param liquidity The amount of liquidity unit added (> 0) / removed (< 0)
/// @param quoteFee The amount of quote token the maker received as fees
event LiquidityChanged(
address indexed maker,
address indexed baseToken,
address indexed quoteToken,
int24 lowerTick,
int24 upperTick,
int256 base,
int256 quote,
int128 liquidity,
uint256 quoteFee
);
/// @notice Emitted when taker's position is being changed
/// @param trader Trader address
/// @param baseToken The address of virtual base token(ETH, BTC, etc...)
/// @param exchangedPositionSize The actual amount swap to uniswapV3 pool
/// @param exchangedPositionNotional The cost of position, include fee
/// @param fee The fee of open/close position
/// @param openNotional The cost of open/close position, < 0: long, > 0: short
/// @param realizedPnl The realized Pnl after open/close position
/// @param sqrtPriceAfterX96 The sqrt price after swap, in X96
event PositionChanged(
address indexed trader,
address indexed baseToken,
int256 exchangedPositionSize,
int256 exchangedPositionNotional,
uint256 fee,
int256 openNotional,
int256 realizedPnl,
uint256 sqrtPriceAfterX96
);
/// @notice Emitted when taker close her position in closed market
/// @param trader Trader address
/// @param baseToken The address of virtual base token(ETH, BTC, etc...)
/// @param closedPositionSize Trader's position size in closed market
/// @param closedPositionNotional Trader's position notional in closed market, based on closed price
/// @param openNotional The cost of open/close position, < 0: long, > 0: short
/// @param realizedPnl The realized Pnl after close position
/// @param closedPrice The close price of position
event PositionClosed(
address indexed trader,
address indexed baseToken,
int256 closedPositionSize,
int256 closedPositionNotional,
int256 openNotional,
int256 realizedPnl,
uint256 closedPrice
);
/// @notice Emitted when settling a trader's funding payment
/// @param trader The address of trader
/// @param baseToken The address of virtual base token(ETH, BTC, etc...)
/// @param fundingPayment The fundingPayment of trader on baseToken market, > 0: payment, < 0 : receipt
event FundingPaymentSettled(address indexed trader, address indexed baseToken, int256 fundingPayment);
/// @notice Emitted when trusted forwarder address changed
/// @dev TrustedForward is only used for metaTx
/// @param forwarder The trusted forwarder address
event TrustedForwarderChanged(address indexed forwarder);
/// @notice Emitted when DelegateApproval address changed
/// @param delegateApproval The address of DelegateApproval
event DelegateApprovalChanged(address indexed delegateApproval);
/// @notice Maker can call `addLiquidity` to provide liquidity on Uniswap V3 pool
/// @dev Tx will fail if adding `base == 0 && quote == 0` / `liquidity == 0`
/// @dev - `AddLiquidityParams.useTakerBalance` is only accept `false` now
/// @param params AddLiquidityParams struct
/// @return response AddLiquidityResponse struct
function addLiquidity(AddLiquidityParams calldata params) external returns (AddLiquidityResponse memory response);
/// @notice Maker can call `removeLiquidity` to remove liquidity
/// @dev remove liquidity will transfer maker impermanent position to taker position,
/// if `liquidity` of RemoveLiquidityParams struct is zero, the action will collect fee from
/// pool to maker
/// @param params RemoveLiquidityParams struct
/// @return response RemoveLiquidityResponse struct
function removeLiquidity(RemoveLiquidityParams calldata params)
external
returns (RemoveLiquidityResponse memory response);
/// @notice Settle all markets fundingPayment to owedRealized Pnl
/// @param trader The address of trader
function settleAllFunding(address trader) external;
/// @notice Trader can call `openPosition` to long/short on baseToken market
/// @dev - `OpenPositionParams.oppositeAmountBound`
/// - B2Q + exact input, want more output quote as possible, so we set a lower bound of output quote
/// - B2Q + exact output, want less input base as possible, so we set a upper bound of input base
/// - Q2B + exact input, want more output base as possible, so we set a lower bound of output base
/// - Q2B + exact output, want less input quote as possible, so we set a upper bound of input quote
/// > when it's set to 0, it will disable slippage protection entirely regardless of exact input or output
/// > when it's over or under the bound, it will be reverted
/// @dev - `OpenPositionParams.sqrtPriceLimitX96`
/// - B2Q: the price cannot be less than this value after the swap
/// - Q2B: the price cannot be greater than this value after the swap
/// > it will fill the trade until it reaches the price limit but WON'T REVERT
/// > when it's set to 0, it will disable price limit;
/// > when it's 0 and exact output, the output amount is required to be identical to the param amount
/// @param params OpenPositionParams struct
/// @return base The amount of baseToken the taker got or spent
/// @return quote The amount of quoteToken the taker got or spent
function openPosition(OpenPositionParams memory params) external returns (uint256 base, uint256 quote);
/// @param trader The address of trader
/// @param params OpenPositionParams struct is the same as `openPosition()`
/// @return base The amount of baseToken the taker got or spent
/// @return quote The amount of quoteToken the taker got or spent
/// @return fee The trading fee
function openPositionFor(address trader, OpenPositionParams memory params)
external
returns (
uint256 base,
uint256 quote,
uint256 fee
);
/// @notice Close trader's position
/// @param params ClosePositionParams struct
/// @return base The amount of baseToken the taker got or spent
/// @return quote The amount of quoteToken the taker got or spent
function closePosition(ClosePositionParams calldata params) external returns (uint256 base, uint256 quote);
/// @notice If trader is underwater, any one can call `liquidate` to liquidate this trader
/// @dev If trader has open orders, need to call `cancelAllExcessOrders` first
/// @dev If positionSize is greater than maxLiquidatePositionSize, liquidate maxLiquidatePositionSize by default
/// @dev If margin ratio >= 0.5 * mmRatio,
/// maxLiquidateRatio = MIN((1, 0.5 * totalAbsPositionValue / absPositionValue)
/// @dev If margin ratio < 0.5 * mmRatio, maxLiquidateRatio = 1
/// @dev maxLiquidatePositionSize = positionSize * maxLiquidateRatio
/// @param trader The address of trader
/// @param baseToken The address of baseToken
/// @param positionSize the position size to be liquidated by liquidator
// and MUST be the same direction as trader's position size
function liquidate(
address trader,
address baseToken,
int256 positionSize
) external;
/// @notice liquidate trader's position and will liquidate the max possible position size
/// @dev If margin ratio >= 0.5 * mmRatio,
/// maxLiquidateRatio = MIN((1, 0.5 * totalAbsPositionValue / absPositionValue)
/// @dev If margin ratio < 0.5 * mmRatio, maxLiquidateRatio = 1
/// @dev maxLiquidatePositionSize = positionSize * maxLiquidateRatio
/// @param trader The address of trader
/// @param baseToken The address of baseToken
function liquidate(address trader, address baseToken) external;
/// @notice Cancel excess order of a maker
/// @dev Order id can get from `OrderBook.getOpenOrderIds`
/// @param maker The address of Maker
/// @param baseToken The address of baseToken
/// @param orderIds The id of the order
function cancelExcessOrders(
address maker,
address baseToken,
bytes32[] calldata orderIds
) external;
/// @notice Cancel all excess orders of a maker if the maker is underwater
/// @dev This function won't fail if the maker has no order but fails when maker is not underwater
/// @param maker The address of maker
/// @param baseToken The address of baseToken
function cancelAllExcessOrders(address maker, address baseToken) external;
/// @notice Close all positions and remove all liquidities of a trader in the closed market
/// @param trader The address of trader
/// @param baseToken The address of baseToken
/// @return base The amount of base token that is closed
/// @return quote The amount of quote token that is closed
function quitMarket(address trader, address baseToken) external returns (uint256 base, uint256 quote);
/// @notice Get account value of trader
/// @dev accountValue = totalCollateralValue + totalUnrealizedPnl, in 18 decimals
/// @param trader The address of trader
/// @return accountValue The account value of trader
function getAccountValue(address trader) external view returns (int256 accountValue);
/// @notice Get QuoteToken address
/// @return quoteToken The quote token address
function getQuoteToken() external view returns (address quoteToken);
/// @notice Get UniswapV3Factory address
/// @return factory UniswapV3Factory address
function getUniswapV3Factory() external view returns (address factory);
/// @notice Get ClearingHouseConfig address
/// @return clearingHouseConfig ClearingHouseConfig address
function getClearingHouseConfig() external view returns (address clearingHouseConfig);
/// @notice Get `Vault` address
/// @return vault `Vault` address
function getVault() external view returns (address vault);
/// @notice Get `Exchange` address
/// @return exchange `Exchange` address
function getExchange() external view returns (address exchange);
/// @notice Get `OrderBook` address
/// @return orderBook `OrderBook` address
function getOrderBook() external view returns (address orderBook);
/// @notice Get AccountBalance address
/// @return accountBalance `AccountBalance` address
function getAccountBalance() external view returns (address accountBalance);
/// @notice Get `InsuranceFund` address
/// @return insuranceFund `InsuranceFund` address
function getInsuranceFund() external view returns (address insuranceFund);
/// @notice Get `DelegateApproval` address
/// @return delegateApproval `DelegateApproval` address
function getDelegateApproval() external view returns (address delegateApproval);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
interface IClearingHouseConfigEvent {
event LiquidationPenaltyRatioChanged(uint24 liquidationPenaltyRatio);
event PartialCloseRatioChanged(uint24 partialCloseRatio);
event TwapIntervalChanged(uint256 twapInterval);
event MaxMarketsPerAccountChanged(uint8 maxMarketsPerAccount);
event SettlementTokenBalanceCapChanged(uint256 cap);
event MaxFundingRateChanged(uint24 rate);
event MarkPriceMarketTwapIntervalChanged(uint32 twapInterval);
event MarkPricePremiumIntervalChanged(uint32 premiumInterval);
}
interface IClearingHouseConfig is IClearingHouseConfigEvent {
/// @return maxMarketsPerAccount Max value of total markets per account
function getMaxMarketsPerAccount() external view returns (uint8 maxMarketsPerAccount);
/// @return imRatio Initial margin ratio
function getImRatio() external view returns (uint24 imRatio);
/// @return mmRatio Maintenance margin requirement ratio
function getMmRatio() external view returns (uint24 mmRatio);
/// @return liquidationPenaltyRatio Liquidation penalty ratio
function getLiquidationPenaltyRatio() external view returns (uint24 liquidationPenaltyRatio);
/// @notice **Deprecated function, will be removed in later release**
/// @return partialCloseRatio Partial close ratio
function getPartialCloseRatio() external view returns (uint24 partialCloseRatio);
/// @return twapInterval TwapInterval for funding and prices (market & index) calculations
function getTwapInterval() external view returns (uint32 twapInterval);
/// @return settlementTokenBalanceCap Max value of settlement token balance
function getSettlementTokenBalanceCap() external view returns (uint256 settlementTokenBalanceCap);
/// @return maxFundingRate Max value of funding rate
function getMaxFundingRate() external view returns (uint24 maxFundingRate);
/// @return marketTwapInterval MarketTwapInterval is the interval of market twap used for mark price calculations
/// @return premiumInterval PremiumInterval is the interval of premium used for mark price calculations
function getMarkPriceConfig() external view returns (uint32 marketTwapInterval, uint32 premiumInterval);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { Collateral } from "../lib/Collateral.sol";
interface ICollateralManager {
/// @notice Emitted when owner add collateral
/// @param token address of token
/// @param priceFeed address of price feed
/// @param collateralRatio collateral ratio
/// @param discountRatio discount ratio for the collateral liquidation
/// @param depositCap max amount of collateral that can be deposited
event CollateralAdded(
address indexed token,
address priceFeed,
uint24 collateralRatio,
uint24 discountRatio,
uint256 depositCap
);
/// @notice Emitted when owner update the address of clearing house config
/// @param clearingHouseConfig address of clearing house config
event ClearingHouseConfigChanged(address indexed clearingHouseConfig);
/// @notice Emitted when owner update the address of vault
/// @param vault address of vault
event VaultChanged(address indexed vault);
/// @notice Emitted when owner update the price feed address of a collateral token
/// @param token address of token
/// @param priceFeed address of price feed
event PriceFeedChanged(address indexed token, address priceFeed);
/// @notice Emitted when owner update the collateral ratio of a collateral token
/// @param token address of token
/// @param collateralRatio collateral ratio
event CollateralRatioChanged(address indexed token, uint24 collateralRatio);
/// @notice Emitted when owner change the discount ratio
/// @param token address of token
/// @param discountRatio discount ratio for the collateral liquidation
event DiscountRatioChanged(address indexed token, uint24 discountRatio);
/// @notice Emitted when owner update the deposit cap of a collateral token
/// @param token address of token
/// @param depositCap max amount of the collateral that can be deposited
event DepositCapChanged(address indexed token, uint256 depositCap);
/// @notice Emitted when owner init or update the max collateral tokens that per account can have,
/// this is can prevent high gas cost.
/// @param maxCollateralTokensPerAccount max amount of collateral tokens that per account can have
event MaxCollateralTokensPerAccountChanged(uint8 maxCollateralTokensPerAccount);
/// @notice Emitted when owner init or update the maintenance margin ratio buffer,
/// the value provides a safe range between the mmRatio & the collateralMMRatio.
/// @param mmRatioBuffer safe buffer number (bps)
event MmRatioBufferChanged(uint24 mmRatioBuffer);
/// @notice Emitted when owner init or update the debt non-settlement token value ratio,
/// maximum `debt / nonSettlementTokenValue` before the account's is liquidatable
/// @param debtNonSettlementTokenValueRatio debt non-settlement token value ratio, ≤ 1
event DebtNonSettlementTokenValueRatioChanged(uint24 debtNonSettlementTokenValueRatio);
/// @notice Emitted when owner init or update the liquidation ratio,
/// the value presents the max repaid ratio of the collateral liquidation.
/// @param liquidationRatio liquidation ratio, ≤ 1
event LiquidationRatioChanged(uint24 liquidationRatio);
/// @notice Emitted when owner init or update the clearing house insurance fund fee ratio,
/// charge fee for clearing house insurance fund.
/// @param clInsuranceFundFeeRatio clearing house insurance fund fee ratio, ≤ 1
event CLInsuranceFundFeeRatioChanged(uint24 clInsuranceFundFeeRatio);
/// @notice Emitted when owner init or update the debt threshold,
/// maximum debt allowed before an account’s collateral is liquidatable.
/// @param debtThreshold debt threshold
event DebtThresholdChanged(uint256 debtThreshold);
/// @notice Emitted when owner init or update the whitelisted debt threshold,
/// maximum debt allowed before an account’s collateral is liquidatable.
/// @param whitelistedDebtThreshold whitelisted debt threshold
event WhitelistedDebtThresholdChanged(address trader, uint256 whitelistedDebtThreshold);
/// @notice Emitted when owner init or update the collateral value dust,
/// if a trader’s debt value falls below this dust threshold,
/// the liquidator will ignore the liquidationRatio.
/// @param collateralValueDust collateral value dust
event CollateralValueDustChanged(uint256 collateralValueDust);
/// @notice Get the address of vault
/// @return vault address of vault
function getVault() external view returns (address);
/// @notice Get the address of clearing house config
/// @return clearingHouseConfig address of clearing house config
function getClearingHouseConfig() external view returns (address);
/// @notice Get collateral config by token address
/// @param token address of token
/// @return collateral config
function getCollateralConfig(address token) external view returns (Collateral.Config memory);
/// @notice Get price feed decimals of the collateral token
/// @param token address of token
/// @return decimals of the price feed
function getPriceFeedDecimals(address token) external view returns (uint8);
/// @notice Get the price of the collateral token
/// @param token address of token
/// @return price of the certain period
function getPrice(address token, uint256 interval) external view returns (uint256);
/// @notice Get the max number of collateral tokens per account
/// @return max number of collateral tokens per account
function getMaxCollateralTokensPerAccount() external view returns (uint8);
/// @notice Get the minimum `margin ratio - mmRatio` before the account's collateral is liquidatable
/// @dev 6 decimals, same decimals as _mmRatio
/// @return ratio
function getMmRatioBuffer() external view returns (uint24);
/// @notice Get the maximum `debt / nonSettlementTokenValue` before the account's collaterals are liquidated
/// @dev 6 decimals
/// @return ratio
function getDebtNonSettlementTokenValueRatio() external view returns (uint24);
/// @notice Get the maximum ratio of debt can be repaid in one transaction
/// @dev 6 decimals. For example, `liquidationRatio` = 50% means
/// the liquidator can repay as much as half of the trader’s debt in one liquidation
/// @return liquidation ratio
function getLiquidationRatio() external view returns (uint24);
/// @notice Get the insurance fund fee ratio when liquidating a trader's collateral
/// @dev 6 decimals. For example, `clInsuranceFundFeeRatio` = 5% means
/// the liquidator will pay 5% of transferred settlement token to insurance fund
/// @return insurance fund fee ratio
function getCLInsuranceFundFeeRatio() external view returns (uint24);
/// @notice Get the default maximum debt (denominated in settlement token) allowed
/// before an account’s collateral is liquidatable.
/// @dev 6 decimals
/// @return debtThreshold
function getDebtThreshold() external view returns (uint256);
/// @notice Get the maximum whitelisted debt (denominated in settlement token) allowed
/// before an account’s collateral is liquidatable.
/// @dev 6 decimals
/// @return debtThreshold
function getDebtThresholdByTrader(address trader) external view returns (uint256);
/// @notice Get the total whitelisted debt (denominated in settlement token) allowed
/// @dev 6 decimals
/// @return totalDebtThreshold
function getTotalWhitelistedDebtThreshold() external view returns (uint256);
/// @notice Get the threshold of the minium repaid.
/// If a trader’s collateral value (denominated in settlement token) falls below the threshold,
/// the liquidator can convert it with 100% `liquidationRatio` so there is no dust left
/// @dev 6 decimals
/// @return Dust collateral value
function getCollateralValueDust() external view returns (uint256);
/// @notice Check if the given token is one of collateral tokens
/// @param token address of token
/// @return true if the token is one of collateral tokens
function isCollateral(address token) external view returns (bool);
/// @notice Require and get the the valid collateral maintenance margin ratio by mmRatioBuffer
/// @param mmRatioBuffer safe margin ratio buffer; 6 decimals, same decimals as _mmRatio
/// @return collateralMmRatio the collateral maintenance margin ratio
function requireValidCollateralMmRatio(uint24 mmRatioBuffer) external view returns (uint24);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
interface IDelegateApproval {
/// @param trader The address of trader
/// @param delegate The address of delegate
/// @param actions The actions to be approved
event DelegationApproved(address indexed trader, address delegate, uint8 actions);
/// @param trader The address of trader
/// @param delegate The address of delegate
/// @param actions The actions to be revoked
event DelegationRevoked(address indexed trader, address delegate, uint8 actions);
/// @param delegate The address of delegate
/// @param actions The actions to be approved
function approve(address delegate, uint8 actions) external;
/// @param delegate The address of delegate
/// @param actions The actions to be revoked
function revoke(address delegate, uint8 actions) external;
/// @return action The value of action `_CLEARINGHOUSE_OPENPOSITION`
function getClearingHouseOpenPositionAction() external pure returns (uint8);
/// @return action The value of action `_CLEARINGHOUSE_ADDLIQUIDITY`
function getClearingHouseAddLiquidityAction() external pure returns (uint8);
/// @return action The value of action `_CLEARINGHOUSE_REMOVELIQUIDITY`
function getClearingHouseRemoveLiquidityAction() external pure returns (uint8);
/// @param trader The address of trader
/// @param delegate The address of delegate
/// @return actions The approved actions
function getApprovedActions(address trader, address delegate) external view returns (uint8);
/// @param trader The address of trader
/// @param delegate The address of delegate
/// @param actions The actions to be checked
/// @return true if delegate is allowed to perform **each** actions for trader, otherwise false
function hasApprovalFor(
address trader,
address delegate,
uint8 actions
) external view returns (bool);
/// @param trader The address of trader
/// @param delegate The address of delegate
/// @return true if delegate can open position for trader, otherwise false
function canOpenPositionFor(address trader, address delegate) external view returns (bool);
/// @param trader The address of trader
/// @param delegate The address of delegate
/// @return true if delegate can add liquidity for trader, otherwise false
function canAddLiquidityFor(address trader, address delegate) external view returns (bool);
/// @param trader The address of trader
/// @param delegate The address of delegate
/// @return true if delegate can remove liquidity for trader, otherwise false
function canRemoveLiquidityFor(address trader, address delegate) external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20Upgradeable {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { Funding } from "../lib/Funding.sol";
interface IExchange {
/// @param amount when closing position, amount(uint256) == takerPositionSize(int256),
/// as amount is assigned as takerPositionSize in ClearingHouse.closePosition()
struct SwapParams {
address trader;
address baseToken;
bool isBaseToQuote;
bool isExactInput;
bool isClose;
uint256 amount;
uint160 sqrtPriceLimitX96;
}
struct SwapResponse {
uint256 base;
uint256 quote;
int256 exchangedPositionSize;
int256 exchangedPositionNotional;
uint256 fee;
uint256 insuranceFundFee;
int256 pnlToBeRealized;
uint256 sqrtPriceAfterX96;
int24 tick;
bool isPartialClose;
uint24 closedRatio;
}
struct SwapCallbackData {
address trader;
address baseToken;
address pool;
uint24 uniswapFeeRatio;
uint256 fee;
}
struct RealizePnlParams {
address trader;
address baseToken;
int256 base;
int256 quote;
}
/// @notice Emitted when the global funding growth is updated
/// @param baseToken Address of the base token
/// @param marketTwap The market twap price when the funding growth is updated
/// @param indexTwap The index twap price when the funding growth is updated
event FundingUpdated(address indexed baseToken, uint256 marketTwap, uint256 indexTwap);
/// @notice Emitted when maxTickCrossedWithinBlock is updated
/// @param baseToken Address of the base token
/// @param maxTickCrossedWithinBlock Max tick allowed to be crossed within block when reducing position
event MaxTickCrossedWithinBlockChanged(address indexed baseToken, uint24 maxTickCrossedWithinBlock);
/// @notice Emitted when accountBalance is updated
/// @param accountBalance The address of accountBalance contract
event AccountBalanceChanged(address accountBalance);
/// @notice The actual swap function
/// @dev can only be called from ClearingHouse
/// @param params The parameters of the swap
/// @return swapResponse The result of the swap
function swap(SwapParams memory params) external returns (SwapResponse memory swapResponse);
/// @notice Settle the funding payment for the time interval since the last settlement
/// @dev This function should be called at the beginning of every high-level function, such as `openPosition()`
/// while it doesn't matter who calls this function
/// this function 1. settles personal funding payment 2. updates global funding growth
/// personal funding payment is settled whenever there is pending funding payment
/// the global funding growth update only happens once per unique timestamp (not blockNumber, due to Arbitrum)
/// @return fundingPayment the funding payment of a trader in one market should be settled into owned realized Pnl
/// @return fundingGrowthGlobal the up-to-date globalFundingGrowth, usually used for later calculations
function settleFunding(address trader, address baseToken)
external
returns (int256 fundingPayment, Funding.Growth memory fundingGrowthGlobal);
/// @notice Get the max ticks allowed to be crossed within a block when reducing position
/// @param baseToken Address of the base token
/// @return maxTickCrossedWithinBlock The max ticks allowed to be crossed within a block when reducing position
function getMaxTickCrossedWithinBlock(address baseToken) external view returns (uint24 maxTickCrossedWithinBlock);
/// @notice Get all the pending funding payment for a trader
/// @return pendingFundingPayment The pending funding payment of the trader.
/// Positive value means the trader pays funding, negative value means the trader receives funding.
function getAllPendingFundingPayment(address trader) external view returns (int256 pendingFundingPayment);
/// @notice Check if current price spread between market price and index twap is over maximum price spread.
/// @param baseToken Address of the base token
/// @return true if over the maximum price spread
function isOverPriceSpread(address baseToken) external view returns (bool);
/// @notice Get the pending funding payment for a trader in a given market
/// @dev this is the view version of _updateFundingGrowth()
/// @return pendingFundingPayment The pending funding payment of a trader in one market,
/// including liquidity & balance coefficients. Positive value means the trader pays funding,
/// negative value means the trader receives funding.
function getPendingFundingPayment(address trader, address baseToken)
external
view
returns (int256 pendingFundingPayment);
/// @notice **Deprecated function, will be removed in the next release, use `getSqrtMarketTwapX96()` instead**
/// Get the square root of the market twap price with the given time interval
/// @dev The return value is a X96 number
/// @param baseToken Address of the base token
/// @param twapInterval The time interval in seconds
/// @return sqrtMarkTwapX96 The square root of the market twap price
function getSqrtMarkTwapX96(address baseToken, uint32 twapInterval) external view returns (uint160 sqrtMarkTwapX96);
/// @notice Get the square root of the market twap price with the given time interval
/// @dev The return value is a X96 number
/// @param baseToken Address of the base token
/// @param twapInterval The time interval in seconds
/// @return sqrtMarketTwapX96 The square root of the market twap price
function getSqrtMarketTwapX96(address baseToken, uint32 twapInterval)
external
view
returns (uint160 sqrtMarketTwapX96);
/// @notice Get the pnl that can be realized if trader reduce position
/// @dev This function normally won't be needed by traders, but it might be useful for 3rd party
/// @param params The params needed to do the query, encoded as `RealizePnlParams` in calldata
/// @return pnlToBeRealized The pnl that can be realized if trader reduce position
function getPnlToBeRealized(RealizePnlParams memory params) external view returns (int256 pnlToBeRealized);
/// @notice Get `OrderBook` contract address
/// @return orderBook `OrderBook` contract address
function getOrderBook() external view returns (address orderBook);
/// @notice Get `AccountBalance` contract address
/// @return accountBalance `AccountBalance` contract address
function getAccountBalance() external view returns (address accountBalance);
/// @notice Get `ClearingHouseConfig` contract address
/// @return clearingHouse `ClearingHouseConfig` contract address
function getClearingHouseConfig() external view returns (address clearingHouse);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
interface IIndexPrice {
/// @notice Returns the index price of the token.
/// @param interval The interval represents twap interval.
/// @return indexPrice Twap price with interval
function getIndexPrice(uint256 interval) external view returns (uint256 indexPrice);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
interface IInsuranceFund {
/// @param borrower The address of the borrower (actually is `Vault` address)
/// @dev (Deprecated function, will be removed in the next release), In the previous version `Vault`
/// used to "borrow" from IF by calling `IF.borrow()`. We have since removed the behavior but
/// kept the variable name "borrower" for backward-compatibility
event BorrowerChanged(address borrower);
/// @param vault The address of the vault
event VaultChanged(address vault);
/// @param repaidAmount Repaid amount of the token
/// @param tokenBalanceAfterRepaid InsuranceFund's token balance after repay
event Repaid(uint256 repaidAmount, uint256 tokenBalanceAfterRepaid);
/// @param distributionThreshold Distribution threshold amount
/// @dev We will transfer fee to `SurplusBeneficiary` if `InsuranceFund` free collateral
/// is over distribution threshold
event DistributionThresholdChanged(uint256 distributionThreshold);
/// @param surplusBeneficiary The address of `SurplusBeneficiary`
event SurplusBeneficiaryChanged(address surplusBeneficiary);
/// @param surplus The amount of distribution
/// @param insuranceFundCapacity The capacity of `insuranceFund` contract
/// @param insuranceFundFreeCollateral The free collateral(usdc) of `insuranceFund` contract in vault
/// @param distributionThreshold The distribution threshold amount
event FeeDistributed(
uint256 surplus,
uint256 insuranceFundCapacity,
uint256 insuranceFundFreeCollateral,
uint256 distributionThreshold
);
/// @notice If insurance has negative accountValue of vault, will deposit amount to vault
function repay() external;
/// @notice If balance of `InsuranceFund` is over `distributionThreshold`, transfer diff to `SurplusBeneficiary`
/// @dev Insurance Fund should only distribute revenues surplus earned on the platform.
/// In other words, funds directly held in the Insurance Fund contract (`insuranceFundWalletBalance`)
/// contributes to `insuranceFundTotalBalance` but not necessarily to `surplus`. Anyone can send funds to
/// Insurance Fund and help it reach `distributionThreshold` sooner, but once `surplus` exceeds
/// the revenues earned on the platform (`insuranceFundFreeCollateral`), sending more funds
/// won’t increase `surplus` further
/// @return surplus The surplus of distribution
function distributeFee() external returns (uint256 surplus);
/// @notice Get settlement token address
/// @return token The address of settlement token
function getToken() external view returns (address token);
/// @notice (Deprecated function, will be removed in the next release), Get borrower(`Vault`) address
/// @return vault The address of `Vault`
function getBorrower() external view returns (address vault);
/// @notice Get `Vault` address
/// @return vault The address of `Vault`
function getVault() external view returns (address vault);
/// @notice Get `InsuranceFund` capacity
/// @return capacityX10_S The capacity value (settlementTokenValue + walletBalance) in settlement token's decimals
function getInsuranceFundCapacity() external view returns (int256 capacityX10_S);
/// @notice Get insurance distributution threshold, this value is for fee distribution
/// @return distributionThreshold The distribution threshold number
function getDistributionThreshold() external view returns (uint256 distributionThreshold);
/// @notice Get SurplusBeneficiary
/// @return surplusBeneficiary The address of `SurplusBeneficiary`
function getSurplusBeneficiary() external view returns (address surplusBeneficiary);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
interface IMarketRegistry {
struct MarketInfo {
address pool;
uint24 exchangeFeeRatio;
uint24 uniswapFeeRatio;
uint24 insuranceFundFeeRatio;
uint24 maxPriceSpreadRatio;
}
/// @notice Emitted when a new market is created.
/// @param baseToken The address of the base token
/// @param feeRatio Fee ratio of the market
/// @param pool The address of the pool
event PoolAdded(address indexed baseToken, uint24 indexed feeRatio, address indexed pool);
/// @notice Emitted when the fee ratio of a market is updated.
/// @param baseToken The address of the base token
/// @param feeRatio Fee ratio of the market
event FeeRatioChanged(address baseToken, uint24 feeRatio);
/// @notice Emitted when the insurance fund fee ratio is updated.
/// @param baseToken The address of the base token
/// @param feeRatio Insurance fund fee ratio
event InsuranceFundFeeRatioChanged(address baseToken, uint24 feeRatio);
/// @notice Emitted when the max orders per market is updated.
/// @param maxOrdersPerMarket Max orders per market
event MaxOrdersPerMarketChanged(uint8 maxOrdersPerMarket);
/// @notice Emitted when the max market price spread ratio is updated.
/// @param baseToken The address of the base token
/// @param spreadRatio Max market price spread ratio
event MarketMaxPriceSpreadRatioChanged(address indexed baseToken, uint24 spreadRatio);
/// @notice Emitted when the trader's fee discount ratio gets updated.
/// @param trader The address of the trader
/// @param discountRatio Fee discount ratio (percent-off)
event FeeDiscountRatioChanged(address indexed trader, uint24 discountRatio);
/// @notice Get the pool address (UNIv3 pool) by given base token address
/// @param baseToken The address of the base token
/// @return pool The address of the pool
function getPool(address baseToken) external view returns (address pool);
/// @notice Get the fee ratio of a given market
/// @dev The ratio is in `1e6` format, that means `1% = 1e4`
/// @param baseToken The address of the base token
/// @return feeRatio The fee ratio of the market, it is a decimal in `1e6`
function getFeeRatio(address baseToken) external view returns (uint24 feeRatio);
/// @notice Get the insurance fund fee ratio of a given market
/// @dev The ratio is in `1e6` format, that means `1% = 1e4`
/// @param baseToken The address of the base token
/// @return feeRatio The fee ratio of the market, it is a decimal in `1e6`
function getInsuranceFundFeeRatio(address baseToken) external view returns (uint24 feeRatio);
/// @notice Get the market info by given base token address
/// @param baseToken The address of the base token
/// @return info The market info encoded as `MarketInfo`
function getMarketInfo(address baseToken) external view returns (MarketInfo memory info);
/// @notice Get the market info by given trader address and base token address
/// @param trader The address of the trader
/// @param baseToken The address of the base token
/// @return info The market info encoded as `MarketInfo`
function getMarketInfoByTrader(address trader, address baseToken) external view returns (MarketInfo memory info);
/// @notice Get the quote token address
/// @return quoteToken The address of the quote token
function getQuoteToken() external view returns (address quoteToken);
/// @notice Get Uniswap factory address
/// @return factory The address of the Uniswap factory
function getUniswapV3Factory() external view returns (address factory);
/// @notice Get max allowed orders per market
/// @return maxOrdersPerMarket The max allowed orders per market
function getMaxOrdersPerMarket() external view returns (uint8 maxOrdersPerMarket);
/// @notice Check if a pool exist by given base token address
/// @return hasPool True if the pool exist, false otherwise
function hasPool(address baseToken) external view returns (bool hasPool);
/// @return marketMaxPriceSpreadRatio Max price spread ratio of the market
function getMarketMaxPriceSpreadRatio(address baseToken) external view returns (uint24 marketMaxPriceSpreadRatio);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
interface IMarketRegistryFeeManager {
/// @notice Emitted when the Fee Manager is changed
/// @param account The address of the account being changed
/// @param isFeeManager Indicate if the address is a Fee Manager
event FeeManagerChanged(address account, bool isFeeManager);
/// @notice Check if address is Fee Manager
/// @return isFeeManager Indicate if the address is a Fee Manager
function isFeeManager(address account) external view returns (bool isFeeManager);
function setFeeDiscountRatio(address trader, uint24 discountRatio) external;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { Funding } from "../lib/Funding.sol";
import { OpenOrder } from "../lib/OpenOrder.sol";
interface IOrderBook {
struct AddLiquidityParams {
address trader;
address baseToken;
uint256 base;
uint256 quote;
int24 lowerTick;
int24 upperTick;
Funding.Growth fundingGrowthGlobal;
}
struct RemoveLiquidityParams {
address maker;
address baseToken;
int24 lowerTick;
int24 upperTick;
uint128 liquidity;
}
struct AddLiquidityResponse {
uint256 base;
uint256 quote;
uint256 fee;
uint128 liquidity;
}
struct RemoveLiquidityResponse {
uint256 base;
uint256 quote;
uint256 fee;
int256 takerBase;
int256 takerQuote;
}
struct ReplaySwapParams {
address baseToken;
bool isBaseToQuote;
bool shouldUpdateState;
int256 amount;
uint160 sqrtPriceLimitX96;
uint24 exchangeFeeRatio;
uint24 uniswapFeeRatio;
Funding.Growth globalFundingGrowth;
address pool;
uint24 insuranceFundFeeRatio;
}
/// @param insuranceFundFee = fee * insuranceFundFeeRatio
struct ReplaySwapResponse {
int24 tick;
uint256 fee;
uint256 insuranceFundFee;
}
struct MintCallbackData {
address trader;
address pool;
}
/// @notice Emitted when the `Exchange` contract address changed
/// @param exchange The address of exchange contract
event ExchangeChanged(address indexed exchange);
/// @notice Add liquidity logic
/// @dev Only used by `ClearingHouse` contract
/// @param params Add liquidity params, detail on `IOrderBook.AddLiquidityParams`
/// @return response Add liquidity response, detail on `IOrderBook.AddLiquidityResponse`
function addLiquidity(AddLiquidityParams calldata params) external returns (AddLiquidityResponse memory response);
/// @notice Remove liquidity logic, only used by `ClearingHouse` contract
/// @param params Remove liquidity params, detail on `IOrderBook.RemoveLiquidityParams`
/// @return response Remove liquidity response, detail on `IOrderBook.RemoveLiquidityResponse`
function removeLiquidity(RemoveLiquidityParams calldata params)
external
returns (RemoveLiquidityResponse memory response);
/// @dev This is the non-view version of `getLiquidityCoefficientInFundingPayment()`,
/// only can be called by `Exchange` contract
/// @param trader The trader address
/// @param baseToken The base token address
/// @param fundingGrowthGlobal The funding growth info, detail on `Funding.Growth`
/// @return liquidityCoefficientInFundingPayment the funding payment of all orders/liquidity of a maker
function updateFundingGrowthAndLiquidityCoefficientInFundingPayment(
address trader,
address baseToken,
Funding.Growth memory fundingGrowthGlobal
) external returns (int256 liquidityCoefficientInFundingPayment);
/// @notice Replay the swap and get the swap result (price impact and swap fee),
/// only can be called by `Exchange` contract;
/// @dev `ReplaySwapResponse.insuranceFundFee = fee * insuranceFundFeeRatio`
/// @param params ReplaySwap params, detail on `IOrderBook.ReplaySwapParams`
/// @return response The swap result encoded in `ReplaySwapResponse`
function replaySwap(ReplaySwapParams memory params) external returns (ReplaySwapResponse memory response);
function updateOrderDebt(
bytes32 orderId,
int256 base,
int256 quote
) external;
/// @notice Get open order ids of a trader in the given market
/// @param trader The trader address
/// @param baseToken The base token address
/// @return orderIds The open order ids
function getOpenOrderIds(address trader, address baseToken) external view returns (bytes32[] memory orderIds);
/// @notice Get open order info by given order id
/// @param orderId The order id
/// @return info The open order info encoded in `OpenOrder.Info`
function getOpenOrderById(bytes32 orderId) external view returns (OpenOrder.Info memory info);
/// @notice Get open order info by given base token, upper tick and lower tick
/// @param trader The trader address
/// @param baseToken The base token address
/// @param upperTick The upper tick
/// @param lowerTick The lower tick
/// @return info he open order info encoded in `OpenOrder.Info`
function getOpenOrder(
address trader,
address baseToken,
int24 lowerTick,
int24 upperTick
) external view returns (OpenOrder.Info memory info);
/// @notice Check if the specified trader has order in given markets
/// @param trader The trader address
/// @param tokens The base token addresses
/// @return hasOrder True if the trader has order in given markets
function hasOrder(address trader, address[] calldata tokens) external view returns (bool hasOrder);
/// @notice Get the total quote token amount and pending fees of all orders in given markets
/// @param trader The trader address
/// @param baseTokens The base token addresses
/// @return totalQuoteAmountInPools The total quote token amount
/// @return totalPendingFee The total pending fees in the orders
function getTotalQuoteBalanceAndPendingFee(address trader, address[] calldata baseTokens)
external
view
returns (int256 totalQuoteAmountInPools, uint256 totalPendingFee);
/// @notice Get the total token amount (quote or base) and pending fees of all orders in the given market
/// @param trader The trader address
/// @param baseToken The base token addresses
/// @param fetchBase True if fetch base token amount, false if fetch quote token amount
/// @return tokenAmount The total quote/base token amount
/// @return totalPendingFee The total pending fees in the orders
function getTotalTokenAmountInPoolAndPendingFee(
address trader,
address baseToken,
bool fetchBase
) external view returns (uint256 tokenAmount, uint256 totalPendingFee);
/// @notice Get the total debt token amount (base or quote) of all orders in the given market
/// @param trader The trader address
/// @param baseToken The base token address
/// @param fetchBase True if fetch base token amount, false if fetch quote token amount
/// @return debtAmount The total debt token amount
function getTotalOrderDebt(
address trader,
address baseToken,
bool fetchBase
) external view returns (uint256 debtAmount);
/// @notice Get the pending funding payment of all orders in the given market
/// @dev This is the view version of `updateFundingGrowthAndLiquidityCoefficientInFundingPayment()`, so only
/// part of the funding payment will be returned. Use it with caution because it does not return all the pending
/// funding payment of orders. **Normally you won't need to use this function**
/// @return liquidityCoefficientInFundingPayment the funding payment of all orders/liquidity of a maker
function getLiquidityCoefficientInFundingPayment(
address trader,
address baseToken,
Funding.Growth memory fundingGrowthGlobal
) external view returns (int256 liquidityCoefficientInFundingPayment);
/// @notice Get the pending fees of a order
/// @param trader The trader address
/// @param baseToken The base token address
/// @param lowerTick The lower tick
/// @param upperTick The upper tick
/// @return fee The pending fees
function getPendingFee(
address trader,
address baseToken,
int24 lowerTick,
int24 upperTick
) external view returns (uint256 fee);
/// @notice Get `Exchange` contract address
/// @return exchange The `Exchange` contract address
function getExchange() external view returns (address exchange);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
interface IVault {
/// @notice Emitted when trader deposit collateral into vault
/// @param collateralToken The address of token deposited
/// @param trader The address of trader
/// @param amount The amount of token deposited
event Deposited(address indexed collateralToken, address indexed trader, uint256 amount);
/// @notice Emitted when trader withdraw collateral from vault
/// @param collateralToken The address of token withdrawn
/// @param trader The address of trader
/// @param amount The amount of token withdrawn
event Withdrawn(address indexed collateralToken, address indexed trader, uint256 amount);
/// @notice Emitted when a trader's collateral is liquidated
/// @param trader The address of trader
/// @param collateralToken The address of the token that is liquidated
/// @param liquidator The address of liquidator
/// @param collateral The amount of collateral token liquidated
/// @param repaidSettlementWithoutInsuranceFundFeeX10_S The amount of settlement token repaid
/// for trader (in settlement token's decimals)
/// @param insuranceFundFeeX10_S The amount of insurance fund fee paid(in settlement token's decimals)
/// @param discountRatio The discount ratio of liquidation price
event CollateralLiquidated(
address indexed trader,
address indexed collateralToken,
address indexed liquidator,
uint256 collateral,
uint256 repaidSettlementWithoutInsuranceFundFeeX10_S,
uint256 insuranceFundFeeX10_S,
uint24 discountRatio
);
/// @notice Emitted when trustedForwarder is changed
/// @dev trustedForwarder is only used for metaTx
/// @param trustedForwarder The address of trustedForwarder
event TrustedForwarderChanged(address indexed trustedForwarder);
/// @notice Emitted when clearingHouse is changed
/// @param clearingHouse The address of clearingHouse
event ClearingHouseChanged(address indexed clearingHouse);
/// @notice Emitted when collateralManager is changed
/// @param collateralManager The address of collateralManager
event CollateralManagerChanged(address indexed collateralManager);
/// @notice Emitted when WETH9 is changed
/// @param WETH9 The address of WETH9
event WETH9Changed(address indexed WETH9);
/// @notice Emitted when bad debt realized and settled
/// @param trader Address of the trader
/// @param amount Absolute amount of bad debt
event BadDebtSettled(address indexed trader, uint256 amount);
/// @notice Deposit collateral into vault
/// @param token The address of the token to deposit
/// @param amount The amount of the token to deposit
function deposit(address token, uint256 amount) external;
/// @notice Deposit the collateral token for other account
/// @param to The address of the account to deposit to
/// @param token The address of collateral token
/// @param amount The amount of the token to deposit
function depositFor(
address to,
address token,
uint256 amount
) external;
/// @notice Deposit ETH as collateral into vault
function depositEther() external payable;
/// @notice Deposit ETH as collateral for specified account
/// @param to The address of the account to deposit to
function depositEtherFor(address to) external payable;
/// @notice Withdraw collateral from vault
/// @param token The address of the token to withdraw
/// @param amount The amount of the token to withdraw
function withdraw(address token, uint256 amount) external;
/// @notice Withdraw ETH from vault
/// @param amount The amount of the ETH to withdraw
function withdrawEther(uint256 amount) external;
/// @notice Withdraw all free collateral from vault
/// @param token The address of the token to withdraw
/// @return amount The amount of the token withdrawn
function withdrawAll(address token) external returns (uint256 amount);
/// @notice Withdraw all free collateral of ETH from vault
/// @return amount The amount of ETH withdrawn
function withdrawAllEther() external returns (uint256 amount);
/// @notice Liquidate trader's collateral by given settlement token amount or non settlement token amount
/// @param trader The address of trader that will be liquidated
/// @param token The address of non settlement collateral token that the trader will be liquidated
/// @param amount The amount of settlement token that the liquidator will repay for trader or
/// the amount of non-settlement collateral token that the liquidator will charge from trader
/// @param isDenominatedInSettlementToken Whether the amount is denominated in settlement token or not
/// @return returnAmount The amount of a non-settlement token (in its native decimals) that is liquidated
/// when `isDenominatedInSettlementToken` is true or the amount of settlement token that is repaid
/// when `isDenominatedInSettlementToken` is false
function liquidateCollateral(
address trader,
address token,
uint256 amount,
bool isDenominatedInSettlementToken
) external returns (uint256 returnAmount);
/// @notice Settle trader's bad debt
/// @param trader The address of trader that will be settled
function settleBadDebt(address trader) external;
/// @notice Get the specified trader's settlement token balance, without pending fee, funding payment
/// and owed realized PnL
/// @param trader The address of the trader
/// @dev The function is equivalent to `getBalanceByToken(trader, settlementToken)`
/// We keep this function solely for backward-compatibility with the older single-collateral system.
/// In practical applications, the developer might want to use `getSettlementTokenValue()` instead
/// because the latter includes pending fee, funding payment etc.
/// and therefore more accurately reflects a trader's settlement (ex. USDC) balance
/// @return balance The balance amount (in settlement token's decimals)
function getBalance(address trader) external view returns (int256 balance);
/// @notice Get the balance of Vault of the specified collateral token and trader
/// @param trader The address of the trader
/// @param token The address of the collateral token
/// @return balance The balance amount (in its native decimals)
function getBalanceByToken(address trader, address token) external view returns (int256 balance);
/// @notice Get the array of collateral token addresses that a trader has in their account
/// @param trader The address of the trader
/// @return collateralTokens array of collateral token addresses
function getCollateralTokens(address trader) external view returns (address[] memory collateralTokens);
/// @notice Get account value of the specified trader
/// @param trader The address of the trader
/// @return accountValueX10_S account value (in settlement token's decimals)
function getAccountValue(address trader) external view returns (int256 accountValueX10_S);
/// @notice Get the free collateral value denominated in the settlement token of the specified trader
/// @param trader The address of the trader
/// @return freeCollateral the value (in settlement token's decimals) of free collateral available
/// for withdraw or opening new positions or orders)
function getFreeCollateral(address trader) external view returns (uint256 freeCollateral);
/// @notice Get the free collateral amount of the specified trader and collateral ratio
/// @dev There are three configurations for different insolvency risk tolerances:
/// **conservative, moderate &aggressive**. We will start with the **conservative** one
/// and gradually move to **aggressive** to increase capital efficiency
/// @param trader The address of the trader
/// @param ratio The margin requirement ratio, imRatio or mmRatio
/// @return freeCollateralByRatio freeCollateral (in settlement token's decimals), by using the
/// input margin requirement ratio; can be negative
function getFreeCollateralByRatio(address trader, uint24 ratio)
external
view
returns (int256 freeCollateralByRatio);
/// @notice Get the free collateral amount of the specified collateral token of specified trader
/// @param trader The address of the trader
/// @param token The address of the collateral token
/// @return freeCollateral amount of that token (in the token's native decimals)
function getFreeCollateralByToken(address trader, address token) external view returns (uint256 freeCollateral);
/// @notice Get the specified trader's settlement value, including pending fee, funding payment,
/// owed realized PnL and unrealized PnL
/// @dev Note the difference between `settlementTokenBalanceX10_S`, `getSettlementTokenValue()` and `getBalance()`:
/// They are all settlement token balances but with or without
/// pending fee, funding payment, owed realized PnL, unrealized PnL, respectively
/// In practical applications, we use `getSettlementTokenValue()` to get the trader's debt (if < 0)
/// @param trader The address of the trader
/// @return balance The balance amount (in settlement token's decimals)
function getSettlementTokenValue(address trader) external view returns (int256 balance);
/// @notice Get the settlement token address
/// @dev We assume the settlement token should match the denominator of the price oracle.
/// i.e. if the settlement token is USDC, then the oracle should be priced in USD
/// @return settlementToken The address of the settlement token
function getSettlementToken() external view returns (address settlementToken);
/// @notice Check if a given trader's collateral token can be liquidated; liquidation criteria:
/// 1. margin ratio falls below maintenance threshold + 20bps (mmRatioBuffer)
/// 2. USDC debt > nonSettlementTokenValue * debtNonSettlementTokenValueRatio (ex: 75%)
/// 3. USDC debt > debtThreshold (ex: $10000)
// USDC debt = USDC balance + Total Unrealized PnL
/// @param trader The address of the trader
/// @return isLiquidatable If the trader can be liquidated
function isLiquidatable(address trader) external view returns (bool isLiquidatable);
/// @notice get the margin requirement for collateral liquidation of a trader
/// @dev this value is compared with `ClearingHouse.getAccountValue()` (int)
/// @param trader The address of the trader
/// @return marginRequirement margin requirement (in 18 decimals)
function getMarginRequirementForCollateralLiquidation(address trader)
external
view
returns (int256 marginRequirement);
/// @notice Get the maintenance margin ratio for collateral liquidation
/// @return collateralMmRatio The maintenance margin ratio for collateral liquidation
function getCollateralMmRatio() external view returns (uint24 collateralMmRatio);
/// @notice Get a trader's liquidatable collateral amount by a given settlement amount
/// @param token The address of the token of the trader's collateral
/// @param settlementX10_S The amount of settlement token the liquidator wants to pay
/// @return collateral The collateral amount(in its native decimals) the liquidator can get
function getLiquidatableCollateralBySettlement(address token, uint256 settlementX10_S)
external
view
returns (uint256 collateral);
/// @notice Get a trader's repaid settlement amount by a given collateral amount
/// @param token The address of the token of the trader's collateral
/// @param collateral The amount of collateral token the liquidator wants to get
/// @return settlementX10_S The settlement amount(in settlement token's decimals) the liquidator needs to pay
function getRepaidSettlementByCollateral(address token, uint256 collateral)
external
view
returns (uint256 settlementX10_S);
/// @notice Get a trader's max repaid settlement & max liquidatable collateral by a given collateral token
/// @param trader The address of the trader
/// @param token The address of the token of the trader's collateral
/// @return maxRepaidSettlementX10_S The maximum settlement amount(in settlement token's decimals)
/// the liquidator needs to pay to liquidate a trader's collateral token
/// @return maxLiquidatableCollateral The maximum liquidatable collateral amount
/// (in the collateral token's native decimals) of a trader
function getMaxRepaidSettlementAndLiquidatableCollateral(address trader, address token)
external
view
returns (uint256 maxRepaidSettlementX10_S, uint256 maxLiquidatableCollateral);
/// @notice Get settlement token decimals
/// @dev cached the settlement token's decimal for gas optimization
/// @return decimals The decimals of settlement token
function decimals() external view returns (uint8 decimals);
/// @notice (Deprecated) Get the borrowed settlement token amount from insurance fund
/// @return debtAmount The debt amount (in settlement token's decimals)
function getTotalDebt() external view returns (uint256 debtAmount);
/// @notice Get `ClearingHouseConfig` contract address
/// @return clearingHouseConfig The address of `ClearingHouseConfig` contract
function getClearingHouseConfig() external view returns (address clearingHouseConfig);
/// @notice Get `AccountBalance` contract address
/// @return accountBalance The address of `AccountBalance` contract
function getAccountBalance() external view returns (address accountBalance);
/// @notice Get `InsuranceFund` contract address
/// @return insuranceFund The address of `InsuranceFund` contract
function getInsuranceFund() external view returns (address insuranceFund);
/// @notice Get `Exchange` contract address
/// @return exchange The address of `Exchange` contract
function getExchange() external view returns (address exchange);
/// @notice Get `ClearingHouse` contract address
/// @return clearingHouse The address of `ClearingHouse` contract
function getClearingHouse() external view returns (address clearingHouse);
/// @notice Get `CollateralManager` contract address
/// @return clearingHouse The address of `CollateralManager` contract
function getCollateralManager() external view returns (address clearingHouse);
/// @notice Get `WETH9` contract address
/// @return clearingHouse The address of `WETH9` contract
function getWETH9() external view returns (address clearingHouse);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
interface IVirtualToken {
function isInWhitelist(address account) external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
library AccountMarket {
/// @param lastTwPremiumGrowthGlobalX96 the last time weighted premiumGrowthGlobalX96
struct Info {
int256 takerPositionSize;
int256 takerOpenNotional;
int256 lastTwPremiumGrowthGlobalX96;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.6;
library Collateral {
struct Config {
address priceFeed;
uint24 collateralRatio;
uint24 discountRatio;
uint256 depositCap;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { Tick } from "./Tick.sol";
import { PerpMath } from "./PerpMath.sol";
import { OpenOrder } from "./OpenOrder.sol";
import { PerpSafeCast } from "./PerpSafeCast.sol";
import { PerpFixedPoint96 } from "./PerpFixedPoint96.sol";
import { TickMath } from "@uniswap/v3-core/contracts/libraries/TickMath.sol";
import { LiquidityAmounts } from "@uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol";
import { SignedSafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
library Funding {
using PerpSafeCast for uint256;
using PerpSafeCast for uint128;
using SignedSafeMathUpgradeable for int256;
//
// STRUCT
//
/// @dev tw: time-weighted
/// @param twPremiumX96 overflow inspection (as twPremiumX96 > twPremiumDivBySqrtPriceX96):
// max = 2 ^ (255 - 96) = 2 ^ 159 = 7.307508187E47
// assume premium = 10000, time = 10 year = 60 * 60 * 24 * 365 * 10 -> twPremium = 3.1536E12
struct Growth {
int256 twPremiumX96;
int256 twPremiumDivBySqrtPriceX96;
}
//
// CONSTANT
//
/// @dev block-based funding is calculated as: premium * timeFraction / 1 day, for 1 day as the default period
int256 internal constant _DEFAULT_FUNDING_PERIOD = 1 days;
//
// INTERNAL PURE
//
function calcPendingFundingPaymentWithLiquidityCoefficient(
int256 baseBalance,
int256 twPremiumGrowthGlobalX96,
Growth memory fundingGrowthGlobal,
int256 liquidityCoefficientInFundingPayment
) internal pure returns (int256) {
int256 balanceCoefficientInFundingPayment =
PerpMath.mulDiv(
baseBalance,
fundingGrowthGlobal.twPremiumX96.sub(twPremiumGrowthGlobalX96),
uint256(PerpFixedPoint96._IQ96)
);
return
liquidityCoefficientInFundingPayment.add(balanceCoefficientInFundingPayment).div(_DEFAULT_FUNDING_PERIOD);
}
/// @dev the funding payment of an order/liquidity is composed of
/// 1. funding accrued inside the range 2. funding accrued below the range
/// there is no funding when the price goes above the range, as liquidity is all swapped into quoteToken
/// @return liquidityCoefficientInFundingPayment the funding payment of an order/liquidity
function calcLiquidityCoefficientInFundingPaymentByOrder(
OpenOrder.Info memory order,
Tick.FundingGrowthRangeInfo memory fundingGrowthRangeInfo
) internal pure returns (int256) {
uint160 sqrtPriceX96AtUpperTick = TickMath.getSqrtRatioAtTick(order.upperTick);
// base amount below the range
uint256 baseAmountBelow =
LiquidityAmounts.getAmount0ForLiquidity(
TickMath.getSqrtRatioAtTick(order.lowerTick),
sqrtPriceX96AtUpperTick,
order.liquidity
);
// funding below the range
int256 fundingBelowX96 =
baseAmountBelow.toInt256().mul(
fundingGrowthRangeInfo.twPremiumGrowthBelowX96.sub(order.lastTwPremiumGrowthBelowX96)
);
// funding inside the range =
// liquidity * (ΔtwPremiumDivBySqrtPriceGrowthInsideX96 - ΔtwPremiumGrowthInsideX96 / sqrtPriceAtUpperTick)
int256 fundingInsideX96 =
order.liquidity.toInt256().mul(
// ΔtwPremiumDivBySqrtPriceGrowthInsideX96
fundingGrowthRangeInfo
.twPremiumDivBySqrtPriceGrowthInsideX96
.sub(order.lastTwPremiumDivBySqrtPriceGrowthInsideX96)
.sub(
// ΔtwPremiumGrowthInsideX96
PerpMath.mulDiv(
fundingGrowthRangeInfo.twPremiumGrowthInsideX96.sub(order.lastTwPremiumGrowthInsideX96),
PerpFixedPoint96._IQ96,
sqrtPriceX96AtUpperTick
)
)
);
return fundingBelowX96.add(fundingInsideX96).div(PerpFixedPoint96._IQ96);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
library OpenOrder {
/// @param lastFeeGrowthInsideX128 fees in quote token recorded in Exchange
/// because of block-based funding, quote-only and customized fee, all fees are in quote token
struct Info {
uint128 liquidity;
int24 lowerTick;
int24 upperTick;
uint256 lastFeeGrowthInsideX128;
int256 lastTwPremiumGrowthInsideX96;
int256 lastTwPremiumGrowthBelowX96;
int256 lastTwPremiumDivBySqrtPriceGrowthInsideX96;
uint256 baseDebt;
uint256 quoteDebt;
}
function calcOrderKey(
address trader,
address baseToken,
int24 lowerTick,
int24 upperTick
) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(trader, baseToken, lowerTick, upperTick));
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
library PerpFixedPoint96 {
int256 internal constant _IQ96 = 0x1000000000000000000000000;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { FixedPoint96 } from "@uniswap/v3-core/contracts/libraries/FixedPoint96.sol";
import { FullMath } from "@uniswap/v3-core/contracts/libraries/FullMath.sol";
import { PerpSafeCast } from "./PerpSafeCast.sol";
import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/MathUpgradeable.sol";
import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import { SignedSafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
library PerpMath {
using PerpSafeCast for int256;
using SignedSafeMathUpgradeable for int256;
using SafeMathUpgradeable for uint256;
function formatSqrtPriceX96ToPriceX96(uint160 sqrtPriceX96) internal pure returns (uint256) {
return FullMath.mulDiv(sqrtPriceX96, sqrtPriceX96, FixedPoint96.Q96);
}
function formatX10_18ToX96(uint256 valueX10_18) internal pure returns (uint256) {
return FullMath.mulDiv(valueX10_18, FixedPoint96.Q96, 1 ether);
}
function formatX96ToX10_18(uint256 valueX96) internal pure returns (uint256) {
return FullMath.mulDiv(valueX96, 1 ether, FixedPoint96.Q96);
}
function max(int256 a, int256 b) internal pure returns (int256) {
return a >= b ? a : b;
}
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
function abs(int256 value) internal pure returns (uint256) {
return value >= 0 ? value.toUint256() : neg256(value).toUint256();
}
function neg256(int256 a) internal pure returns (int256) {
require(a > -2**255, "PerpMath: inversion overflow");
return -a;
}
function neg256(uint256 a) internal pure returns (int256) {
return -PerpSafeCast.toInt256(a);
}
function neg128(int128 a) internal pure returns (int128) {
require(a > -2**127, "PerpMath: inversion overflow");
return -a;
}
function neg128(uint128 a) internal pure returns (int128) {
return -PerpSafeCast.toInt128(a);
}
function divBy10_18(int256 value) internal pure returns (int256) {
// no overflow here
return value / (1 ether);
}
function divBy10_18(uint256 value) internal pure returns (uint256) {
// no overflow here
return value / (1 ether);
}
function subRatio(uint24 a, uint24 b) internal pure returns (uint24) {
require(b <= a, "PerpMath: subtraction overflow");
return a - b;
}
function mulRatio(uint256 value, uint24 ratio) internal pure returns (uint256) {
return FullMath.mulDiv(value, ratio, 1e6);
}
function mulRatio(int256 value, uint24 ratio) internal pure returns (int256) {
return mulDiv(value, int256(ratio), 1e6);
}
function divRatio(uint256 value, uint24 ratio) internal pure returns (uint256) {
return FullMath.mulDiv(value, 1e6, ratio);
}
/// @param denominator cannot be 0 and is checked in FullMath.mulDiv()
function mulDiv(
int256 a,
int256 b,
uint256 denominator
) internal pure returns (int256 result) {
uint256 unsignedA = a < 0 ? uint256(neg256(a)) : uint256(a);
uint256 unsignedB = b < 0 ? uint256(neg256(b)) : uint256(b);
bool negative = ((a < 0 && b > 0) || (a > 0 && b < 0)) ? true : false;
uint256 unsignedResult = FullMath.mulDiv(unsignedA, unsignedB, denominator);
result = negative ? neg256(unsignedResult) : PerpSafeCast.toInt256(unsignedResult);
return result;
}
function findMedianOfThree(
uint256 v1,
uint256 v2,
uint256 v3
) internal pure returns (uint256) {
return MathUpgradeable.max(MathUpgradeable.min(v1, v2), MathUpgradeable.min(MathUpgradeable.max(v1, v2), v3));
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.7.6;
/**
* @dev copy from "@openzeppelin/contracts-upgradeable/utils/SafeCastUpgradeable.sol"
* and rename to avoid naming conflict with uniswap
*/
library PerpSafeCast {
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128 returnValue) {
require(((returnValue = uint128(value)) == value), "SafeCast: value doesn't fit in 128 bits");
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64 returnValue) {
require(((returnValue = uint64(value)) == value), "SafeCast: value doesn't fit in 64 bits");
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32 returnValue) {
require(((returnValue = uint32(value)) == value), "SafeCast: value doesn't fit in 32 bits");
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24 returnValue) {
require(((returnValue = uint24(value)) == value), "SafeCast: value doesn't fit in 24 bits");
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16 returnValue) {
require(((returnValue = uint16(value)) == value), "SafeCast: value doesn't fit in 16 bits");
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits.
*/
function toUint8(uint256 value) internal pure returns (uint8 returnValue) {
require(((returnValue = uint8(value)) == value), "SafeCast: value doesn't fit in 8 bits");
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128 returnValue) {
require(((returnValue = int128(value)) == value), "SafeCast: value doesn't fit in 128 bits");
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64 returnValue) {
require(((returnValue = int64(value)) == value), "SafeCast: value doesn't fit in 64 bits");
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32 returnValue) {
require(((returnValue = int32(value)) == value), "SafeCast: value doesn't fit in 32 bits");
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16 returnValue) {
require(((returnValue = int16(value)) == value), "SafeCast: value doesn't fit in 16 bits");
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits.
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8 returnValue) {
require(((returnValue = int8(value)) == value), "SafeCast: value doesn't fit in 8 bits");
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
return int256(value);
}
/**
* @dev Returns the downcasted uint24 from int256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must be greater than or equal to 0 and into 24 bit.
*/
function toUint24(int256 value) internal pure returns (uint24 returnValue) {
require(
((returnValue = uint24(value)) == value),
"SafeCast: value must be positive or value doesn't fit in an 24 bits"
);
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 returnValue) {
require(((returnValue = int24(value)) == value), "SafeCast: value doesn't fit in an 24 bits");
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import { SignedSafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
/// @dev decimals of settlementToken token MUST be less than 18
library SettlementTokenMath {
using SafeMathUpgradeable for uint256;
using SignedSafeMathUpgradeable for int256;
function lte(
uint256 settlementToken,
// solhint-disable-next-line var-name-mixedcase
uint256 amountX10_18,
uint8 decimals
) internal pure returns (bool) {
return parseSettlementToken(settlementToken, decimals) <= amountX10_18;
}
function lte(
int256 settlementToken,
// solhint-disable-next-line var-name-mixedcase
int256 amountX10_18,
uint8 decimals
) internal pure returns (bool) {
return parseSettlementToken(settlementToken, decimals) <= amountX10_18;
}
function lt(
uint256 settlementToken,
// solhint-disable-next-line var-name-mixedcase
uint256 amountX10_18,
uint8 decimals
) internal pure returns (bool) {
return parseSettlementToken(settlementToken, decimals) < amountX10_18;
}
function lt(
int256 settlementToken,
// solhint-disable-next-line var-name-mixedcase
int256 amountX10_18,
uint8 decimals
) internal pure returns (bool) {
return parseSettlementToken(settlementToken, decimals) < amountX10_18;
}
function gt(
uint256 settlementToken,
// solhint-disable-next-line var-name-mixedcase
uint256 amountX10_18,
uint8 decimals
) internal pure returns (bool) {
return parseSettlementToken(settlementToken, decimals) > amountX10_18;
}
function gt(
int256 settlementToken,
// solhint-disable-next-line var-name-mixedcase
int256 amountX10_18,
uint8 decimals
) internal pure returns (bool) {
return parseSettlementToken(settlementToken, decimals) > amountX10_18;
}
function gte(
uint256 settlementToken,
// solhint-disable-next-line var-name-mixedcase
uint256 amountX10_18,
uint8 decimals
) internal pure returns (bool) {
return parseSettlementToken(settlementToken, decimals) >= amountX10_18;
}
function gte(
int256 settlementToken,
// solhint-disable-next-line var-name-mixedcase
int256 amountX10_18,
uint8 decimals
) internal pure returns (bool) {
return parseSettlementToken(settlementToken, decimals) >= amountX10_18;
}
// returns number with 18 decimals
function parseSettlementToken(uint256 amount, uint8 decimals) internal pure returns (uint256) {
return amount.mul(10**(18 - decimals));
}
// returns number with 18 decimals
function parseSettlementToken(int256 amount, uint8 decimals) internal pure returns (int256) {
return amount.mul(int256(10**(18 - decimals)));
}
// returns number converted from 18 decimals to settlementToken's decimals
function formatSettlementToken(uint256 amount, uint8 decimals) internal pure returns (uint256) {
return amount.div(10**(18 - decimals));
}
// returns number converted from 18 decimals to settlementToken's decimals
// will always round down no matter positive value or negative value
function formatSettlementToken(int256 amount, uint8 decimals) internal pure returns (int256) {
uint256 denominator = 10**(18 - decimals);
int256 rounding = 0;
if (amount < 0 && uint256(-amount) % denominator != 0) {
rounding = -1;
}
return amount.div(int256(denominator)).add(rounding);
}
// returns number converted between specified decimals
function convertTokenDecimals(
uint256 amount,
uint8 fromDecimals,
uint8 toDecimals
) internal pure returns (uint256) {
if (fromDecimals == toDecimals) {
return amount;
}
return
fromDecimals > toDecimals
? amount.div(10**(fromDecimals - toDecimals))
: amount.mul(10**(toDecimals - fromDecimals));
}
// returns number converted between specified decimals
function convertTokenDecimals(
int256 amount,
uint8 fromDecimals,
uint8 toDecimals
) internal pure returns (int256) {
if (fromDecimals == toDecimals) {
return amount;
}
if (fromDecimals < toDecimals) {
return amount.mul(int256(10**(toDecimals - fromDecimals)));
}
uint256 denominator = 10**(fromDecimals - toDecimals);
int256 rounding = 0;
if (amount < 0 && uint256(-amount) % denominator != 0) {
rounding = -1;
}
return amount.div(int256(denominator)).add(rounding);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { PerpMath } from "./PerpMath.sol";
import { PerpSafeCast } from "./PerpSafeCast.sol";
import { FullMath } from "@uniswap/v3-core/contracts/libraries/FullMath.sol";
import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
library SwapMath {
using PerpMath for int256;
using PerpSafeCast for uint256;
using SafeMathUpgradeable for uint256;
//
// CONSTANT
//
uint256 internal constant _ONE_HUNDRED_PERCENT = 1e6; // 100%
//
// INTERNAL PURE
//
function calcAmountScaledByFeeRatio(
uint256 amount,
uint24 feeRatio,
bool isScaledUp
) internal pure returns (uint256) {
// when scaling up, round up to avoid imprecision; it's okay as long as we round down later
return
isScaledUp
? FullMath.mulDivRoundingUp(amount, _ONE_HUNDRED_PERCENT, uint256(_ONE_HUNDRED_PERCENT).sub(feeRatio))
: FullMath.mulDiv(amount, uint256(_ONE_HUNDRED_PERCENT).sub(feeRatio), _ONE_HUNDRED_PERCENT);
}
/// @return scaledAmountForUniswapV3PoolSwap the unsigned scaled amount for UniswapV3Pool.swap()
/// @return signedScaledAmountForReplaySwap the signed scaled amount for _replaySwap()
/// @dev for UniswapV3Pool.swap(), scaling the amount is necessary to achieve the custom fee effect
/// @dev for _replaySwap(), however, as we can input ExchangeFeeRatioRatio directly in SwapMath.computeSwapStep(),
/// there is no need to stick to the scaled amount
/// @dev refer to CH._openPosition() docstring for explainer diagram
function calcScaledAmountForSwaps(
bool isBaseToQuote,
bool isExactInput,
uint256 amount,
uint24 exchangeFeeRatio,
uint24 uniswapFeeRatio
) internal pure returns (uint256 scaledAmountForUniswapV3PoolSwap, int256 signedScaledAmountForReplaySwap) {
if (isBaseToQuote) {
scaledAmountForUniswapV3PoolSwap = isExactInput
? calcAmountScaledByFeeRatio(amount, uniswapFeeRatio, true)
: calcAmountScaledByFeeRatio(amount, exchangeFeeRatio, true);
} else {
scaledAmountForUniswapV3PoolSwap = isExactInput
? calcAmountWithFeeRatioReplaced(amount, uniswapFeeRatio, exchangeFeeRatio, true)
: amount;
}
// x : uniswapFeeRatio, y : exchangeFeeRatioRatio
// since we can input ExchangeFeeRatioRatio directly in SwapMath.computeSwapStep() in _replaySwap(),
// when !isBaseToQuote, we can use the original amount directly
// ex: when x(uniswapFeeRatio) = 1%, y(exchangeFeeRatioRatio) = 3%, input == 1 quote
// our target is to get fee == 0.03 quote
// if scaling the input as 1 * 0.97 / 0.99, the fee calculated in `_replaySwap()` won't be 0.03
signedScaledAmountForReplaySwap = isBaseToQuote
? scaledAmountForUniswapV3PoolSwap.toInt256()
: amount.toInt256();
signedScaledAmountForReplaySwap = isExactInput
? signedScaledAmountForReplaySwap
: signedScaledAmountForReplaySwap.neg256();
}
/// @param isReplacingUniswapFeeRatio is to replace uniswapFeeRatio or clearingHouseFeeRatio
/// let x : uniswapFeeRatio, y : clearingHouseFeeRatio
/// true: replacing uniswapFeeRatio with clearingHouseFeeRatio: amount * (1 - y) / (1 - x)
/// false: replacing clearingHouseFeeRatio with uniswapFeeRatio: amount * (1 - x) / (1 - y)
/// multiplying a fee is applying it as the new standard and dividing a fee is removing its effect
/// @dev calculate the amount when feeRatio is switched between uniswapFeeRatio and clearingHouseFeeRatio
function calcAmountWithFeeRatioReplaced(
uint256 amount,
uint24 uniswapFeeRatio,
uint24 clearingHouseFeeRatio,
bool isReplacingUniswapFeeRatio
) internal pure returns (uint256) {
(uint24 newFeeRatio, uint24 replacedFeeRatio) =
isReplacingUniswapFeeRatio
? (clearingHouseFeeRatio, uniswapFeeRatio)
: (uniswapFeeRatio, clearingHouseFeeRatio);
return
FullMath.mulDivRoundingUp(
amount,
uint256(_ONE_HUNDRED_PERCENT).sub(newFeeRatio),
uint256(_ONE_HUNDRED_PERCENT).sub(replacedFeeRatio)
);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
library Tick {
struct GrowthInfo {
uint256 feeX128;
int256 twPremiumX96;
int256 twPremiumDivBySqrtPriceX96;
}
struct FundingGrowthRangeInfo {
int256 twPremiumGrowthInsideX96;
int256 twPremiumGrowthBelowX96;
int256 twPremiumDivBySqrtPriceGrowthInsideX96;
}
/// @dev call this function only if (liquidityGrossBefore == 0 && liquidityDelta != 0)
/// @dev per Uniswap: we assume that all growths before a tick is initialized happen "below" the tick
function initialize(
mapping(int24 => GrowthInfo) storage self,
int24 tick,
int24 currentTick,
GrowthInfo memory globalGrowthInfo
) internal {
if (tick <= currentTick) {
GrowthInfo storage growthInfo = self[tick];
growthInfo.feeX128 = globalGrowthInfo.feeX128;
growthInfo.twPremiumX96 = globalGrowthInfo.twPremiumX96;
growthInfo.twPremiumDivBySqrtPriceX96 = globalGrowthInfo.twPremiumDivBySqrtPriceX96;
}
}
function cross(
mapping(int24 => GrowthInfo) storage self,
int24 tick,
GrowthInfo memory globalGrowthInfo
) internal {
GrowthInfo storage growthInfo = self[tick];
growthInfo.feeX128 = globalGrowthInfo.feeX128 - growthInfo.feeX128;
growthInfo.twPremiumX96 = globalGrowthInfo.twPremiumX96 - growthInfo.twPremiumX96;
growthInfo.twPremiumDivBySqrtPriceX96 =
globalGrowthInfo.twPremiumDivBySqrtPriceX96 -
growthInfo.twPremiumDivBySqrtPriceX96;
}
function clear(mapping(int24 => GrowthInfo) storage self, int24 tick) internal {
delete self[tick];
}
/// @dev all values in this function are scaled by 2^128 (X128), thus adding the suffix to external params
/// @return feeGrowthInsideX128 this value can underflow per Tick.feeGrowthOutside specs
function getFeeGrowthInsideX128(
mapping(int24 => GrowthInfo) storage self,
int24 lowerTick,
int24 upperTick,
int24 currentTick,
uint256 feeGrowthGlobalX128
) internal view returns (uint256 feeGrowthInsideX128) {
uint256 lowerFeeGrowthOutside = self[lowerTick].feeX128;
uint256 upperFeeGrowthOutside = self[upperTick].feeX128;
uint256 feeGrowthBelow =
currentTick >= lowerTick ? lowerFeeGrowthOutside : feeGrowthGlobalX128 - lowerFeeGrowthOutside;
uint256 feeGrowthAbove =
currentTick < upperTick ? upperFeeGrowthOutside : feeGrowthGlobalX128 - upperFeeGrowthOutside;
return feeGrowthGlobalX128 - feeGrowthBelow - feeGrowthAbove;
}
/// @return all values returned can underflow per feeGrowthOutside specs;
/// see https://www.notion.so/32990980ba8b43859f6d2541722a739b
function getAllFundingGrowth(
mapping(int24 => GrowthInfo) storage self,
int24 lowerTick,
int24 upperTick,
int24 currentTick,
int256 twPremiumGrowthGlobalX96,
int256 twPremiumDivBySqrtPriceGrowthGlobalX96
) internal view returns (FundingGrowthRangeInfo memory) {
GrowthInfo storage lowerTickGrowthInfo = self[lowerTick];
GrowthInfo storage upperTickGrowthInfo = self[upperTick];
int256 lowerTwPremiumGrowthOutsideX96 = lowerTickGrowthInfo.twPremiumX96;
int256 upperTwPremiumGrowthOutsideX96 = upperTickGrowthInfo.twPremiumX96;
FundingGrowthRangeInfo memory fundingGrowthRangeInfo;
fundingGrowthRangeInfo.twPremiumGrowthBelowX96 = currentTick >= lowerTick
? lowerTwPremiumGrowthOutsideX96
: twPremiumGrowthGlobalX96 - lowerTwPremiumGrowthOutsideX96;
int256 twPremiumGrowthAboveX96 =
currentTick < upperTick
? upperTwPremiumGrowthOutsideX96
: twPremiumGrowthGlobalX96 - upperTwPremiumGrowthOutsideX96;
int256 lowerTwPremiumDivBySqrtPriceGrowthOutsideX96 = lowerTickGrowthInfo.twPremiumDivBySqrtPriceX96;
int256 upperTwPremiumDivBySqrtPriceGrowthOutsideX96 = upperTickGrowthInfo.twPremiumDivBySqrtPriceX96;
int256 twPremiumDivBySqrtPriceGrowthBelowX96 =
currentTick >= lowerTick
? lowerTwPremiumDivBySqrtPriceGrowthOutsideX96
: twPremiumDivBySqrtPriceGrowthGlobalX96 - lowerTwPremiumDivBySqrtPriceGrowthOutsideX96;
int256 twPremiumDivBySqrtPriceGrowthAboveX96 =
currentTick < upperTick
? upperTwPremiumDivBySqrtPriceGrowthOutsideX96
: twPremiumDivBySqrtPriceGrowthGlobalX96 - upperTwPremiumDivBySqrtPriceGrowthOutsideX96;
fundingGrowthRangeInfo.twPremiumGrowthInsideX96 =
twPremiumGrowthGlobalX96 -
fundingGrowthRangeInfo.twPremiumGrowthBelowX96 -
twPremiumGrowthAboveX96;
fundingGrowthRangeInfo.twPremiumDivBySqrtPriceGrowthInsideX96 =
twPremiumDivBySqrtPriceGrowthGlobalX96 -
twPremiumDivBySqrtPriceGrowthBelowX96 -
twPremiumDivBySqrtPriceGrowthAboveX96;
return fundingGrowthRangeInfo;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import { IUniswapV3Factory } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
import { TickMath } from "@uniswap/v3-core/contracts/libraries/TickMath.sol";
import { LiquidityAmounts } from "@uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol";
import { PoolAddress } from "@uniswap/v3-periphery/contracts/libraries/PoolAddress.sol";
import { BitMath } from "@uniswap/v3-core/contracts/libraries/BitMath.sol";
import { PerpSafeCast } from "./PerpSafeCast.sol";
import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import { PerpMath } from "../lib/PerpMath.sol";
/**
* Uniswap's v3 pool: token0 & token1
* -> token0's price = token1 / token0; tick index = log(1.0001, token0's price)
* Our system: base & quote
* -> base's price = quote / base; tick index = log(1.0001, base price)
* Thus, we require that (base, quote) = (token0, token1) is always true for convenience
*/
library UniswapV3Broker {
using SafeMathUpgradeable for uint256;
using PerpMath for int256;
using PerpMath for uint256;
using PerpSafeCast for uint256;
using PerpSafeCast for int256;
//
// STRUCT
//
struct AddLiquidityParams {
address pool;
int24 lowerTick;
int24 upperTick;
uint256 base;
uint256 quote;
bytes data;
}
struct AddLiquidityResponse {
uint256 base;
uint256 quote;
uint128 liquidity;
}
struct RemoveLiquidityParams {
address pool;
address recipient;
int24 lowerTick;
int24 upperTick;
uint128 liquidity;
}
/// @param base amount of base token received from burning the liquidity (excl. fee)
/// @param quote amount of quote token received from burning the liquidity (excl. fee)
struct RemoveLiquidityResponse {
uint256 base;
uint256 quote;
}
struct SwapState {
int24 tick;
uint160 sqrtPriceX96;
int256 amountSpecifiedRemaining;
uint256 feeGrowthGlobalX128;
uint128 liquidity;
}
struct SwapParams {
address pool;
address recipient;
bool isBaseToQuote;
bool isExactInput;
uint256 amount;
uint160 sqrtPriceLimitX96;
bytes data;
}
struct SwapResponse {
uint256 base;
uint256 quote;
}
//
// CONSTANT
//
uint256 internal constant _DUST = 10;
//
// INTERNAL NON-VIEW
//
function addLiquidity(AddLiquidityParams memory params) internal returns (AddLiquidityResponse memory) {
(uint160 sqrtMarkPrice, , , , , , ) = getSlot0(params.pool);
// get the equivalent amount of liquidity from amount0 & amount1 with current price
uint128 liquidity =
LiquidityAmounts.getLiquidityForAmounts(
sqrtMarkPrice,
TickMath.getSqrtRatioAtTick(params.lowerTick),
TickMath.getSqrtRatioAtTick(params.upperTick),
params.base,
params.quote
);
(uint256 addedAmount0, uint256 addedAmount1) =
IUniswapV3Pool(params.pool).mint(address(this), params.lowerTick, params.upperTick, liquidity, params.data);
return AddLiquidityResponse({ base: addedAmount0, quote: addedAmount1, liquidity: liquidity });
}
function removeLiquidity(RemoveLiquidityParams memory params) internal returns (RemoveLiquidityResponse memory) {
// call burn(), which only updates tokensOwed instead of transferring the tokens
(uint256 amount0Burned, uint256 amount1Burned) =
IUniswapV3Pool(params.pool).burn(params.lowerTick, params.upperTick, params.liquidity);
// call collect() to transfer tokens to CH
// we don't care about the returned values here as they include:
// 1. every maker's fee in the same range (ClearingHouse is the only maker in the pool's perspective)
// 2. the amount of token equivalent to liquidity burned
IUniswapV3Pool(params.pool).collect(
params.recipient,
params.lowerTick,
params.upperTick,
type(uint128).max,
type(uint128).max
);
return RemoveLiquidityResponse({ base: amount0Burned, quote: amount1Burned });
}
function swap(SwapParams memory params) internal returns (SwapResponse memory response) {
// UniswapV3Pool uses the sign to determine isExactInput or not
int256 specifiedAmount = params.isExactInput ? params.amount.toInt256() : params.amount.neg256();
// signedAmount0 & signedAmount1 are delta amounts, in the perspective of the pool
// > 0: pool gets; user pays
// < 0: pool provides; user gets
(int256 signedAmount0, int256 signedAmount1) =
IUniswapV3Pool(params.pool).swap(
params.recipient,
params.isBaseToQuote,
specifiedAmount,
params.sqrtPriceLimitX96 == 0
? (params.isBaseToQuote ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1)
: params.sqrtPriceLimitX96,
params.data
);
(uint256 amount0, uint256 amount1) = (signedAmount0.abs(), signedAmount1.abs());
// isExactInput = true, isZeroForOne = true => exact token0
// isExactInput = false, isZeroForOne = false => exact token0
// isExactInput = false, isZeroForOne = true => exact token1
// isExactInput = true, isZeroForOne = false => exact token1
uint256 exactAmount = params.isExactInput == params.isBaseToQuote ? amount0 : amount1;
// if no price limit, require the full output amount as it's technically possible for amounts to not match
// UB_UOA: unmatched output amount
if (!params.isExactInput && params.sqrtPriceLimitX96 == 0) {
require(
(exactAmount > params.amount ? exactAmount.sub(params.amount) : params.amount.sub(exactAmount)) < _DUST,
"UB_UOA"
);
return params.isBaseToQuote ? SwapResponse(amount0, params.amount) : SwapResponse(params.amount, amount1);
}
return SwapResponse(amount0, amount1);
}
//
// INTERNAL VIEW
//
function getPool(
address factory,
address quoteToken,
address baseToken,
uint24 uniswapFeeRatio
) internal view returns (address) {
PoolAddress.PoolKey memory poolKeys = PoolAddress.getPoolKey(quoteToken, baseToken, uniswapFeeRatio);
return IUniswapV3Factory(factory).getPool(poolKeys.token0, poolKeys.token1, uniswapFeeRatio);
}
function getTickSpacing(address pool) internal view returns (int24 tickSpacing) {
tickSpacing = IUniswapV3Pool(pool).tickSpacing();
}
function getSlot0(address pool)
internal
view
returns (
uint160 sqrtPriceX96,
int24 tick,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext,
uint8 feeProtocol,
bool unlocked
)
{
return IUniswapV3Pool(pool).slot0();
}
function getTick(address pool) internal view returns (int24 tick) {
(, tick, , , , , ) = IUniswapV3Pool(pool).slot0();
}
function getIsTickInitialized(address pool, int24 tick) internal view returns (bool initialized) {
(, , , , , , , initialized) = IUniswapV3Pool(pool).ticks(tick);
}
function getTickLiquidityNet(address pool, int24 tick) internal view returns (int128 liquidityNet) {
(, liquidityNet, , , , , , ) = IUniswapV3Pool(pool).ticks(tick);
}
function getSqrtMarkPriceX96(address pool) internal view returns (uint160 sqrtMarkPrice) {
(sqrtMarkPrice, , , , , , ) = IUniswapV3Pool(pool).slot0();
}
function getLiquidity(address pool) internal view returns (uint128 liquidity) {
return IUniswapV3Pool(pool).liquidity();
}
/// @dev if twapInterval < 10 (should be less than 1 block), return mark price without twap directly,
/// as twapInterval is too short and makes getting twap over such a short period meaningless
function getSqrtMarketTwapX96(address pool, uint32 twapInterval) internal view returns (uint160) {
return getSqrtMarketTwapX96From(pool, 0, twapInterval);
}
function getSqrtMarketTwapX96From(
address pool,
uint32 secondsAgo,
uint32 twapInterval
) internal view returns (uint160) {
// return the current price as twapInterval is too short/ meaningless
if (twapInterval < 10) {
(uint160 sqrtMarkPrice, , , , , , ) = getSlot0(pool);
return sqrtMarkPrice;
}
uint32[] memory secondsAgos = new uint32[](2);
// solhint-disable-next-line not-rely-on-time
secondsAgos[0] = secondsAgo + twapInterval;
secondsAgos[1] = secondsAgo;
(int56[] memory tickCumulatives, ) = IUniswapV3Pool(pool).observe(secondsAgos);
// tick(imprecise as it's an integer) to price
return TickMath.getSqrtRatioAtTick(int24((tickCumulatives[1] - tickCumulatives[0]) / twapInterval));
}
// copied from UniswapV3-core
/// @param isBaseToQuote originally lte, meaning that the next tick < the current tick
function getNextInitializedTickWithinOneWord(
address pool,
int24 tick,
int24 tickSpacing,
bool isBaseToQuote
) internal view returns (int24 next, bool initialized) {
int24 compressed = tick / tickSpacing;
if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity
if (isBaseToQuote) {
(int16 wordPos, uint8 bitPos) = _getPositionOfInitializedTickWithinOneWord(compressed);
// all the 1s at or to the right of the current bitPos
uint256 mask = (1 << bitPos) - 1 + (1 << bitPos);
uint256 masked = _getTickBitmap(pool, wordPos) & mask;
// if there are no initialized ticks to the right of or at the current tick, return rightmost in the word
initialized = masked != 0;
// overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick
next = initialized
? (compressed - int24(bitPos - BitMath.mostSignificantBit(masked))) * tickSpacing
: (compressed - int24(bitPos)) * tickSpacing;
} else {
// start from the word of the next tick, since the current tick state doesn't matter
(int16 wordPos, uint8 bitPos) = _getPositionOfInitializedTickWithinOneWord(compressed + 1);
// all the 1s at or to the left of the bitPos
uint256 mask = ~((1 << bitPos) - 1);
uint256 masked = _getTickBitmap(pool, wordPos) & mask;
// if there are no initialized ticks to the left of the current tick, return leftmost in the word
initialized = masked != 0;
// overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick
next = initialized
? (compressed + 1 + int24(BitMath.leastSignificantBit(masked) - bitPos)) * tickSpacing
: (compressed + 1 + int24(type(uint8).max - bitPos)) * tickSpacing;
}
}
function getSwapState(
address pool,
int256 signedScaledAmountForReplaySwap,
uint256 feeGrowthGlobalX128
) internal view returns (SwapState memory) {
(uint160 sqrtMarkPrice, int24 tick, , , , , ) = getSlot0(pool);
uint128 liquidity = IUniswapV3Pool(pool).liquidity();
return
SwapState({
tick: tick,
sqrtPriceX96: sqrtMarkPrice,
amountSpecifiedRemaining: signedScaledAmountForReplaySwap,
feeGrowthGlobalX128: feeGrowthGlobalX128,
liquidity: liquidity
});
}
//
// PRIVATE VIEW
//
function _getTickBitmap(address pool, int16 wordPos) private view returns (uint256 tickBitmap) {
return IUniswapV3Pool(pool).tickBitmap(wordPos);
}
/// @dev this function is Uniswap's TickBitmap.position()
function _getPositionOfInitializedTickWithinOneWord(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) {
wordPos = int16(tick >> 8);
bitPos = uint8(tick % 256);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { AddressUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import { IERC20Metadata } from "./interface/IERC20Metadata.sol";
import { ClearingHouseCallee } from "./base/ClearingHouseCallee.sol";
import { UniswapV3Broker } from "./lib/UniswapV3Broker.sol";
import { PerpMath } from "./lib/PerpMath.sol";
import { PerpSafeCast } from "./lib/PerpSafeCast.sol";
import { IVirtualToken } from "./interface/IVirtualToken.sol";
import { MarketRegistryStorageV4 } from "./storage/MarketRegistryStorage.sol";
import { IMarketRegistry } from "./interface/IMarketRegistry.sol";
import { IMarketRegistryFeeManager } from "./interface/IMarketRegistryFeeManager.sol";
// never inherit any new stateful contract. never change the orders of parent stateful contracts
contract MarketRegistry is IMarketRegistry, IMarketRegistryFeeManager, ClearingHouseCallee, MarketRegistryStorageV4 {
using AddressUpgradeable for address;
using PerpSafeCast for uint256;
using PerpMath for uint24;
using PerpMath for uint256;
//
// CONSTANT
//
uint24 internal constant _DEFAULT_MAX_MARKET_PRICE_SPREAD_RATIO = 0.1e6; // 10% in decimal 6
uint24 private constant _ONE_HUNDRED_PERCENT_RATIO = 1e6;
//
// MODIFIER
//
modifier checkRatio(uint24 ratio) {
// ratio overflow
require(ratio <= 1e6, "MR_RO");
_;
}
modifier checkPool(address baseToken) {
// pool not exists
require(_poolMap[baseToken] != address(0), "MR_PNE");
_;
}
modifier onlyFeeManager() {
// MR_OFM: only fee manager
require(_feeManagerMap[msg.sender], "MR_OFM");
_;
}
//
// EXTERNAL NON-VIEW
//
function initialize(address uniswapV3FactoryArg, address quoteTokenArg) external initializer {
__ClearingHouseCallee_init();
// UnsiwapV3Factory is not contract
require(uniswapV3FactoryArg.isContract(), "MR_UFNC");
// QuoteToken is not contract
require(quoteTokenArg.isContract(), "MR_QTNC");
// update states
_uniswapV3Factory = uniswapV3FactoryArg;
_quoteToken = quoteTokenArg;
_maxOrdersPerMarket = type(uint8).max;
}
function addPool(address baseToken, uint24 feeRatio) external onlyOwner returns (address) {
// existent pool
require(_poolMap[baseToken] == address(0), "MR_EP");
// baseToken decimals is not 18
require(IERC20Metadata(baseToken).decimals() == 18, "MR_BDN18");
// clearingHouse base token balance not enough
require(IERC20Metadata(baseToken).balanceOf(_clearingHouse) == type(uint256).max, "MR_CHBNE");
// quote token total supply not enough
require(IERC20Metadata(_quoteToken).totalSupply() == type(uint256).max, "MR_QTSNE");
// to ensure the base is always token0 and quote is always token1
// invalid baseToken
require(baseToken < _quoteToken, "MR_IB");
address pool = UniswapV3Broker.getPool(_uniswapV3Factory, _quoteToken, baseToken, feeRatio);
// non-existent pool in uniswapV3 factory
require(pool != address(0), "MR_NEP");
(uint256 sqrtPriceX96, , , , , , ) = UniswapV3Broker.getSlot0(pool);
// pool not (yet) initialized
require(sqrtPriceX96 != 0, "MR_PNI");
// clearingHouse not in baseToken whitelist
require(IVirtualToken(baseToken).isInWhitelist(_clearingHouse), "MR_CNBWL");
// pool not in baseToken whitelist
require(IVirtualToken(baseToken).isInWhitelist(pool), "MR_PNBWL");
// clearingHouse not in quoteToken whitelist
require(IVirtualToken(_quoteToken).isInWhitelist(_clearingHouse), "MR_CHNQWL");
// pool not in quoteToken whitelist
require(IVirtualToken(_quoteToken).isInWhitelist(pool), "MR_PNQWL");
_poolMap[baseToken] = pool;
_uniswapFeeRatioMap[baseToken] = feeRatio;
_exchangeFeeRatioMap[baseToken] = feeRatio;
emit PoolAdded(baseToken, feeRatio, pool);
return pool;
}
function setFeeRatio(address baseToken, uint24 feeRatio)
external
checkPool(baseToken)
checkRatio(feeRatio)
onlyOwner
{
_exchangeFeeRatioMap[baseToken] = feeRatio;
emit FeeRatioChanged(baseToken, feeRatio);
}
function setInsuranceFundFeeRatio(address baseToken, uint24 insuranceFundFeeRatioArg)
external
checkPool(baseToken)
checkRatio(insuranceFundFeeRatioArg)
onlyOwner
{
_insuranceFundFeeRatioMap[baseToken] = insuranceFundFeeRatioArg;
emit InsuranceFundFeeRatioChanged(baseToken, insuranceFundFeeRatioArg);
}
function setMaxOrdersPerMarket(uint8 maxOrdersPerMarketArg) external onlyOwner {
_maxOrdersPerMarket = maxOrdersPerMarketArg;
emit MaxOrdersPerMarketChanged(maxOrdersPerMarketArg);
}
function setMarketMaxPriceSpreadRatio(address baseToken, uint24 ratio) external onlyOwner {
_marketMaxPriceSpreadRatioMap[baseToken] = ratio;
emit MarketMaxPriceSpreadRatioChanged(baseToken, ratio);
}
function setFeeDiscountRatio(address trader, uint24 discountRatio)
external
override
checkRatio(discountRatio)
onlyFeeManager
{
_feeDiscountRatioMap[trader] = discountRatio;
emit FeeDiscountRatioChanged(trader, discountRatio);
}
function setFeeManager(address accountArg, bool isFeeManagerArg) external onlyOwner {
if (_feeManagerMap[accountArg] == isFeeManagerArg) {
return;
}
_feeManagerMap[accountArg] = isFeeManagerArg;
emit FeeManagerChanged(accountArg, isFeeManagerArg);
}
//
// EXTERNAL VIEW
//
/// @inheritdoc IMarketRegistry
function getQuoteToken() external view override returns (address) {
return _quoteToken;
}
/// @inheritdoc IMarketRegistry
function getUniswapV3Factory() external view override returns (address) {
return _uniswapV3Factory;
}
/// @inheritdoc IMarketRegistry
function getMaxOrdersPerMarket() external view override returns (uint8) {
return _maxOrdersPerMarket;
}
/// @inheritdoc IMarketRegistry
function getPool(address baseToken) external view override checkPool(baseToken) returns (address) {
return _poolMap[baseToken];
}
/// @inheritdoc IMarketRegistry
function getFeeRatio(address baseToken) external view override checkPool(baseToken) returns (uint24) {
return _exchangeFeeRatioMap[baseToken];
}
/// @inheritdoc IMarketRegistry
function getInsuranceFundFeeRatio(address baseToken) external view override checkPool(baseToken) returns (uint24) {
return _insuranceFundFeeRatioMap[baseToken];
}
/// @inheritdoc IMarketRegistry
/// @dev if we didn't set the max spread ratio for the market, we will use the default value
function getMarketMaxPriceSpreadRatio(address baseToken) external view override returns (uint24) {
return _getMarketMaxPriceSpreadRatio(baseToken);
}
/// @inheritdoc IMarketRegistryFeeManager
function isFeeManager(address account) external view override returns (bool) {
return _feeManagerMap[account];
}
/// @inheritdoc IMarketRegistry
function getMarketInfo(address baseToken) external view override checkPool(baseToken) returns (MarketInfo memory) {
return
MarketInfo({
pool: _poolMap[baseToken],
exchangeFeeRatio: _exchangeFeeRatioMap[baseToken],
uniswapFeeRatio: _uniswapFeeRatioMap[baseToken],
insuranceFundFeeRatio: _insuranceFundFeeRatioMap[baseToken],
maxPriceSpreadRatio: _getMarketMaxPriceSpreadRatio(baseToken)
});
}
function getMarketInfoByTrader(address trader, address baseToken)
external
view
override
checkPool(baseToken)
returns (MarketInfo memory)
{
uint24 exchangeFeeRatio =
uint256(_exchangeFeeRatioMap[baseToken])
.mulRatio(_ONE_HUNDRED_PERCENT_RATIO.subRatio(_feeDiscountRatioMap[trader]))
.toUint24();
return
MarketInfo({
pool: _poolMap[baseToken],
exchangeFeeRatio: exchangeFeeRatio,
uniswapFeeRatio: _uniswapFeeRatioMap[baseToken],
insuranceFundFeeRatio: _insuranceFundFeeRatioMap[baseToken],
maxPriceSpreadRatio: _getMarketMaxPriceSpreadRatio(baseToken)
});
}
/// @inheritdoc IMarketRegistry
function hasPool(address baseToken) external view override returns (bool) {
return _poolMap[baseToken] != address(0);
}
//
// INTERNAL VIEW
//
function _getMarketMaxPriceSpreadRatio(address baseToken) internal view returns (uint24) {
uint24 maxSpreadRatio =
_marketMaxPriceSpreadRatioMap[baseToken] > 0
? _marketMaxPriceSpreadRatioMap[baseToken]
: _DEFAULT_MAX_MARKET_PRICE_SPREAD_RATIO;
return maxSpreadRatio;
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import { SignedSafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import { FullMath } from "@uniswap/v3-core/contracts/libraries/FullMath.sol";
import { TickMath } from "@uniswap/v3-core/contracts/libraries/TickMath.sol";
import { SwapMath } from "@uniswap/v3-core/contracts/libraries/SwapMath.sol";
import { LiquidityMath } from "@uniswap/v3-core/contracts/libraries/LiquidityMath.sol";
import { FixedPoint128 } from "@uniswap/v3-core/contracts/libraries/FixedPoint128.sol";
import { IUniswapV3MintCallback } from "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3MintCallback.sol";
import { LiquidityAmounts } from "@uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol";
import { UniswapV3Broker } from "./lib/UniswapV3Broker.sol";
import { PerpSafeCast } from "./lib/PerpSafeCast.sol";
import { PerpFixedPoint96 } from "./lib/PerpFixedPoint96.sol";
import { Funding } from "./lib/Funding.sol";
import { PerpMath } from "./lib/PerpMath.sol";
import { Tick } from "./lib/Tick.sol";
import { ClearingHouseCallee } from "./base/ClearingHouseCallee.sol";
import { UniswapV3CallbackBridge } from "./base/UniswapV3CallbackBridge.sol";
import { IMarketRegistry } from "./interface/IMarketRegistry.sol";
import { OrderBookStorageV1 } from "./storage/OrderBookStorage.sol";
import { IOrderBook } from "./interface/IOrderBook.sol";
import { OpenOrder } from "./lib/OpenOrder.sol";
// never inherit any new stateful contract. never change the orders of parent stateful contracts
contract OrderBook is
IOrderBook,
IUniswapV3MintCallback,
ClearingHouseCallee,
UniswapV3CallbackBridge,
OrderBookStorageV1
{
using SafeMathUpgradeable for uint256;
using SafeMathUpgradeable for uint128;
using SignedSafeMathUpgradeable for int256;
using PerpMath for uint256;
using PerpMath for uint160;
using PerpMath for int256;
using PerpMath for int128;
using PerpSafeCast for uint256;
using PerpSafeCast for uint128;
using PerpSafeCast for int256;
using Tick for mapping(int24 => Tick.GrowthInfo);
//
// STRUCT
//
struct InternalAddLiquidityToOrderParams {
address maker;
address baseToken;
address pool;
int24 lowerTick;
int24 upperTick;
uint256 feeGrowthGlobalX128;
uint128 liquidity;
uint256 base;
uint256 quote;
Funding.Growth globalFundingGrowth;
}
struct InternalRemoveLiquidityParams {
address maker;
address baseToken;
address pool;
bytes32 orderId;
int24 lowerTick;
int24 upperTick;
uint128 liquidity;
}
struct InternalSwapStep {
uint160 initialSqrtPriceX96;
int24 nextTick;
bool isNextTickInitialized;
uint160 nextSqrtPriceX96;
uint256 amountIn;
uint256 amountOut;
uint256 fee;
}
//
// EXTERNAL NON-VIEW
//
function initialize(address marketRegistryArg) external initializer {
__ClearingHouseCallee_init();
__UniswapV3CallbackBridge_init(marketRegistryArg);
}
function setExchange(address exchangeArg) external onlyOwner {
_exchange = exchangeArg;
emit ExchangeChanged(exchangeArg);
}
/// @inheritdoc IOrderBook
function addLiquidity(AddLiquidityParams calldata params) external override returns (AddLiquidityResponse memory) {
_requireOnlyClearingHouse();
address pool = IMarketRegistry(_marketRegistry).getPool(params.baseToken);
uint256 feeGrowthGlobalX128 = _feeGrowthGlobalX128Map[params.baseToken];
mapping(int24 => Tick.GrowthInfo) storage tickMap = _growthOutsideTickMap[params.baseToken];
UniswapV3Broker.AddLiquidityResponse memory response;
{
bool initializedBeforeLower = UniswapV3Broker.getIsTickInitialized(pool, params.lowerTick);
bool initializedBeforeUpper = UniswapV3Broker.getIsTickInitialized(pool, params.upperTick);
// add liquidity to pool
response = UniswapV3Broker.addLiquidity(
UniswapV3Broker.AddLiquidityParams(
pool,
params.lowerTick,
params.upperTick,
params.base,
params.quote,
abi.encode(MintCallbackData(params.trader, pool))
)
);
(, int24 currentTick, , , , , ) = UniswapV3Broker.getSlot0(pool);
// initialize tick info
if (!initializedBeforeLower && UniswapV3Broker.getIsTickInitialized(pool, params.lowerTick)) {
tickMap.initialize(
params.lowerTick,
currentTick,
Tick.GrowthInfo(
feeGrowthGlobalX128,
params.fundingGrowthGlobal.twPremiumX96,
params.fundingGrowthGlobal.twPremiumDivBySqrtPriceX96
)
);
}
if (!initializedBeforeUpper && UniswapV3Broker.getIsTickInitialized(pool, params.upperTick)) {
tickMap.initialize(
params.upperTick,
currentTick,
Tick.GrowthInfo(
feeGrowthGlobalX128,
params.fundingGrowthGlobal.twPremiumX96,
params.fundingGrowthGlobal.twPremiumDivBySqrtPriceX96
)
);
}
}
// state changes; if adding liquidity to an existing order, get fees accrued
uint256 fee =
_addLiquidityToOrder(
InternalAddLiquidityToOrderParams({
maker: params.trader,
baseToken: params.baseToken,
pool: pool,
lowerTick: params.lowerTick,
upperTick: params.upperTick,
feeGrowthGlobalX128: feeGrowthGlobalX128,
liquidity: response.liquidity,
base: response.base,
quote: response.quote,
globalFundingGrowth: params.fundingGrowthGlobal
})
);
return
AddLiquidityResponse({
base: response.base,
quote: response.quote,
fee: fee,
liquidity: response.liquidity
});
}
/// @inheritdoc IOrderBook
function removeLiquidity(RemoveLiquidityParams calldata params)
external
override
returns (RemoveLiquidityResponse memory)
{
_requireOnlyClearingHouse();
address pool = IMarketRegistry(_marketRegistry).getPool(params.baseToken);
bytes32 orderId = OpenOrder.calcOrderKey(params.maker, params.baseToken, params.lowerTick, params.upperTick);
return
_removeLiquidity(
InternalRemoveLiquidityParams({
maker: params.maker,
baseToken: params.baseToken,
pool: pool,
orderId: orderId,
lowerTick: params.lowerTick,
upperTick: params.upperTick,
liquidity: params.liquidity
})
);
}
/// @inheritdoc IOrderBook
function updateFundingGrowthAndLiquidityCoefficientInFundingPayment(
address trader,
address baseToken,
Funding.Growth memory fundingGrowthGlobal
) external override returns (int256 liquidityCoefficientInFundingPayment) {
_requireOnlyExchange();
bytes32[] memory orderIds = _openOrderIdsMap[trader][baseToken];
mapping(int24 => Tick.GrowthInfo) storage tickMap = _growthOutsideTickMap[baseToken];
address pool = IMarketRegistry(_marketRegistry).getPool(baseToken);
// funding of liquidity coefficient
uint256 orderIdLength = orderIds.length;
(, int24 tick, , , , , ) = UniswapV3Broker.getSlot0(pool);
for (uint256 i = 0; i < orderIdLength; i++) {
OpenOrder.Info storage order = _openOrderMap[orderIds[i]];
Tick.FundingGrowthRangeInfo memory fundingGrowthRangeInfo =
tickMap.getAllFundingGrowth(
order.lowerTick,
order.upperTick,
tick,
fundingGrowthGlobal.twPremiumX96,
fundingGrowthGlobal.twPremiumDivBySqrtPriceX96
);
// the calculation here is based on cached values
liquidityCoefficientInFundingPayment = liquidityCoefficientInFundingPayment.add(
Funding.calcLiquidityCoefficientInFundingPaymentByOrder(order, fundingGrowthRangeInfo)
);
// thus, state updates have to come after
order.lastTwPremiumGrowthInsideX96 = fundingGrowthRangeInfo.twPremiumGrowthInsideX96;
order.lastTwPremiumGrowthBelowX96 = fundingGrowthRangeInfo.twPremiumGrowthBelowX96;
order.lastTwPremiumDivBySqrtPriceGrowthInsideX96 = fundingGrowthRangeInfo
.twPremiumDivBySqrtPriceGrowthInsideX96;
}
return liquidityCoefficientInFundingPayment;
}
/// @inheritdoc IOrderBook
function updateOrderDebt(
bytes32 orderId,
int256 base,
int256 quote
) external override {
_requireOnlyClearingHouse();
OpenOrder.Info storage openOrder = _openOrderMap[orderId];
openOrder.baseDebt = openOrder.baseDebt.toInt256().add(base).toUint256();
openOrder.quoteDebt = openOrder.quoteDebt.toInt256().add(quote).toUint256();
}
/// @inheritdoc IUniswapV3MintCallback
function uniswapV3MintCallback(
uint256 amount0Owed,
uint256 amount1Owed,
bytes calldata data
) external override checkCallback {
IUniswapV3MintCallback(_clearingHouse).uniswapV3MintCallback(amount0Owed, amount1Owed, data);
}
/// @inheritdoc IOrderBook
function replaySwap(ReplaySwapParams memory params) external override returns (ReplaySwapResponse memory) {
_requireOnlyExchange();
bool isExactInput = params.amount > 0;
uint256 fee;
uint256 insuranceFundFee; // insuranceFundFee = fee * insuranceFundFeeRatio
UniswapV3Broker.SwapState memory swapState =
UniswapV3Broker.getSwapState(params.pool, params.amount, _feeGrowthGlobalX128Map[params.baseToken]);
params.sqrtPriceLimitX96 = params.sqrtPriceLimitX96 == 0
? (params.isBaseToQuote ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1)
: params.sqrtPriceLimitX96;
// if there is residue in amountSpecifiedRemaining, makers can get a tiny little bit less than expected,
// which is safer for the system
int24 tickSpacing = UniswapV3Broker.getTickSpacing(params.pool);
while (swapState.amountSpecifiedRemaining != 0 && swapState.sqrtPriceX96 != params.sqrtPriceLimitX96) {
InternalSwapStep memory step;
step.initialSqrtPriceX96 = swapState.sqrtPriceX96;
// find next tick
// note the search is bounded in one word
(step.nextTick, step.isNextTickInitialized) = UniswapV3Broker.getNextInitializedTickWithinOneWord(
params.pool,
swapState.tick,
tickSpacing,
params.isBaseToQuote
);
// ensure that we do not overshoot the min/max tick, as the tick bitmap is not aware of these bounds
if (step.nextTick < TickMath.MIN_TICK) {
step.nextTick = TickMath.MIN_TICK;
} else if (step.nextTick > TickMath.MAX_TICK) {
step.nextTick = TickMath.MAX_TICK;
}
// get the next price of this step (either next tick's price or the ending price)
// use sqrtPrice instead of tick is more precise
step.nextSqrtPriceX96 = TickMath.getSqrtRatioAtTick(step.nextTick);
// find the next swap checkpoint
// (either reached the next price of this step, or exhausted remaining amount specified)
(swapState.sqrtPriceX96, step.amountIn, step.amountOut, step.fee) = SwapMath.computeSwapStep(
swapState.sqrtPriceX96,
(
params.isBaseToQuote
? step.nextSqrtPriceX96 < params.sqrtPriceLimitX96
: step.nextSqrtPriceX96 > params.sqrtPriceLimitX96
)
? params.sqrtPriceLimitX96
: step.nextSqrtPriceX96,
swapState.liquidity,
swapState.amountSpecifiedRemaining,
// isBaseToQuote: fee is charged in base token in uniswap pool; thus, use uniswapFeeRatio to replay
// !isBaseToQuote: fee is charged in quote token in clearing house; thus, use exchangeFeeRatioRatio
params.isBaseToQuote ? params.uniswapFeeRatio : params.exchangeFeeRatio
);
// user input 1 quote:
// quote token to uniswap ===> 1*0.98/0.99 = 0.98989899
// fee = 0.98989899 * 2% = 0.01979798
if (isExactInput) {
swapState.amountSpecifiedRemaining = swapState.amountSpecifiedRemaining.sub(
step.amountIn.add(step.fee).toInt256()
);
} else {
swapState.amountSpecifiedRemaining = swapState.amountSpecifiedRemaining.add(step.amountOut.toInt256());
}
// update CH's global fee growth if there is liquidity in this range
// note CH only collects quote fee when swapping base -> quote
if (swapState.liquidity > 0) {
if (params.isBaseToQuote) {
step.fee = FullMath.mulDivRoundingUp(step.amountOut, params.exchangeFeeRatio, 1e6);
}
fee += step.fee;
uint256 stepInsuranceFundFee = FullMath.mulDivRoundingUp(step.fee, params.insuranceFundFeeRatio, 1e6);
insuranceFundFee += stepInsuranceFundFee;
uint256 stepMakerFee = step.fee.sub(stepInsuranceFundFee);
swapState.feeGrowthGlobalX128 += FullMath.mulDiv(stepMakerFee, FixedPoint128.Q128, swapState.liquidity);
}
if (swapState.sqrtPriceX96 == step.nextSqrtPriceX96) {
// we have reached the tick's boundary
if (step.isNextTickInitialized) {
if (params.shouldUpdateState) {
// update the tick if it has been initialized
mapping(int24 => Tick.GrowthInfo) storage tickMap = _growthOutsideTickMap[params.baseToken];
// according to the above updating logic,
// if isBaseToQuote, state.feeGrowthGlobalX128 will be updated; else, will never be updated
tickMap.cross(
step.nextTick,
Tick.GrowthInfo({
feeX128: swapState.feeGrowthGlobalX128,
twPremiumX96: params.globalFundingGrowth.twPremiumX96,
twPremiumDivBySqrtPriceX96: params.globalFundingGrowth.twPremiumDivBySqrtPriceX96
})
);
}
int128 liquidityNet = UniswapV3Broker.getTickLiquidityNet(params.pool, step.nextTick);
if (params.isBaseToQuote) liquidityNet = liquidityNet.neg128();
swapState.liquidity = LiquidityMath.addDelta(swapState.liquidity, liquidityNet);
}
swapState.tick = params.isBaseToQuote ? step.nextTick - 1 : step.nextTick;
} else if (swapState.sqrtPriceX96 != step.initialSqrtPriceX96) {
// update state.tick corresponding to the current price if the price has changed in this step
swapState.tick = TickMath.getTickAtSqrtRatio(swapState.sqrtPriceX96);
}
}
if (params.shouldUpdateState) {
// update global states since swap state transitions are all done
_feeGrowthGlobalX128Map[params.baseToken] = swapState.feeGrowthGlobalX128;
}
return ReplaySwapResponse({ tick: swapState.tick, fee: fee, insuranceFundFee: insuranceFundFee });
}
//
// EXTERNAL VIEW
//
/// @inheritdoc IOrderBook
function getExchange() external view override returns (address) {
return _exchange;
}
/// @inheritdoc IOrderBook
function getOpenOrderIds(address trader, address baseToken) external view override returns (bytes32[] memory) {
return _openOrderIdsMap[trader][baseToken];
}
/// @inheritdoc IOrderBook
function getOpenOrderById(bytes32 orderId) external view override returns (OpenOrder.Info memory) {
return _openOrderMap[orderId];
}
/// @inheritdoc IOrderBook
function getOpenOrder(
address trader,
address baseToken,
int24 lowerTick,
int24 upperTick
) external view override returns (OpenOrder.Info memory) {
return _openOrderMap[OpenOrder.calcOrderKey(trader, baseToken, lowerTick, upperTick)];
}
/// @inheritdoc IOrderBook
function hasOrder(address trader, address[] calldata tokens) external view override returns (bool) {
for (uint256 i = 0; i < tokens.length; i++) {
if (_openOrderIdsMap[trader][tokens[i]].length > 0) {
return true;
}
}
return false;
}
/// @inheritdoc IOrderBook
function getTotalQuoteBalanceAndPendingFee(address trader, address[] calldata baseTokens)
external
view
override
returns (int256 totalQuoteAmountInPools, uint256 totalPendingFee)
{
for (uint256 i = 0; i < baseTokens.length; i++) {
address baseToken = baseTokens[i];
(int256 makerQuoteBalance, uint256 pendingFee) =
_getMakerQuoteBalanceAndPendingFee(trader, baseToken, false);
totalQuoteAmountInPools = totalQuoteAmountInPools.add(makerQuoteBalance);
totalPendingFee = totalPendingFee.add(pendingFee);
}
return (totalQuoteAmountInPools, totalPendingFee);
}
/// @inheritdoc IOrderBook
function getTotalTokenAmountInPoolAndPendingFee(
address trader,
address baseToken,
bool fetchBase // true: fetch base amount, false: fetch quote amount
) external view override returns (uint256 tokenAmount, uint256 pendingFee) {
(tokenAmount, pendingFee) = _getTotalTokenAmountInPool(trader, baseToken, fetchBase);
}
/// @inheritdoc IOrderBook
function getLiquidityCoefficientInFundingPayment(
address trader,
address baseToken,
Funding.Growth memory fundingGrowthGlobal
) external view override returns (int256 liquidityCoefficientInFundingPayment) {
bytes32[] memory orderIds = _openOrderIdsMap[trader][baseToken];
mapping(int24 => Tick.GrowthInfo) storage tickMap = _growthOutsideTickMap[baseToken];
address pool = IMarketRegistry(_marketRegistry).getPool(baseToken);
// funding of liquidity coefficient
(, int24 tick, , , , , ) = UniswapV3Broker.getSlot0(pool);
for (uint256 i = 0; i < orderIds.length; i++) {
OpenOrder.Info memory order = _openOrderMap[orderIds[i]];
Tick.FundingGrowthRangeInfo memory fundingGrowthRangeInfo =
tickMap.getAllFundingGrowth(
order.lowerTick,
order.upperTick,
tick,
fundingGrowthGlobal.twPremiumX96,
fundingGrowthGlobal.twPremiumDivBySqrtPriceX96
);
// the calculation here is based on cached values
liquidityCoefficientInFundingPayment = liquidityCoefficientInFundingPayment.add(
Funding.calcLiquidityCoefficientInFundingPaymentByOrder(order, fundingGrowthRangeInfo)
);
}
return liquidityCoefficientInFundingPayment;
}
/// @inheritdoc IOrderBook
function getPendingFee(
address trader,
address baseToken,
int24 lowerTick,
int24 upperTick
) external view override returns (uint256) {
(uint256 pendingFee, ) =
_getPendingFeeAndFeeGrowthInsideX128ByOrder(
baseToken,
_openOrderMap[OpenOrder.calcOrderKey(trader, baseToken, lowerTick, upperTick)]
);
return pendingFee;
}
//
// PUBLIC VIEW
//
/// @inheritdoc IOrderBook
function getTotalOrderDebt(
address trader,
address baseToken,
bool fetchBase
) public view override returns (uint256) {
uint256 totalOrderDebt;
bytes32[] memory orderIds = _openOrderIdsMap[trader][baseToken];
uint256 orderIdLength = orderIds.length;
for (uint256 i = 0; i < orderIdLength; i++) {
OpenOrder.Info memory orderInfo = _openOrderMap[orderIds[i]];
uint256 orderDebt = fetchBase ? orderInfo.baseDebt : orderInfo.quoteDebt;
totalOrderDebt = totalOrderDebt.add(orderDebt);
}
return totalOrderDebt;
}
//
// INTERNAL NON-VIEW
//
function _removeLiquidity(InternalRemoveLiquidityParams memory params)
internal
returns (RemoveLiquidityResponse memory)
{
UniswapV3Broker.RemoveLiquidityResponse memory response =
UniswapV3Broker.removeLiquidity(
UniswapV3Broker.RemoveLiquidityParams(
params.pool,
_clearingHouse,
params.lowerTick,
params.upperTick,
params.liquidity
)
);
// update token info based on existing open order
(uint256 fee, uint256 baseDebt, uint256 quoteDebt) = _removeLiquidityFromOrder(params);
int256 takerBase = response.base.toInt256().sub(baseDebt.toInt256());
int256 takerQuote = response.quote.toInt256().sub(quoteDebt.toInt256());
// if flipped from initialized to uninitialized, clear the tick info
if (!UniswapV3Broker.getIsTickInitialized(params.pool, params.lowerTick)) {
_growthOutsideTickMap[params.baseToken].clear(params.lowerTick);
}
if (!UniswapV3Broker.getIsTickInitialized(params.pool, params.upperTick)) {
_growthOutsideTickMap[params.baseToken].clear(params.upperTick);
}
return
RemoveLiquidityResponse({
base: response.base,
quote: response.quote,
fee: fee,
takerBase: takerBase,
takerQuote: takerQuote
});
}
function _removeLiquidityFromOrder(InternalRemoveLiquidityParams memory params)
internal
returns (
uint256 fee,
uint256 baseDebt,
uint256 quoteDebt
)
{
// update token info based on existing open order
OpenOrder.Info storage openOrder = _openOrderMap[params.orderId];
// as in _addLiquidityToOrder(), fee should be calculated before the states are updated
uint256 feeGrowthInsideX128;
(fee, feeGrowthInsideX128) = _getPendingFeeAndFeeGrowthInsideX128ByOrder(params.baseToken, openOrder);
if (params.liquidity != 0) {
if (openOrder.baseDebt != 0) {
baseDebt = FullMath.mulDiv(openOrder.baseDebt, params.liquidity, openOrder.liquidity);
openOrder.baseDebt = openOrder.baseDebt.sub(baseDebt);
}
if (openOrder.quoteDebt != 0) {
quoteDebt = FullMath.mulDiv(openOrder.quoteDebt, params.liquidity, openOrder.liquidity);
openOrder.quoteDebt = openOrder.quoteDebt.sub(quoteDebt);
}
openOrder.liquidity = openOrder.liquidity.sub(params.liquidity).toUint128();
}
// after the fee is calculated, lastFeeGrowthInsideX128 can be updated if liquidity != 0 after removing
if (openOrder.liquidity == 0) {
_removeOrder(params.maker, params.baseToken, params.orderId);
} else {
openOrder.lastFeeGrowthInsideX128 = feeGrowthInsideX128;
}
return (fee, baseDebt, quoteDebt);
}
function _removeOrder(
address maker,
address baseToken,
bytes32 orderId
) internal {
bytes32[] storage orderIds = _openOrderIdsMap[maker][baseToken];
uint256 orderLen = orderIds.length;
for (uint256 idx = 0; idx < orderLen; idx++) {
if (orderIds[idx] == orderId) {
// found the existing order ID
// remove it from the array efficiently by re-ordering and deleting the last element
if (idx != orderLen - 1) {
orderIds[idx] = orderIds[orderLen - 1];
}
orderIds.pop();
delete _openOrderMap[orderId];
break;
}
}
}
/// @dev this function is extracted from and only used by addLiquidity() to avoid stack too deep error
function _addLiquidityToOrder(InternalAddLiquidityToOrderParams memory params) internal returns (uint256) {
bytes32 orderId = OpenOrder.calcOrderKey(params.maker, params.baseToken, params.lowerTick, params.upperTick);
// get the struct by key, no matter it's a new or existing order
OpenOrder.Info storage openOrder = _openOrderMap[orderId];
// initialization for a new order
if (openOrder.liquidity == 0) {
bytes32[] storage orderIds = _openOrderIdsMap[params.maker][params.baseToken];
// OB_ONE: orders number exceeds
require(orderIds.length < IMarketRegistry(_marketRegistry).getMaxOrdersPerMarket(), "OB_ONE");
// state changes
orderIds.push(orderId);
openOrder.lowerTick = params.lowerTick;
openOrder.upperTick = params.upperTick;
(, int24 tick, , , , , ) = UniswapV3Broker.getSlot0(params.pool);
mapping(int24 => Tick.GrowthInfo) storage tickMap = _growthOutsideTickMap[params.baseToken];
Tick.FundingGrowthRangeInfo memory fundingGrowthRangeInfo =
tickMap.getAllFundingGrowth(
openOrder.lowerTick,
openOrder.upperTick,
tick,
params.globalFundingGrowth.twPremiumX96,
params.globalFundingGrowth.twPremiumDivBySqrtPriceX96
);
openOrder.lastTwPremiumGrowthInsideX96 = fundingGrowthRangeInfo.twPremiumGrowthInsideX96;
openOrder.lastTwPremiumGrowthBelowX96 = fundingGrowthRangeInfo.twPremiumGrowthBelowX96;
openOrder.lastTwPremiumDivBySqrtPriceGrowthInsideX96 = fundingGrowthRangeInfo
.twPremiumDivBySqrtPriceGrowthInsideX96;
}
// fee should be calculated before the states are updated, as for
// - a new order, there is no fee accrued yet
// - an existing order, fees accrued have to be settled before more liquidity is added
(uint256 fee, uint256 feeGrowthInsideX128) =
_getPendingFeeAndFeeGrowthInsideX128ByOrder(params.baseToken, openOrder);
// after the fee is calculated, liquidity & lastFeeGrowthInsideX128 can be updated
openOrder.liquidity = openOrder.liquidity.add(params.liquidity).toUint128();
openOrder.lastFeeGrowthInsideX128 = feeGrowthInsideX128;
openOrder.baseDebt = openOrder.baseDebt.add(params.base);
openOrder.quoteDebt = openOrder.quoteDebt.add(params.quote);
return fee;
}
//
// INTERNAL VIEW
//
/// @return makerBalance maker quote balance
/// @return pendingFee pending fee
function _getMakerQuoteBalanceAndPendingFee(
address trader,
address baseToken,
bool fetchBase
) internal view returns (int256 makerBalance, uint256 pendingFee) {
(uint256 totalBalanceFromOrders, uint256 pendingFee) = _getTotalTokenAmountInPool(trader, baseToken, fetchBase);
uint256 totalOrderDebt = getTotalOrderDebt(trader, baseToken, fetchBase);
// makerBalance = totalTokenAmountInPool - totalOrderDebt
return (totalBalanceFromOrders.toInt256().sub(totalOrderDebt.toInt256()), pendingFee);
}
/// @dev Get total amount of the specified tokens in the specified pool.
/// Note:
/// 1. when querying quote amount, it includes Exchange fees, i.e.:
/// quote amount = quote liquidity + fees
/// base amount = base liquidity
/// 2. quote/base liquidity does NOT include Uniswap pool fees since
/// they do not have any impact to our margin system
/// 3. the returned fee amount is only meaningful when querying quote amount
function _getTotalTokenAmountInPool(
address trader,
address baseToken, // this argument is only for specifying which pool to get base or quote amounts
bool fetchBase // true: fetch base amount, false: fetch quote amount
) internal view returns (uint256 tokenAmount, uint256 pendingFee) {
bytes32[] memory orderIds = _openOrderIdsMap[trader][baseToken];
//
// tick: lower upper
// -|---+-----------------+---|--
// case 1 case 2
//
// if current price < upper tick, maker has base
// case 1 : current price < lower tick
// --> maker only has base token
//
// if current price > lower tick, maker has quote
// case 2 : current price > upper tick
// --> maker only has quote token
(uint160 sqrtMarkPriceX96, , , , , , ) =
UniswapV3Broker.getSlot0(IMarketRegistry(_marketRegistry).getPool(baseToken));
uint256 orderIdLength = orderIds.length;
for (uint256 i = 0; i < orderIdLength; i++) {
OpenOrder.Info memory order = _openOrderMap[orderIds[i]];
uint256 amount;
{
uint160 sqrtPriceAtLowerTick = TickMath.getSqrtRatioAtTick(order.lowerTick);
uint160 sqrtPriceAtUpperTick = TickMath.getSqrtRatioAtTick(order.upperTick);
if (fetchBase && sqrtMarkPriceX96 < sqrtPriceAtUpperTick) {
amount = LiquidityAmounts.getAmount0ForLiquidity(
sqrtMarkPriceX96 > sqrtPriceAtLowerTick ? sqrtMarkPriceX96 : sqrtPriceAtLowerTick,
sqrtPriceAtUpperTick,
order.liquidity
);
} else if (!fetchBase && sqrtMarkPriceX96 > sqrtPriceAtLowerTick) {
amount = LiquidityAmounts.getAmount1ForLiquidity(
sqrtPriceAtLowerTick,
sqrtMarkPriceX96 < sqrtPriceAtUpperTick ? sqrtMarkPriceX96 : sqrtPriceAtUpperTick,
order.liquidity
);
}
}
tokenAmount = tokenAmount.add(amount);
// get uncollected fee (only quote)
if (!fetchBase) {
(uint256 pendingFeeInOrder, ) = _getPendingFeeAndFeeGrowthInsideX128ByOrder(baseToken, order);
pendingFee = pendingFee.add(pendingFeeInOrder);
}
}
return (tokenAmount, pendingFee);
}
/// @dev CANNOT use safeMath for feeGrowthInside calculation, as it can be extremely large and overflow
/// the difference between two feeGrowthInside, however, is correct and won't be affected by overflow or not
function _getPendingFeeAndFeeGrowthInsideX128ByOrder(address baseToken, OpenOrder.Info memory order)
internal
view
returns (uint256 pendingFee, uint256 feeGrowthInsideX128)
{
(, int24 tick, , , , , ) = UniswapV3Broker.getSlot0(IMarketRegistry(_marketRegistry).getPool(baseToken));
mapping(int24 => Tick.GrowthInfo) storage tickMap = _growthOutsideTickMap[baseToken];
feeGrowthInsideX128 = tickMap.getFeeGrowthInsideX128(
order.lowerTick,
order.upperTick,
tick,
_feeGrowthGlobalX128Map[baseToken]
);
pendingFee = FullMath.mulDiv(
feeGrowthInsideX128 - order.lastFeeGrowthInsideX128,
order.liquidity,
FixedPoint128.Q128
);
return (pendingFee, feeGrowthInsideX128);
}
function _requireOnlyExchange() internal view {
// OB_OEX: Only exchange
require(_msgSender() == _exchange, "OB_OEX");
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { VirtualToken } from "./VirtualToken.sol";
contract QuoteToken is VirtualToken {
function initialize(string memory nameArg, string memory symbolArg) external initializer {
__VirtualToken_init(nameArg, symbolArg);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { AccountMarket } from "../lib/AccountMarket.sol";
/// @notice For future upgrades, do not change AccountBalanceStorageV1. Create a new
/// contract which implements AccountBalanceStorageV1 and following the naming convention
/// AccountBalanceStorageVX.
abstract contract AccountBalanceStorageV1 {
address internal _clearingHouseConfig;
address internal _orderBook;
address internal _vault;
// trader => owedRealizedPnl
mapping(address => int256) internal _owedRealizedPnlMap;
// trader => baseTokens
// base token registry of each trader
mapping(address => address[]) internal _baseTokensMap;
// first key: trader, second key: baseToken
mapping(address => mapping(address => AccountMarket.Info)) internal _accountMarketMap;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { IBaseToken } from "../interface/IBaseToken.sol";
/// @notice For future upgrades, do not change BaseTokenStorageV1. Create a new
/// contract which implements BaseTokenStorageV1 and following the naming convention
/// BaseTokenStorageVX.
abstract contract BaseTokenStorageV1 {
// --------- IMMUTABLE ---------
// _priceFeedDecimals is now priceFeedDispatcherDecimals, which is IPriceFeedDispatcher.decimals()
uint8 internal _priceFeedDecimals;
// --------- ^^^^^^^^^ ---------
// _priceFeed is now priceFeedDispatcher, which dispatches either Chainlink or UniswapV3 price
address internal _priceFeed;
}
abstract contract BaseTokenStorageV2 is BaseTokenStorageV1 {
IBaseToken.Status internal _status;
uint256 internal _pausedIndexPrice;
uint256 internal _pausedTimestamp;
uint256 internal _closedPrice;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
/// @notice For future upgrades, do not change ClearingHouseConfigStorageV1. Create a new
/// contract which implements ClearingHouseConfigStorageV1 and following the naming convention
/// ClearingHouseConfigStorageVX.
abstract contract ClearingHouseConfigStorageV1 {
uint8 internal _maxMarketsPerAccount;
uint24 internal _imRatio;
uint24 internal _mmRatio;
uint24 internal _liquidationPenaltyRatio;
// _partialCloseRatio is deprecated
uint24 internal _partialCloseRatio;
uint24 internal _maxFundingRate;
uint32 internal _twapInterval;
uint256 internal _settlementTokenBalanceCap;
}
abstract contract ClearingHouseConfigStorageV2 is ClearingHouseConfigStorageV1 {
// _backstopLiquidityProviderMap is deprecated
mapping(address => bool) internal _backstopLiquidityProviderMap;
}
abstract contract ClearingHouseConfigStorageV3 is ClearingHouseConfigStorageV2 {
uint32 internal _markPriceMarketTwapInterval;
uint32 internal _markPricePremiumInterval;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
/// @notice For future upgrades, do not change ClearingHouseStorageV1. Create a new
/// contract which implements ClearingHouseStorageV1 and following the naming convention
/// ClearingHouseStorageVX.
abstract contract ClearingHouseStorageV1 {
// --------- IMMUTABLE ---------
address internal _quoteToken;
address internal _uniswapV3Factory;
// cache the settlement token's decimals for gas optimization
uint8 internal _settlementTokenDecimals;
// --------- ^^^^^^^^^ ---------
address internal _clearingHouseConfig;
address internal _vault;
address internal _exchange;
address internal _orderBook;
address internal _accountBalance;
address internal _insuranceFund;
}
abstract contract ClearingHouseStorageV2 is ClearingHouseStorageV1 {
address internal _delegateApproval;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.6;
import { Collateral } from "../lib/Collateral.sol";
abstract contract CollateralManagerStorageV1 {
// key: token address, value: collateral config
mapping(address => Collateral.Config) internal _collateralConfigMap;
address internal _clearingHouseConfig;
address internal _vault;
uint8 internal _maxCollateralTokensPerAccount;
uint24 internal _mmRatioBuffer;
uint24 internal _debtNonSettlementTokenValueRatio;
uint24 internal _liquidationRatio;
uint24 internal _clInsuranceFundFeeRatio;
uint256 internal _debtThreshold;
uint256 internal _collateralValueDust;
}
abstract contract CollateralManagerStorageV2 is CollateralManagerStorageV1 {
// key: trader address, value: whitelisted debt threshold
mapping(address => uint256) internal _whitelistedDebtThresholdMap;
uint256 internal _totalWhitelistedDebtThreshold;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
/// @notice For future upgrades, do not change DelegateApprovalStorageV1. Create a new
/// contract which implements DelegateApprovalStorageV1 and following the naming convention
/// DelegateApprovalStorageVX.
abstract contract DelegateApprovalStorageV1 {
// key: the hash of `trader` and `delegate`, see _getApprovalKey()
// value: the bit value of approved actions
mapping(bytes32 => uint8) internal _approvalMap;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { Funding } from "../lib/Funding.sol";
/// @notice For future upgrades, do not change ExchangeStorageV1. Create a new
/// contract which implements ExchangeStorageV1 and following the naming convention
/// ExchangeStorageVX.
abstract contract ExchangeStorageV1 {
address internal _orderBook;
address internal _accountBalance;
address internal _clearingHouseConfig;
mapping(address => int24) internal _lastUpdatedTickMap;
mapping(address => uint256) internal _firstTradedTimestampMap;
// the last timestamp when funding is settled
mapping(address => uint256) internal _lastSettledTimestampMap;
mapping(address => Funding.Growth) internal _globalFundingGrowthX96Map;
// key: base token
// value: a threshold to limit the price impact per block when reducing or closing the position
mapping(address => uint24) internal _maxTickCrossedWithinBlockMap;
// _lastOverPriceLimitTimestampMap is deprecated
// first key: trader, second key: baseToken
// value: the last timestamp when a trader exceeds price limit when closing a position/being liquidated
mapping(address => mapping(address => uint256)) internal _lastOverPriceLimitTimestampMap;
}
abstract contract ExchangeStorageV2 is ExchangeStorageV1 {
// the last timestamp when tick is updated; for price limit check
// key: base token
// value: the last timestamp to update the tick
mapping(address => uint256) internal _lastTickUpdatedTimestampMap;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
/// @notice For future upgrades, do not change InsuranceFundStorageV1. Create a new
/// contract which implements InsuranceFundStorageV1 and following the naming convention
/// InsuranceFundStorageVX.
abstract contract InsuranceFundStorageV1 {
// --------- IMMUTABLE ---------
address internal _token;
// --------- ^^^^^^^^^ ---------
address internal _vault;
}
abstract contract InsuranceFundStorageV2 is InsuranceFundStorageV1 {
address internal _surplusBeneficiary;
// decimal is the same as the settlement token
uint256 internal _distributionThreshold;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
/// @notice For future upgrades, do not change MarketRegistryStorageV1. Create a new
/// contract which implements MarketRegistryStorageV1 and following the naming convention
/// MarketRegistryStorageVX.
abstract contract MarketRegistryStorageV1 {
address internal _uniswapV3Factory;
address internal _quoteToken;
uint8 internal _maxOrdersPerMarket;
// key: baseToken, value: pool
mapping(address => address) internal _poolMap;
// key: baseToken, what insurance fund get = exchangeFee * insuranceFundFeeRatio
mapping(address => uint24) internal _insuranceFundFeeRatioMap;
// key: baseToken , uniswap fee will be ignored and use the exchangeFeeRatio instead
mapping(address => uint24) internal _exchangeFeeRatioMap;
// key: baseToken, _uniswapFeeRatioMap cache only
mapping(address => uint24) internal _uniswapFeeRatioMap;
}
abstract contract MarketRegistryStorageV2 is MarketRegistryStorageV1 {
// key: base token
// value: the max price spread ratio of the market
mapping(address => uint24) internal _marketMaxPriceSpreadRatioMap;
}
abstract contract MarketRegistryStorageV3 is MarketRegistryStorageV2 {
// key: trader
// value: discount ratio (percent-off)
mapping(address => uint24) internal _feeDiscountRatioMap;
}
abstract contract MarketRegistryStorageV4 is MarketRegistryStorageV3 {
mapping(address => bool) internal _feeManagerMap;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { Tick } from "../lib/Tick.sol";
import { Funding } from "../lib/Funding.sol";
import { OpenOrder } from "../lib/OpenOrder.sol";
/// @notice For future upgrades, do not change OrderBookStorageV1. Create a new
/// contract which implements OrderBookStorageV1 and following the naming convention
/// OrderBookStorageVX.
abstract contract OrderBookStorageV1 {
address internal _exchange;
// first key: trader, second key: base token
mapping(address => mapping(address => bytes32[])) internal _openOrderIdsMap;
// key: openOrderId
mapping(bytes32 => OpenOrder.Info) internal _openOrderMap;
// first key: base token, second key: tick index
// value: the accumulator of **Tick.GrowthInfo** outside each tick of each pool
mapping(address => mapping(int24 => Tick.GrowthInfo)) internal _growthOutsideTickMap;
// key: base token
// value: the global accumulator of **quote fee transformed from base fee** of each pool
mapping(address => uint256) internal _feeGrowthGlobalX128Map;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
/// @notice For future upgrades, do not change VaultStorageV1. Create a new
/// contract which implements VaultStorageV1 and following the naming convention
/// VaultStorageVX.
abstract contract VaultStorageV1 {
// --------- IMMUTABLE ---------
uint8 internal _decimals;
address internal _settlementToken;
// --------- ^^^^^^^^^ ---------
address internal _clearingHouseConfig;
address internal _accountBalance;
address internal _insuranceFund;
address internal _exchange;
address internal _clearingHouse;
// _totalDebt is deprecated
uint256 internal _totalDebt;
// key: trader, token address
mapping(address => mapping(address => int256)) internal _balance;
}
abstract contract VaultStorageV2 is VaultStorageV1 {
address internal _collateralManager;
address internal _WETH9;
// trader => collateral token
// collateral token registry of each trader
mapping(address => address[]) internal _collateralTokensMap;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
pragma abicoder v2;
import { AddressUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol";
import { SignedSafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SignedSafeMathUpgradeable.sol";
import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/MathUpgradeable.sol";
import {
SafeERC20Upgradeable,
IERC20Upgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC20/SafeERC20Upgradeable.sol";
import { FullMath } from "@uniswap/v3-core/contracts/libraries/FullMath.sol";
import { TransferHelper } from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
import { PerpSafeCast } from "./lib/PerpSafeCast.sol";
import { SettlementTokenMath } from "./lib/SettlementTokenMath.sol";
import { PerpMath } from "./lib/PerpMath.sol";
import { IERC20Metadata } from "./interface/IERC20Metadata.sol";
import { IInsuranceFund } from "./interface/IInsuranceFund.sol";
import { IExchange } from "./interface/IExchange.sol";
import { IAccountBalance } from "./interface/IAccountBalance.sol";
import { IClearingHouseConfig } from "./interface/IClearingHouseConfig.sol";
import { IClearingHouse } from "./interface/IClearingHouse.sol";
import { BaseRelayRecipient } from "./gsn/BaseRelayRecipient.sol";
import { OwnerPausable } from "./base/OwnerPausable.sol";
import { VaultStorageV2 } from "./storage/VaultStorage.sol";
import { Collateral } from "./lib/Collateral.sol";
import { IVault } from "./interface/IVault.sol";
import { IWETH9 } from "./interface/external/IWETH9.sol";
import { ICollateralManager } from "./interface/ICollateralManager.sol";
// never inherit any new stateful contract. never change the orders of parent stateful contracts
contract Vault is IVault, ReentrancyGuardUpgradeable, OwnerPausable, BaseRelayRecipient, VaultStorageV2 {
using SafeMathUpgradeable for uint256;
using PerpSafeCast for uint256;
using PerpSafeCast for int256;
using SignedSafeMathUpgradeable for int256;
using SettlementTokenMath for uint256;
using SettlementTokenMath for int256;
using PerpMath for int256;
using PerpMath for uint256;
using PerpMath for uint24;
using FullMath for uint256;
using AddressUpgradeable for address;
uint24 private constant _ONE_HUNDRED_PERCENT_RATIO = 1e6;
uint32 private constant _COLLATERAL_TWAP_INTERVAL = 900; // 15 minutes
//
// MODIFIER
//
modifier onlySettlementOrCollateralToken(address token) {
// V_OSCT: only settlement or collateral token
require(token == _settlementToken || _isCollateral(token), "V_OSCT");
_;
}
//
// EXTERNAL NON-VIEW
//
/// @dev only used for unwrapping weth in withdrawETH
receive() external payable {
// V_SNW: sender is not WETH9
require(_msgSender() == _WETH9, "V_SNW");
}
function initialize(
address insuranceFundArg,
address clearingHouseConfigArg,
address accountBalanceArg,
address exchangeArg
) external initializer {
address settlementTokenArg = IInsuranceFund(insuranceFundArg).getToken();
uint8 decimalsArg = IERC20Metadata(settlementTokenArg).decimals();
// invalid settlementToken decimals
require(decimalsArg <= 18, "V_ISTD");
// ClearingHouseConfig address is not contract
require(clearingHouseConfigArg.isContract(), "V_CHCNC");
// accountBalance address is not contract
require(accountBalanceArg.isContract(), "V_ABNC");
// exchange address is not contract
require(exchangeArg.isContract(), "V_ENC");
__ReentrancyGuard_init();
__OwnerPausable_init();
// update states
_decimals = decimalsArg;
_settlementToken = settlementTokenArg;
_insuranceFund = insuranceFundArg;
_clearingHouseConfig = clearingHouseConfigArg;
_accountBalance = accountBalanceArg;
_exchange = exchangeArg;
}
function setTrustedForwarder(address trustedForwarderArg) external onlyOwner {
// V_TFNC: TrustedForwarder address is not contract
require(trustedForwarderArg.isContract(), "V_TFNC");
_setTrustedForwarder(trustedForwarderArg);
emit TrustedForwarderChanged(trustedForwarderArg);
}
function setClearingHouse(address clearingHouseArg) external onlyOwner {
// V_CHNC: ClearingHouse is not contract
require(clearingHouseArg.isContract(), "V_CHNC");
_clearingHouse = clearingHouseArg;
emit ClearingHouseChanged(clearingHouseArg);
}
function setCollateralManager(address collateralManagerArg) external onlyOwner {
// V_CMNC: CollateralManager is not contract
require(collateralManagerArg.isContract(), "V_CMNC");
_collateralManager = collateralManagerArg;
emit CollateralManagerChanged(collateralManagerArg);
}
function setWETH9(address WETH9Arg) external onlyOwner {
// V_WNC: WETH9 is not contract
require(WETH9Arg.isContract(), "V_WNC");
_WETH9 = WETH9Arg;
emit WETH9Changed(WETH9Arg);
}
/// @inheritdoc IVault
function deposit(address token, uint256 amount)
external
override
whenNotPaused
nonReentrant
onlySettlementOrCollateralToken(token)
{
// input requirement checks:
// token: here
// amount: _deposit
address from = _msgSender();
_deposit(from, from, token, amount);
}
/// @inheritdoc IVault
function depositFor(
address to,
address token,
uint256 amount
) external override whenNotPaused nonReentrant onlySettlementOrCollateralToken(token) {
// input requirement checks:
// token: here
// amount: _deposit
// V_DFZA: Deposit for zero address
require(to != address(0), "V_DFZA");
address from = _msgSender();
_deposit(from, to, token, amount);
}
/// @inheritdoc IVault
function depositEther() external payable override whenNotPaused nonReentrant {
address to = _msgSender();
_depositEther(to);
}
/// @inheritdoc IVault
function depositEtherFor(address to) external payable override whenNotPaused nonReentrant {
// input requirement checks:
// to: here
// V_DFZA: Deposit for zero address
require(to != address(0), "V_DFZA");
_depositEther(to);
}
/// @inheritdoc IVault
// the full process of withdrawal:
// 1. settle funding payment to owedRealizedPnl
// 2. collect fee to owedRealizedPnl
// 3. call Vault.withdraw(token, amount)
// 4. settle pnl to trader balance in Vault
// 5. transfer the amount to trader
function withdraw(address token, uint256 amount)
external
override
whenNotPaused
nonReentrant
onlySettlementOrCollateralToken(token)
{
// input requirement checks:
// token: here
// amount: in _settleAndDecreaseBalance()
address to = _msgSender();
_withdraw(to, token, amount);
}
/// @inheritdoc IVault
function withdrawEther(uint256 amount) external override whenNotPaused nonReentrant {
// input requirement checks:
// amount: in _settleAndDecreaseBalance()
_requireWETH9IsCollateral();
address to = _msgSender();
_withdrawEther(to, amount);
}
/// @inheritdoc IVault
function withdrawAll(address token)
external
override
whenNotPaused
nonReentrant
onlySettlementOrCollateralToken(token)
returns (uint256 amount)
{
// input requirement checks:
// token: here
address to = _msgSender();
amount = getFreeCollateralByToken(to, token);
_withdraw(to, token, amount);
return amount;
}
/// @inheritdoc IVault
function withdrawAllEther() external override whenNotPaused nonReentrant returns (uint256 amount) {
_requireWETH9IsCollateral();
address to = _msgSender();
amount = getFreeCollateralByToken(to, _WETH9);
_withdrawEther(to, amount);
return amount;
}
/// @inheritdoc IVault
function liquidateCollateral(
address trader,
address token,
uint256 amount,
bool isDenominatedInSettlementToken
) external override whenNotPaused nonReentrant returns (uint256) {
// input requirement checks:
// trader: here
// token: in _isCollateral()
// amount: here
// isDenominatedInSettlementToken: X
// V_NL: Not liquidatable
require(isLiquidatable(trader), "V_NL");
(uint256 maxRepaidSettlementX10_S, uint256 maxLiquidatableCollateral) =
getMaxRepaidSettlementAndLiquidatableCollateral(trader, token);
uint256 collateral;
uint256 settlementX10_S;
uint256 returnAmount;
if (isDenominatedInSettlementToken) {
settlementX10_S = amount;
// V_MSAE: Maximum settlement amount exceeded
require(settlementX10_S <= maxRepaidSettlementX10_S, "V_MSAE");
collateral = settlementX10_S == maxRepaidSettlementX10_S
? maxLiquidatableCollateral
: getLiquidatableCollateralBySettlement(token, settlementX10_S);
returnAmount = collateral;
} else {
collateral = amount;
// V_MCAE: Maximum collateral amount exceeded
require(collateral <= maxLiquidatableCollateral, "V_MCAE");
settlementX10_S = collateral == maxLiquidatableCollateral
? maxRepaidSettlementX10_S
: getRepaidSettlementByCollateral(token, collateral);
returnAmount = settlementX10_S;
}
_liquidateCollateral(trader, token, settlementX10_S, collateral);
return returnAmount;
}
//
// EXTERNAL VIEW
//
/// @inheritdoc IVault
function getSettlementToken() external view override returns (address) {
return _settlementToken;
}
/// @inheritdoc IVault
function decimals() external view override returns (uint8) {
return _decimals;
}
/// @inheritdoc IVault
function getTotalDebt() external view override returns (uint256) {
return _totalDebt;
}
/// @inheritdoc IVault
function getClearingHouseConfig() external view override returns (address) {
return _clearingHouseConfig;
}
/// @inheritdoc IVault
function getAccountBalance() external view override returns (address) {
return _accountBalance;
}
/// @inheritdoc IVault
function getInsuranceFund() external view override returns (address) {
return _insuranceFund;
}
/// @inheritdoc IVault
function getExchange() external view override returns (address) {
return _exchange;
}
/// @inheritdoc IVault
function getClearingHouse() external view override returns (address) {
return _clearingHouse;
}
/// @inheritdoc IVault
function getCollateralManager() external view override returns (address) {
return _collateralManager;
}
/// @inheritdoc IVault
function getWETH9() external view override returns (address) {
return _WETH9;
}
/// @inheritdoc IVault
function getFreeCollateral(address trader) external view override returns (uint256) {
return _getFreeCollateral(trader).formatSettlementToken(_decimals);
}
/// @inheritdoc IVault
function getFreeCollateralByRatio(address trader, uint24 ratio) external view override returns (int256) {
return _getFreeCollateralByRatio(trader, ratio).formatSettlementToken(_decimals);
}
/// @inheritdoc IVault
function getSettlementTokenValue(address trader) external view override returns (int256) {
return _getSettlementTokenValue(trader).formatSettlementToken(_decimals);
}
/// @inheritdoc IVault
function getAccountValue(address trader) external view override returns (int256) {
(int256 accountValueX10_18, ) = _getAccountValueAndTotalCollateralValue(trader);
return accountValueX10_18.formatSettlementToken(_decimals);
}
/// @inheritdoc IVault
function getCollateralTokens(address trader) external view override returns (address[] memory) {
return _collateralTokensMap[trader];
}
//
// PUBLIC VIEW
//
/// @inheritdoc IVault
function getBalance(address trader) public view override returns (int256) {
return _balance[trader][_settlementToken];
}
/// @inheritdoc IVault
function getBalanceByToken(address trader, address token) public view override returns (int256) {
return _balance[trader][token];
}
/// @inheritdoc IVault
/// @dev getFreeCollateralByToken(token) = (getSettlementTokenValue() >= 0)
/// ? min(getFreeCollateral() / indexPrice[token], getBalanceByToken(token))
/// : 0
/// @dev if token is settlementToken, then indexPrice[token] = 1
function getFreeCollateralByToken(address trader, address token) public view override returns (uint256) {
// do not check settlementTokenValue == 0 because user's settlement token balance may be zero
if (_getSettlementTokenValue(trader) < 0) {
return 0;
}
uint256 freeCollateralX10_18 = _getFreeCollateral(trader);
if (freeCollateralX10_18 == 0) {
return 0;
}
if (token == _settlementToken) {
(int256 settlementTokenBalanceX10_18, ) = _getSettlementTokenBalanceAndUnrealizedPnl(trader);
return
settlementTokenBalanceX10_18 <= 0
? 0
: MathUpgradeable
.min(freeCollateralX10_18, settlementTokenBalanceX10_18.toUint256())
.formatSettlementToken(_decimals);
}
(uint256 indexTwap, uint8 priceFeedDecimals) = _getIndexPriceAndDecimals(token);
uint24 collateralRatio = ICollateralManager(_collateralManager).getCollateralConfig(token).collateralRatio;
return
MathUpgradeable.min(
_getCollateralBySettlement(token, freeCollateralX10_18, indexTwap, priceFeedDecimals).divRatio(
collateralRatio
),
// non-settlement token is always positive number
getBalanceByToken(trader, token).toUint256()
);
}
/// @inheritdoc IVault
function isLiquidatable(address trader) public view override returns (bool) {
address[] storage collateralTokens = _collateralTokensMap[trader];
if (collateralTokens.length == 0) {
return false;
}
(int256 accountValueX10_18, ) = _getAccountValueAndTotalCollateralValue(trader);
if (accountValueX10_18 < getMarginRequirementForCollateralLiquidation(trader)) {
return true;
}
int256 settlementTokenValueX10_18 = _getSettlementTokenValue(trader);
uint256 settlementTokenDebtX10_18 =
settlementTokenValueX10_18 < 0 ? settlementTokenValueX10_18.neg256().toUint256() : 0;
if (
settlementTokenDebtX10_18 >
_getNonSettlementTokenValue(trader).mulRatio(
ICollateralManager(_collateralManager).getDebtNonSettlementTokenValueRatio()
)
) {
return true;
}
if (
settlementTokenDebtX10_18.formatSettlementToken(_decimals) >
ICollateralManager(_collateralManager).getDebtThresholdByTrader(trader)
) {
return true;
}
return false;
}
/// @inheritdoc IVault
function getMarginRequirementForCollateralLiquidation(address trader) public view override returns (int256) {
return
IAccountBalance(_accountBalance)
.getTotalAbsPositionValue(trader)
.mulRatio(getCollateralMmRatio())
.toInt256();
}
/// @inheritdoc IVault
function getCollateralMmRatio() public view override returns (uint24) {
uint24 collateralMmRatio =
ICollateralManager(_collateralManager).requireValidCollateralMmRatio(
ICollateralManager(_collateralManager).getMmRatioBuffer()
);
return collateralMmRatio;
}
/// @inheritdoc IVault
function getRepaidSettlementByCollateral(address token, uint256 collateral)
public
view
override
returns (uint256 settlementX10_S)
{
uint24 discountRatio = ICollateralManager(_collateralManager).getCollateralConfig(token).discountRatio;
(uint256 indexTwap, uint8 priceFeedDecimals) = _getIndexPriceAndDecimals(token);
return
_getSettlementByCollateral(
token,
collateral,
indexTwap.mulRatio(_ONE_HUNDRED_PERCENT_RATIO.subRatio(discountRatio)),
priceFeedDecimals
)
.formatSettlementToken(_decimals);
}
/// @inheritdoc IVault
function getLiquidatableCollateralBySettlement(address token, uint256 settlementX10_S)
public
view
override
returns (uint256 collateral)
{
uint24 discountRatio = ICollateralManager(_collateralManager).getCollateralConfig(token).discountRatio;
(uint256 indexTwap, uint8 priceFeedDecimals) = _getIndexPriceAndDecimals(token);
return
_getCollateralBySettlement(
token,
settlementX10_S.parseSettlementToken(_decimals),
indexTwap.mulRatio(_ONE_HUNDRED_PERCENT_RATIO.subRatio(discountRatio)),
priceFeedDecimals
);
}
/// @inheritdoc IVault
/// @dev formula:
/// maxRepaidSettlement = maxLiquidatableCollateral * (indexTwap * (1 - discountRatio))
/// maxLiquidatableCollateral =
/// min(maxRepaidSettlement / (indexTwap * (1 - discountRatio)), getBalanceByToken(trader, token))
function getMaxRepaidSettlementAndLiquidatableCollateral(address trader, address token)
public
view
override
returns (uint256 maxRepaidSettlementX10_S, uint256 maxLiquidatableCollateral)
{
// V_TINAC: token is not a collateral
require(_isCollateral(token), "V_TINAC");
uint256 maxRepaidSettlementX10_18 = _getMaxRepaidSettlement(trader);
uint24 discountRatio = ICollateralManager(_collateralManager).getCollateralConfig(token).discountRatio;
(uint256 indexTwap, uint8 priceFeedDecimals) = _getIndexPriceAndDecimals(token);
uint256 discountedIndexTwap = indexTwap.mulRatio(_ONE_HUNDRED_PERCENT_RATIO.subRatio(discountRatio));
maxLiquidatableCollateral = _getCollateralBySettlement(
token,
maxRepaidSettlementX10_18,
discountedIndexTwap,
priceFeedDecimals
);
uint256 tokenBalance = getBalanceByToken(trader, token).toUint256();
if (maxLiquidatableCollateral > tokenBalance) {
maxLiquidatableCollateral = tokenBalance;
// Deliberately rounding down when calculating settlement. Thus, when calculating
// collateral with settlement, the result is always <= maxCollateral.
// This makes sure that collateral will always be <= user's collateral balance.
maxRepaidSettlementX10_18 = _getSettlementByCollateral(
token,
maxLiquidatableCollateral,
discountedIndexTwap,
priceFeedDecimals
);
}
maxRepaidSettlementX10_S = maxRepaidSettlementX10_18.formatSettlementToken(_decimals);
return (maxRepaidSettlementX10_S, maxLiquidatableCollateral);
}
/// @inheritdoc IVault
/// @dev will only settle the bad debt when trader didn't have position and non-settlement collateral
function settleBadDebt(address trader) public override {
// V_CSI: can't settle insuranceFund
require(trader != _insuranceFund, "V_CSI");
// trader has position or trader has non-settlement collateral
if (
IAccountBalance(_accountBalance).getBaseTokens(trader).length != 0 ||
_collateralTokensMap[trader].length != 0
) {
return;
}
// assume trader has no position and no non-settlement collateral
// so accountValue = settlement token balance
(int256 accountValueX10_18, ) = _getSettlementTokenBalanceAndUnrealizedPnl(trader);
int256 accountValueX10_S = accountValueX10_18.formatSettlementToken(_decimals);
if (accountValueX10_S >= 0) {
return;
}
// settle bad debt for trader
int256 badDebt = accountValueX10_S.neg256();
address settlementToken = _settlementToken; // SLOAD gas saving
_modifyBalance(_insuranceFund, settlementToken, accountValueX10_S);
_modifyBalance(trader, settlementToken, badDebt);
uint256 absBadDebt = badDebt.toUint256();
emit BadDebtSettled(trader, absBadDebt);
}
//
// INTERNAL NON-VIEW
//
/// @param token the collateral token needs to be transferred into vault
/// @param from the address of account who owns the collateral token
/// @param amount the amount of collateral token needs to be transferred
function _transferTokenIn(
address token,
address from,
uint256 amount
) internal {
// check for deflationary tokens by assuring balances before and after transferring to be the same
uint256 balanceBefore = IERC20Metadata(token).balanceOf(address(this));
SafeERC20Upgradeable.safeTransferFrom(IERC20Upgradeable(token), from, address(this), amount);
// V_IBA: inconsistent balance amount, to prevent from deflationary tokens
require((IERC20Metadata(token).balanceOf(address(this)).sub(balanceBefore)) == amount, "V_IBA");
}
/// @param from deposit token from this address
/// @param to deposit token to this address
/// @param token the collateral token wish to deposit
/// @param amount the amount of token to deposit
function _deposit(
address from,
address to,
address token,
uint256 amount
) internal {
// V_ZA: Zero amount
require(amount > 0, "V_ZA");
_transferTokenIn(token, from, amount);
_checkDepositCapAndRegister(token, to, amount);
}
/// @param to deposit ETH to this address
function _depositEther(address to) internal {
uint256 amount = msg.value;
// V_ZA: Zero amount
require(amount > 0, "V_ZA");
_requireWETH9IsCollateral();
// SLOAD for gas saving
address WETH9 = _WETH9;
// wrap ETH into WETH
IWETH9(WETH9).deposit{ value: amount }();
_checkDepositCapAndRegister(WETH9, to, amount);
}
/// @param token the collateral token needs to be transferred out of vault
/// @param to the address of account that the collateral token deposit to
/// @param amount the amount of collateral token to be deposited
function _checkDepositCapAndRegister(
address token,
address to,
uint256 amount
) internal {
if (token == _settlementToken) {
uint256 settlementTokenBalanceCap =
IClearingHouseConfig(_clearingHouseConfig).getSettlementTokenBalanceCap();
// V_GTSTBC: greater than settlement token balance cap
require(IERC20Metadata(token).balanceOf(address(this)) <= settlementTokenBalanceCap, "V_GTSTBC");
} else {
uint256 depositCap = ICollateralManager(_collateralManager).getCollateralConfig(token).depositCap;
// V_GTDC: greater than deposit cap
require(IERC20Metadata(token).balanceOf(address(this)) <= depositCap, "V_GTDC");
}
_modifyBalance(to, token, amount.toInt256());
emit Deposited(token, to, amount);
}
function _settleAndDecreaseBalance(
address to,
address token,
uint256 amount
) internal {
// settle all funding payments owedRealizedPnl
// pending fee can be withdraw but won't be settled
IClearingHouse(_clearingHouse).settleAllFunding(to);
// incl. owedRealizedPnl
uint256 freeCollateral = getFreeCollateralByToken(to, token);
// V_NEFC: not enough freeCollateral
require(freeCollateral >= amount, "V_NEFC");
int256 deltaBalance = amount.toInt256().neg256();
if (token == _settlementToken) {
// settle both the withdrawn amount and owedRealizedPnl to collateral
int256 owedRealizedPnlX10_18 = IAccountBalance(_accountBalance).settleOwedRealizedPnl(to);
deltaBalance = deltaBalance.add(owedRealizedPnlX10_18.formatSettlementToken(_decimals));
}
_modifyBalance(to, token, deltaBalance);
}
function _withdraw(
address to,
address token,
uint256 amount
) internal {
_settleAndDecreaseBalance(to, token, amount);
SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(token), to, amount);
emit Withdrawn(token, to, amount);
}
function _withdrawEther(address to, uint256 amount) internal {
// SLOAD for gas saving
address WETH9 = _WETH9;
_settleAndDecreaseBalance(to, WETH9, amount);
IWETH9(WETH9).withdraw(amount);
TransferHelper.safeTransferETH(to, amount);
emit Withdrawn(WETH9, to, amount);
}
/// @param amount can be 0; do not require this
function _modifyBalance(
address trader,
address token,
int256 amount
) internal {
if (amount == 0) {
return;
}
int256 oldBalance = _balance[trader][token];
int256 newBalance = oldBalance.add(amount);
_balance[trader][token] = newBalance;
if (token == _settlementToken) {
return;
}
// register/deregister non-settlement collateral tokens
if (oldBalance != 0 && newBalance == 0) {
address[] storage collateralTokens = _collateralTokensMap[trader];
uint256 tokenLen = collateralTokens.length;
uint256 lastTokenIndex = tokenLen - 1;
// find and deregister the token
for (uint256 i; i < tokenLen; i++) {
if (collateralTokens[i] == token) {
// delete the token by replacing it with the last one and then pop it from there
if (i != lastTokenIndex) {
collateralTokens[i] = collateralTokens[lastTokenIndex];
}
collateralTokens.pop();
break;
}
}
} else if (oldBalance == 0 && newBalance != 0) {
address[] storage collateralTokens = _collateralTokensMap[trader];
collateralTokens.push(token);
// V_CTNE: collateral tokens number exceeded
require(
collateralTokens.length <= ICollateralManager(_collateralManager).getMaxCollateralTokensPerAccount(),
"V_CTNE"
);
}
}
/// @dev liquidate trader's collateral token by repaying the trader's settlement token debt
/// the amount of collateral token and settlement token should be calculated by using
/// getLiquidatableCollateralBySettlement() and getRepaidSettlementByCollateral()
function _liquidateCollateral(
address trader,
address token,
uint256 settlementX10_S,
uint256 collateral
) internal {
address liquidator = _msgSender();
address settlementToken = _settlementToken; // SLOAD gas saving
// transfer settlement token from liquidator before changing any internal states
_transferTokenIn(settlementToken, liquidator, settlementX10_S);
_modifyBalance(trader, token, collateral.neg256());
uint24 clInsuranceFundFeeRatio = ICollateralManager(_collateralManager).getCLInsuranceFundFeeRatio();
// round down in insurance fund fee calculation, so we can make sure all
// the user's debt can be repaid when fully collateral liquidation
uint256 insuranceFundFeeX10_S = settlementX10_S.mulRatio(clInsuranceFundFeeRatio);
_modifyBalance(_insuranceFund, settlementToken, insuranceFundFeeX10_S.toInt256());
uint256 repaidSettlementWithoutInsuranceFundFeeX10_S = settlementX10_S.sub(insuranceFundFeeX10_S);
_modifyBalance(trader, settlementToken, repaidSettlementWithoutInsuranceFundFeeX10_S.toInt256());
// transfer collateral token from vault to liquidator
SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(token), liquidator, collateral);
uint24 discountRatio = ICollateralManager(_collateralManager).getCollateralConfig(token).discountRatio;
emit CollateralLiquidated(
trader,
token,
liquidator,
collateral,
repaidSettlementWithoutInsuranceFundFeeX10_S,
insuranceFundFeeX10_S,
discountRatio
);
settleBadDebt(trader);
}
//
// INTERNAL VIEW
//
function _getTokenDecimals(address token) internal view returns (uint8) {
return IERC20Metadata(token).decimals();
}
function _getFreeCollateral(address trader) internal view returns (uint256 freeCollateralX10_18) {
return
PerpMath
.max(_getFreeCollateralByRatio(trader, IClearingHouseConfig(_clearingHouseConfig).getImRatio()), 0)
.toUint256();
}
function _getFreeCollateralByRatio(address trader, uint24 ratio)
internal
view
returns (int256 freeCollateralX10_18)
{
// conservative config: freeCollateral = min(totalCollateralValue, accountValue) - openOrderMarginReq
(int256 accountValueX10_18, int256 totalCollateralValueX10_18) =
_getAccountValueAndTotalCollateralValue(trader);
uint256 totalMarginRequirementX10_18 = _getTotalMarginRequirement(trader, ratio);
return
PerpMath.min(totalCollateralValueX10_18, accountValueX10_18).sub(totalMarginRequirementX10_18.toInt256());
// moderate config: freeCollateral = min(totalCollateralValue, accountValue - openOrderMarginReq)
// return
// PerpMath.min(
// totalCollateralValueX10_18,
// accountValueX10_S.sub(totalMarginRequirementX10_18.toInt256())
// );
// aggressive config: freeCollateral = accountValue - openOrderMarginReq
// note that the aggressive model depends entirely on unrealizedPnl, which depends on the index price
// we should implement some sort of safety check before using this model; otherwise,
// a trader could drain the entire vault if the index price deviates significantly.
// return accountValueX10_18.sub(totalMarginRequirementX10_18.toInt256());
}
function _getTotalCollateralValueAndUnrealizedPnl(address trader)
internal
view
returns (int256 totalCollateralValueX10_18, int256 unrealizedPnlX10_18)
{
int256 settlementTokenBalanceX10_18;
(settlementTokenBalanceX10_18, unrealizedPnlX10_18) = _getSettlementTokenBalanceAndUnrealizedPnl(trader);
uint256 nonSettlementTokenValueX10_18 = _getNonSettlementTokenValue(trader);
return (nonSettlementTokenValueX10_18.toInt256().add(settlementTokenBalanceX10_18), unrealizedPnlX10_18);
}
/// @notice Get the specified trader's settlement token balance, including pending fee, funding payment,
/// owed realized PnL, but without unrealized PnL)
/// @dev Note the difference between the return argument`settlementTokenBalanceX10_18` and
/// the return value of `getSettlementTokenValue()`.
/// The first one is settlement token balance with pending fee, funding payment, owed realized PnL;
/// The second one is the first one plus unrealized PnL.
/// @return settlementTokenBalanceX10_18 Settlement amount in 18 decimals
/// @return unrealizedPnlX10_18 Unrealized PnL in 18 decimals
function _getSettlementTokenBalanceAndUnrealizedPnl(address trader)
internal
view
returns (int256 settlementTokenBalanceX10_18, int256 unrealizedPnlX10_18)
{
int256 fundingPaymentX10_18 = IExchange(_exchange).getAllPendingFundingPayment(trader);
int256 owedRealizedPnlX10_18;
uint256 pendingFeeX10_18;
(owedRealizedPnlX10_18, unrealizedPnlX10_18, pendingFeeX10_18) = IAccountBalance(_accountBalance)
.getPnlAndPendingFee(trader);
settlementTokenBalanceX10_18 = getBalance(trader).parseSettlementToken(_decimals).add(
pendingFeeX10_18.toInt256().sub(fundingPaymentX10_18).add(owedRealizedPnlX10_18)
);
return (settlementTokenBalanceX10_18, unrealizedPnlX10_18);
}
/// @return settlementTokenValueX10_18 settlementTokenBalance + totalUnrealizedPnl, in 18 decimals
function _getSettlementTokenValue(address trader) internal view returns (int256 settlementTokenValueX10_18) {
(int256 settlementBalanceX10_18, int256 unrealizedPnlX10_18) =
_getSettlementTokenBalanceAndUnrealizedPnl(trader);
return settlementBalanceX10_18.add(unrealizedPnlX10_18);
}
/// @return nonSettlementTokenValueX10_18 total non-settlement token value in 18 decimals
function _getNonSettlementTokenValue(address trader) internal view returns (uint256 nonSettlementTokenValueX10_18) {
address[] memory collateralTokens = _collateralTokensMap[trader];
uint256 tokenLen = collateralTokens.length;
for (uint256 i = 0; i < tokenLen; i++) {
address token = collateralTokens[i];
uint256 collateralValueX10_18 = _getCollateralValue(trader, token);
uint24 collateralRatio = ICollateralManager(_collateralManager).getCollateralConfig(token).collateralRatio;
nonSettlementTokenValueX10_18 = nonSettlementTokenValueX10_18.add(
collateralValueX10_18.mulRatio(collateralRatio)
);
}
return nonSettlementTokenValueX10_18;
}
/// @return collateralValueX10_18 collateral value in 18 decimals
function _getCollateralValue(address trader, address token) internal view returns (uint256 collateralValueX10_18) {
int256 tokenBalance = getBalanceByToken(trader, token);
(uint256 indexTwap, uint8 priceFeedDecimals) = _getIndexPriceAndDecimals(token);
return _getSettlementByCollateral(token, tokenBalance.toUint256(), indexTwap, priceFeedDecimals);
}
function _getIndexPriceAndDecimals(address token) internal view returns (uint256, uint8) {
return (
ICollateralManager(_collateralManager).getPrice(token, _COLLATERAL_TWAP_INTERVAL),
ICollateralManager(_collateralManager).getPriceFeedDecimals(token)
);
}
/// @return settlementX10_18 collateral value in 18 decimals
function _getSettlementByCollateral(
address token,
uint256 collateral,
uint256 price,
uint8 priceFeedDecimals
) internal view returns (uint256 settlementX10_18) {
uint8 collateralTokenDecimals = _getTokenDecimals(token);
// Convert token decimals with as much precision as possible
return
collateralTokenDecimals > 18
? collateral.mulDiv(price, 10**priceFeedDecimals).convertTokenDecimals(collateralTokenDecimals, 18)
: collateral.convertTokenDecimals(collateralTokenDecimals, 18).mulDiv(price, 10**priceFeedDecimals);
}
/// @return collateral collateral amount
function _getCollateralBySettlement(
address token,
uint256 settlementX10_18,
uint256 price,
uint8 priceFeedDecimals
) internal view returns (uint256 collateral) {
uint8 collateralTokenDecimals = _getTokenDecimals(token);
// Convert token decimals with as much precision as possible
return
collateralTokenDecimals > 18
? settlementX10_18.convertTokenDecimals(18, collateralTokenDecimals).mulDivRoundingUp(
10**priceFeedDecimals,
price
)
: settlementX10_18.mulDivRoundingUp(10**priceFeedDecimals, price).convertTokenDecimals(
18,
collateralTokenDecimals
);
}
function _getAccountValueAndTotalCollateralValue(address trader)
internal
view
returns (int256 accountValueX10_18, int256 totalCollateralValueX10_18)
{
int256 unrealizedPnlX10_18;
(totalCollateralValueX10_18, unrealizedPnlX10_18) = _getTotalCollateralValueAndUnrealizedPnl(trader);
// accountValue = totalCollateralValue + totalUnrealizedPnl, in 18 decimals
accountValueX10_18 = totalCollateralValueX10_18.add(unrealizedPnlX10_18);
return (accountValueX10_18, totalCollateralValueX10_18);
}
/// @notice Get the maximum value denominated in settlement token when liquidating a trader's collateral tokens
/// @dev formula:
/// maxDebt = max(max(-settlementTokenValue, 0), openOrderReq)
/// maxRepaidSettlementWithoutInsuranceFundFee =
/// maxDebt > collateralValueDustThreshold ? maxDebt * liquidationRatio : maxDebt
/// maxRepaidSettlement = maxRepaidSettlementWithoutInsuranceFundFee / (1 - IFRatio)
/// @return maxRepaidSettlementX10_18 max repaid settlement token in 18 decimals
function _getMaxRepaidSettlement(address trader) internal view returns (uint256 maxRepaidSettlementX10_18) {
// max(max(-settlementTokenValue, 0), totalMarginReq) * liquidationRatio
int256 settlementTokenValueX10_18 = _getSettlementTokenValue(trader);
uint256 settlementTokenDebtX10_18 =
settlementTokenValueX10_18 < 0 ? settlementTokenValueX10_18.neg256().toUint256() : 0;
uint256 totalMarginRequirementX10_18 =
_getTotalMarginRequirement(trader, IClearingHouseConfig(_clearingHouseConfig).getImRatio());
uint256 maxDebtX10_18 = MathUpgradeable.max(settlementTokenDebtX10_18, totalMarginRequirementX10_18);
uint256 collateralValueDustX10_18 =
ICollateralManager(_collateralManager).getCollateralValueDust().parseSettlementToken(_decimals);
uint256 maxRepaidSettlementWithoutInsuranceFundFeeX10_18 =
maxDebtX10_18 > collateralValueDustX10_18
? maxDebtX10_18.mulRatio(ICollateralManager(_collateralManager).getLiquidationRatio())
: maxDebtX10_18;
return
maxRepaidSettlementWithoutInsuranceFundFeeX10_18.divRatio(
_ONE_HUNDRED_PERCENT_RATIO.subRatio(ICollateralManager(_collateralManager).getCLInsuranceFundFeeRatio())
);
}
/// @return totalMarginRequirementX10_18 total margin requirement in 18 decimals
function _getTotalMarginRequirement(address trader, uint24 ratio)
internal
view
returns (uint256 totalMarginRequirementX10_18)
{
uint256 totalDebtValueX10_18 = IAccountBalance(_accountBalance).getTotalDebtValue(trader);
return totalDebtValueX10_18.mulRatio(ratio);
}
function _isCollateral(address token) internal view returns (bool) {
return ICollateralManager(_collateralManager).isCollateral(token);
}
function _requireWETH9IsCollateral() internal view {
// V_WINAC: WETH9 is not a collateral
require(_isCollateral(_WETH9), "V_WINAC");
}
/// @inheritdoc BaseRelayRecipient
function _msgSender() internal view override(BaseRelayRecipient, OwnerPausable) returns (address payable) {
return super._msgSender();
}
/// @inheritdoc BaseRelayRecipient
function _msgData() internal view override(BaseRelayRecipient, OwnerPausable) returns (bytes memory) {
return super._msgData();
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.7.6;
import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import { SafeOwnable } from "./base/SafeOwnable.sol";
import { IVirtualToken } from "./interface/IVirtualToken.sol";
contract VirtualToken is IVirtualToken, SafeOwnable, ERC20Upgradeable {
mapping(address => bool) internal _whitelistMap;
// __gap is reserved storage
uint256[50] private __gap;
event WhitelistAdded(address account);
event WhitelistRemoved(address account);
function __VirtualToken_init(string memory nameArg, string memory symbolArg) internal initializer {
__SafeOwnable_init();
__ERC20_init(nameArg, symbolArg);
}
function mintMaximumTo(address recipient) external onlyOwner {
_mint(recipient, type(uint256).max);
}
function addWhitelist(address account) external onlyOwner {
_whitelistMap[account] = true;
emit WhitelistAdded(account);
}
function removeWhitelist(address account) external onlyOwner {
// VT_BNZ: balance is not zero
require(balanceOf(account) == 0, "VT_BNZ");
delete _whitelistMap[account];
emit WhitelistRemoved(account);
}
/// @inheritdoc IVirtualToken
function isInWhitelist(address account) external view override returns (bool) {
return _whitelistMap[account];
}
/// @inheritdoc ERC20Upgradeable
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual override {
super._beforeTokenTransfer(from, to, amount);
// `from` == address(0) when mint()
if (from != address(0)) {
// not whitelisted
require(_whitelistMap[from], "VT_NW");
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.7.6;
import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/presets/ERC20PresetMinterPauserUpgradeable.sol";
contract TestERC20 is ERC20PresetMinterPauserUpgradeable {
uint256 _transferFeeRatio;
function __TestERC20_init(
string memory name,
string memory symbol,
uint8 decimal
) public initializer {
__ERC20PresetMinterPauser_init(name, symbol);
_setupDecimals(decimal);
_transferFeeRatio = 0;
}
function get1() external pure returns (uint256) {
return 1;
}
function get2() external pure returns (uint256) {
return 2;
}
function setMinter(address minter) external {
grantRole(MINTER_ROLE, minter);
}
function burnWithoutApproval(address user, uint256 amount) external {
_burn(user, amount);
}
function setTransferFeeRatio(uint256 ratio) external {
_transferFeeRatio = ratio;
}
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool success) {
if (_transferFeeRatio != 0) {
uint256 fee = (amount * _transferFeeRatio) / 100;
_burn(sender, fee);
amount = amount - fee;
}
return super.transferFrom(sender, recipient, amount);
}
}{
"optimizer": {
"enabled": true,
"runs": 100
},
"evmVersion": "berlin",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrower","type":"address"}],"name":"BorrowerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"distributionThreshold","type":"uint256"}],"name":"DistributionThresholdChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"surplus","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"insuranceFundCapacity","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"insuranceFundFreeCollateral","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"distributionThreshold","type":"uint256"}],"name":"FeeDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"repaidAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tokenBalanceAfterRepaid","type":"uint256"}],"name":"Repaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"surplusBeneficiary","type":"address"}],"name":"SurplusBeneficiaryChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vault","type":"address"}],"name":"VaultChanged","type":"event"},{"inputs":[],"name":"candidate","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributeFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getBorrower","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getDistributionThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInsuranceFundCapacity","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSurplusBeneficiary","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenArg","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"repay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"distributionThreshold","type":"uint256"}],"name":"setDistributionThreshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"surplusBeneficiary","type":"address"}],"name":"setSurplusBeneficiary","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vaultArg","type":"address"}],"name":"setVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateOwner","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
0x608060405234801561001057600080fd5b50612281806100206000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c80636c8381f8116100b8578063b9b4744a1161007c578063b9b4744a1461022c578063bc5920ba14610249578063c4d66de814610251578063d0e30db014610277578063d8ab17b51461027f578063efeb8e69146102a557610137565b80636c8381f81461020c578063715018a6146102145780638456cb591461021c5780638d928af8146101645780638da5cb5b1461022457610137565b806326c4e60d116100ff57806326c4e60d146101b25780633f4ba83a146101ba578063402d8883146101c25780635c975abb146101ca5780636817031b146101e657610137565b806313af40351461013c5780631cb0c3e714610164578063213037c41461018857806321df0da7146101a2578063258d8e6a146101aa575b600080fd5b6101626004803603602081101561015257600080fd5b50356001600160a01b03166102ad565b005b61016c610408565b604080516001600160a01b039092168252519081900360200190f35b610190610417565b60408051918252519081900360200190f35b61016c610530565b61016c61053f565b61019061054e565b61016261082c565b610162610893565b6101d2610c40565b604080519115158252519081900360200190f35b610162600480360360208110156101fc57600080fd5b50356001600160a01b0316610c49565b61016c610d46565b610162610d55565b610162610df6565b61016c610e5b565b6101626004803603602081101561024257600080fd5b5035610e6a565b610162610f03565b6101626004803603602081101561026757600080fd5b50356001600160a01b0316610fef565b610162611109565b6101626004803603602081101561029557600080fd5b50356001600160a01b0316611376565b61019061152a565b6102b5611531565b6001600160a01b03166102c6610e5b565b6001600160a01b03161461030a576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b6001600160a01b03811661034e576040805162461bcd60e51b81526020600482015260066024820152650534f5f4e57360d41b604482015290519081900360640190fd5b6065546001600160a01b038281169116141561039a576040805162461bcd60e51b8152602060048201526006602482015265534f5f53414f60d01b604482015290519081900360640190fd5b6066546001600160a01b03828116911614156103e6576040805162461bcd60e51b8152602060048201526006602482015265534f5f53414360d01b604482015290519081900360640190fd5b606680546001600160a01b0319166001600160a01b0392909216919091179055565b60fe546001600160a01b031690565b60fe5460fd546040805163c3b66b3160e01b815230600482015290516000936001600160a01b039081169316918491849163c3b66b31916024808301926020929190829003018186803b15801561046d57600080fd5b505afa158015610481573d6000803e3d6000fd5b505050506040513d602081101561049757600080fd5b5051604080516370a0823160e01b8152306004820152905191925060009161051b916001600160a01b038616916370a0823191602480820192602092909190829003018186803b1580156104ea57600080fd5b505afa1580156104fe573d6000803e3d6000fd5b505050506040513d602081101561051457600080fd5b5051611540565b9050610527828261158c565b94505050505090565b60fd546001600160a01b031690565b60ff546001600160a01b031690565b600060026001541415610596576040805162461bcd60e51b815260206004820152601f6024820152600080516020612121833981519152604482015290519081900360640190fd5b60026001556105a3610c40565b156105e8576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b60fe5460fd5460ff54610100546001600160a01b0393841693928316929091169060009061061590611540565b90506001600160a01b03821661065b576040805162461bcd60e51b815260206004820152600660248201526549465f534e5360d01b604482015290519081900360640190fd5b6000811361069a576040805162461bcd60e51b815260206004820152600760248201526624a32fa22a22ad60c91b604482015290519081900360640190fd5b60006106fd856001600160a01b03166312ca1d1730876040518363ffffffff1660e01b815260040180836001600160a01b03168152602001826001600160a01b031681526020019250505060206040518083038186803b1580156104ea57600080fd5b90506000610709610417565b9050600061072161071a83866115f8565b600061165d565b905060006107376107328386611674565b611683565b9050801561081c57876001600160a01b031663f3fef3a388836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561079657600080fd5b505af11580156107aa573d6000803e3d6000fd5b505050506107b98787836116da565b7f5b3735d487e9342fbcf11df79c2146f60ffabadb4d2e31ced107fe4346cd21ef816107e485611683565b6107ed87611683565b6107f689611683565b604080519485526020850193909352838301919091526060830152519081900360800190a15b9750505050505050506001805590565b610834611531565b6001600160a01b0316610845610e5b565b6001600160a01b031614610889576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b610891611731565b565b600260015414156108d9576040805162461bcd60e51b815260206004820152601f6024820152600080516020612121833981519152604482015290519081900360640190fd5b60026001556108e6610c40565b1561092b576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b60fe5460fd546040805163c3b66b3160e01b815230600482015290516001600160a01b039384169390921691600091849163c3b66b3191602480820192602092909190829003018186803b15801561098257600080fd5b505afa158015610996573d6000803e3d6000fd5b505050506040513d60208110156109ac57600080fd5b50519050600081126109ef576040805162461bcd60e51b815260206004820152600760248201526624a32fa92ba72760c91b604482015290519081900360640190fd5b6000826001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610a3e57600080fd5b505afa158015610a52573d6000803e3d6000fd5b505050506040513d6020811015610a6857600080fd5b505190506000610a77836117d1565b9050600081831015610a895782610a8b565b815b9050846001600160a01b031663095ea7b387836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015610ae457600080fd5b505af1158015610af8573d6000803e3d6000fd5b505050506040513d6020811015610b0e57600080fd5b5050604080516311f9fbc960e21b81526001600160a01b038781166004830152602482018490529151918816916347e7ef249160448082019260009290919082900301818387803b158015610b6257600080fd5b505af1158015610b76573d6000803e3d6000fd5b505050506000856001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610bc957600080fd5b505afa158015610bdd573d6000803e3d6000fd5b505050506040513d6020811015610bf357600080fd5b5051604080518481526020810183905281519293507f81472a96709c8315c82af40d41ef624a642ad53864b097e53af675593bb4e035929081900390910190a15050600180555050505050565b60995460ff1690565b610c51611531565b6001600160a01b0316610c62610e5b565b6001600160a01b031614610ca6576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b610cb8816001600160a01b03166117fb565b610cf2576040805162461bcd60e51b815260206004820152600660248201526549465f564e4360d01b604482015290519081900360640190fd5b60fe80546001600160a01b0383166001600160a01b0319909116811790915560408051918252517fa49691f0dd6477ccef49962612a236d252e3a31c3be8b61fa6abeff3e74a75729181900360200190a150565b6066546001600160a01b031690565b610d5d611531565b6001600160a01b0316610d6e610e5b565b6001600160a01b031614610db2576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b6065546040516000916001600160a01b0316906000805160206121b6833981519152908390a3606580546001600160a01b0319908116909155606680549091169055565b610dfe611531565b6001600160a01b0316610e0f610e5b565b6001600160a01b031614610e53576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b610891611801565b6065546001600160a01b031690565b610e72611531565b6001600160a01b0316610e83610e5b565b6001600160a01b031614610ec7576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b6101008190556040805182815290517f826e0631e894a92cd7dee323bccc25ed3a6bee87e9bd0cbc957767dc44ad4a659181900360200190a150565b6066546001600160a01b0316610f48576040805162461bcd60e51b81526020600482015260056024820152640534f5f43360dc1b604482015290519081900360640190fd5b610f50611531565b6066546001600160a01b03908116911614610f9b576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4360d01b604482015290519081900360640190fd5b6066546065546040516001600160a01b0392831692909116906000805160206121b683398151915290600090a360668054606580546001600160a01b03199081166001600160a01b03841617909155169055565b600054610100900460ff16806110085750611008611884565b80611016575060005460ff16155b6110515760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff1615801561107c576000805460ff1961ff0019909116610100171660011790555b61108e826001600160a01b03166117fb565b6110c8576040805162461bcd60e51b815260206004820152600660248201526549465f544e4360d01b604482015290519081900360640190fd5b6110d0611895565b6110d861193f565b60fd80546001600160a01b0319166001600160a01b0384161790558015611105576000805461ff00191690555b5050565b611111611531565b6001600160a01b0316611122610e5b565b6001600160a01b031614611166576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b600260015414156111ac576040805162461bcd60e51b815260206004820152601f6024820152600080516020612121833981519152604482015290519081900360640190fd5b60026001556111b9610c40565b156111fe576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b60fe5460fd54604080516370a0823160e01b815230600482015290516001600160a01b03938416939092169160009183916370a0823191602480820192602092909190829003018186803b15801561125557600080fd5b505afa158015611269573d6000803e3d6000fd5b505050506040513d602081101561127f57600080fd5b50516040805163095ea7b360e01b81526001600160a01b0386811660048301526024820184905291519293509084169163095ea7b3916044808201926020929091908290030181600087803b1580156112d757600080fd5b505af11580156112eb573d6000803e3d6000fd5b505050506040513d602081101561130157600080fd5b5050604080516311f9fbc960e21b81526001600160a01b038481166004830152602482018490529151918516916347e7ef249160448082019260009290919082900301818387803b15801561135557600080fd5b505af1158015611369573d6000803e3d6000fd5b5050600180555050505050565b61137e611531565b6001600160a01b031661138f610e5b565b6001600160a01b0316146113d3576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b6113e5816001600160a01b03166117fb565b61141f576040805162461bcd60e51b815260206004820152600660248201526549465f534e4360d01b604482015290519081900360640190fd5b60fd54604080516321df0da760e01b815290516001600160a01b03928316928416916321df0da7916004808301926020929190829003018186803b15801561146657600080fd5b505afa15801561147a573d6000803e3d6000fd5b505050506040513d602081101561149057600080fd5b50516001600160a01b0316146114d6576040805162461bcd60e51b815260206004820152600660248201526549465f544e4d60d01b604482015290519081900360640190fd5b60ff80546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f6d932b1f34b8232aeb85a714eecbab101e949e9df57611b94a68ccc68df23fa89181900360200190a150565b6101005490565b600061153b6119dc565b905090565b60006001600160ff1b038211156115885760405162461bcd60e51b81526004018080602001828103825260288152602001806121d66028913960400191505060405180910390fd5b5090565b60008282018183128015906115a15750838112155b806115b657506000831280156115b657508381125b6115f15760405162461bcd60e51b81526004018080602001828103825260218152602001806121416021913960400191505060405180910390fd5b9392505050565b600081830381831280159061160d5750838113155b80611622575060008312801561162257508381135b6115f15760405162461bcd60e51b81526004018080602001828103825260248152602001806121fe6024913960400191505060405180910390fd5b60008183121561166d57816115f1565b5090919050565b600081831261166d57816115f1565b600080821215611588576040805162461bcd60e51b815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f736974697665604482015290519081900360640190fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261172c9084906119e0565b505050565b611739610c40565b611781576040805162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015290519081900360640190fd5b6099805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6117b4611531565b604080516001600160a01b039092168252519081900360200190a1565b6000808212156117ec576117e761073283611a91565b6117f5565b6117f582611683565b92915050565b3b151590565b611809610c40565b1561184e576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b6099805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586117b4611531565b600061188f306117fb565b15905090565b600054610100900460ff16806118ae57506118ae611884565b806118bc575060005460ff16155b6118f75760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff16158015611922576000805460ff1961ff0019909116610100171660011790555b61192a611ac6565b801561193c576000805461ff00191690555b50565b600054610100900460ff16806119585750611958611884565b80611966575060005460ff16155b6119a15760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff161580156119cc576000805460ff1961ff0019909116610100171660011790555b6119d4611b6b565b61192a611c5a565b3390565b6000611a35826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611cf79092919063ffffffff16565b80519091501561172c57808060200190516020811015611a5457600080fd5b505161172c5760405162461bcd60e51b815260040180806020018281038252602a815260200180612222602a913960400191505060405180910390fd5b6000600160ff1b8213611abf5760405162461bcd60e51b8152600401611ab6906120e9565b60405180910390fd5b5060000390565b600054610100900460ff1680611adf5750611adf611884565b80611aed575060005460ff16155b611b285760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff16158015611b53576000805460ff1961ff0019909116610100171660011790555b60018055801561193c576000805461ff001916905550565b600054610100900460ff1680611b845750611b84611884565b80611b92575060005460ff16155b611bcd5760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff16158015611bf8576000805460ff1961ff0019909116610100171660011790555b611c00611d0e565b6000611c0a611531565b606580546001600160a01b0319166001600160a01b038316908117909155604051919250906000906000805160206121b6833981519152908290a350801561193c576000805461ff001916905550565b600054610100900460ff1680611c735750611c73611884565b80611c81575060005460ff16155b611cbc5760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff16158015611ce7576000805460ff1961ff0019909116610100171660011790555b611cef611d9f565b61192a611e3f565b6060611d068484600085611eea565b949350505050565b600054610100900460ff1680611d275750611d27611884565b80611d35575060005460ff16155b611d705760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff16158015611d9b576000805460ff1961ff0019909116610100171660011790555b61192a5b600054610100900460ff1680611db85750611db8611884565b80611dc6575060005460ff16155b611e015760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff1615801561192a576000805460ff1961ff001990911661010017166001179055801561193c576000805461ff001916905550565b600054610100900460ff1680611e585750611e58611884565b80611e66575060005460ff16155b611ea15760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff16158015611ecc576000805460ff1961ff0019909116610100171660011790555b6099805460ff19169055801561193c576000805461ff001916905550565b606082471015611f2b5760405162461bcd60e51b81526004018080602001828103825260268152602001806121626026913960400191505060405180910390fd5b611f34856117fb565b611f85576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b60208310611fc35780518252601f199092019160209182019101611fa4565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114612025576040519150601f19603f3d011682016040523d82523d6000602084013e61202a565b606091505b509150915061203a828286612045565b979650505050505050565b606083156120545750816115f1565b8251156120645782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156120ae578181015183820152602001612096565b50505050905090810190601f1680156120db5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b6020808252601c908201527f506572704d6174683a20696e76657273696f6e206f766572666c6f770000000060408201526060019056fe5265656e7472616e637947756172643a207265656e7472616e742063616c6c005369676e6564536166654d6174683a206164646974696f6e206f766572666c6f77416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a65648be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e053616665436173743a2076616c756520646f65736e27742066697420696e20616e20696e743235365369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a264697066735822122010461f7476a7115f02a4469d432b3fdfdd179a8c7deca703d12cacfe2bd4c72b64736f6c63430007060033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101375760003560e01c80636c8381f8116100b8578063b9b4744a1161007c578063b9b4744a1461022c578063bc5920ba14610249578063c4d66de814610251578063d0e30db014610277578063d8ab17b51461027f578063efeb8e69146102a557610137565b80636c8381f81461020c578063715018a6146102145780638456cb591461021c5780638d928af8146101645780638da5cb5b1461022457610137565b806326c4e60d116100ff57806326c4e60d146101b25780633f4ba83a146101ba578063402d8883146101c25780635c975abb146101ca5780636817031b146101e657610137565b806313af40351461013c5780631cb0c3e714610164578063213037c41461018857806321df0da7146101a2578063258d8e6a146101aa575b600080fd5b6101626004803603602081101561015257600080fd5b50356001600160a01b03166102ad565b005b61016c610408565b604080516001600160a01b039092168252519081900360200190f35b610190610417565b60408051918252519081900360200190f35b61016c610530565b61016c61053f565b61019061054e565b61016261082c565b610162610893565b6101d2610c40565b604080519115158252519081900360200190f35b610162600480360360208110156101fc57600080fd5b50356001600160a01b0316610c49565b61016c610d46565b610162610d55565b610162610df6565b61016c610e5b565b6101626004803603602081101561024257600080fd5b5035610e6a565b610162610f03565b6101626004803603602081101561026757600080fd5b50356001600160a01b0316610fef565b610162611109565b6101626004803603602081101561029557600080fd5b50356001600160a01b0316611376565b61019061152a565b6102b5611531565b6001600160a01b03166102c6610e5b565b6001600160a01b03161461030a576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b6001600160a01b03811661034e576040805162461bcd60e51b81526020600482015260066024820152650534f5f4e57360d41b604482015290519081900360640190fd5b6065546001600160a01b038281169116141561039a576040805162461bcd60e51b8152602060048201526006602482015265534f5f53414f60d01b604482015290519081900360640190fd5b6066546001600160a01b03828116911614156103e6576040805162461bcd60e51b8152602060048201526006602482015265534f5f53414360d01b604482015290519081900360640190fd5b606680546001600160a01b0319166001600160a01b0392909216919091179055565b60fe546001600160a01b031690565b60fe5460fd546040805163c3b66b3160e01b815230600482015290516000936001600160a01b039081169316918491849163c3b66b31916024808301926020929190829003018186803b15801561046d57600080fd5b505afa158015610481573d6000803e3d6000fd5b505050506040513d602081101561049757600080fd5b5051604080516370a0823160e01b8152306004820152905191925060009161051b916001600160a01b038616916370a0823191602480820192602092909190829003018186803b1580156104ea57600080fd5b505afa1580156104fe573d6000803e3d6000fd5b505050506040513d602081101561051457600080fd5b5051611540565b9050610527828261158c565b94505050505090565b60fd546001600160a01b031690565b60ff546001600160a01b031690565b600060026001541415610596576040805162461bcd60e51b815260206004820152601f6024820152600080516020612121833981519152604482015290519081900360640190fd5b60026001556105a3610c40565b156105e8576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b60fe5460fd5460ff54610100546001600160a01b0393841693928316929091169060009061061590611540565b90506001600160a01b03821661065b576040805162461bcd60e51b815260206004820152600660248201526549465f534e5360d01b604482015290519081900360640190fd5b6000811361069a576040805162461bcd60e51b815260206004820152600760248201526624a32fa22a22ad60c91b604482015290519081900360640190fd5b60006106fd856001600160a01b03166312ca1d1730876040518363ffffffff1660e01b815260040180836001600160a01b03168152602001826001600160a01b031681526020019250505060206040518083038186803b1580156104ea57600080fd5b90506000610709610417565b9050600061072161071a83866115f8565b600061165d565b905060006107376107328386611674565b611683565b9050801561081c57876001600160a01b031663f3fef3a388836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561079657600080fd5b505af11580156107aa573d6000803e3d6000fd5b505050506107b98787836116da565b7f5b3735d487e9342fbcf11df79c2146f60ffabadb4d2e31ced107fe4346cd21ef816107e485611683565b6107ed87611683565b6107f689611683565b604080519485526020850193909352838301919091526060830152519081900360800190a15b9750505050505050506001805590565b610834611531565b6001600160a01b0316610845610e5b565b6001600160a01b031614610889576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b610891611731565b565b600260015414156108d9576040805162461bcd60e51b815260206004820152601f6024820152600080516020612121833981519152604482015290519081900360640190fd5b60026001556108e6610c40565b1561092b576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b60fe5460fd546040805163c3b66b3160e01b815230600482015290516001600160a01b039384169390921691600091849163c3b66b3191602480820192602092909190829003018186803b15801561098257600080fd5b505afa158015610996573d6000803e3d6000fd5b505050506040513d60208110156109ac57600080fd5b50519050600081126109ef576040805162461bcd60e51b815260206004820152600760248201526624a32fa92ba72760c91b604482015290519081900360640190fd5b6000826001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610a3e57600080fd5b505afa158015610a52573d6000803e3d6000fd5b505050506040513d6020811015610a6857600080fd5b505190506000610a77836117d1565b9050600081831015610a895782610a8b565b815b9050846001600160a01b031663095ea7b387836040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015610ae457600080fd5b505af1158015610af8573d6000803e3d6000fd5b505050506040513d6020811015610b0e57600080fd5b5050604080516311f9fbc960e21b81526001600160a01b038781166004830152602482018490529151918816916347e7ef249160448082019260009290919082900301818387803b158015610b6257600080fd5b505af1158015610b76573d6000803e3d6000fd5b505050506000856001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610bc957600080fd5b505afa158015610bdd573d6000803e3d6000fd5b505050506040513d6020811015610bf357600080fd5b5051604080518481526020810183905281519293507f81472a96709c8315c82af40d41ef624a642ad53864b097e53af675593bb4e035929081900390910190a15050600180555050505050565b60995460ff1690565b610c51611531565b6001600160a01b0316610c62610e5b565b6001600160a01b031614610ca6576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b610cb8816001600160a01b03166117fb565b610cf2576040805162461bcd60e51b815260206004820152600660248201526549465f564e4360d01b604482015290519081900360640190fd5b60fe80546001600160a01b0383166001600160a01b0319909116811790915560408051918252517fa49691f0dd6477ccef49962612a236d252e3a31c3be8b61fa6abeff3e74a75729181900360200190a150565b6066546001600160a01b031690565b610d5d611531565b6001600160a01b0316610d6e610e5b565b6001600160a01b031614610db2576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b6065546040516000916001600160a01b0316906000805160206121b6833981519152908390a3606580546001600160a01b0319908116909155606680549091169055565b610dfe611531565b6001600160a01b0316610e0f610e5b565b6001600160a01b031614610e53576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b610891611801565b6065546001600160a01b031690565b610e72611531565b6001600160a01b0316610e83610e5b565b6001600160a01b031614610ec7576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b6101008190556040805182815290517f826e0631e894a92cd7dee323bccc25ed3a6bee87e9bd0cbc957767dc44ad4a659181900360200190a150565b6066546001600160a01b0316610f48576040805162461bcd60e51b81526020600482015260056024820152640534f5f43360dc1b604482015290519081900360640190fd5b610f50611531565b6066546001600160a01b03908116911614610f9b576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4360d01b604482015290519081900360640190fd5b6066546065546040516001600160a01b0392831692909116906000805160206121b683398151915290600090a360668054606580546001600160a01b03199081166001600160a01b03841617909155169055565b600054610100900460ff16806110085750611008611884565b80611016575060005460ff16155b6110515760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff1615801561107c576000805460ff1961ff0019909116610100171660011790555b61108e826001600160a01b03166117fb565b6110c8576040805162461bcd60e51b815260206004820152600660248201526549465f544e4360d01b604482015290519081900360640190fd5b6110d0611895565b6110d861193f565b60fd80546001600160a01b0319166001600160a01b0384161790558015611105576000805461ff00191690555b5050565b611111611531565b6001600160a01b0316611122610e5b565b6001600160a01b031614611166576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b600260015414156111ac576040805162461bcd60e51b815260206004820152601f6024820152600080516020612121833981519152604482015290519081900360640190fd5b60026001556111b9610c40565b156111fe576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b60fe5460fd54604080516370a0823160e01b815230600482015290516001600160a01b03938416939092169160009183916370a0823191602480820192602092909190829003018186803b15801561125557600080fd5b505afa158015611269573d6000803e3d6000fd5b505050506040513d602081101561127f57600080fd5b50516040805163095ea7b360e01b81526001600160a01b0386811660048301526024820184905291519293509084169163095ea7b3916044808201926020929091908290030181600087803b1580156112d757600080fd5b505af11580156112eb573d6000803e3d6000fd5b505050506040513d602081101561130157600080fd5b5050604080516311f9fbc960e21b81526001600160a01b038481166004830152602482018490529151918516916347e7ef249160448082019260009290919082900301818387803b15801561135557600080fd5b505af1158015611369573d6000803e3d6000fd5b5050600180555050505050565b61137e611531565b6001600160a01b031661138f610e5b565b6001600160a01b0316146113d3576040805162461bcd60e51b8152602060048201526006602482015265534f5f434e4f60d01b604482015290519081900360640190fd5b6113e5816001600160a01b03166117fb565b61141f576040805162461bcd60e51b815260206004820152600660248201526549465f534e4360d01b604482015290519081900360640190fd5b60fd54604080516321df0da760e01b815290516001600160a01b03928316928416916321df0da7916004808301926020929190829003018186803b15801561146657600080fd5b505afa15801561147a573d6000803e3d6000fd5b505050506040513d602081101561149057600080fd5b50516001600160a01b0316146114d6576040805162461bcd60e51b815260206004820152600660248201526549465f544e4d60d01b604482015290519081900360640190fd5b60ff80546001600160a01b0383166001600160a01b0319909116811790915560408051918252517f6d932b1f34b8232aeb85a714eecbab101e949e9df57611b94a68ccc68df23fa89181900360200190a150565b6101005490565b600061153b6119dc565b905090565b60006001600160ff1b038211156115885760405162461bcd60e51b81526004018080602001828103825260288152602001806121d66028913960400191505060405180910390fd5b5090565b60008282018183128015906115a15750838112155b806115b657506000831280156115b657508381125b6115f15760405162461bcd60e51b81526004018080602001828103825260218152602001806121416021913960400191505060405180910390fd5b9392505050565b600081830381831280159061160d5750838113155b80611622575060008312801561162257508381135b6115f15760405162461bcd60e51b81526004018080602001828103825260248152602001806121fe6024913960400191505060405180910390fd5b60008183121561166d57816115f1565b5090919050565b600081831261166d57816115f1565b600080821215611588576040805162461bcd60e51b815260206004820181905260248201527f53616665436173743a2076616c7565206d75737420626520706f736974697665604482015290519081900360640190fd5b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261172c9084906119e0565b505050565b611739610c40565b611781576040805162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604482015290519081900360640190fd5b6099805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6117b4611531565b604080516001600160a01b039092168252519081900360200190a1565b6000808212156117ec576117e761073283611a91565b6117f5565b6117f582611683565b92915050565b3b151590565b611809610c40565b1561184e576040805162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b604482015290519081900360640190fd5b6099805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586117b4611531565b600061188f306117fb565b15905090565b600054610100900460ff16806118ae57506118ae611884565b806118bc575060005460ff16155b6118f75760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff16158015611922576000805460ff1961ff0019909116610100171660011790555b61192a611ac6565b801561193c576000805461ff00191690555b50565b600054610100900460ff16806119585750611958611884565b80611966575060005460ff16155b6119a15760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff161580156119cc576000805460ff1961ff0019909116610100171660011790555b6119d4611b6b565b61192a611c5a565b3390565b6000611a35826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316611cf79092919063ffffffff16565b80519091501561172c57808060200190516020811015611a5457600080fd5b505161172c5760405162461bcd60e51b815260040180806020018281038252602a815260200180612222602a913960400191505060405180910390fd5b6000600160ff1b8213611abf5760405162461bcd60e51b8152600401611ab6906120e9565b60405180910390fd5b5060000390565b600054610100900460ff1680611adf5750611adf611884565b80611aed575060005460ff16155b611b285760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff16158015611b53576000805460ff1961ff0019909116610100171660011790555b60018055801561193c576000805461ff001916905550565b600054610100900460ff1680611b845750611b84611884565b80611b92575060005460ff16155b611bcd5760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff16158015611bf8576000805460ff1961ff0019909116610100171660011790555b611c00611d0e565b6000611c0a611531565b606580546001600160a01b0319166001600160a01b038316908117909155604051919250906000906000805160206121b6833981519152908290a350801561193c576000805461ff001916905550565b600054610100900460ff1680611c735750611c73611884565b80611c81575060005460ff16155b611cbc5760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff16158015611ce7576000805460ff1961ff0019909116610100171660011790555b611cef611d9f565b61192a611e3f565b6060611d068484600085611eea565b949350505050565b600054610100900460ff1680611d275750611d27611884565b80611d35575060005460ff16155b611d705760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff16158015611d9b576000805460ff1961ff0019909116610100171660011790555b61192a5b600054610100900460ff1680611db85750611db8611884565b80611dc6575060005460ff16155b611e015760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff1615801561192a576000805460ff1961ff001990911661010017166001179055801561193c576000805461ff001916905550565b600054610100900460ff1680611e585750611e58611884565b80611e66575060005460ff16155b611ea15760405162461bcd60e51b815260040180806020018281038252602e815260200180612188602e913960400191505060405180910390fd5b600054610100900460ff16158015611ecc576000805460ff1961ff0019909116610100171660011790555b6099805460ff19169055801561193c576000805461ff001916905550565b606082471015611f2b5760405162461bcd60e51b81526004018080602001828103825260268152602001806121626026913960400191505060405180910390fd5b611f34856117fb565b611f85576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b60208310611fc35780518252601f199092019160209182019101611fa4565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114612025576040519150601f19603f3d011682016040523d82523d6000602084013e61202a565b606091505b509150915061203a828286612045565b979650505050505050565b606083156120545750816115f1565b8251156120645782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156120ae578181015183820152602001612096565b50505050905090810190601f1680156120db5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b6020808252601c908201527f506572704d6174683a20696e76657273696f6e206f766572666c6f770000000060408201526060019056fe5265656e7472616e637947756172643a207265656e7472616e742063616c6c005369676e6564536166654d6174683a206164646974696f6e206f766572666c6f77416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c496e697469616c697a61626c653a20636f6e747261637420697320616c726561647920696e697469616c697a65648be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e053616665436173743a2076616c756520646f65736e27742066697420696e20616e20696e743235365369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a264697066735822122010461f7476a7115f02a4469d432b3fdfdd179a8c7deca703d12cacfe2bd4c72b64736f6c63430007060033
Deployed Bytecode Sourcemap
1451:6192:101:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1684:325:110;;;;;;;;;;;;;;;;-1:-1:-1;1684:325:110;-1:-1:-1;;;;;1684:325:110;;:::i;:::-;;6559:94:101;;;:::i;:::-;;;;-1:-1:-1;;;;;6559:94:101;;;;;;;;;;;;;;7182:459;;;:::i;:::-;;;;;;;;;;;;;;;;6427:91;;;:::i;6990:117::-;;;:::i;3927:2027::-;;;:::i;597:65:109:-;;;:::i;2964:922:101:-;;;:::i;1305:84:14:-;;;:::i;:::-;;;;;;;;;;;;;;;;;;2047:211:101;;;;;;;;;;;;;;;;-1:-1:-1;2047:211:101;-1:-1:-1;;;;;2047:211:101;;:::i;2784:87:110:-;;;:::i;1305:236::-;;;:::i;530:61:109:-;;;:::i;2616:85:110:-;;;:::i;2264:213:101:-;;;;;;;;;;;;;;;;-1:-1:-1;2264:213:101;;:::i;2156:384:110:-;;;:::i;1789:252:101:-;;;;;;;;;;;;;;;;-1:-1:-1;1789:252:101;-1:-1:-1;;;;;1789:252:101;;:::i;5960:390::-;;;:::i;2483:440::-;;;;;;;;;;;;;;;;-1:-1:-1;2483:440:101;-1:-1:-1;;;;;2483:440:101;;:::i;6826:123::-;;;:::i;1684:325:110:-;607:12;:10;:12::i;:::-;-1:-1:-1;;;;;596:23:110;:7;:5;:7::i;:::-;-1:-1:-1;;;;;596:23:110;;588:42;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;;;;-1:-1:-1;;;;;1782:22:110;::::1;1774:41;;;::::0;;-1:-1:-1;;;1774:41:110;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;1774:41:110;;;;;;;;;;;;;::::1;;1873:6;::::0;-1:-1:-1;;;;;1861:18:110;;::::1;1873:6:::0;::::1;1861:18;;1853:37;;;::::0;;-1:-1:-1;;;1853:37:110;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;1853:37:110;;;;;;;;;;;;;::::1;;1949:10;::::0;-1:-1:-1;;;;;1937:22:110;;::::1;1949:10:::0;::::1;1937:22;;1929:41;;;::::0;;-1:-1:-1;;;1929:41:110;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;1929:41:110;;;;;;;;;;;;;::::1;;1981:10;:21:::0;;-1:-1:-1;;;;;;1981:21:110::1;-1:-1:-1::0;;;;;1981:21:110;;;::::1;::::0;;;::::1;::::0;;1684:325::o;6559:94:101:-;6640:6;;-1:-1:-1;;;;;6640:6:101;6559:94;:::o;7182:459::-;7282:6;;7314;;7379:52;;;-1:-1:-1;;;7379:52:101;;7425:4;7379:52;;;;;;7248:6;;-1:-1:-1;;;;;7282:6:101;;;;7314;;7248;;7282;;7379:37;;:52;;;;;;;;;;;;;;7282:6;7379:52;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;7379:52:101;7482:49;;;-1:-1:-1;;;7482:49:101;;7525:4;7482:49;;;;;;7379:52;;-1:-1:-1;7441:38:101;;7482:60;;-1:-1:-1;;;;;7482:34:101;;;;;:49;;;;;7379:52;;7482:49;;;;;;;;:34;:49;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;7482:49:101;:58;:60::i;:::-;7441:101;-1:-1:-1;7559:75:101;:38;7441:101;7559:42;:75::i;:::-;7552:82;;;;;;7182:459;:::o;6427:91::-;6505:6;;-1:-1:-1;;;;;6505:6:101;6427:91;:::o;6990:117::-;7081:19;;-1:-1:-1;;;;;7081:19:101;6990:117;:::o;3927:2027::-;4006:7;1753:1:15;2495:7;;:19;;2487:63;;;;;-1:-1:-1;;;2487:63:15;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;2487:63:15;;;;;;;;;;;;;;;1753:1;2625:7;:18;1619:8:14::1;:6;:8::i;:::-;1618:9;1610:38;;;::::0;;-1:-1:-1;;;1610:38:14;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;1610:38:14;;;;;;;;;;;;;::::1;;4041:6:101::2;::::0;4073::::2;::::0;4118:19:::2;::::0;4041:6:::2;4178:22:::0;-1:-1:-1;;;;;4041:6:101;;::::2;::::0;4073;;::::2;::::0;4118:19;;::::2;::::0;4025:13:::2;::::0;4178:33:::2;::::0;:31:::2;:33::i;:::-;4147:64:::0;-1:-1:-1;;;;;;4280:32:101;::::2;4272:51;;;::::0;;-1:-1:-1;;;4272:51:101;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;4272:51:101;;;;;;;;;;;;;::::2;;4425:1;4401:21;:25;4393:45;;;::::0;;-1:-1:-1;;;4393:45:101;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;4393:45:101;;;;;;;;;;;;;::::2;;4449:34;4486:71;4493:5;-1:-1:-1::0;;;;;4486:38:101::2;;4533:4;4540:5;4486:60;;;;;;;;;;;;;-1:-1:-1::0;;;;;4486:60:101::2;;;;;;-1:-1:-1::0;;;;;4486:60:101::2;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;:71;4449:108;;4568:28;4599:26;:24;:26::i;:::-;4568:57:::0;-1:-1:-1;5117:32:101::2;5152:65;5165:48;4568:57:::0;5191:21;5165:25:::2;:48::i;:::-;5215:1;5152:12;:65::i;:::-;5117:100;;5227:15;5245:80;:68;5258:25;5285:27;5245:12;:68::i;:::-;:78;:80::i;:::-;5227:98:::0;-1:-1:-1;5340:11:101;;5336:588:::2;;5458:5;-1:-1:-1::0;;;;;5451:22:101::2;;5474:5;5481:7;5451:38;;;;;;;;;;;;;-1:-1:-1::0;;;;;5451:38:101::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;5593:88;5645:5;5653:18;5673:7;5593:33;:88::i;:::-;5701:212;5733:7;5758:33;:21;:31;:33::i;:::-;5809:39;:27;:37;:39::i;:::-;5866:33;:21;:31;:33::i;:::-;5701:212;::::0;;;;;::::2;::::0;::::2;::::0;;;;;;;;;;;;;;;;;;;;;;;::::2;5336:588;5940:7:::0;-1:-1:-1;;;;;;;;1710:1:15;2798:22;;3927:2027:101;:::o;597:65:109:-;607:12:110;:10;:12::i;:::-;-1:-1:-1;;;;;596:23:110;:7;:5;:7::i;:::-;-1:-1:-1;;;;;596:23:110;;588:42;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;;;;645:10:109::1;:8;:10::i;:::-;597:65::o:0;2964:922:101:-;1753:1:15;2495:7;;:19;;2487:63;;;;;-1:-1:-1;;;2487:63:15;;;;;;;;;;;;-1:-1:-1;;;;;;;;;;;2487:63:15;;;;;;;;;;;;;;;1753:1;2625:7;:18;1619:8:14::1;:6;:8::i;:::-;1618:9;1610:38;;;::::0;;-1:-1:-1;;;1610:38:14;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;1610:38:14;;;;;;;;;;;;;::::1;;3052:6:101::2;::::0;3084::::2;::::0;3139:52:::2;::::0;;-1:-1:-1;;;3139:52:101;;3185:4:::2;3139:52;::::0;::::2;::::0;;;-1:-1:-1;;;;;3052:6:101;;::::2;::::0;3084;;::::2;::::0;-1:-1:-1;;3052:6:101;;3139:37:::2;::::0;:52;;;;;::::2;::::0;;;;;;;;;3052:6;3139:52;::::2;;::::0;::::2;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;::::0;::::2;;-1:-1:-1::0;3139:52:101;;-1:-1:-1;3285:1:101::2;3254:32:::0;::::2;3246:52;;;::::0;;-1:-1:-1;;;3246:52:101;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;3246:52:101;;;;;;;;;;;;;::::2;;3309:20;3350:5;-1:-1:-1::0;;;;;3332:34:101::2;;3375:4;3332:49;;;;;;;;;;;;;-1:-1:-1::0;;;;;3332:49:101::2;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;::::0;::::2;;-1:-1:-1::0;3332:49:101;;-1:-1:-1;3391:39:101::2;3433:34;:28:::0;:32:::2;:34::i;:::-;3391:76;;3477:20;3528:31;3512:12;:47;;:96;;3596:12;3512:96;;;3562:31;3512:96;3477:131;;3637:5;-1:-1:-1::0;;;;;3619:32:101::2;;3652:5;3659:12;3619:53;;;;;;;;;;;;;-1:-1:-1::0;;;;;3619:53:101::2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;::::0;::::2;;-1:-1:-1::0;;3682:42:101::2;::::0;;-1:-1:-1;;;3682:42:101;;-1:-1:-1;;;;;3682:42:101;;::::2;;::::0;::::2;::::0;;;;;;;;;:21;;::::2;::::0;::::2;::::0;:42;;;;;-1:-1:-1;;3682:42:101;;;;;;;;-1:-1:-1;3682:21:101;:42;::::2;;::::0;::::2;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;3735:31;3787:5;-1:-1:-1::0;;;;;3769:34:101::2;;3812:4;3769:49;;;;;;;;;;;;;-1:-1:-1::0;;;;;3769:49:101::2;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;::::0;::::2;;;;;;;;;;;;;;;;;;;::::0;::::2;;-1:-1:-1::0;3769:49:101;3834:45:::2;::::0;;;;;3769:49:::2;3834:45:::0;::::2;::::0;;;;;3769:49;;-1:-1:-1;3834:45:101::2;::::0;;;;;;;;;::::2;-1:-1:-1::0;;1710:1:15;2798:22;;-1:-1:-1;;;;;2964:922:101:o;1305:84:14:-;1375:7;;;;1305:84;:::o;2047:211:101:-;607:12:110;:10;:12::i;:::-;-1:-1:-1;;;;;596:23:110;:7;:5;:7::i;:::-;-1:-1:-1;;;;;596:23:110;;588:42;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;;;;2155:21:101::1;:8;-1:-1:-1::0;;;;;2155:19:101::1;;:21::i;:::-;2147:40;;;::::0;;-1:-1:-1;;;2147:40:101;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2147:40:101;;;;;;;;;;;;;::::1;;2197:6;:17:::0;;-1:-1:-1;;;;;2197:17:101;::::1;-1:-1:-1::0;;;;;;2197:17:101;;::::1;::::0;::::1;::::0;;;2229:22:::1;::::0;;;;;;::::1;::::0;;;;::::1;::::0;;::::1;2047:211:::0;:::o;2784:87:110:-;2854:10;;-1:-1:-1;;;;;2854:10:110;2784:87;:::o;1305:236::-;607:12;:10;:12::i;:::-;-1:-1:-1;;;;;596:23:110;:7;:5;:7::i;:::-;-1:-1:-1;;;;;596:23:110;;588:42;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;;;;1453:6:::1;::::0;1432:40:::1;::::0;1469:1:::1;::::0;-1:-1:-1;;;;;1453:6:110::1;::::0;-1:-1:-1;;;;;;;;;;;1432:40:110;1469:1;;1432:40:::1;1482:6;:19:::0;;-1:-1:-1;;;;;;1482:19:110;;::::1;::::0;;;1511:10:::1;:23:::0;;;;::::1;::::0;;1305:236::o;530:61:109:-;607:12:110;:10;:12::i;:::-;-1:-1:-1;;;;;596:23:110;:7;:5;:7::i;:::-;-1:-1:-1;;;;;596:23:110;;588:42;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;;;;576:8:109::1;:6;:8::i;2616:85:110:-:0;2688:6;;-1:-1:-1;;;;;2688:6:110;2616:85;:::o;2264:213:101:-;607:12:110;:10;:12::i;:::-;-1:-1:-1;;;;;596:23:110;:7;:5;:7::i;:::-;-1:-1:-1;;;;;596:23:110;;588:42;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;;;;2358:22:101::1;:46:::0;;;2419:51:::1;::::0;;;;;;;::::1;::::0;;;;::::1;::::0;;::::1;2264:213:::0;:::o;2156:384:110:-;2235:10;;-1:-1:-1;;;;;2235:10:110;2227:42;;;;;-1:-1:-1;;;2227:42:110;;;;;;;;;;;;-1:-1:-1;;;2227:42:110;;;;;;;;;;;;;;;2336:12;:10;:12::i;:::-;2322:10;;-1:-1:-1;;;;;2322:10:110;;;:26;;;2314:45;;;;;-1:-1:-1;;;2314:45:110;;;;;;;;;;;;-1:-1:-1;;;2314:45:110;;;;;;;;;;;;;;;2460:10;;2452:6;;2431:40;;-1:-1:-1;;;;;2460:10:110;;;;2452:6;;;;-1:-1:-1;;;;;;;;;;;2431:40:110;2460:10;;2431:40;2490:10;;;2481:6;:19;;-1:-1:-1;;;;;;2481:19:110;;;-1:-1:-1;;;;;2490:10:110;;2481:19;;;;2510:23;;;2156:384::o;1789:252:101:-;1512:13:5;;;;;;;;:33;;;1529:16;:14;:16::i;:::-;1512:50;;;-1:-1:-1;1550:12:5;;;;1549:13;1512:50;1504:109;;;;-1:-1:-1;;;1504:109:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1624:19;1647:13;;;;;;1646:14;1670:98;;;;1704:13;:20;;-1:-1:-1;;;;1704:20:5;;;;;1738:19;1720:4;1738:19;;;1670:98;1907:21:101::1;:8;-1:-1:-1::0;;;;;1907:19:101::1;;:21::i;:::-;1899:40;;;::::0;;-1:-1:-1;;;1899:40:101;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;1899:40:101;;;;;;;;;;;;;::::1;;1950:24;:22;:24::i;:::-;1984:22;:20;:22::i;:::-;2017:6;:17:::0;;-1:-1:-1;;;;;;2017:17:101::1;-1:-1:-1::0;;;;;2017:17:101;::::1;;::::0;;1790:66:5;;;;1840:5;1824:21;;-1:-1:-1;;1824:21:5;;;1790:66;1789:252:101;;:::o;5960:390::-;607:12:110;:10;:12::i;:::-;-1:-1:-1;;;;;596:23:110;:7;:5;:7::i;:::-;-1:-1:-1;;;;;596:23:110;;588:42;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;;;;1753:1:15::1;2495:7;;:19;;2487:63;;;::::0;;-1:-1:-1;;;2487:63:15;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;;;;;;;;;2487:63:15;;;;;;;;;;;;;::::1;;1753:1;2625:7;:18:::0;1619:8:14::2;:6;:8::i;:::-;1618:9;1610:38;;;::::0;;-1:-1:-1;;;1610:38:14;;::::2;;::::0;::::2;::::0;::::2;::::0;;;;-1:-1:-1;;;1610:38:14;;;;;;;;;;;;;::::2;;6051:6:101::3;::::0;6083::::3;::::0;6141:49:::3;::::0;;-1:-1:-1;;;6141:49:101;;6184:4:::3;6141:49;::::0;::::3;::::0;;;-1:-1:-1;;;;;6051:6:101;;::::3;::::0;6083;;::::3;::::0;-1:-1:-1;;6083:6:101;;6141:34:::3;::::0;:49;;;;;::::3;::::0;;;;;;;;;6083:6;6141:49;::::3;;::::0;::::3;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;;;;;;;;::::0;::::3;;-1:-1:-1::0;6141:49:101;6200:72:::3;::::0;;-1:-1:-1;;;6200:72:101;;-1:-1:-1;;;;;6200:72:101;;::::3;;::::0;::::3;::::0;;;;;;;;;6141:49;;-1:-1:-1;6200:32:101;;::::3;::::0;::::3;::::0;:72;;;;;6141:49:::3;::::0;6200:72;;;;;;;;-1:-1:-1;6200:32:101;:72;::::3;;::::0;::::3;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;;;;;;;;;;;;;;;::::0;::::3;;-1:-1:-1::0;;6282:61:101::3;::::0;;-1:-1:-1;;;6282:61:101;;-1:-1:-1;;;;;6282:61:101;;::::3;;::::0;::::3;::::0;;;;;;;;;:21;;::::3;::::0;::::3;::::0;:61;;;;;-1:-1:-1;;6282:61:101;;;;;;;;-1:-1:-1;6282:21:101;:61;::::3;;::::0;::::3;;;;::::0;::::3;;;;;;;;;;;;::::0;::::3;;;;;-1:-1:-1::0;;1710:1:15::1;2798:22:::0;;-1:-1:-1;;;;;5960:390:101:o;2483:440::-;607:12:110;:10;:12::i;:::-;-1:-1:-1;;;;;596:23:110;:7;:5;:7::i;:::-;-1:-1:-1;;;;;596:23:110;;588:42;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;-1:-1:-1;;;588:42:110;;;;;;;;;;;;;;;2635:31:101::1;:18;-1:-1:-1::0;;;;;2635:29:101::1;;:31::i;:::-;2627:50;;;::::0;;-1:-1:-1;;;2627:50:101;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2627:50:101;;;;;;;;;;;;;::::1;;2788:6;::::0;2734:50:::1;::::0;;-1:-1:-1;;;2734:50:101;;;;-1:-1:-1;;;;;2788:6:101;;::::1;::::0;2734:48;::::1;::::0;::::1;::::0;:50:::1;::::0;;::::1;::::0;::::1;::::0;;;;;;;;:48;:50;::::1;;::::0;::::1;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;::::0;::::1;;-1:-1:-1::0;2734:50:101;-1:-1:-1;;;;;2734:60:101::1;;2726:79;;;::::0;;-1:-1:-1;;;2726:79:101;;::::1;;::::0;::::1;::::0;::::1;::::0;;;;-1:-1:-1;;;2726:79:101;;;;;;;;;;;;;::::1;;2816:19;:40:::0;;-1:-1:-1;;;;;2816:40:101;::::1;-1:-1:-1::0;;;;;;2816:40:101;;::::1;::::0;::::1;::::0;;;2871:45:::1;::::0;;;;;;::::1;::::0;;;;::::1;::::0;;::::1;2483:440:::0;:::o;6826:123::-;6920:22;;6826:123;:::o;668:121:109:-;730:15;764:18;:16;:18::i;:::-;757:25;;668:121;:::o;6246:198:140:-;6302:6;-1:-1:-1;;;;;6328:5:140;:34;;6320:87;;;;-1:-1:-1;;;6320:87:140;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;6431:5:140;6246:198::o;2465:210:3:-;2521:6;2550:5;;;2574:6;;;;;;:16;;;2589:1;2584;:6;;2574:16;2573:38;;;;2600:1;2596;:5;:14;;;;;2609:1;2605;:5;2596:14;2565:84;;;;-1:-1:-1;;;2565:84:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2667:1;2465:210;-1:-1:-1;;;2465:210:3:o;2019:213::-;2075:6;2104:5;;;2128:6;;;;;;:16;;;2143:1;2138;:6;;2128:16;2127:38;;;;2154:1;2150;:5;:14;;;;;2163:1;2159;:5;2150:14;2119:87;;;;-1:-1:-1;;;2119:87:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1278:102:139;1334:6;1364:1;1359;:6;;:14;;1372:1;1359:14;;;-1:-1:-1;1368:1:139;;1352:21;-1:-1:-1;1278:102:139:o;1386:101::-;1442:6;1471:1;1467;:5;:13;;1479:1;1467:13;;3221:168:140;3277:7;3313:1;3304:5;:10;;3296:55;;;;;-1:-1:-1;;;3296:55:140;;;;;;;;;;;;;;;;;;;;;;;;;;;;770:186:10;890:58;;;-1:-1:-1;;;;;890:58:10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;890:58:10;-1:-1:-1;;;890:58:10;;;863:86;;883:5;;863:19;:86::i;:::-;770:186;;;:::o;2317:117:14:-;1884:8;:6;:8::i;:::-;1876:41;;;;;-1:-1:-1;;;1876:41:14;;;;;;;;;;;;-1:-1:-1;;;1876:41:14;;;;;;;;;;;;;;;2375:7:::1;:15:::0;;-1:-1:-1;;2375:15:14::1;::::0;;2405:22:::1;2414:12;:10;:12::i;:::-;2405:22;::::0;;-1:-1:-1;;;;;2405:22:14;;::::1;::::0;;;;;;;::::1;::::0;;::::1;2317:117::o:0;1493:141:139:-;1543:7;1578:1;1569:5;:10;;:58;;1602:25;:13;1609:5;1602:6;:13::i;:25::-;1569:58;;;1582:17;:5;:15;:17::i;:::-;1562:65;1493:141;-1:-1:-1;;1493:141:139:o;737:413:11:-;1097:20;1135:8;;;737:413::o;2070:115:14:-;1619:8;:6;:8::i;:::-;1618:9;1610:38;;;;;-1:-1:-1;;;1610:38:14;;;;;;;;;;;;-1:-1:-1;;;1610:38:14;;;;;;;;;;;;;;;2129:7:::1;:14:::0;;-1:-1:-1;;2129:14:14::1;2139:4;2129:14;::::0;;2158:20:::1;2165:12;:10;:12::i;1952:123:5:-:0;2000:4;2024:44;2062:4;2024:29;:44::i;:::-;2023:45;2016:52;;1952:123;:::o;1791:106:15:-;1512:13:5;;;;;;;;:33;;;1529:16;:14;:16::i;:::-;1512:50;;;-1:-1:-1;1550:12:5;;;;1549:13;1512:50;1504:109;;;;-1:-1:-1;;;1504:109:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1624:19;1647:13;;;;;;1646:14;1670:98;;;;1704:13;:20;;-1:-1:-1;;;;1704:20:5;;;;;1738:19;1720:4;1738:19;;;1670:98;1856:34:15::1;:32;:34::i;:::-;1794:14:5::0;1790:66;;;1840:5;1824:21;;-1:-1:-1;;1824:21:5;;;1790:66;1791:106:15;:::o;407:117:109:-;1512:13:5;;;;;;;;:33;;;1529:16;:14;:16::i;:::-;1512:50;;;-1:-1:-1;1550:12:5;;;;1549:13;1512:50;1504:109;;;;-1:-1:-1;;;1504:109:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1624:19;1647:13;;;;;;1646:14;1670:98;;;;1704:13;:20;;-1:-1:-1;;;;1704:20:5;;;;;1738:19;1720:4;1738:19;;;1670:98;470:20:109::1;:18;:20::i;:::-;500:17;:15;:17::i;828:104:12:-:0;915:10;828:104;:::o;3088:762:10:-;3518:23;3544:69;3572:4;3544:69;;;;;;;;;;;;;;;;;3552:5;-1:-1:-1;;;;;3544:27:10;;;:69;;;;;:::i;:::-;3627:17;;3518:95;;-1:-1:-1;3627:21:10;3623:221;;3767:10;3756:30;;;;;;;;;;;;;;;-1:-1:-1;3756:30:10;3748:85;;;;-1:-1:-1;;;3748:85:10;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1640:145:139;1689:6;-1:-1:-1;;;1715:1:139;:11;1707:52;;;;-1:-1:-1;;;1707:52:139;;;;;;;:::i;:::-;;;;;;;;;-1:-1:-1;1776:2:139;;;1640:145::o;1903:104:15:-;1512:13:5;;;;;;;;:33;;;1529:16;:14;:16::i;:::-;1512:50;;;-1:-1:-1;1550:12:5;;;;1549:13;1512:50;1504:109;;;;-1:-1:-1;;;1504:109:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1624:19;1647:13;;;;;;1646:14;1670:98;;;;1704:13;:20;;-1:-1:-1;;;;1704:20:5;;;;;1738:19;1720:4;1738:19;;;1670:98;1710:1:15::1;1978:22:::0;;1790:66:5;;;;1840:5;1824:21;;-1:-1:-1;;1824:21:5;;;1903:104:15;:::o;751:212:110:-;1512:13:5;;;;;;;;:33;;;1529:16;:14;:16::i;:::-;1512:50;;;-1:-1:-1;1550:12:5;;;;1549:13;1512:50;1504:109;;;;-1:-1:-1;;;1504:109:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1624:19;1647:13;;;;;;1646:14;1670:98;;;;1704:13;:20;;-1:-1:-1;;;;1704:20:5;;;;;1738:19;1720:4;1738:19;;;1670:98;812:16:110::1;:14;:16::i;:::-;838:17;858:12;:10;:12::i;:::-;880:6;:18:::0;;-1:-1:-1;;;;;;880:18:110::1;-1:-1:-1::0;;;;;880:18:110;::::1;::::0;;::::1;::::0;;;913:43:::1;::::0;880:18;;-1:-1:-1;880:18:110;-1:-1:-1;;;;;;;;;;;;;913:43:110;-1:-1:-1;;913:43:110::1;1778:1:5;1794:14:::0;1790:66;;;1840:5;1824:21;;-1:-1:-1;;1824:21:5;;;751:212:110;:::o;986:128:14:-;1512:13:5;;;;;;;;:33;;;1529:16;:14;:16::i;:::-;1512:50;;;-1:-1:-1;1550:12:5;;;;1549:13;1512:50;1504:109;;;;-1:-1:-1;;;1504:109:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1624:19;1647:13;;;;;;1646:14;1670:98;;;;1704:13;:20;;-1:-1:-1;;;;1704:20:5;;;;;1738:19;1720:4;1738:19;;;1670:98;1044:26:14::1;:24;:26::i;:::-;1080:27;:25;:27::i;3592:193:11:-:0;3695:12;3726:52;3748:6;3756:4;3762:1;3765:12;3726:21;:52::i;:::-;3719:59;3592:193;-1:-1:-1;;;;3592:193:11:o;663:90:12:-;1512:13:5;;;;;;;;:33;;;1529:16;:14;:16::i;:::-;1512:50;;;-1:-1:-1;1550:12:5;;;;1549:13;1512:50;1504:109;;;;-1:-1:-1;;;1504:109:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1624:19;1647:13;;;;;;1646:14;1670:98;;;;1704:13;:20;;-1:-1:-1;;;;1704:20:5;;;;;1738:19;1720:4;1738:19;;;1670:98;720:26:12::1;759:64:::0;1512:13:5;;;;;;;;:33;;;1529:16;:14;:16::i;:::-;1512:50;;;-1:-1:-1;1550:12:5;;;;1549:13;1512:50;1504:109;;;;-1:-1:-1;;;1504:109:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1624:19;1647:13;;;;;;1646:14;1670:98;;;;1704:13;:20;;-1:-1:-1;;;;1704:20:5;;;;;1738:19;1720:4;1738:19;;;1794:14;1790:66;;;1840:5;1824:21;;-1:-1:-1;;1824:21:5;;;759:64:12;:::o;1120:90:14:-;1512:13:5;;;;;;;;:33;;;1529:16;:14;:16::i;:::-;1512:50;;;-1:-1:-1;1550:12:5;;;;1549:13;1512:50;1504:109;;;;-1:-1:-1;;;1504:109:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;1624:19;1647:13;;;;;;1646:14;1670:98;;;;1704:13;:20;;-1:-1:-1;;;;1704:20:5;;;;;1738:19;1720:4;1738:19;;;1670:98;1188:7:14::1;:15:::0;;-1:-1:-1;;1188:15:14::1;::::0;;1790:66:5;;;;1840:5;1824:21;;-1:-1:-1;;1824:21:5;;;1120:90:14;:::o;4619:523:11:-;4746:12;4803:5;4778:21;:30;;4770:81;;;;-1:-1:-1;;;4770:81:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4869:18;4880:6;4869:10;:18::i;:::-;4861:60;;;;;-1:-1:-1;;;4861:60:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;4992:12;5006:23;5033:6;-1:-1:-1;;;;;5033:11:11;5053:5;5061:4;5033:33;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;5033:33:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4991:75;;;;5083:52;5101:7;5110:10;5122:12;5083:17;:52::i;:::-;5076:59;4619:523;-1:-1:-1;;;;;;;4619:523:11:o;6122:725::-;6237:12;6265:7;6261:580;;;-1:-1:-1;6295:10:11;6288:17;;6261:580;6406:17;;:21;6402:429;;6664:10;6658:17;6724:15;6711:10;6707:2;6703:19;6696:44;6613:145;6803:12;6796:20;;-1:-1:-1;;;6796:20:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;14:352:157;216:2;198:21;;;255:2;235:18;;;228:30;294;289:2;274:18;;267:58;357:2;342:18;;188:178::o
Swarm Source
ipfs://10461f7476a7115f02a4469d432b3fdfdd179a8c7deca703d12cacfe2bd4c72b
Net Worth in USD
Net Worth in ETH
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
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.