Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
YieldYakSwapArbitrumFacet
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
// Last deployed from commit: e9e05b6e564514c1bcd1b5e49f5e45250e72bf98;
pragma solidity 0.8.17;
import "../avalanche/YieldYakSwapFacet.sol";
contract YieldYakSwapArbitrumFacet is YieldYakSwapFacet {
function YY_ROUTER() internal override pure returns (address) {
return 0xb32C79a25291265eF240Eb32E9faBbc6DcEE3cE3;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @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() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)
pragma solidity ^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 proxied contracts do not make use of 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 {ERC1967Proxy-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.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
* initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() initializer {}
* ```
* ====
*/
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() {
// If the contract is initializing we ignore whether _initialized is set in order to support multiple
// inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
// contract may have been reentered.
require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} modifier, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
function _isConstructor() private view returns (bool) {
return !AddressUpgradeable.isContract(address(this));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/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 onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// 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;
}
/**
* This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @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
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://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");
(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");
(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");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// 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
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
import "../proxy/utils/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 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 onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
/**
* This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// 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;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.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 `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
/**
* @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
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a / b + (a % b == 0 ? 0 : 1);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/math/SafeMath.sol)
pragma solidity ^0.8.0;
// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.
/**
* @dev Wrappers over Solidity's arithmetic operations.
*
* NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
* now has built in overflow checking.
*/
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) {
unchecked {
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) {
unchecked {
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) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the 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) {
return a + b;
}
/**
* @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) {
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) {
return a * b;
}
/**
* @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.
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
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) {
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) {
unchecked {
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.
*
* 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) {
unchecked {
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) {
unchecked {
require(b > 0, errorMessage);
return a % b;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)
pragma solidity ^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 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;
if (lastIndex != toDeleteIndex) {
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] = valueIndex; // Replace lastvalue's index to valueIndex
}
// 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) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// 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);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
return _values(set._inner);
}
// 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))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly {
result := store
}
return result;
}
// 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));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "./RedstoneConstants.sol";
/**
* @title The base contract with the main logic of data extraction from calldata
* @author The Redstone Oracles team
* @dev This contract was created to reuse the same logic in the RedstoneConsumerBase
* and the ProxyConnector contracts
*/
contract CalldataExtractor is RedstoneConstants {
using SafeMath for uint256;
function _extractByteSizeOfUnsignedMetadata() internal pure returns (uint256) {
// Checking if the calldata ends with the RedStone marker
bool hasValidRedstoneMarker;
assembly {
let calldataLast32Bytes := calldataload(sub(calldatasize(), STANDARD_SLOT_BS))
hasValidRedstoneMarker := eq(
REDSTONE_MARKER_MASK,
and(calldataLast32Bytes, REDSTONE_MARKER_MASK)
)
}
if (!hasValidRedstoneMarker) {
revert CalldataMustHaveValidPayload();
}
// Using uint24, because unsigned metadata byte size number has 3 bytes
uint24 unsignedMetadataByteSize;
if (REDSTONE_MARKER_BS_PLUS_STANDARD_SLOT_BS > msg.data.length) {
revert CalldataOverOrUnderFlow();
}
assembly {
unsignedMetadataByteSize := calldataload(
sub(calldatasize(), REDSTONE_MARKER_BS_PLUS_STANDARD_SLOT_BS)
)
}
uint256 calldataNegativeOffset = unsignedMetadataByteSize
+ UNSGINED_METADATA_BYTE_SIZE_BS
+ REDSTONE_MARKER_BS;
if (calldataNegativeOffset + DATA_PACKAGES_COUNT_BS > msg.data.length) {
revert IncorrectUnsignedMetadataSize();
}
return calldataNegativeOffset;
}
// We return uint16, because unsigned metadata byte size number has 2 bytes
function _extractDataPackagesCountFromCalldata(uint256 calldataNegativeOffset)
internal
pure
returns (uint16 dataPackagesCount)
{
uint256 calldataNegativeOffsetWithStandardSlot = calldataNegativeOffset + STANDARD_SLOT_BS;
if (calldataNegativeOffsetWithStandardSlot > msg.data.length) {
revert CalldataOverOrUnderFlow();
}
assembly {
dataPackagesCount := calldataload(
sub(calldatasize(), calldataNegativeOffsetWithStandardSlot)
)
}
return dataPackagesCount;
}
function _extractDataPointValueAndDataFeedId(
uint256 calldataNegativeOffsetForDataPackage,
uint256 defaultDataPointValueByteSize,
uint256 dataPointIndex
) internal pure virtual returns (bytes32 dataPointDataFeedId, uint256 dataPointValue) {
uint256 negativeOffsetToDataPoints = calldataNegativeOffsetForDataPackage + DATA_PACKAGE_WITHOUT_DATA_POINTS_BS;
uint256 dataPointNegativeOffset = negativeOffsetToDataPoints.add(
(1 + dataPointIndex).mul((defaultDataPointValueByteSize + DATA_POINT_SYMBOL_BS))
);
uint256 dataPointCalldataOffset = msg.data.length.sub(dataPointNegativeOffset);
assembly {
dataPointDataFeedId := calldataload(dataPointCalldataOffset)
dataPointValue := calldataload(add(dataPointCalldataOffset, DATA_POINT_SYMBOL_BS))
}
}
function _extractDataPointsDetailsForDataPackage(uint256 calldataNegativeOffsetForDataPackage)
internal
pure
returns (uint256 dataPointsCount, uint256 eachDataPointValueByteSize)
{
// Using uint24, because data points count byte size number has 3 bytes
uint24 dataPointsCount_;
// Using uint32, because data point value byte size has 4 bytes
uint32 eachDataPointValueByteSize_;
// Extract data points count
uint256 negativeCalldataOffset = calldataNegativeOffsetForDataPackage + SIG_BS;
uint256 calldataOffset = msg.data.length.sub(negativeCalldataOffset + STANDARD_SLOT_BS);
assembly {
dataPointsCount_ := calldataload(calldataOffset)
}
// Extract each data point value size
calldataOffset = calldataOffset.sub(DATA_POINTS_COUNT_BS);
assembly {
eachDataPointValueByteSize_ := calldataload(calldataOffset)
}
// Prepare returned values
dataPointsCount = dataPointsCount_;
eachDataPointValueByteSize = eachDataPointValueByteSize_;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.4;
import "./RedstoneConstants.sol";
import "./CalldataExtractor.sol";
/**
* @title The base contract for forwarding redstone payload to other contracts
* @author The Redstone Oracles team
*/
contract ProxyConnector is RedstoneConstants, CalldataExtractor {
error ProxyCalldataFailedWithoutErrMsg();
error ProxyCalldataFailedWithStringMessage(string message);
error ProxyCalldataFailedWithCustomError(bytes result);
function proxyCalldata(
address contractAddress,
bytes memory encodedFunction,
bool forwardValue
) internal returns (bytes memory) {
bytes memory message = _prepareMessage(encodedFunction);
(bool success, bytes memory result) =
contractAddress.call{value: forwardValue ? msg.value : 0}(message);
return _prepareReturnValue(success, result);
}
function proxyDelegateCalldata(address contractAddress, bytes memory encodedFunction)
internal
returns (bytes memory)
{
bytes memory message = _prepareMessage(encodedFunction);
(bool success, bytes memory result) = contractAddress.delegatecall(message);
return _prepareReturnValue(success, result);
}
function proxyCalldataView(address contractAddress, bytes memory encodedFunction)
internal
view
returns (bytes memory)
{
bytes memory message = _prepareMessage(encodedFunction);
(bool success, bytes memory result) = contractAddress.staticcall(message);
return _prepareReturnValue(success, result);
}
function _prepareMessage(bytes memory encodedFunction) private pure returns (bytes memory) {
uint256 encodedFunctionBytesCount = encodedFunction.length;
uint256 redstonePayloadByteSize = _getRedstonePayloadByteSize();
uint256 resultMessageByteSize = encodedFunctionBytesCount + redstonePayloadByteSize;
if (redstonePayloadByteSize > msg.data.length) {
revert CalldataOverOrUnderFlow();
}
bytes memory message;
assembly {
message := mload(FREE_MEMORY_PTR) // sets message pointer to first free place in memory
// Saving the byte size of the result message (it's a standard in EVM)
mstore(message, resultMessageByteSize)
// Copying function and its arguments
for {
let from := add(BYTES_ARR_LEN_VAR_BS, encodedFunction)
let fromEnd := add(from, encodedFunctionBytesCount)
let to := add(BYTES_ARR_LEN_VAR_BS, message)
} lt (from, fromEnd) {
from := add(from, STANDARD_SLOT_BS)
to := add(to, STANDARD_SLOT_BS)
} {
// Copying data from encodedFunction to message (32 bytes at a time)
mstore(to, mload(from))
}
// Copying redstone payload to the message bytes
calldatacopy(
add(message, add(BYTES_ARR_LEN_VAR_BS, encodedFunctionBytesCount)), // address
sub(calldatasize(), redstonePayloadByteSize), // offset
redstonePayloadByteSize // bytes length to copy
)
// Updating free memory pointer
mstore(
FREE_MEMORY_PTR,
add(
add(message, add(redstonePayloadByteSize, encodedFunctionBytesCount)),
BYTES_ARR_LEN_VAR_BS
)
)
}
return message;
}
function _getRedstonePayloadByteSize() private pure returns (uint256) {
uint256 calldataNegativeOffset = _extractByteSizeOfUnsignedMetadata();
uint256 dataPackagesCount = _extractDataPackagesCountFromCalldata(calldataNegativeOffset);
calldataNegativeOffset += DATA_PACKAGES_COUNT_BS;
for (uint256 dataPackageIndex = 0; dataPackageIndex < dataPackagesCount; dataPackageIndex++) {
uint256 dataPackageByteSize = _getDataPackageByteSize(calldataNegativeOffset);
calldataNegativeOffset += dataPackageByteSize;
}
return calldataNegativeOffset;
}
function _getDataPackageByteSize(uint256 calldataNegativeOffset) private pure returns (uint256) {
(
uint256 dataPointsCount,
uint256 eachDataPointValueByteSize
) = _extractDataPointsDetailsForDataPackage(calldataNegativeOffset);
return
dataPointsCount *
(DATA_POINT_SYMBOL_BS + eachDataPointValueByteSize) +
DATA_PACKAGE_WITHOUT_DATA_POINTS_BS;
}
function _prepareReturnValue(bool success, bytes memory result)
internal
pure
returns (bytes memory)
{
if (!success) {
if (result.length == 0) {
revert ProxyCalldataFailedWithoutErrMsg();
} else {
bool isStringErrorMessage;
assembly {
let first32BytesOfResult := mload(add(result, BYTES_ARR_LEN_VAR_BS))
isStringErrorMessage := eq(first32BytesOfResult, STRING_ERR_MESSAGE_MASK)
}
if (isStringErrorMessage) {
string memory receivedErrMsg;
assembly {
receivedErrMsg := add(result, REVERT_MSG_OFFSET)
}
revert ProxyCalldataFailedWithStringMessage(receivedErrMsg);
} else {
revert ProxyCalldataFailedWithCustomError(result);
}
}
}
return result;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.4;
/**
* @title The base contract with helpful constants
* @author The Redstone Oracles team
* @dev It mainly contains redstone-related values, which improve readability
* of other contracts (e.g. CalldataExtractor and RedstoneConsumerBase)
*/
contract RedstoneConstants {
// === Abbreviations ===
// BS - Bytes size
// PTR - Pointer (memory location)
// SIG - Signature
// Solidity and YUL constants
uint256 internal constant STANDARD_SLOT_BS = 32;
uint256 internal constant FREE_MEMORY_PTR = 0x40;
uint256 internal constant BYTES_ARR_LEN_VAR_BS = 32;
uint256 internal constant FUNCTION_SIGNATURE_BS = 4;
uint256 internal constant REVERT_MSG_OFFSET = 68; // Revert message structure described here: https://ethereum.stackexchange.com/a/66173/106364
uint256 internal constant STRING_ERR_MESSAGE_MASK = 0x08c379a000000000000000000000000000000000000000000000000000000000;
// RedStone protocol consts
uint256 internal constant SIG_BS = 65;
uint256 internal constant TIMESTAMP_BS = 6;
uint256 internal constant DATA_PACKAGES_COUNT_BS = 2;
uint256 internal constant DATA_POINTS_COUNT_BS = 3;
uint256 internal constant DATA_POINT_VALUE_BYTE_SIZE_BS = 4;
uint256 internal constant DATA_POINT_SYMBOL_BS = 32;
uint256 internal constant DEFAULT_DATA_POINT_VALUE_BS = 32;
uint256 internal constant UNSGINED_METADATA_BYTE_SIZE_BS = 3;
uint256 internal constant REDSTONE_MARKER_BS = 9; // byte size of 0x000002ed57011e0000
uint256 internal constant REDSTONE_MARKER_MASK = 0x0000000000000000000000000000000000000000000000000002ed57011e0000;
// Derived values (based on consts)
uint256 internal constant TIMESTAMP_NEGATIVE_OFFSET_IN_DATA_PACKAGE_WITH_STANDARD_SLOT_BS = 104; // SIG_BS + DATA_POINTS_COUNT_BS + DATA_POINT_VALUE_BYTE_SIZE_BS + STANDARD_SLOT_BS
uint256 internal constant DATA_PACKAGE_WITHOUT_DATA_POINTS_BS = 78; // DATA_POINT_VALUE_BYTE_SIZE_BS + TIMESTAMP_BS + DATA_POINTS_COUNT_BS + SIG_BS
uint256 internal constant DATA_PACKAGE_WITHOUT_DATA_POINTS_AND_SIG_BS = 13; // DATA_POINT_VALUE_BYTE_SIZE_BS + TIMESTAMP_BS + DATA_POINTS_COUNT_BS
uint256 internal constant REDSTONE_MARKER_BS_PLUS_STANDARD_SLOT_BS = 41; // REDSTONE_MARKER_BS + STANDARD_SLOT_BS
// Error messages
error CalldataOverOrUnderFlow();
error IncorrectUnsignedMetadataSize();
error InsufficientNumberOfUniqueSigners(uint256 receviedSignersCount, uint256 requiredSignersCount);
error EachSignerMustProvideTheSameValue();
error EmptyCalldataPointersArr();
error InvalidCalldataPointer();
error CalldataMustHaveValidPayload();
error SignerNotAuthorised(address receivedSigner);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "./RedstoneConstants.sol";
import "./RedstoneDefaultsLib.sol";
import "./CalldataExtractor.sol";
import "../libs/BitmapLib.sol";
import "../libs/SignatureLib.sol";
/**
* @title The base contract with the main Redstone logic
* @author The Redstone Oracles team
* @dev Do not use this contract directly in consumer contracts, take a
* look at `RedstoneConsumerNumericBase` and `RedstoneConsumerBytesBase` instead
*/
abstract contract RedstoneConsumerBase is CalldataExtractor {
using SafeMath for uint256;
/* ========== VIRTUAL FUNCTIONS (MAY BE OVERRIDEN IN CHILD CONTRACTS) ========== */
/**
* @dev This function must be implemented by the child consumer contract.
* It should return a unique index for a given signer address if the signer
* is authorised, otherwise it should revert
* @param receviedSigner The address of a signer, recovered from ECDSA signature
* @return Unique index for a signer in the range [0..255]
*/
function getAuthorisedSignerIndex(address receviedSigner) public view virtual returns (uint8);
/**
* @dev This function may be overriden by the child consumer contract.
* It should validate the timestamp against the current time (block.timestamp)
* It should revert with a helpful message if the timestamp is not valid
* @param receivedTimestampMilliseconds Timestamp extracted from calldata
*/
function validateTimestamp(uint256 receivedTimestampMilliseconds) public view virtual {
RedstoneDefaultsLib.validateTimestamp(receivedTimestampMilliseconds);
}
/**
* @dev This function should be overriden by the child consumer contract.
* @return The minimum required value of unique authorised signers
*/
function getUniqueSignersThreshold() public view virtual returns (uint8) {
return 1;
}
/**
* @dev This function may be overriden by the child consumer contract.
* It should aggregate values from different signers to a single uint value.
* By default, it calculates the median value
* @param values An array of uint256 values from different signers
* @return Result of the aggregation in the form of a single number
*/
function aggregateValues(uint256[] memory values) public view virtual returns (uint256) {
return RedstoneDefaultsLib.aggregateValues(values);
}
/* ========== FUNCTIONS WITH IMPLEMENTATION (CAN NOT BE OVERRIDEN) ========== */
/**
* @dev This is an internal helpful function for secure extraction oracle values
* from the tx calldata. Security is achieved by signatures verification, timestamp
* validation, and aggregating values from different authorised signers into a
* single numeric value. If any of the required conditions (e.g. too old timestamp or
* insufficient number of autorised signers) do not match, the function will revert.
*
* Note! You should not call this function in a consumer contract. You can use
* `getOracleNumericValuesFromTxMsg` or `getOracleNumericValueFromTxMsg` instead.
*
* @param dataFeedIds An array of unique data feed identifiers
* @return An array of the extracted and verified oracle values in the same order
* as they are requested in dataFeedIds array
*/
function _securelyExtractOracleValuesFromTxMsg(bytes32[] memory dataFeedIds)
internal
view
returns (uint256[] memory)
{
// Initializing helpful variables and allocating memory
uint256[] memory uniqueSignerCountForDataFeedIds = new uint256[](dataFeedIds.length);
uint256[] memory signersBitmapForDataFeedIds = new uint256[](dataFeedIds.length);
uint256[][] memory valuesForDataFeeds = new uint256[][](dataFeedIds.length);
for (uint256 i = 0; i < dataFeedIds.length; i++) {
// The line below is commented because newly allocated arrays are filled with zeros
// But we left it for better readability
// signersBitmapForDataFeedIds[i] = 0; // <- setting to an empty bitmap
valuesForDataFeeds[i] = new uint256[](getUniqueSignersThreshold());
}
// Extracting the number of data packages from calldata
uint256 calldataNegativeOffset = _extractByteSizeOfUnsignedMetadata();
uint256 dataPackagesCount = _extractDataPackagesCountFromCalldata(calldataNegativeOffset);
calldataNegativeOffset += DATA_PACKAGES_COUNT_BS;
// Saving current free memory pointer
uint256 freeMemPtr;
assembly {
freeMemPtr := mload(FREE_MEMORY_PTR)
}
// Data packages extraction in a loop
for (uint256 dataPackageIndex = 0; dataPackageIndex < dataPackagesCount; dataPackageIndex++) {
// Extract data package details and update calldata offset
uint256 dataPackageByteSize = _extractDataPackage(
dataFeedIds,
uniqueSignerCountForDataFeedIds,
signersBitmapForDataFeedIds,
valuesForDataFeeds,
calldataNegativeOffset
);
calldataNegativeOffset += dataPackageByteSize;
// Shifting memory pointer back to the "safe" value
assembly {
mstore(FREE_MEMORY_PTR, freeMemPtr)
}
}
// Validating numbers of unique signers and calculating aggregated values for each dataFeedId
return _getAggregatedValues(valuesForDataFeeds, uniqueSignerCountForDataFeedIds);
}
/**
* @dev This is a private helpful function, which extracts data for a data package based
* on the given negative calldata offset, verifies them, and in the case of successful
* verification updates the corresponding data package values in memory
*
* @param dataFeedIds an array of unique data feed identifiers
* @param uniqueSignerCountForDataFeedIds an array with the numbers of unique signers
* for each data feed
* @param signersBitmapForDataFeedIds an array of sginers bitmaps for data feeds
* @param valuesForDataFeeds 2-dimensional array, valuesForDataFeeds[i][j] contains
* j-th value for the i-th data feed
* @param calldataNegativeOffset negative calldata offset for the given data package
*
* @return An array of the aggregated values
*/
function _extractDataPackage(
bytes32[] memory dataFeedIds,
uint256[] memory uniqueSignerCountForDataFeedIds,
uint256[] memory signersBitmapForDataFeedIds,
uint256[][] memory valuesForDataFeeds,
uint256 calldataNegativeOffset
) private view returns (uint256) {
uint256 signerIndex;
(
uint256 dataPointsCount,
uint256 eachDataPointValueByteSize
) = _extractDataPointsDetailsForDataPackage(calldataNegativeOffset);
// We use scopes to resolve problem with too deep stack
{
uint48 extractedTimestamp;
address signerAddress;
bytes32 signedHash;
bytes memory signedMessage;
uint256 signedMessageBytesCount;
signedMessageBytesCount = dataPointsCount.mul(eachDataPointValueByteSize + DATA_POINT_SYMBOL_BS)
+ DATA_PACKAGE_WITHOUT_DATA_POINTS_AND_SIG_BS;
uint256 timestampCalldataOffset = msg.data.length.sub(
calldataNegativeOffset + TIMESTAMP_NEGATIVE_OFFSET_IN_DATA_PACKAGE_WITH_STANDARD_SLOT_BS);
uint256 signedMessageCalldataOffset = msg.data.length.sub(
calldataNegativeOffset + SIG_BS + signedMessageBytesCount);
assembly {
// Extracting the signed message
signedMessage := extractBytesFromCalldata(
signedMessageCalldataOffset,
signedMessageBytesCount
)
// Hashing the signed message
signedHash := keccak256(add(signedMessage, BYTES_ARR_LEN_VAR_BS), signedMessageBytesCount)
// Extracting timestamp
extractedTimestamp := calldataload(timestampCalldataOffset)
function initByteArray(bytesCount) -> ptr {
ptr := mload(FREE_MEMORY_PTR)
mstore(ptr, bytesCount)
ptr := add(ptr, BYTES_ARR_LEN_VAR_BS)
mstore(FREE_MEMORY_PTR, add(ptr, bytesCount))
}
function extractBytesFromCalldata(offset, bytesCount) -> extractedBytes {
let extractedBytesStartPtr := initByteArray(bytesCount)
calldatacopy(
extractedBytesStartPtr,
offset,
bytesCount
)
extractedBytes := sub(extractedBytesStartPtr, BYTES_ARR_LEN_VAR_BS)
}
}
// Validating timestamp
validateTimestamp(extractedTimestamp);
// Verifying the off-chain signature against on-chain hashed data
signerAddress = SignatureLib.recoverSignerAddress(
signedHash,
calldataNegativeOffset + SIG_BS
);
signerIndex = getAuthorisedSignerIndex(signerAddress);
}
// Updating helpful arrays
{
bytes32 dataPointDataFeedId;
uint256 dataPointValue;
for (uint256 dataPointIndex = 0; dataPointIndex < dataPointsCount; dataPointIndex++) {
// Extracting data feed id and value for the current data point
(dataPointDataFeedId, dataPointValue) = _extractDataPointValueAndDataFeedId(
calldataNegativeOffset,
eachDataPointValueByteSize,
dataPointIndex
);
for (
uint256 dataFeedIdIndex = 0;
dataFeedIdIndex < dataFeedIds.length;
dataFeedIdIndex++
) {
if (dataPointDataFeedId == dataFeedIds[dataFeedIdIndex]) {
uint256 bitmapSignersForDataFeedId = signersBitmapForDataFeedIds[dataFeedIdIndex];
if (
!BitmapLib.getBitFromBitmap(bitmapSignersForDataFeedId, signerIndex) && /* current signer was not counted for current dataFeedId */
uniqueSignerCountForDataFeedIds[dataFeedIdIndex] < getUniqueSignersThreshold()
) {
// Increase unique signer counter
uniqueSignerCountForDataFeedIds[dataFeedIdIndex]++;
// Add new value
valuesForDataFeeds[dataFeedIdIndex][
uniqueSignerCountForDataFeedIds[dataFeedIdIndex] - 1
] = dataPointValue;
// Update signers bitmap
signersBitmapForDataFeedIds[dataFeedIdIndex] = BitmapLib.setBitInBitmap(
bitmapSignersForDataFeedId,
signerIndex
);
}
// Breaking, as there couldn't be several indexes for the same feed ID
break;
}
}
}
}
// Return total data package byte size
return
DATA_PACKAGE_WITHOUT_DATA_POINTS_BS +
(eachDataPointValueByteSize + DATA_POINT_SYMBOL_BS) *
dataPointsCount;
}
/**
* @dev This is a private helpful function, which aggregates values from different
* authorised signers for the given arrays of values for each data feed
*
* @param valuesForDataFeeds 2-dimensional array, valuesForDataFeeds[i][j] contains
* j-th value for the i-th data feed
* @param uniqueSignerCountForDataFeedIds an array with the numbers of unique signers
* for each data feed
*
* @return An array of the aggregated values
*/
function _getAggregatedValues(
uint256[][] memory valuesForDataFeeds,
uint256[] memory uniqueSignerCountForDataFeedIds
) private view returns (uint256[] memory) {
uint256[] memory aggregatedValues = new uint256[](valuesForDataFeeds.length);
uint256 uniqueSignersThreshold = getUniqueSignersThreshold();
for (uint256 dataFeedIndex = 0; dataFeedIndex < valuesForDataFeeds.length; dataFeedIndex++) {
if (uniqueSignerCountForDataFeedIds[dataFeedIndex] < uniqueSignersThreshold) {
revert InsufficientNumberOfUniqueSigners(
uniqueSignerCountForDataFeedIds[dataFeedIndex],
uniqueSignersThreshold);
}
uint256 aggregatedValueForDataFeedId = aggregateValues(valuesForDataFeeds[dataFeedIndex]);
aggregatedValues[dataFeedIndex] = aggregatedValueForDataFeedId;
}
return aggregatedValues;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.4;
import "./RedstoneConsumerBase.sol";
/**
* @title The base contract for Redstone consumers' contracts that allows to
* securely calculate numeric redstone oracle values
* @author The Redstone Oracles team
* @dev This contract can extend other contracts to allow them
* securely fetch Redstone oracle data from transactions calldata
*/
abstract contract RedstoneConsumerNumericBase is RedstoneConsumerBase {
/**
* @dev This function can be used in a consumer contract to securely extract an
* oracle value for a given data feed id. Security is achieved by
* signatures verification, timestamp validation, and aggregating values
* from different authorised signers into a single numeric value. If any of the
* required conditions do not match, the function will revert.
* Note! This function expects that tx calldata contains redstone payload in the end
* Learn more about redstone payload here: https://github.com/redstone-finance/redstone-oracles-monorepo/tree/main/packages/evm-connector#readme
* @param dataFeedId bytes32 value that uniquely identifies the data feed
* @return Extracted and verified numeric oracle value for the given data feed id
*/
function getOracleNumericValueFromTxMsg(bytes32 dataFeedId)
internal
view
virtual
returns (uint256)
{
bytes32[] memory dataFeedIds = new bytes32[](1);
dataFeedIds[0] = dataFeedId;
return getOracleNumericValuesFromTxMsg(dataFeedIds)[0];
}
/**
* @dev This function can be used in a consumer contract to securely extract several
* numeric oracle values for a given array of data feed ids. Security is achieved by
* signatures verification, timestamp validation, and aggregating values
* from different authorised signers into a single numeric value. If any of the
* required conditions do not match, the function will revert.
* Note! This function expects that tx calldata contains redstone payload in the end
* Learn more about redstone payload here: https://github.com/redstone-finance/redstone-oracles-monorepo/tree/main/packages/evm-connector#readme
* @param dataFeedIds An array of unique data feed identifiers
* @return An array of the extracted and verified oracle values in the same order
* as they are requested in the dataFeedIds array
*/
function getOracleNumericValuesFromTxMsg(bytes32[] memory dataFeedIds)
internal
view
virtual
returns (uint256[] memory)
{
return _securelyExtractOracleValuesFromTxMsg(dataFeedIds);
}
/**
* @dev This function works similarly to the `getOracleNumericValuesFromTxMsg` with the
* only difference that it allows to request oracle data for an array of data feeds
* that may contain duplicates
*
* @param dataFeedIdsWithDuplicates An array of data feed identifiers (duplicates are allowed)
* @return An array of the extracted and verified oracle values in the same order
* as they are requested in the dataFeedIdsWithDuplicates array
*/
function getOracleNumericValuesWithDuplicatesFromTxMsg(bytes32[] memory dataFeedIdsWithDuplicates) internal view returns (uint256[] memory) {
// Building an array without duplicates
bytes32[] memory dataFeedIdsWithoutDuplicates = new bytes32[](dataFeedIdsWithDuplicates.length);
bool alreadyIncluded;
uint256 uniqueDataFeedIdsCount = 0;
for (uint256 indexWithDup = 0; indexWithDup < dataFeedIdsWithDuplicates.length; indexWithDup++) {
// Checking if current element is already included in `dataFeedIdsWithoutDuplicates`
alreadyIncluded = false;
for (uint256 indexWithoutDup = 0; indexWithoutDup < uniqueDataFeedIdsCount; indexWithoutDup++) {
if (dataFeedIdsWithoutDuplicates[indexWithoutDup] == dataFeedIdsWithDuplicates[indexWithDup]) {
alreadyIncluded = true;
break;
}
}
// Adding if not included
if (!alreadyIncluded) {
dataFeedIdsWithoutDuplicates[uniqueDataFeedIdsCount] = dataFeedIdsWithDuplicates[indexWithDup];
uniqueDataFeedIdsCount++;
}
}
// Overriding dataFeedIdsWithoutDuplicates.length
// Equivalent to: dataFeedIdsWithoutDuplicates.length = uniqueDataFeedIdsCount;
assembly {
mstore(dataFeedIdsWithoutDuplicates, uniqueDataFeedIdsCount)
}
// Requesting oracle values (without duplicates)
uint256[] memory valuesWithoutDuplicates = getOracleNumericValuesFromTxMsg(dataFeedIdsWithoutDuplicates);
// Preparing result values array
uint256[] memory valuesWithDuplicates = new uint256[](dataFeedIdsWithDuplicates.length);
for (uint256 indexWithDup = 0; indexWithDup < dataFeedIdsWithDuplicates.length; indexWithDup++) {
for (uint256 indexWithoutDup = 0; indexWithoutDup < dataFeedIdsWithoutDuplicates.length; indexWithoutDup++) {
if (dataFeedIdsWithDuplicates[indexWithDup] == dataFeedIdsWithoutDuplicates[indexWithoutDup]) {
valuesWithDuplicates[indexWithDup] = valuesWithoutDuplicates[indexWithoutDup];
break;
}
}
}
return valuesWithDuplicates;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.4;
import "../libs/NumericArrayLib.sol";
/**
* @title Default implementations of virtual redstone consumer base functions
* @author The Redstone Oracles team
*/
library RedstoneDefaultsLib {
uint256 constant DEFAULT_MAX_DATA_TIMESTAMP_DELAY_SECONDS = 3 minutes;
uint256 constant DEFAULT_MAX_DATA_TIMESTAMP_AHEAD_SECONDS = 1 minutes;
error TimestampFromTooLongFuture(uint256 receivedTimestampSeconds, uint256 blockTimestamp);
error TimestampIsTooOld(uint256 receivedTimestampSeconds, uint256 blockTimestamp);
function validateTimestamp(uint256 receivedTimestampMilliseconds) internal view {
// Getting data timestamp from future seems quite unlikely
// But we've already spent too much time with different cases
// Where block.timestamp was less than dataPackage.timestamp.
// Some blockchains may case this problem as well.
// That's why we add MAX_BLOCK_TIMESTAMP_DELAY
// and allow data "from future" but with a small delay
uint256 receivedTimestampSeconds = receivedTimestampMilliseconds / 1000;
if (block.timestamp < receivedTimestampSeconds) {
if ((receivedTimestampSeconds - block.timestamp) > DEFAULT_MAX_DATA_TIMESTAMP_AHEAD_SECONDS) {
revert TimestampFromTooLongFuture(receivedTimestampSeconds, block.timestamp);
}
} else if ((block.timestamp - receivedTimestampSeconds) > DEFAULT_MAX_DATA_TIMESTAMP_DELAY_SECONDS) {
revert TimestampIsTooOld(receivedTimestampSeconds, block.timestamp);
}
}
function aggregateValues(uint256[] memory values) internal pure returns (uint256) {
return NumericArrayLib.pickMedian(values);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.4;
import "../core/RedstoneConsumerNumericBase.sol";
contract AvalancheDataServiceConsumerBase is RedstoneConsumerNumericBase {
function getUniqueSignersThreshold() public view virtual override returns (uint8) {
return 3;
}
function getAuthorisedSignerIndex(
address signerAddress
) public view virtual override returns (uint8) {
if (signerAddress == 0x1eA62d73EdF8AC05DfceA1A34b9796E937a29EfF) {
return 0;
} else if (signerAddress == 0x2c59617248994D12816EE1Fa77CE0a64eEB456BF) {
return 1;
} else if (signerAddress == 0x12470f7aBA85c8b81D63137DD5925D6EE114952b) {
return 2;
} else if (signerAddress == 0x109B4a318A4F5ddcbCA6349B45f881B4137deaFB) {
return 3;
} else if (signerAddress == 0x83cbA8c619fb629b81A65C2e67fE15cf3E3C9747) {
return 4;
} else {
revert SignerNotAuthorised(signerAddress);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
library BitmapLib {
function setBitInBitmap(uint256 bitmap, uint256 bitIndex) internal pure returns (uint256) {
return bitmap | (1 << bitIndex);
}
function getBitFromBitmap(uint256 bitmap, uint256 bitIndex) internal pure returns (bool) {
uint256 bitAtIndex = bitmap & (1 << bitIndex);
return bitAtIndex > 0;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
library NumericArrayLib {
// This function sort array in memory using bubble sort algorithm,
// which performs even better than quick sort for small arrays
uint256 constant BYTES_ARR_LEN_VAR_BS = 32;
uint256 constant UINT256_VALUE_BS = 32;
error CanNotPickMedianOfEmptyArray();
// This function modifies the array
function pickMedian(uint256[] memory arr) internal pure returns (uint256) {
if (arr.length == 0) {
revert CanNotPickMedianOfEmptyArray();
}
sort(arr);
uint256 middleIndex = arr.length / 2;
if (arr.length % 2 == 0) {
uint256 sum = SafeMath.add(arr[middleIndex - 1], arr[middleIndex]);
return sum / 2;
} else {
return arr[middleIndex];
}
}
function sort(uint256[] memory arr) internal pure {
assembly {
let arrLength := mload(arr)
let valuesPtr := add(arr, BYTES_ARR_LEN_VAR_BS)
let endPtr := add(valuesPtr, mul(arrLength, UINT256_VALUE_BS))
for {
let arrIPtr := valuesPtr
} lt(arrIPtr, endPtr) {
arrIPtr := add(arrIPtr, UINT256_VALUE_BS) // arrIPtr += 32
} {
for {
let arrJPtr := valuesPtr
} lt(arrJPtr, arrIPtr) {
arrJPtr := add(arrJPtr, UINT256_VALUE_BS) // arrJPtr += 32
} {
let arrI := mload(arrIPtr)
let arrJ := mload(arrJPtr)
if lt(arrI, arrJ) {
mstore(arrIPtr, arrJ)
mstore(arrJPtr, arrI)
}
}
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
library SignatureLib {
uint256 constant ECDSA_SIG_R_BS = 32;
uint256 constant ECDSA_SIG_S_BS = 32;
function recoverSignerAddress(bytes32 signedHash, uint256 signatureCalldataNegativeOffset)
internal
pure
returns (address)
{
bytes32 r;
bytes32 s;
uint8 v;
assembly {
let signatureCalldataStartPos := sub(calldatasize(), signatureCalldataNegativeOffset)
r := calldataload(signatureCalldataStartPos)
signatureCalldataStartPos := add(signatureCalldataStartPos, ECDSA_SIG_R_BS)
s := calldataload(signatureCalldataStartPos)
signatureCalldataStartPos := add(signatureCalldataStartPos, ECDSA_SIG_S_BS)
v := byte(0, calldataload(signatureCalldataStartPos)) // last byte of the signature memory array
}
return ecrecover(signedHash, v, r, s);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.6.0;
// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
function safeApprove(
address token,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('approve(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::safeApprove: approve failed'
);
}
function safeTransfer(
address token,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::safeTransfer: transfer failed'
);
}
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::transferFrom: transferFrom failed'
);
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
}
}// SPDX-License-Identifier: BUSL-1.1
// Last deployed from commit: 97d6cc3cb60bfd6feda4ea784b13bf0e7daac710;
pragma solidity 0.8.17;
import "./interfaces/IDiamondBeacon.sol";
//This path is updated during deployment
import "./lib/arbitrum/DeploymentConstants.sol";
/**
* DiamondHelper
* Helper methods
**/
contract DiamondHelper {
function _getFacetAddress(bytes4 methodSelector) internal view returns (address solvencyFacetAddress) {
solvencyFacetAddress = IDiamondBeacon(payable(DeploymentConstants.getDiamondAddress())).implementation(methodSelector);
}
}// SPDX-License-Identifier: BUSL-1.1
// Last deployed from commit: ;
pragma solidity 0.8.17;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "../interfaces/ITokenManager.sol";
import "../interfaces/IStakingPositions.sol";
//This path is updated during deployment
import "../lib/arbitrum/DeploymentConstants.sol";
contract AssetsExposureController {
function resetPrimeAccountAssetsExposure() external {
bytes32[] memory ownedAssets = DeploymentConstants.getAllOwnedAssets();
IStakingPositions.StakedPosition[] storage positions = DiamondStorageLib.stakedPositions();
ITokenManager tokenManager = DeploymentConstants.getTokenManager();
for(uint i=0; i<ownedAssets.length; i++){
IERC20Metadata token = IERC20Metadata(tokenManager.getAssetAddress(ownedAssets[i], true));
tokenManager.decreaseProtocolExposure(ownedAssets[i], token.balanceOf(address(this)) * 1e18 / 10**token.decimals());
}
for(uint i=0; i<positions.length; i++){
(bool success, bytes memory result) = address(this).staticcall(abi.encodeWithSelector(positions[i].balanceSelector));
if (success) {
uint256 balance = abi.decode(result, (uint256));
uint256 decimals = IERC20Metadata(tokenManager.getAssetAddress(positions[i].symbol, true)).decimals();
tokenManager.decreaseProtocolExposure(positions[i].identifier, balance * 1e18 / 10**decimals);
}
}
}
function setPrimeAccountAssetsExposure() external {
bytes32[] memory ownedAssets = DeploymentConstants.getAllOwnedAssets();
IStakingPositions.StakedPosition[] storage positions = DiamondStorageLib.stakedPositions();
ITokenManager tokenManager = DeploymentConstants.getTokenManager();
for(uint i=0; i<ownedAssets.length; i++){
IERC20Metadata token = IERC20Metadata(tokenManager.getAssetAddress(ownedAssets[i], true));
tokenManager.increaseProtocolExposure(ownedAssets[i], token.balanceOf(address(this)) * 1e18 / 10**token.decimals());
}
for(uint i=0; i<positions.length; i++){
(bool success, bytes memory result) = address(this).staticcall(abi.encodeWithSelector(positions[i].balanceSelector));
if (success) {
uint256 balance = abi.decode(result, (uint256));
uint256 decimals = IERC20Metadata(tokenManager.getAssetAddress(positions[i].symbol, true)).decimals();
tokenManager.increaseProtocolExposure(positions[i].identifier, balance * 1e18 / 10**decimals);
}
}
}
}// SPDX-License-Identifier: BUSL-1.1
// Last deployed from commit: e9e05b6e564514c1bcd1b5e49f5e45250e72bf98;
pragma solidity 0.8.17;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../../interfaces/facets/IYieldYakRouter.sol";
import "../../ReentrancyGuardKeccak.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import {DiamondStorageLib} from "../../lib/DiamondStorageLib.sol";
import "../../lib/SolvencyMethods.sol";
import "../../interfaces/ITokenManager.sol";
//This path is updated during deployment
import "../../lib/arbitrum/DeploymentConstants.sol";
contract YieldYakSwapFacet is ReentrancyGuardKeccak, SolvencyMethods {
using TransferHelper for address;
struct SwapTokensDetails {
bytes32 tokenSoldSymbol;
bytes32 tokenBoughtSymbol;
IERC20Metadata soldToken;
IERC20Metadata boughtToken;
uint256 initialSoldTokenBalance;
uint256 initialBoughtTokenBalance;
}
function getInitialTokensDetails(address _soldTokenAddress, address _boughtTokenAddress) internal returns (SwapTokensDetails memory){
ITokenManager tokenManager = DeploymentConstants.getTokenManager();
if (_boughtTokenAddress == 0xaE64d55a6f09E4263421737397D1fdFA71896a69) {
_boughtTokenAddress = 0x9e295B5B976a184B14aD8cd72413aD846C299660;
}
if (_soldTokenAddress == 0x9e295B5B976a184B14aD8cd72413aD846C299660) {
_soldTokenAddress = 0xaE64d55a6f09E4263421737397D1fdFA71896a69;
}
bytes32 _tokenSoldSymbol = tokenManager.tokenAddressToSymbol(_soldTokenAddress);
bytes32 _tokenBoughtSymbol = tokenManager.tokenAddressToSymbol(_boughtTokenAddress);
require(tokenManager.isTokenAssetActive(_boughtTokenAddress), "Asset not supported.");
IERC20Metadata _soldToken = IERC20Metadata(_soldTokenAddress);
IERC20Metadata _boughtToken = IERC20Metadata(_boughtTokenAddress);
return SwapTokensDetails({
tokenSoldSymbol: _tokenSoldSymbol,
tokenBoughtSymbol: _tokenBoughtSymbol,
soldToken: _soldToken,
boughtToken: _boughtToken,
initialSoldTokenBalance: _soldToken.balanceOf(address(this)),
initialBoughtTokenBalance: _boughtToken.balanceOf(address(this))
});
}
function yakSwap(uint256 _amountIn, uint256 _amountOut, address[] calldata _path, address[] calldata _adapters) external nonReentrant onlyOwner noBorrowInTheSameBlock recalculateAssetsExposure remainsSolvent{
SwapTokensDetails memory swapTokensDetails = getInitialTokensDetails(_path[0], _path[_path.length - 1]);
_amountIn = Math.min(swapTokensDetails.soldToken.balanceOf(address(this)), _amountIn);
require(_amountIn > 0, "Amount of tokens to sell has to be greater than 0");
address(swapTokensDetails.soldToken).safeApprove(YY_ROUTER(), 0);
address(swapTokensDetails.soldToken).safeApprove(YY_ROUTER(), _amountIn);
IYieldYakRouter router = IYieldYakRouter(YY_ROUTER());
IYieldYakRouter.Trade memory trade = IYieldYakRouter.Trade({
amountIn: _amountIn,
amountOut: _amountOut,
path: _path,
adapters: _adapters
});
router.swapNoSplit(trade, address(this), 0);
// Add asset to ownedAssets
if (swapTokensDetails.boughtToken.balanceOf(address(this)) > 0) {
DiamondStorageLib.addOwnedAsset(swapTokensDetails.tokenBoughtSymbol, address(swapTokensDetails.boughtToken));
}
// Remove asset from ownedAssets if the asset balance is 0 after the swap
if (swapTokensDetails.soldToken.balanceOf(address(this)) == 0) {
DiamondStorageLib.removeOwnedAsset(swapTokensDetails.tokenSoldSymbol);
}
uint256 boughtTokenFinalAmount = swapTokensDetails.boughtToken.balanceOf(address(this)) - swapTokensDetails.initialBoughtTokenBalance;
require(boughtTokenFinalAmount >= _amountOut, "Insufficient output amount");
emit Swap(
msg.sender,
swapTokensDetails.tokenSoldSymbol,
swapTokensDetails.tokenBoughtSymbol,
swapTokensDetails.initialSoldTokenBalance - swapTokensDetails.soldToken.balanceOf(address(this)),
boughtTokenFinalAmount,
block.timestamp
);
}
function YY_ROUTER() internal virtual pure returns (address) {
return 0xC4729E56b831d74bBc18797e0e17A295fA77488c;
}
modifier onlyOwner() {
DiamondStorageLib.enforceIsContractOwner();
_;
}
/**
* @dev emitted after a swap of assets
* @param user the address of user making the purchase
* @param soldAsset sold by the user
* @param boughtAsset bought by the user
* @param amountSold amount of tokens sold
* @param amountBought amount of tokens bought
* @param timestamp time of the swap
**/
event Swap(address indexed user, bytes32 indexed soldAsset, bytes32 indexed boughtAsset, uint256 amountSold, uint256 amountBought, uint256 timestamp);
}// SPDX-License-Identifier: BUSL-1.1
// Last deployed from commit: ;
pragma solidity 0.8.17;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@redstone-finance/evm-connector/contracts/data-services/AvalancheDataServiceConsumerBase.sol";
import "../interfaces/ITokenManager.sol";
import "../Pool.sol";
import "../DiamondHelper.sol";
import "../interfaces/IStakingPositions.sol";
import "../interfaces/facets/avalanche/ITraderJoeV2Facet.sol";
import "../interfaces/uniswap-v3-periphery/INonfungiblePositionManager.sol";
import "../lib/uniswap-v3/UniswapV3IntegrationHelper.sol";
import {PriceHelper} from "../lib/joe-v2/PriceHelper.sol";
import {Uint256x256Math} from "../lib/joe-v2/math/Uint256x256Math.sol";
import {TickMath} from "../lib/uniswap-v3/TickMath.sol";
import {FullMath} from "../lib/uniswap-v3/FullMath.sol";
//This path is updated during deployment
import "../lib/arbitrum/DeploymentConstants.sol";
//TODO: that probably can be removed later
import "../interfaces/facets/avalanche/IUniswapV3Facet.sol";
contract SolvencyFacetProd is AvalancheDataServiceConsumerBase, DiamondHelper {
using PriceHelper for uint256;
using Uint256x256Math for uint256;
struct AssetPrice {
bytes32 asset;
uint256 price;
}
// Struct used in the liquidation process to obtain necessary prices only once
struct CachedPrices {
AssetPrice[] ownedAssetsPrices;
AssetPrice[] debtAssetsPrices;
AssetPrice[] stakedPositionsPrices;
AssetPrice[] assetsToRepayPrices;
}
/**
* Checks if the loan is solvent.
* It means that the Health Ratio is greater than 1e18.
* @dev This function uses the redstone-evm-connector
**/
function isSolvent() public view returns (bool) {
return getHealthRatio() >= 1e18;
}
/**
* Checks if the loan is solvent.
* It means that the Health Ratio is greater than 1e18.
* Uses provided AssetPrice struct arrays instead of extracting the pricing data from the calldata again.
* @param cachedPrices Struct containing arrays of Asset/Price structs used to calculate value of owned assets, debt and staked positions
**/
function isSolventWithPrices(CachedPrices memory cachedPrices) public view returns (bool) {
return getHealthRatioWithPrices(cachedPrices) >= 1e18;
}
/**
* Returns an array of Asset/Price structs of staked positions.
* @dev This function uses the redstone-evm-connector
**/
function getStakedPositionsPrices() public view returns(AssetPrice[] memory result) {
IStakingPositions.StakedPosition[] storage positions = DiamondStorageLib.stakedPositions();
bytes32[] memory symbols = new bytes32[](positions.length);
for(uint256 i=0; i<positions.length; i++) {
symbols[i] = positions[i].symbol;
}
uint256[] memory stakedPositionsPrices = getOracleNumericValuesWithDuplicatesFromTxMsg(symbols);
result = new AssetPrice[](stakedPositionsPrices.length);
for(uint i; i<stakedPositionsPrices.length; i++){
result[i] = AssetPrice({
asset: symbols[i],
price: stakedPositionsPrices[i]
});
}
}
/**
* Returns an array of bytes32[] symbols of debt (borrowable) assets.
**/
function getDebtAssets() public view returns(bytes32[] memory result) {
ITokenManager tokenManager = DeploymentConstants.getTokenManager();
result = tokenManager.getAllPoolAssets();
}
/**
* Returns an array of Asset/Price structs of debt (borrowable) assets.
* @dev This function uses the redstone-evm-connector
**/
function getDebtAssetsPrices() public view returns(AssetPrice[] memory result) {
bytes32[] memory debtAssets = getDebtAssets();
uint256[] memory debtAssetsPrices = getOracleNumericValuesFromTxMsg(debtAssets);
result = new AssetPrice[](debtAssetsPrices.length);
for(uint i; i<debtAssetsPrices.length; i++){
result[i] = AssetPrice({
asset: debtAssets[i],
price: debtAssetsPrices[i]
});
}
}
/**
* Returns an array of Asset/Price structs of enriched (always containing AVAX at index 0) owned assets.
* @dev This function uses the redstone-evm-connector
**/
function getOwnedAssetsWithNativePrices() public view returns(AssetPrice[] memory result) {
bytes32[] memory assetsEnriched = getOwnedAssetsWithNative();
uint256[] memory prices = getOracleNumericValuesFromTxMsg(assetsEnriched);
result = new AssetPrice[](assetsEnriched.length);
for(uint i; i<assetsEnriched.length; i++){
result[i] = AssetPrice({
asset: assetsEnriched[i],
price: prices[i]
});
}
}
/**
* Returns an array of bytes32[] symbols of staked positions.
**/
function getStakedAssets() internal view returns (bytes32[] memory result) {
IStakingPositions.StakedPosition[] storage positions = DiamondStorageLib.stakedPositions();
result = new bytes32[](positions.length);
for(uint i; i<positions.length; i++) {
result[i] = positions[i].symbol;
}
}
function copyToArray(bytes32[] memory target, bytes32[] memory source, uint256 offset, uint256 numberOfItems) pure internal {
require(numberOfItems <= source.length, "numberOfItems > target array length");
require(offset + numberOfItems <= target.length, "offset + numberOfItems > target array length");
for(uint i; i<numberOfItems; i++){
target[i + offset] = source[i];
}
}
function copyToAssetPriceArray(AssetPrice[] memory target, bytes32[] memory sourceAssets, uint256[] memory sourcePrices, uint256 offset, uint256 numberOfItems) pure internal {
require(numberOfItems <= sourceAssets.length, "numberOfItems > sourceAssets array length");
require(numberOfItems <= sourcePrices.length, "numberOfItems > sourcePrices array length");
require(offset + numberOfItems <= sourceAssets.length, "offset + numberOfItems > sourceAssets array length");
require(offset + numberOfItems <= sourcePrices.length, "offset + numberOfItems > sourcePrices array length");
for(uint i; i<numberOfItems; i++){
target[i] = AssetPrice({
asset: sourceAssets[i+offset],
price: sourcePrices[i+offset]
});
}
}
/**
* Returns CachedPrices struct consisting of Asset/Price arrays for ownedAssets, debtAssets, stakedPositions and assetsToRepay.
* Used during the liquidation process in order to obtain all necessary prices from calldata only once.
* @dev This function uses the redstone-evm-connector
**/
function getAllPricesForLiquidation(bytes32[] memory assetsToRepay) public view returns (CachedPrices memory result) {
bytes32[] memory ownedAssetsEnriched = getOwnedAssetsWithNative();
bytes32[] memory debtAssets = getDebtAssets();
bytes32[] memory stakedAssets = getStakedAssets();
bytes32[] memory allAssetsSymbols = new bytes32[](ownedAssetsEnriched.length + debtAssets.length + stakedAssets.length + assetsToRepay.length);
uint256 offset;
// Populate allAssetsSymbols with owned assets symbols
copyToArray(allAssetsSymbols, ownedAssetsEnriched, offset, ownedAssetsEnriched.length);
offset += ownedAssetsEnriched.length;
// Populate allAssetsSymbols with debt assets symbols
copyToArray(allAssetsSymbols, debtAssets, offset, debtAssets.length);
offset += debtAssets.length;
// Populate allAssetsSymbols with staked assets symbols
copyToArray(allAssetsSymbols, stakedAssets, offset, stakedAssets.length);
offset += stakedAssets.length;
// Populate allAssetsSymbols with assets to repay symbols
copyToArray(allAssetsSymbols, assetsToRepay, offset, assetsToRepay.length);
uint256[] memory allAssetsPrices = getOracleNumericValuesWithDuplicatesFromTxMsg(allAssetsSymbols);
offset = 0;
// Populate ownedAssetsPrices struct
AssetPrice[] memory ownedAssetsPrices = new AssetPrice[](ownedAssetsEnriched.length);
copyToAssetPriceArray(ownedAssetsPrices, allAssetsSymbols, allAssetsPrices, offset, ownedAssetsEnriched.length);
offset += ownedAssetsEnriched.length;
// Populate debtAssetsPrices struct
AssetPrice[] memory debtAssetsPrices = new AssetPrice[](debtAssets.length);
copyToAssetPriceArray(debtAssetsPrices, allAssetsSymbols, allAssetsPrices, offset, debtAssets.length);
offset += debtAssetsPrices.length;
// Populate stakedPositionsPrices struct
AssetPrice[] memory stakedPositionsPrices = new AssetPrice[](stakedAssets.length);
copyToAssetPriceArray(stakedPositionsPrices, allAssetsSymbols, allAssetsPrices, offset, stakedAssets.length);
offset += stakedAssets.length;
// Populate assetsToRepayPrices struct
// Stack too deep :F
AssetPrice[] memory assetsToRepayPrices = new AssetPrice[](assetsToRepay.length);
for(uint i=0; i<assetsToRepay.length; i++){
assetsToRepayPrices[i] = AssetPrice({
asset: allAssetsSymbols[i+offset],
price: allAssetsPrices[i+offset]
});
}
result = CachedPrices({
ownedAssetsPrices: ownedAssetsPrices,
debtAssetsPrices: debtAssetsPrices,
stakedPositionsPrices: stakedPositionsPrices,
assetsToRepayPrices: assetsToRepayPrices
});
}
// Check whether there is enough debt-denominated tokens to fully repaid what was previously borrowed
function canRepayDebtFully() external view returns(bool) {
ITokenManager tokenManager = DeploymentConstants.getTokenManager();
bytes32[] memory poolAssets = tokenManager.getAllPoolAssets();
for(uint i; i< poolAssets.length; i++) {
Pool pool = Pool(DeploymentConstants.getTokenManager().getPoolAddress(poolAssets[i]));
IERC20 token = IERC20(pool.tokenAddress());
if(token.balanceOf(address(this)) < pool.getBorrowed(address(this))) {
return false;
}
}
return true;
}
/**
* Helper method exposing the redstone-evm-connector getOracleNumericValuesFromTxMsg() method.
* @dev This function uses the redstone-evm-connector
**/
function getPrices(bytes32[] memory symbols) external view returns (uint256[] memory) {
return getOracleNumericValuesFromTxMsg(symbols);
}
/**
* Helper method exposing the redstone-evm-connector getOracleNumericValueFromTxMsg() method.
* @dev This function uses the redstone-evm-connector
**/
function getPrice(bytes32 symbol) external view returns (uint256) {
return getOracleNumericValueFromTxMsg(symbol);
}
/**
* Returns TotalWeightedValue of OwnedAssets in USD based on the supplied array of Asset/Price struct, tokenBalance and debtCoverage
**/
function _getTWVOwnedAssets(AssetPrice[] memory ownedAssetsPrices) internal view returns (uint256) {
bytes32 nativeTokenSymbol = DeploymentConstants.getNativeTokenSymbol();
ITokenManager tokenManager = DeploymentConstants.getTokenManager();
uint256 weightedValueOfTokens = ownedAssetsPrices[0].price * address(this).balance * tokenManager.debtCoverage(tokenManager.getAssetAddress(nativeTokenSymbol, true)) / (10 ** 26);
if (ownedAssetsPrices.length > 0) {
for (uint256 i = 0; i < ownedAssetsPrices.length; i++) {
IERC20Metadata token = IERC20Metadata(tokenManager.getAssetAddress(ownedAssetsPrices[i].asset, true));
weightedValueOfTokens = weightedValueOfTokens + (ownedAssetsPrices[i].price * token.balanceOf(address(this)) * tokenManager.debtCoverage(address(token)) / (10 ** token.decimals() * 1e8));
}
}
return weightedValueOfTokens;
}
/**
* Returns TotalWeightedValue of StakedPositions in USD based on the supplied array of Asset/Price struct, positionBalance and debtCoverage
**/
function _getTWVStakedPositions(AssetPrice[] memory stakedPositionsPrices) internal view returns (uint256) {
ITokenManager tokenManager = DeploymentConstants.getTokenManager();
IStakingPositions.StakedPosition[] storage positions = DiamondStorageLib.stakedPositions();
uint256 weightedValueOfStaked;
for (uint256 i; i < positions.length; i++) {
require(stakedPositionsPrices[i].asset == positions[i].symbol, "Position-price symbol mismatch.");
(bool success, bytes memory result) = address(this).staticcall(abi.encodeWithSelector(positions[i].balanceSelector));
if (success) {
uint256 balance = abi.decode(result, (uint256));
IERC20Metadata token = IERC20Metadata(DeploymentConstants.getTokenManager().getAssetAddress(stakedPositionsPrices[i].asset, true));
weightedValueOfStaked += stakedPositionsPrices[i].price * balance * tokenManager.debtCoverageStaked(positions[i].identifier) / (10 ** token.decimals() * 10**8);
}
}
return weightedValueOfStaked;
}
function _getThresholdWeightedValueBase(AssetPrice[] memory ownedAssetsPrices, AssetPrice[] memory stakedPositionsPrices) internal view virtual returns (uint256) {
return _getTWVOwnedAssets(ownedAssetsPrices) + _getTWVStakedPositions(stakedPositionsPrices) + _getTotalTraderJoeV2(true) + _getTotalUniswapV3(true);
}
/**
* Returns the threshold weighted value of assets in USD including all tokens as well as staking and LP positions
* @dev This function uses the redstone-evm-connector
**/
function getThresholdWeightedValue() public view virtual returns (uint256) {
AssetPrice[] memory ownedAssetsPrices = getOwnedAssetsWithNativePrices();
AssetPrice[] memory stakedPositionsPrices = getStakedPositionsPrices();
return _getThresholdWeightedValueBase(ownedAssetsPrices, stakedPositionsPrices);
}
/**
* Returns the threshold weighted value of assets in USD including all tokens as well as staking and LP positions
* Uses provided AssetPrice struct arrays instead of extracting the pricing data from the calldata again.
**/
function getThresholdWeightedValueWithPrices(AssetPrice[] memory ownedAssetsPrices, AssetPrice[] memory stakedPositionsPrices) public view virtual returns (uint256) {
return _getThresholdWeightedValueBase(ownedAssetsPrices, stakedPositionsPrices);
}
/**
* Returns the current debt denominated in USD
* Uses provided AssetPrice struct array instead of extracting the pricing data from the calldata again.
**/
function getDebtBase(AssetPrice[] memory debtAssetsPrices) internal view returns (uint256){
ITokenManager tokenManager = DeploymentConstants.getTokenManager();
uint256 debt;
for (uint256 i; i < debtAssetsPrices.length; i++) {
IERC20Metadata token = IERC20Metadata(tokenManager.getAssetAddress(debtAssetsPrices[i].asset, true));
Pool pool = Pool(tokenManager.getPoolAddress(debtAssetsPrices[i].asset));
//10**18 (wei in eth) / 10**8 (precision of oracle feed) = 10**10
debt = debt + pool.getBorrowed(address(this)) * debtAssetsPrices[i].price * 10 ** 10
/ 10 ** token.decimals();
}
return debt;
}
/**
* Returns the current debt denominated in USD
* @dev This function uses the redstone-evm-connector
**/
function getDebt() public view virtual returns (uint256) {
AssetPrice[] memory debtAssetsPrices = getDebtAssetsPrices();
return getDebtBase(debtAssetsPrices);
}
/**
* Returns the current debt denominated in USD
* Uses provided AssetPrice struct array instead of extracting the pricing data from the calldata again.
**/
function getDebtWithPrices(AssetPrice[] memory debtAssetsPrices) public view virtual returns (uint256) {
return getDebtBase(debtAssetsPrices);
}
/**
* Returns the current value of Prime Account in USD including all tokens as well as staking and LP positions
* Uses provided AssetPrice struct array instead of extracting the pricing data from the calldata again.
**/
function _getTotalAssetsValueBase(AssetPrice[] memory ownedAssetsPrices) public view returns (uint256) {
if (ownedAssetsPrices.length > 0) {
ITokenManager tokenManager = DeploymentConstants.getTokenManager();
uint256 total = address(this).balance * ownedAssetsPrices[0].price / 10 ** 8;
for (uint256 i = 0; i < ownedAssetsPrices.length; i++) {
IERC20Metadata token = IERC20Metadata(tokenManager.getAssetAddress(ownedAssetsPrices[i].asset, true));
uint256 assetBalance = token.balanceOf(address(this));
total = total + (ownedAssetsPrices[i].price * 10 ** 10 * assetBalance / (10 ** token.decimals()));
}
return total;
} else {
return 0;
}
}
/**
* Returns the current value of Prime Account in USD including all tokens as well as staking and LP positions
* @dev This function uses the redstone-evm-connector
**/
function getTotalAssetsValue() public view virtual returns (uint256) {
AssetPrice[] memory ownedAssetsPrices = getOwnedAssetsWithNativePrices();
return _getTotalAssetsValueBase(ownedAssetsPrices);
}
/**
* Returns the current value of Prime Account in USD including all tokens as well as staking and LP positions
* Uses provided AssetPrice struct array instead of extracting the pricing data from the calldata again.
**/
function getTotalAssetsValueWithPrices(AssetPrice[] memory ownedAssetsPrices) public view virtual returns (uint256) {
return _getTotalAssetsValueBase(ownedAssetsPrices);
}
/**
* Returns list of owned assets that always included NativeToken at index 0
**/
function getOwnedAssetsWithNative() public view returns(bytes32[] memory){
bytes32[] memory ownedAssets = DeploymentConstants.getAllOwnedAssets();
bytes32 nativeTokenSymbol = DeploymentConstants.getNativeTokenSymbol();
// If account already owns the native token the use ownedAssets.length; Otherwise add one element to account for additional native token.
uint256 numberOfAssets = DiamondStorageLib.hasAsset(nativeTokenSymbol) ? ownedAssets.length : ownedAssets.length + 1;
bytes32[] memory assetsWithNative = new bytes32[](numberOfAssets);
uint256 lastUsedIndex;
assetsWithNative[0] = nativeTokenSymbol; // First asset = NativeToken
for(uint i=0; i< ownedAssets.length; i++){
if(ownedAssets[i] != nativeTokenSymbol){
lastUsedIndex += 1;
assetsWithNative[lastUsedIndex] = ownedAssets[i];
}
}
return assetsWithNative;
}
/**
* Returns the current value of staked positions in USD.
* Uses provided AssetPrice struct array instead of extracting the pricing data from the calldata again.
**/
function _getStakedValueBase(AssetPrice[] memory stakedPositionsPrices) internal view returns (uint256) {
IStakingPositions.StakedPosition[] storage positions = DiamondStorageLib.stakedPositions();
uint256 usdValue;
for (uint256 i; i < positions.length; i++) {
require(stakedPositionsPrices[i].asset == positions[i].symbol, "Position-price symbol mismatch.");
(bool success, bytes memory result) = address(this).staticcall(abi.encodeWithSelector(positions[i].balanceSelector));
if (success) {
uint256 balance = abi.decode(result, (uint256));
IERC20Metadata token = IERC20Metadata(DeploymentConstants.getTokenManager().getAssetAddress(stakedPositionsPrices[i].asset, true));
usdValue += stakedPositionsPrices[i].price * 10 ** 10 * balance / (10 ** token.decimals());
}
}
return usdValue;
}
/**
**/
function getTotalTraderJoeV2() public view virtual returns (uint256) {
return getTotalTraderJoeV2WithPrices();
}
/**
**/
function _getTotalTraderJoeV2(bool weighted) internal view returns (uint256) {
uint256 total;
ITraderJoeV2Facet.TraderJoeV2Bin[] memory ownedTraderJoeV2Bins = DiamondStorageLib.getTjV2OwnedBinsView();
uint256[] memory prices = new uint256[](2);
if (ownedTraderJoeV2Bins.length > 0) {
for (uint256 i; i < ownedTraderJoeV2Bins.length; i++) {
ITraderJoeV2Facet.TraderJoeV2Bin memory binInfo = ownedTraderJoeV2Bins[i];
uint256 price;
uint256 liquidity;
{
bytes32[] memory symbols = new bytes32[](2);
symbols[0] = DeploymentConstants.getTokenManager().tokenAddressToSymbol(address(binInfo.pair.getTokenX()));
symbols[1] = DeploymentConstants.getTokenManager().tokenAddressToSymbol(address(binInfo.pair.getTokenY()));
prices = getOracleNumericValuesFromTxMsg(symbols);
}
{
(uint128 binReserveX, uint128 binReserveY) = binInfo.pair.getBin(binInfo.id);
price = PriceHelper.convert128x128PriceToDecimal(binInfo.pair.getPriceFromId(binInfo.id)); // how is it denominated (what precision)?
liquidity = price * binReserveX
/ 10 ** IERC20Metadata(address(binInfo.pair.getTokenX())).decimals()
+ binReserveY;
}
{
uint256 debtCoverageX = weighted ? DeploymentConstants.getTokenManager().debtCoverage(address(binInfo.pair.getTokenX())) : 1e18;
uint256 debtCoverageY = weighted ? DeploymentConstants.getTokenManager().debtCoverage(address(binInfo.pair.getTokenY())) : 1e18;
total = total +
Math.min(
debtCoverageX * liquidity * prices[0] / (price * 10 ** 8),
debtCoverageY * liquidity / 10 ** IERC20Metadata(address(binInfo.pair.getTokenY())).decimals() * prices[1] / 10 ** 8
)
* binInfo.pair.balanceOf(address(this), binInfo.id) / binInfo.pair.totalSupply(binInfo.id);
}
}
return total;
} else {
return 0;
}
}
/**
**/
function getTotalUniswapV3() public view virtual returns (uint256) {
return getTotalUniswapV3WithPrices();
}
/**
**/
function _getTotalUniswapV3(bool weighted) internal view returns (uint256) {
uint256 total;
uint256[] memory ownedUniswapV3TokenIds = DiamondStorageLib.getUV3OwnedTokenIdsView();
if (ownedUniswapV3TokenIds.length > 0) {
for (uint256 i; i < ownedUniswapV3TokenIds.length; i++) {
IUniswapV3Facet.UniswapV3Position memory position = getUniswapV3Position(INonfungiblePositionManager(0x655C406EBFa14EE2006250925e54ec43AD184f8B), ownedUniswapV3TokenIds[0]);
uint256[] memory prices = new uint256[](2);
{
bytes32[] memory symbols = new bytes32[](2);
symbols[0] = DeploymentConstants.getTokenManager().tokenAddressToSymbol(position.token0);
symbols[1] = DeploymentConstants.getTokenManager().tokenAddressToSymbol(position.token1);
prices = getOracleNumericValuesFromTxMsg(symbols);
}
//TODO: check if this approach is not too harsh for Market Maker (Prime Account)
{
uint256 debtCoverage0 = weighted ? DeploymentConstants.getTokenManager().debtCoverage(position.token0) : 1e18;
uint256 debtCoverage1 = weighted ? DeploymentConstants.getTokenManager().debtCoverage(position.token1) : 1e18;
uint160 sqrtPriceX96_a = TickMath.getSqrtRatioAtTick(position.tickLower);
uint160 sqrtPriceX96_b = TickMath.getSqrtRatioAtTick(position.tickUpper);
uint256 sqrtPrice_a = UniswapV3IntegrationHelper.sqrtPriceX96ToUint(sqrtPriceX96_a, IERC20Metadata(position.token0).decimals());
uint256 sqrtPrice_b = UniswapV3IntegrationHelper.sqrtPriceX96ToUint(sqrtPriceX96_b, IERC20Metadata(position.token0).decimals());
total = total +
//TODO: there is an assumption here that the position value is the lowest at edges (x = 0 or y = 0). Need to confirm that!
//TODO: tickerUpper = p_b, tickerLower = p_a, first check if that's correct, secondly check what's the denomination and accuracy of these numbers
//TODO: check for possible under/overflowsL557
Math.min(
debtCoverage0 * position.liquidity / 1e18 * (1e36 / sqrtPrice_a - 1e36 / sqrtPrice_b) / 10 ** IERC20Metadata(position.token0).decimals() * prices[0] / 10 ** 8,
debtCoverage1 * position.liquidity / 1e18 * (sqrtPrice_b - sqrtPrice_a) / 10 ** IERC20Metadata(position.token1).decimals() * prices[1] / 10 ** 8
);
}
}
return total;
} else {
return 0;
}
}
function getUniswapV3Position(
INonfungiblePositionManager positionManager,
uint256 tokenId) internal view returns (IUniswapV3Facet.UniswapV3Position memory position) {
(, , address token0, address token1, , int24 tickLower, int24 tickUpper, uint128 liquidity) = positionManager.positions(tokenId);
position = IUniswapV3Facet.UniswapV3Position(token0, token1, tickLower, tickUpper, liquidity);
}
/**
* Returns the current value of staked positions in USD.
* Uses provided AssetPrice struct array instead of extracting the pricing data from the calldata again.
**/
function getStakedValueWithPrices(AssetPrice[] memory stakedPositionsPrices) public view returns (uint256) {
return _getStakedValueBase(stakedPositionsPrices);
}
/**
* Returns the current value of Liquidity Book positions in USD.
* Uses provided AssetPrice struct array instead of extracting the pricing data from the calldata again.
**/
function getTotalTraderJoeV2WithPrices() public view returns (uint256) {
return _getTotalTraderJoeV2(false);
}
/**
* Returns the current value of Uniswap V3 positions in USD.
* Uses provided AssetPrice struct array instead of extracting the pricing data from the calldata again.
**/
function getTotalUniswapV3WithPrices() public view returns (uint256) {
return _getTotalUniswapV3(false);
}
/**
* Returns the current value of staked positions in USD.
* @dev This function uses the redstone-evm-connector
**/
function getStakedValue() public view virtual returns (uint256) {
AssetPrice[] memory stakedPositionsPrices = getStakedPositionsPrices();
return _getStakedValueBase(stakedPositionsPrices);
}
/**
* Returns the current value of Prime Account in USD including all tokens as well as staking and LP positions
* @dev This function uses the redstone-evm-connector
**/
function getTotalValue() public view virtual returns (uint256) {
return getTotalAssetsValue() + getStakedValue() + getTotalTraderJoeV2() + getTotalUniswapV3();
}
/**
* Returns the current value of Prime Account in USD including all tokens as well as staking and LP positions
* Uses provided AssetPrice struct arrays instead of extracting the pricing data from the calldata again.
**/
function getTotalValueWithPrices(AssetPrice[] memory ownedAssetsPrices, AssetPrice[] memory assetsPrices, AssetPrice[] memory stakedPositionsPrices) public view virtual returns (uint256) {
return getTotalAssetsValueWithPrices(ownedAssetsPrices) + getStakedValueWithPrices(stakedPositionsPrices) + getTotalTraderJoeV2WithPrices() + getTotalUniswapV3WithPrices();
}
function getFullLoanStatus() public view returns (uint256[5] memory) {
return [getTotalValue(), getDebt(), getThresholdWeightedValue(), getHealthRatio(), isSolvent() ? uint256(1) : uint256(0)];
}
/**
* Returns current health ratio (solvency) associated with the loan, defined as threshold weighted value of divided
* by current debt
* @dev This function uses the redstone-evm-connector
**/
function getHealthRatio() public view virtual returns (uint256) {
CachedPrices memory cachedPrices = getAllPricesForLiquidation(new bytes32[](0));
uint256 debt = getDebtWithPrices(cachedPrices.debtAssetsPrices);
uint256 thresholdWeightedValue = getThresholdWeightedValueWithPrices(cachedPrices.ownedAssetsPrices, cachedPrices.stakedPositionsPrices);
if (debt == 0) {
return type(uint256).max;
} else {
return thresholdWeightedValue * 1e18 / debt;
}
}
/**
* Returns current health ratio (solvency) associated with the loan, defined as threshold weighted value of divided
* by current debt
* Uses provided AssetPrice struct arrays instead of extracting the pricing data from the calldata again.
**/
function getHealthRatioWithPrices(CachedPrices memory cachedPrices) public view virtual returns (uint256) {
uint256 debt = getDebtWithPrices(cachedPrices.debtAssetsPrices);
uint256 thresholdWeightedValue = getThresholdWeightedValueWithPrices(cachedPrices.ownedAssetsPrices, cachedPrices.stakedPositionsPrices);
if (debt == 0) {
return type(uint256).max;
} else {
return thresholdWeightedValue * 1e18 / debt;
}
}
}pragma solidity ^0.8.17;
import "../../joe-v2/ILBRouter.sol";
interface ITraderJoeV2Facet {
struct TraderJoeV2Bin {
ILBPair pair;
uint24 id;
}
struct RemoveLiquidityParameters {
IERC20 tokenX;
IERC20 tokenY;
uint16 binStep;
uint256 amountXMin;
uint256 amountYMin;
uint256[] ids;
uint256[] amounts;
uint256 deadline;
}
function addLiquidityTraderJoeV2(ILBRouter.LiquidityParameters memory liquidityParameters) external;
function removeLiquidityTraderJoeV2(RemoveLiquidityParameters memory parameters) external;
function getOwnedTraderJoeV2Bins() external view returns (TraderJoeV2Bin[] memory result);
}pragma solidity ^0.8.17;
import "../../joe-v2/ILBRouter.sol";
import "../../uniswap-v3/IUniswapV3Pool.sol";
import "../../uniswap-v3-periphery/INonfungiblePositionManager.sol";
interface IUniswapV3Facet {
struct UniswapV3Position {
address token0;
address token1;
int24 tickLower;
int24 tickUpper;
uint128 liquidity;
}
function mintLiquidityUniswapV3(INonfungiblePositionManager.MintParams calldata params) external;
function increaseLiquidityUniswapV3(INonfungiblePositionManager.IncreaseLiquidityParams calldata params) external;
function decreaseLiquidityUniswapV3(INonfungiblePositionManager.DecreaseLiquidityParams calldata params) external;
function burnLiquidityUniswapV3(uint256 tokenId) external;
function getOwnedUniswapV3TokenIds() external view returns (uint256[] memory result);
}// SPDX-License-Identifier: BUSL-1.1
// Last deployed from commit: ;
pragma solidity ^0.8.17;
interface IYieldYakRouter {
struct Trade {
uint256 amountIn;
uint256 amountOut;
address[] path;
address[] adapters;
}
struct FormattedOffer {
uint256[] amounts;
address[] adapters;
address[] path;
}
function swapNoSplit(
Trade calldata _trade,
address _to,
uint256 _fee
) external;
}// SPDX-License-Identifier: BUSL-1.1
// Last deployed from commit: ;
pragma solidity 0.8.17;
/**
* @title IBorrowersRegistry
* Keeps a registry of created trading accounts to verify their borrowing rights
*/
interface IBorrowersRegistry {
function canBorrow(address _account) external view returns (bool);
function getLoanForOwner(address _owner) external view returns (address);
function getOwnerOfLoan(address _loan) external view returns (address);
}// SPDX-License-Identifier: BUSL-1.1
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
pragma solidity 0.8.17;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IDiamondBeacon {
function implementation() external view returns (address);
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {SmartLoanDiamondProxy} will check that this address is a contract.
*/
function implementation(bytes4) external view returns (address);
function getStatus() external view returns (bool);
function proposeBeaconOwnershipTransfer(address _newOwner) external;
function acceptBeaconOwnership() external;
}// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /******************************************************************************\ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) * EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535 /******************************************************************************/ interface IDiamondCut { enum FacetCutAction {Add, Replace, Remove} // Add=0, Replace=1, Remove=2 struct FacetCut { address facetAddress; FacetCutAction action; bytes4[] functionSelectors; } /// @notice Add/replace/remove any number of functions and optionally execute /// a function with delegatecall /// @param _diamondCut Contains the facet addresses and function selectors /// @param _init The address of the contract or facet to execute _calldata /// @param _calldata A function call, including function selector and arguments /// _calldata is executed with delegatecall on _init function diamondCut( FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata ) external; function pause() external; function unpause() external; event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); }
// SPDX-License-Identifier: BUSL-1.1
// Last deployed from commit: c5c938a0524b45376dd482cd5c8fb83fa94c2fcc;
pragma solidity 0.8.17;
interface IIndex {
function setRate(uint256 _rate) external;
function updateUser(address user) external;
function getIndex() external view returns (uint256);
function getIndexedValue(uint256 value, address user) external view returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
// Last deployed from commit: ;
pragma solidity ^0.8.17;
interface IPoolRewarder {
function stakeFor(uint _amount, address _stakeFor) external;
function withdrawFor(uint _amount, address _unstakeFor) external returns (uint);
function getRewardsFor(address _user) external;
function earned(address _account) external view returns (uint);
function balanceOf(address _account) external view returns (uint);
}// SPDX-License-Identifier: BUSL-1.1
// Last deployed from commit: ;
pragma solidity 0.8.17;
/**
* @title IRatesCalculator
* @dev Interface defining base method for contracts implementing interest rates calculation.
* The calculated value could be based on the relation between funds borrowed and deposited.
*/
interface IRatesCalculator {
function calculateBorrowingRate(uint256 totalLoans, uint256 totalDeposits) external view returns (uint256);
function calculateDepositRate(uint256 totalLoans, uint256 totalDeposits) external view returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
// Last deployed from commit: ;
pragma solidity 0.8.17;
/**
* @title IStakingPositions
* Types for staking
*/
interface IStakingPositions {
struct StakedPosition {
// Asset is either the token (symbol) address being staked or the address of the PTP LP token in case where a pool for that token (symbol) already exists within the VectorFinance
address asset;
bytes32 symbol;
bytes32 identifier;
bytes4 balanceSelector;
bytes4 unstakeSelector;
}
}interface ITokenManager {
struct poolAsset {
bytes32 asset;
address poolAddress;
}
struct Asset {
bytes32 asset;
address assetAddress;
uint256 debtCoverage;
}
function activateToken ( address token ) external;
function addPoolAssets ( poolAsset[] memory poolAssets ) external;
function addTokenAssets ( Asset[] memory tokenAssets ) external;
function deactivateToken ( address token ) external;
function debtCoverage ( address ) external view returns ( uint256 );
function debtCoverageStaked ( bytes32 ) external view returns ( uint256 );
function getAllPoolAssets ( ) external view returns ( bytes32[] memory result );
function getAllTokenAssets ( ) external view returns ( bytes32[] memory result );
function getAssetAddress ( bytes32 _asset, bool allowInactive ) external view returns ( address );
function getPoolAddress ( bytes32 _asset ) external view returns ( address );
function getSupportedTokensAddresses ( ) external view returns ( address[] memory);
function initialize ( Asset[] memory tokenAssets, poolAsset[] memory poolAssets ) external;
function increaseProtocolExposure ( bytes32 assetIdentifier, uint256 exposureIncrease ) external;
function decreaseProtocolExposure(bytes32 assetIdentifier, uint256 exposureDecrease) external;
function isTokenAssetActive ( address token ) external view returns ( bool );
function owner ( ) external view returns ( address );
function removePoolAssets ( bytes32[] memory _poolAssets ) external;
function removeTokenAssets ( bytes32[] memory _tokenAssets ) external;
function renounceOwnership ( ) external;
function setDebtCoverage ( address token, uint256 coverage ) external;
function setDebtCoverageStaked ( bytes32 stakedAsset, uint256 coverage ) external;
function supportedTokensList ( uint256 ) external view returns ( address );
function tokenAddressToSymbol ( address ) external view returns ( bytes32 );
function tokenToStatus ( address ) external view returns ( uint256 );
function transferOwnership ( address newOwner ) external;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.10;
/// @title Joe V1 Factory Interface
/// @notice Interface to interact with Joe V1 Factory
interface IJoeFactory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint256);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function migrator() external view returns (address);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint256) external view returns (address pair);
function allPairsLength() external view returns (uint256);
function createPair(address tokenA, address tokenB) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
function setMigrator(address) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ILBPair} from "./ILBPair.sol";
import {IPendingOwnable} from "./IPendingOwnable.sol";
/**
* @title Liquidity Book Factory Interface
* @author Trader Joe
* @notice Required interface of LBFactory contract
*/
interface ILBFactory is IPendingOwnable {
error LBFactory__IdenticalAddresses(IERC20 token);
error LBFactory__QuoteAssetNotWhitelisted(IERC20 quoteAsset);
error LBFactory__QuoteAssetAlreadyWhitelisted(IERC20 quoteAsset);
error LBFactory__AddressZero();
error LBFactory__LBPairAlreadyExists(IERC20 tokenX, IERC20 tokenY, uint256 _binStep);
error LBFactory__LBPairDoesNotExist(IERC20 tokenX, IERC20 tokenY, uint256 binStep);
error LBFactory__LBPairNotCreated(IERC20 tokenX, IERC20 tokenY, uint256 binStep);
error LBFactory__FlashLoanFeeAboveMax(uint256 fees, uint256 maxFees);
error LBFactory__BinStepTooLow(uint256 binStep);
error LBFactory__PresetIsLockedForUsers(address user, uint256 binStep);
error LBFactory__LBPairIgnoredIsAlreadyInTheSameState();
error LBFactory__BinStepHasNoPreset(uint256 binStep);
error LBFactory__PresetOpenStateIsAlreadyInTheSameState();
error LBFactory__SameFeeRecipient(address feeRecipient);
error LBFactory__SameFlashLoanFee(uint256 flashLoanFee);
error LBFactory__LBPairSafetyCheckFailed(address LBPairImplementation);
error LBFactory__SameImplementation(address LBPairImplementation);
error LBFactory__ImplementationNotSet();
/**
* @dev Structure to store the LBPair information, such as:
* binStep: The bin step of the LBPair
* LBPair: The address of the LBPair
* createdByOwner: Whether the pair was created by the owner of the factory
* ignoredForRouting: Whether the pair is ignored for routing or not. An ignored pair will not be explored during routes finding
*/
struct LBPairInformation {
uint16 binStep;
ILBPair LBPair;
bool createdByOwner;
bool ignoredForRouting;
}
event LBPairCreated(
IERC20 indexed tokenX, IERC20 indexed tokenY, uint256 indexed binStep, ILBPair LBPair, uint256 pid
);
event FeeRecipientSet(address oldRecipient, address newRecipient);
event FlashLoanFeeSet(uint256 oldFlashLoanFee, uint256 newFlashLoanFee);
event LBPairImplementationSet(address oldLBPairImplementation, address LBPairImplementation);
event LBPairIgnoredStateChanged(ILBPair indexed LBPair, bool ignored);
event PresetSet(
uint256 indexed binStep,
uint256 baseFactor,
uint256 filterPeriod,
uint256 decayPeriod,
uint256 reductionFactor,
uint256 variableFeeControl,
uint256 protocolShare,
uint256 maxVolatilityAccumulator
);
event PresetOpenStateChanged(uint256 indexed binStep, bool indexed isOpen);
event PresetRemoved(uint256 indexed binStep);
event QuoteAssetAdded(IERC20 indexed quoteAsset);
event QuoteAssetRemoved(IERC20 indexed quoteAsset);
function getMinBinStep() external pure returns (uint256);
function getFeeRecipient() external view returns (address);
function getMaxFlashLoanFee() external pure returns (uint256);
function getFlashLoanFee() external view returns (uint256);
function getLBPairImplementation() external view returns (address);
function getNumberOfLBPairs() external view returns (uint256);
function getLBPairAtIndex(uint256 id) external returns (ILBPair);
function getNumberOfQuoteAssets() external view returns (uint256);
function getQuoteAssetAtIndex(uint256 index) external view returns (IERC20);
function isQuoteAsset(IERC20 token) external view returns (bool);
function getLBPairInformation(IERC20 tokenX, IERC20 tokenY, uint256 binStep)
external
view
returns (LBPairInformation memory);
function getPreset(uint256 binStep)
external
view
returns (
uint256 baseFactor,
uint256 filterPeriod,
uint256 decayPeriod,
uint256 reductionFactor,
uint256 variableFeeControl,
uint256 protocolShare,
uint256 maxAccumulator,
bool isOpen
);
function getAllBinSteps() external view returns (uint256[] memory presetsBinStep);
function getOpenBinSteps() external view returns (uint256[] memory openBinStep);
function getAllLBPairs(IERC20 tokenX, IERC20 tokenY)
external
view
returns (LBPairInformation[] memory LBPairsBinStep);
function setLBPairImplementation(address lbPairImplementation) external;
function createLBPair(IERC20 tokenX, IERC20 tokenY, uint24 activeId, uint16 binStep)
external
returns (ILBPair pair);
function setLBPairIgnored(IERC20 tokenX, IERC20 tokenY, uint16 binStep, bool ignored) external;
function setPreset(
uint16 binStep,
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator,
bool isOpen
) external;
function setPresetOpenState(uint16 binStep, bool isOpen) external;
function removePreset(uint16 binStep) external;
function setFeesParametersOnPair(
IERC20 tokenX,
IERC20 tokenY,
uint16 binStep,
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator
) external;
function setFeeRecipient(address feeRecipient) external;
function setFlashLoanFee(uint256 flashLoanFee) external;
function addQuoteAsset(IERC20 quoteAsset) external;
function removeQuoteAsset(IERC20 quoteAsset) external;
function forceDecay(ILBPair lbPair) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/// @title Liquidity Book Flashloan Callback Interface
/// @author Trader Joe
/// @notice Required interface to interact with LB flash loans
interface ILBFlashLoanCallback {
function LBFlashLoanCallback(
address sender,
IERC20 tokenX,
IERC20 tokenY,
bytes32 amounts,
bytes32 totalFees,
bytes calldata data
) external returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ILBLegacyPair} from "./ILBLegacyPair.sol";
import {IPendingOwnable} from "./IPendingOwnable.sol";
/// @title Liquidity Book Factory Interface
/// @author Trader Joe
/// @notice Required interface of LBFactory contract
interface ILBLegacyFactory is IPendingOwnable {
/// @dev Structure to store the LBPair information, such as:
/// - binStep: The bin step of the LBPair
/// - LBPair: The address of the LBPair
/// - createdByOwner: Whether the pair was created by the owner of the factory
/// - ignoredForRouting: Whether the pair is ignored for routing or not. An ignored pair will not be explored during routes finding
struct LBPairInformation {
uint16 binStep;
ILBLegacyPair LBPair;
bool createdByOwner;
bool ignoredForRouting;
}
event LBPairCreated(
IERC20 indexed tokenX, IERC20 indexed tokenY, uint256 indexed binStep, ILBLegacyPair LBPair, uint256 pid
);
event FeeRecipientSet(address oldRecipient, address newRecipient);
event FlashLoanFeeSet(uint256 oldFlashLoanFee, uint256 newFlashLoanFee);
event FeeParametersSet(
address indexed sender,
ILBLegacyPair indexed LBPair,
uint256 binStep,
uint256 baseFactor,
uint256 filterPeriod,
uint256 decayPeriod,
uint256 reductionFactor,
uint256 variableFeeControl,
uint256 protocolShare,
uint256 maxVolatilityAccumulator
);
event FactoryLockedStatusUpdated(bool unlocked);
event LBPairImplementationSet(address oldLBPairImplementation, address LBPairImplementation);
event LBPairIgnoredStateChanged(ILBLegacyPair indexed LBPair, bool ignored);
event PresetSet(
uint256 indexed binStep,
uint256 baseFactor,
uint256 filterPeriod,
uint256 decayPeriod,
uint256 reductionFactor,
uint256 variableFeeControl,
uint256 protocolShare,
uint256 maxVolatilityAccumulator,
uint256 sampleLifetime
);
event PresetRemoved(uint256 indexed binStep);
event QuoteAssetAdded(IERC20 indexed quoteAsset);
event QuoteAssetRemoved(IERC20 indexed quoteAsset);
function MAX_FEE() external pure returns (uint256);
function MIN_BIN_STEP() external pure returns (uint256);
function MAX_BIN_STEP() external pure returns (uint256);
function MAX_PROTOCOL_SHARE() external pure returns (uint256);
function LBPairImplementation() external view returns (address);
function getNumberOfQuoteAssets() external view returns (uint256);
function getQuoteAsset(uint256 index) external view returns (IERC20);
function isQuoteAsset(IERC20 token) external view returns (bool);
function feeRecipient() external view returns (address);
function flashLoanFee() external view returns (uint256);
function creationUnlocked() external view returns (bool);
function allLBPairs(uint256 id) external returns (ILBLegacyPair);
function getNumberOfLBPairs() external view returns (uint256);
function getLBPairInformation(IERC20 tokenX, IERC20 tokenY, uint256 binStep)
external
view
returns (LBPairInformation memory);
function getPreset(uint16 binStep)
external
view
returns (
uint256 baseFactor,
uint256 filterPeriod,
uint256 decayPeriod,
uint256 reductionFactor,
uint256 variableFeeControl,
uint256 protocolShare,
uint256 maxAccumulator,
uint256 sampleLifetime
);
function getAllBinSteps() external view returns (uint256[] memory presetsBinStep);
function getAllLBPairs(IERC20 tokenX, IERC20 tokenY)
external
view
returns (LBPairInformation[] memory LBPairsBinStep);
function setLBPairImplementation(address LBPairImplementation) external;
function createLBPair(IERC20 tokenX, IERC20 tokenY, uint24 activeId, uint16 binStep)
external
returns (ILBLegacyPair pair);
function setLBPairIgnored(IERC20 tokenX, IERC20 tokenY, uint256 binStep, bool ignored) external;
function setPreset(
uint16 binStep,
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator,
uint16 sampleLifetime
) external;
function removePreset(uint16 binStep) external;
function setFeesParametersOnPair(
IERC20 tokenX,
IERC20 tokenY,
uint16 binStep,
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator
) external;
function setFeeRecipient(address feeRecipient) external;
function setFlashLoanFee(uint256 flashLoanFee) external;
function setFactoryLockedState(bool locked) external;
function addQuoteAsset(IERC20 quoteAsset) external;
function removeQuoteAsset(IERC20 quoteAsset) external;
function forceDecay(ILBLegacyPair LBPair) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ILBLegacyToken} from "./ILBLegacyToken.sol";
/// @title Liquidity Book Pair V2 Interface
/// @author Trader Joe
/// @notice Required interface of LBPair contract
interface ILBLegacyPair is ILBLegacyToken {
/// @dev Structure to store the protocol fees:
/// - binStep: The bin step
/// - baseFactor: The base factor
/// - filterPeriod: The filter period, where the fees stays constant
/// - decayPeriod: The decay period, where the fees are halved
/// - reductionFactor: The reduction factor, used to calculate the reduction of the accumulator
/// - variableFeeControl: The variable fee control, used to control the variable fee, can be 0 to disable them
/// - protocolShare: The share of fees sent to protocol
/// - maxVolatilityAccumulated: The max value of volatility accumulated
/// - volatilityAccumulated: The value of volatility accumulated
/// - volatilityReference: The value of volatility reference
/// - indexRef: The index reference
/// - time: The last time the accumulator was called
struct FeeParameters {
// 144 lowest bits in slot
uint16 binStep;
uint16 baseFactor;
uint16 filterPeriod;
uint16 decayPeriod;
uint16 reductionFactor;
uint24 variableFeeControl;
uint16 protocolShare;
uint24 maxVolatilityAccumulated;
// 112 highest bits in slot
uint24 volatilityAccumulated;
uint24 volatilityReference;
uint24 indexRef;
uint40 time;
}
/// @dev Structure used during swaps to distributes the fees:
/// - total: The total amount of fees
/// - protocol: The amount of fees reserved for protocol
struct FeesDistribution {
uint128 total;
uint128 protocol;
}
/// @dev Structure to store the reserves of bins:
/// - reserveX: The current reserve of tokenX of the bin
/// - reserveY: The current reserve of tokenY of the bin
struct Bin {
uint112 reserveX;
uint112 reserveY;
uint256 accTokenXPerShare;
uint256 accTokenYPerShare;
}
/// @dev Structure to store the information of the pair such as:
/// slot0:
/// - activeId: The current id used for swaps, this is also linked with the price
/// - reserveX: The sum of amounts of tokenX across all bins
/// slot1:
/// - reserveY: The sum of amounts of tokenY across all bins
/// - oracleSampleLifetime: The lifetime of an oracle sample
/// - oracleSize: The current size of the oracle, can be increase by users
/// - oracleActiveSize: The current active size of the oracle, composed only from non empty data sample
/// - oracleLastTimestamp: The current last timestamp at which a sample was added to the circular buffer
/// - oracleId: The current id of the oracle
/// slot2:
/// - feesX: The current amount of fees to distribute in tokenX (total, protocol)
/// slot3:
/// - feesY: The current amount of fees to distribute in tokenY (total, protocol)
struct PairInformation {
uint24 activeId;
uint136 reserveX;
uint136 reserveY;
uint16 oracleSampleLifetime;
uint16 oracleSize;
uint16 oracleActiveSize;
uint40 oracleLastTimestamp;
uint16 oracleId;
FeesDistribution feesX;
FeesDistribution feesY;
}
/// @dev Structure to store the debts of users
/// - debtX: The tokenX's debt
/// - debtY: The tokenY's debt
struct Debts {
uint256 debtX;
uint256 debtY;
}
/// @dev Structure to store fees:
/// - tokenX: The amount of fees of token X
/// - tokenY: The amount of fees of token Y
struct Fees {
uint128 tokenX;
uint128 tokenY;
}
/// @dev Structure to minting informations:
/// - amountXIn: The amount of token X sent
/// - amountYIn: The amount of token Y sent
/// - amountXAddedToPair: The amount of token X that have been actually added to the pair
/// - amountYAddedToPair: The amount of token Y that have been actually added to the pair
/// - activeFeeX: Fees X currently generated
/// - activeFeeY: Fees Y currently generated
/// - totalDistributionX: Total distribution of token X. Should be 1e18 (100%) or 0 (0%)
/// - totalDistributionY: Total distribution of token Y. Should be 1e18 (100%) or 0 (0%)
/// - id: Id of the current working bin when looping on the distribution array
/// - amountX: The amount of token X deposited in the current bin
/// - amountY: The amount of token Y deposited in the current bin
/// - distributionX: Distribution of token X for the current working bin
/// - distributionY: Distribution of token Y for the current working bin
struct MintInfo {
uint256 amountXIn;
uint256 amountYIn;
uint256 amountXAddedToPair;
uint256 amountYAddedToPair;
uint256 activeFeeX;
uint256 activeFeeY;
uint256 totalDistributionX;
uint256 totalDistributionY;
uint256 id;
uint256 amountX;
uint256 amountY;
uint256 distributionX;
uint256 distributionY;
}
event Swap(
address indexed sender,
address indexed recipient,
uint256 indexed id,
bool swapForY,
uint256 amountIn,
uint256 amountOut,
uint256 volatilityAccumulated,
uint256 fees
);
event FlashLoan(address indexed sender, address indexed receiver, IERC20 token, uint256 amount, uint256 fee);
event CompositionFee(
address indexed sender, address indexed recipient, uint256 indexed id, uint256 feesX, uint256 feesY
);
event DepositedToBin(
address indexed sender, address indexed recipient, uint256 indexed id, uint256 amountX, uint256 amountY
);
event WithdrawnFromBin(
address indexed sender, address indexed recipient, uint256 indexed id, uint256 amountX, uint256 amountY
);
event FeesCollected(address indexed sender, address indexed recipient, uint256 amountX, uint256 amountY);
event ProtocolFeesCollected(address indexed sender, address indexed recipient, uint256 amountX, uint256 amountY);
event OracleSizeIncreased(uint256 previousSize, uint256 newSize);
function tokenX() external view returns (IERC20);
function tokenY() external view returns (IERC20);
function factory() external view returns (address);
function getReservesAndId() external view returns (uint256 reserveX, uint256 reserveY, uint256 activeId);
function getGlobalFees()
external
view
returns (uint128 feesXTotal, uint128 feesYTotal, uint128 feesXProtocol, uint128 feesYProtocol);
function getOracleParameters()
external
view
returns (
uint256 oracleSampleLifetime,
uint256 oracleSize,
uint256 oracleActiveSize,
uint256 oracleLastTimestamp,
uint256 oracleId,
uint256 min,
uint256 max
);
function getOracleSampleFrom(uint256 timeDelta)
external
view
returns (uint256 cumulativeId, uint256 cumulativeAccumulator, uint256 cumulativeBinCrossed);
function feeParameters() external view returns (FeeParameters memory);
function findFirstNonEmptyBinId(uint24 id_, bool sentTokenY) external view returns (uint24 id);
function getBin(uint24 id) external view returns (uint256 reserveX, uint256 reserveY);
function pendingFees(address account, uint256[] memory ids)
external
view
returns (uint256 amountX, uint256 amountY);
function swap(bool sentTokenY, address to) external returns (uint256 amountXOut, uint256 amountYOut);
function flashLoan(address receiver, IERC20 token, uint256 amount, bytes calldata data) external;
function mint(
uint256[] calldata ids,
uint256[] calldata distributionX,
uint256[] calldata distributionY,
address to
) external returns (uint256 amountXAddedToPair, uint256 amountYAddedToPair, uint256[] memory liquidityMinted);
function burn(uint256[] calldata ids, uint256[] calldata amounts, address to)
external
returns (uint256 amountX, uint256 amountY);
function increaseOracleLength(uint16 newSize) external;
function collectFees(address account, uint256[] calldata ids) external returns (uint256 amountX, uint256 amountY);
function collectProtocolFees() external returns (uint128 amountX, uint128 amountY);
function setFeesParameters(bytes32 packedFeeParameters) external;
function forceDecay() external;
function initialize(
IERC20 tokenX,
IERC20 tokenY,
uint24 activeId,
uint16 sampleLifetime,
bytes32 packedFeeParameters
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ILBFactory} from "./ILBFactory.sol";
import {IJoeFactory} from "./IJoeFactory.sol";
import {ILBLegacyPair} from "./ILBLegacyPair.sol";
import {ILBToken} from "./ILBToken.sol";
import {IWNATIVE} from "./IWNATIVE.sol";
/// @title Liquidity Book Router Interface
/// @author Trader Joe
/// @notice Required interface of LBRouter contract
interface ILBLegacyRouter {
struct LiquidityParameters {
IERC20 tokenX;
IERC20 tokenY;
uint256 binStep;
uint256 amountX;
uint256 amountY;
uint256 amountXMin;
uint256 amountYMin;
uint256 activeIdDesired;
uint256 idSlippage;
int256[] deltaIds;
uint256[] distributionX;
uint256[] distributionY;
address to;
uint256 deadline;
}
function factory() external view returns (address);
function wavax() external view returns (address);
function oldFactory() external view returns (address);
function getIdFromPrice(ILBLegacyPair LBPair, uint256 price) external view returns (uint24);
function getPriceFromId(ILBLegacyPair LBPair, uint24 id) external view returns (uint256);
function getSwapIn(ILBLegacyPair lbPair, uint256 amountOut, bool swapForY)
external
view
returns (uint256 amountIn, uint256 feesIn);
function getSwapOut(ILBLegacyPair lbPair, uint256 amountIn, bool swapForY)
external
view
returns (uint256 amountOut, uint256 feesIn);
function createLBPair(IERC20 tokenX, IERC20 tokenY, uint24 activeId, uint16 binStep)
external
returns (ILBLegacyPair pair);
function addLiquidity(LiquidityParameters calldata liquidityParameters)
external
returns (uint256[] memory depositIds, uint256[] memory liquidityMinted);
function addLiquidityAVAX(LiquidityParameters calldata liquidityParameters)
external
payable
returns (uint256[] memory depositIds, uint256[] memory liquidityMinted);
function removeLiquidity(
IERC20 tokenX,
IERC20 tokenY,
uint16 binStep,
uint256 amountXMin,
uint256 amountYMin,
uint256[] memory ids,
uint256[] memory amounts,
address to,
uint256 deadline
) external returns (uint256 amountX, uint256 amountY);
function removeLiquidityAVAX(
IERC20 token,
uint16 binStep,
uint256 amountTokenMin,
uint256 amountAVAXMin,
uint256[] memory ids,
uint256[] memory amounts,
address payable to,
uint256 deadline
) external returns (uint256 amountToken, uint256 amountAVAX);
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address to,
uint256 deadline
) external returns (uint256 amountOut);
function swapExactTokensForAVAX(
uint256 amountIn,
uint256 amountOutMinAVAX,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address payable to,
uint256 deadline
) external returns (uint256 amountOut);
function swapExactAVAXForTokens(
uint256 amountOutMin,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address to,
uint256 deadline
) external payable returns (uint256 amountOut);
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address to,
uint256 deadline
) external returns (uint256[] memory amountsIn);
function swapTokensForExactAVAX(
uint256 amountOut,
uint256 amountInMax,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address payable to,
uint256 deadline
) external returns (uint256[] memory amountsIn);
function swapAVAXForExactTokens(
uint256 amountOut,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address to,
uint256 deadline
) external payable returns (uint256[] memory amountsIn);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address to,
uint256 deadline
) external returns (uint256 amountOut);
function swapExactTokensForAVAXSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMinAVAX,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address payable to,
uint256 deadline
) external returns (uint256 amountOut);
function swapExactAVAXForTokensSupportingFeeOnTransferTokens(
uint256 amountOutMin,
uint256[] memory pairBinSteps,
IERC20[] memory tokenPath,
address to,
uint256 deadline
) external payable returns (uint256 amountOut);
function sweep(IERC20 token, address to, uint256 amount) external;
function sweepLBToken(ILBToken _lbToken, address _to, uint256[] calldata _ids, uint256[] calldata _amounts)
external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/// @title Liquidity Book V2 Token Interface
/// @author Trader Joe
/// @notice Required interface of LBToken contract
interface ILBLegacyToken is IERC165 {
event TransferSingle(address indexed sender, address indexed from, address indexed to, uint256 id, uint256 amount);
event TransferBatch(
address indexed sender, address indexed from, address indexed to, uint256[] ids, uint256[] amounts
);
event ApprovalForAll(address indexed account, address indexed sender, bool approved);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function balanceOf(address account, uint256 id) external view returns (uint256);
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
external
view
returns (uint256[] memory batchBalances);
function totalSupply(uint256 id) external view returns (uint256);
function isApprovedForAll(address owner, address spender) external view returns (bool);
function setApprovalForAll(address sender, bool approved) external;
function safeTransferFrom(address from, address to, uint256 id, uint256 amount) external;
function safeBatchTransferFrom(address from, address to, uint256[] calldata id, uint256[] calldata amount)
external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ILBFactory} from "./ILBFactory.sol";
import {ILBFlashLoanCallback} from "./ILBFlashLoanCallback.sol";
import {ILBToken} from "./ILBToken.sol";
interface ILBPair is ILBToken {
error LBPair__ZeroBorrowAmount();
error LBPair__AddressZero();
error LBPair__AlreadyInitialized();
error LBPair__EmptyMarketConfigs();
error LBPair__FlashLoanCallbackFailed();
error LBPair__FlashLoanInsufficientAmount();
error LBPair__InsufficientAmountIn();
error LBPair__InsufficientAmountOut();
error LBPair__InvalidInput();
error LBPair__InvalidStaticFeeParameters();
error LBPair__OnlyFactory();
error LBPair__OnlyProtocolFeeRecipient();
error LBPair__OutOfLiquidity();
error LBPair__TokenNotSupported();
error LBPair__ZeroAmount(uint24 id);
error LBPair__ZeroAmountsOut(uint24 id);
error LBPair__ZeroShares(uint24 id);
error LBPair__MaxTotalFeeExceeded();
struct MintArrays {
uint256[] ids;
bytes32[] amounts;
uint256[] liquidityMinted;
}
event DepositedToBins(address indexed sender, address indexed to, uint256[] ids, bytes32[] amounts);
event WithdrawnFromBins(address indexed sender, address indexed to, uint256[] ids, bytes32[] amounts);
event CompositionFees(address indexed sender, uint24 id, bytes32 totalFees, bytes32 protocolFees);
event CollectedProtocolFees(address indexed feeRecipient, bytes32 protocolFees);
event Swap(
address indexed sender,
address indexed to,
uint24 id,
bytes32 amountsIn,
bytes32 amountsOut,
uint24 volatilityAccumulator,
bytes32 totalFees,
bytes32 protocolFees
);
event StaticFeeParametersSet(
address indexed sender,
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator
);
event FlashLoan(
address indexed sender,
ILBFlashLoanCallback indexed receiver,
uint24 activeId,
bytes32 amounts,
bytes32 totalFees,
bytes32 protocolFees
);
event OracleLengthIncreased(address indexed sender, uint16 oracleLength);
event ForcedDecay(address indexed sender, uint24 idReference, uint24 volatilityReference);
function initialize(
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator,
uint24 activeId
) external;
function getFactory() external view returns (ILBFactory factory);
function getTokenX() external view returns (IERC20 tokenX);
function getTokenY() external view returns (IERC20 tokenY);
function getBinStep() external view returns (uint16 binStep);
function getReserves() external view returns (uint128 reserveX, uint128 reserveY);
function getActiveId() external view returns (uint24 activeId);
function getBin(uint24 id) external view returns (uint128 binReserveX, uint128 binReserveY);
function getNextNonEmptyBin(bool swapForY, uint24 id) external view returns (uint24 nextId);
function getProtocolFees() external view returns (uint128 protocolFeeX, uint128 protocolFeeY);
function getStaticFeeParameters()
external
view
returns (
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator
);
function getVariableFeeParameters()
external
view
returns (uint24 volatilityAccumulator, uint24 volatilityReference, uint24 idReference, uint40 timeOfLastUpdate);
function getOracleParameters()
external
view
returns (uint8 sampleLifetime, uint16 size, uint16 activeSize, uint40 lastUpdated, uint40 firstTimestamp);
function getOracleSampleAt(uint40 lookupTimestamp)
external
view
returns (uint64 cumulativeId, uint64 cumulativeVolatility, uint64 cumulativeBinCrossed);
function getPriceFromId(uint24 id) external view returns (uint256 price);
function getIdFromPrice(uint256 price) external view returns (uint24 id);
function getSwapIn(uint128 amountOut, bool swapForY)
external
view
returns (uint128 amountIn, uint128 amountOutLeft, uint128 fee);
function getSwapOut(uint128 amountIn, bool swapForY)
external
view
returns (uint128 amountInLeft, uint128 amountOut, uint128 fee);
function swap(bool swapForY, address to) external returns (bytes32 amountsOut);
function flashLoan(ILBFlashLoanCallback receiver, bytes32 amounts, bytes calldata data) external;
function mint(address to, bytes32[] calldata liquidityConfigs, address refundTo)
external
returns (bytes32 amountsReceived, bytes32 amountsLeft, uint256[] memory liquidityMinted);
function burn(address from, address to, uint256[] calldata ids, uint256[] calldata amountsToBurn)
external
returns (bytes32[] memory amounts);
function collectProtocolFees() external returns (bytes32 collectedProtocolFees);
function increaseOracleLength(uint16 newLength) external;
function setStaticFeeParameters(
uint16 baseFactor,
uint16 filterPeriod,
uint16 decayPeriod,
uint16 reductionFactor,
uint24 variableFeeControl,
uint16 protocolShare,
uint24 maxVolatilityAccumulator
) external;
function forceDecay() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IJoeFactory} from "./IJoeFactory.sol";
import {ILBFactory} from "./ILBFactory.sol";
import {ILBLegacyFactory} from "./ILBLegacyFactory.sol";
import {ILBLegacyRouter} from "./ILBLegacyRouter.sol";
import {ILBPair} from "./ILBPair.sol";
import {ILBToken} from "./ILBToken.sol";
import {IWNATIVE} from "./IWNATIVE.sol";
/**
* @title Liquidity Book Router Interface
* @author Trader Joe
* @notice Required interface of LBRouter contract
*/
interface ILBRouter {
error LBRouter__SenderIsNotWNATIVE();
error LBRouter__PairNotCreated(address tokenX, address tokenY, uint256 binStep);
error LBRouter__WrongAmounts(uint256 amount, uint256 reserve);
error LBRouter__SwapOverflows(uint256 id);
error LBRouter__BrokenSwapSafetyCheck();
error LBRouter__NotFactoryOwner();
error LBRouter__TooMuchTokensIn(uint256 excess);
error LBRouter__BinReserveOverflows(uint256 id);
error LBRouter__IdOverflows(int256 id);
error LBRouter__LengthsMismatch();
error LBRouter__WrongTokenOrder();
error LBRouter__IdSlippageCaught(uint256 activeIdDesired, uint256 idSlippage, uint256 activeId);
error LBRouter__AmountSlippageCaught(uint256 amountXMin, uint256 amountX, uint256 amountYMin, uint256 amountY);
error LBRouter__IdDesiredOverflows(uint256 idDesired, uint256 idSlippage);
error LBRouter__FailedToSendNATIVE(address recipient, uint256 amount);
error LBRouter__DeadlineExceeded(uint256 deadline, uint256 currentTimestamp);
error LBRouter__AmountSlippageBPTooBig(uint256 amountSlippage);
error LBRouter__InsufficientAmountOut(uint256 amountOutMin, uint256 amountOut);
error LBRouter__MaxAmountInExceeded(uint256 amountInMax, uint256 amountIn);
error LBRouter__InvalidTokenPath(address wrongToken);
error LBRouter__InvalidVersion(uint256 version);
error LBRouter__WrongNativeLiquidityParameters(
address tokenX, address tokenY, uint256 amountX, uint256 amountY, uint256 msgValue
);
/**
* @dev This enum represents the version of the pair requested
* - V1: Joe V1 pair
* - V2: LB pair V2. Also called legacyPair
* - V2_1: LB pair V2.1 (current version)
*/
enum Version {
V1,
V2,
V2_1
}
/**
* @dev The liquidity parameters, such as:
* - tokenX: The address of token X
* - tokenY: The address of token Y
* - binStep: The bin step of the pair
* - amountX: The amount to send of token X
* - amountY: The amount to send of token Y
* - amountXMin: The min amount of token X added to liquidity
* - amountYMin: The min amount of token Y added to liquidity
* - activeIdDesired: The active id that user wants to add liquidity from
* - idSlippage: The number of id that are allowed to slip
* - deltaIds: The list of delta ids to add liquidity (`deltaId = activeId - desiredId`)
* - distributionX: The distribution of tokenX with sum(distributionX) = 100e18 (100%) or 0 (0%)
* - distributionY: The distribution of tokenY with sum(distributionY) = 100e18 (100%) or 0 (0%)
* - to: The address of the recipient
* - refundTo: The address of the recipient of the refunded tokens if too much tokens are sent
* - deadline: The deadline of the transaction
*/
struct LiquidityParameters {
IERC20 tokenX;
IERC20 tokenY;
uint256 binStep;
uint256 amountX;
uint256 amountY;
uint256 amountXMin;
uint256 amountYMin;
uint256 activeIdDesired;
uint256 idSlippage;
int256[] deltaIds;
uint256[] distributionX;
uint256[] distributionY;
address to;
address refundTo;
uint256 deadline;
}
/**
* @dev The path parameters, such as:
* - pairBinSteps: The list of bin steps of the pairs to go through
* - versions: The list of versions of the pairs to go through
* - tokenPath: The list of tokens in the path to go through
*/
struct Path {
uint256[] pairBinSteps;
Version[] versions;
IERC20[] tokenPath;
}
function getFactory() external view returns (ILBFactory);
function getLegacyFactory() external view returns (ILBLegacyFactory);
function getV1Factory() external view returns (IJoeFactory);
function getLegacyRouter() external view returns (ILBLegacyRouter);
function getWNATIVE() external view returns (IWNATIVE);
function getIdFromPrice(ILBPair LBPair, uint256 price) external view returns (uint24);
function getPriceFromId(ILBPair LBPair, uint24 id) external view returns (uint256);
function getSwapIn(ILBPair LBPair, uint128 amountOut, bool swapForY)
external
view
returns (uint128 amountIn, uint128 amountOutLeft, uint128 fee);
function getSwapOut(ILBPair LBPair, uint128 amountIn, bool swapForY)
external
view
returns (uint128 amountInLeft, uint128 amountOut, uint128 fee);
function createLBPair(IERC20 tokenX, IERC20 tokenY, uint24 activeId, uint16 binStep)
external
returns (ILBPair pair);
function addLiquidity(LiquidityParameters calldata liquidityParameters)
external
returns (
uint256 amountXAdded,
uint256 amountYAdded,
uint256 amountXLeft,
uint256 amountYLeft,
uint256[] memory depositIds,
uint256[] memory liquidityMinted
);
function addLiquidityNATIVE(LiquidityParameters calldata liquidityParameters)
external
payable
returns (
uint256 amountXAdded,
uint256 amountYAdded,
uint256 amountXLeft,
uint256 amountYLeft,
uint256[] memory depositIds,
uint256[] memory liquidityMinted
);
function removeLiquidity(
IERC20 tokenX,
IERC20 tokenY,
uint16 binStep,
uint256 amountXMin,
uint256 amountYMin,
uint256[] memory ids,
uint256[] memory amounts,
address to,
uint256 deadline
) external returns (uint256 amountX, uint256 amountY);
function removeLiquidityNATIVE(
IERC20 token,
uint16 binStep,
uint256 amountTokenMin,
uint256 amountNATIVEMin,
uint256[] memory ids,
uint256[] memory amounts,
address payable to,
uint256 deadline
) external returns (uint256 amountToken, uint256 amountNATIVE);
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
Path memory path,
address to,
uint256 deadline
) external returns (uint256 amountOut);
function swapExactTokensForNATIVE(
uint256 amountIn,
uint256 amountOutMinNATIVE,
Path memory path,
address payable to,
uint256 deadline
) external returns (uint256 amountOut);
function swapExactNATIVEForTokens(uint256 amountOutMin, Path memory path, address to, uint256 deadline)
external
payable
returns (uint256 amountOut);
function swapTokensForExactTokens(
uint256 amountOut,
uint256 amountInMax,
Path memory path,
address to,
uint256 deadline
) external returns (uint256[] memory amountsIn);
function swapTokensForExactNATIVE(
uint256 amountOut,
uint256 amountInMax,
Path memory path,
address payable to,
uint256 deadline
) external returns (uint256[] memory amountsIn);
function swapNATIVEForExactTokens(uint256 amountOut, Path memory path, address to, uint256 deadline)
external
payable
returns (uint256[] memory amountsIn);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMin,
Path memory path,
address to,
uint256 deadline
) external returns (uint256 amountOut);
function swapExactTokensForNATIVESupportingFeeOnTransferTokens(
uint256 amountIn,
uint256 amountOutMinNATIVE,
Path memory path,
address payable to,
uint256 deadline
) external returns (uint256 amountOut);
function swapExactNATIVEForTokensSupportingFeeOnTransferTokens(
uint256 amountOutMin,
Path memory path,
address to,
uint256 deadline
) external payable returns (uint256 amountOut);
function sweep(IERC20 token, address to, uint256 amount) external;
function sweepLBToken(ILBToken _lbToken, address _to, uint256[] calldata _ids, uint256[] calldata _amounts)
external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
/**
* @title Liquidity Book Token Interface
* @author Trader Joe
* @notice Interface to interact with the LBToken.
*/
interface ILBToken {
error LBToken__AddressThisOrZero();
error LBToken__InvalidLength();
error LBToken__SelfApproval(address owner);
error LBToken__SpenderNotApproved(address from, address spender);
error LBToken__TransferExceedsBalance(address from, uint256 id, uint256 amount);
error LBToken__BurnExceedsBalance(address from, uint256 id, uint256 amount);
event TransferBatch(
address indexed sender, address indexed from, address indexed to, uint256[] ids, uint256[] amounts
);
event ApprovalForAll(address indexed account, address indexed sender, bool approved);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function totalSupply(uint256 id) external view returns (uint256);
function balanceOf(address account, uint256 id) external view returns (uint256);
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
external
view
returns (uint256[] memory);
function isApprovedForAll(address owner, address spender) external view returns (bool);
function approveForAll(address spender, bool approved) external;
function batchTransferFrom(address from, address to, uint256[] calldata ids, uint256[] calldata amounts) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
/**
* @title Liquidity Book Pending Ownable Interface
* @author Trader Joe
* @notice Required interface of Pending Ownable contract used for LBFactory
*/
interface IPendingOwnable {
error PendingOwnable__AddressZero();
error PendingOwnable__NoPendingOwner();
error PendingOwnable__NotOwner();
error PendingOwnable__NotPendingOwner();
error PendingOwnable__PendingOwnerAlreadySet();
event PendingOwnerSet(address indexed pendingOwner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function owner() external view returns (address);
function pendingOwner() external view returns (address);
function setPendingOwner(address pendingOwner) external;
function revokePendingOwner() external;
function becomeOwner() external;
function renounceOwnership() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title WNATIVE Interface
* @notice Required interface of Wrapped NATIVE contract
*/
interface IWNATIVE is IERC20 {
function deposit() external payable;
function withdraw(uint256) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.5;
pragma abicoder v2;
import '../../lib/uniswap-v3/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
{
/// @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
//TODO: dirty hack here: removed the last 4 returned values
// feeGrowthInside0LastX128 The fee growth of token0 as of the last action on the individual position
// feeGrowthInside1LastX128 The fee growth of token1 as of the last action on the individual position
// tokensOwed0 The uncollected amount of token0 owed to the position as of the last computation
// 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
);
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 './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 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: BUSL-1.1
// Last deployed from commit: b75e073cf23a3eb181f55a89a800ef040b7ba456;
pragma solidity 0.8.17;
import "../../interfaces/ITokenManager.sol";
import {DiamondStorageLib} from "../../lib/DiamondStorageLib.sol";
/**
* DeploymentConstants
* These constants are updated during test and prod deployments using JS scripts. Defined as constants
* to decrease gas costs. Not meant to be updated unless really necessary.
* BE CAREFUL WHEN UPDATING. CONSTANTS CAN BE USED AMONG MANY FACETS.
**/
library DeploymentConstants {
// Used for LiquidationBonus calculations
uint256 private constant _PERCENTAGE_PRECISION = 1000;
bytes32 private constant _NATIVE_TOKEN_SYMBOL = 'ARBI';
address private constant _NATIVE_ADDRESS = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1;
address private constant _DIAMOND_BEACON_ADDRESS = 0x62Cf82FB0484aF382714cD09296260edc1DC0c6c;
address private constant _SMART_LOANS_FACTORY_ADDRESS = 0xFf5e3dDaefF411a1dC6CcE00014e4Bca39265c20;
address private constant _TOKEN_MANAGER_ADDRESS = 0x0a0D954d4b0F0b47a5990C0abd179A90fF74E255;
address private constant _ADDRESS_PROVIDER = 0x6Aa0Fe94731aDD419897f5783712eBc13E8F3982;
//implementation-specific
function getPercentagePrecision() internal pure returns (uint256) {
return _PERCENTAGE_PRECISION;
}
//blockchain-specific
function getNativeTokenSymbol() internal pure returns (bytes32 symbol) {
return _NATIVE_TOKEN_SYMBOL;
}
function getNativeToken() internal pure returns (address payable) {
return payable(_NATIVE_ADDRESS);
}
//deployment-specific
function getDiamondAddress() internal pure returns (address) {
return _DIAMOND_BEACON_ADDRESS;
}
function getSmartLoansFactoryAddress() internal pure returns (address) {
return _SMART_LOANS_FACTORY_ADDRESS;
}
function getTokenManager() internal pure returns (ITokenManager) {
return ITokenManager(_TOKEN_MANAGER_ADDRESS);
}
function getAddressProvider() internal pure returns (address) {
return _ADDRESS_PROVIDER;
}
/**
* Returns all owned assets keys
**/
function getAllOwnedAssets() internal view returns (bytes32[] memory result) {
DiamondStorageLib.SmartLoanStorage storage sls = DiamondStorageLib.smartLoanStorage();
return sls.ownedAssets._inner._keys._inner._values;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
//It's Open Zeppelin EnumerableMap library modified to accept bytes32 type as a key
/**
* @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.
*
*/
library EnumerableMap {
using EnumerableSet for EnumerableSet.Bytes32Set;
// 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 Bytes32ToAddressMap) are just wrappers around
// the underlying Map.
// This means that we can only create new EnumerableMaps for types that fit
// in bytes32.
struct Map {
// Storage of keys
EnumerableSet.Bytes32Set _keys;
mapping(bytes32 => bytes32) _values;
}
/**
* @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) {
map._values[key] = value;
return map._keys.add(key);
}
/**
* @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) {
delete map._values[key];
return map._keys.remove(key);
}
/**
* @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._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function _length(Map storage map) private view returns (uint256) {
return map._keys.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) {
bytes32 key = map._keys.at(index);
return (key, map._values[key]);
}
/**
* @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) {
bytes32 value = map._values[key];
if (value == bytes32(0)) {
return (_contains(map, key), bytes32(0));
} else {
return (true, value);
}
}
/**
* @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) {
bytes32 value = map._values[key];
require(value != 0 || _contains(map, key), "EnumerableMap: nonexistent key");
return value;
}
/**
* @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) {
bytes32 value = map._values[key];
require(value != 0 || _contains(map, key), errorMessage);
return value;
}
// Bytes32ToAddressMap
struct Bytes32ToAddressMap {
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(
Bytes32ToAddressMap storage map,
bytes32 key,
address value
) internal returns (bool) {
return _set(map._inner, 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(Bytes32ToAddressMap storage map, bytes32 key) internal returns (bool) {
return _remove(map._inner, key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (bool) {
return _contains(map._inner, key);
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(Bytes32ToAddressMap 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(Bytes32ToAddressMap storage map, uint256 index) internal view returns (bytes32, address) {
(bytes32 key, bytes32 value) = _at(map._inner, index);
return (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(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (bool, address) {
(bool success, bytes32 value) = _tryGet(map._inner, 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(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (address) {
return address(uint160(uint256(_get(map._inner, 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(
Bytes32ToAddressMap storage map,
bytes32 key,
string memory errorMessage
) internal view returns (address) {
return address(uint160(uint256(_get(map._inner, key, errorMessage))));
}
}// SPDX-License-Identifier: MIT pragma solidity 0.8.17; /******************************************************************************\ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) * EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535 /******************************************************************************/ import {IDiamondCut} from "../interfaces/IDiamondCut.sol"; import "../lib/Bytes32EnumerableMap.sol"; import "../interfaces/IStakingPositions.sol"; import "../interfaces/facets/avalanche/ITraderJoeV2Facet.sol"; // Remember to add the loupe functions from DiamondLoupeFacet to the diamond. // The loupe functions are required by the EIP2535 Diamonds standard library DiamondStorageLib { using EnumerableMap for EnumerableMap.Bytes32ToAddressMap; bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage"); bytes32 constant LIQUIDATION_STORAGE_POSITION = keccak256("diamond.standard.liquidation.storage"); bytes32 constant SMARTLOAN_STORAGE_POSITION = keccak256("diamond.standard.smartloan.storage"); bytes32 constant REENTRANCY_GUARD_STORAGE_POSITION = keccak256("diamond.standard.reentrancy.guard.storage"); bytes32 constant OWNED_TRADERJOE_V2_BINS_POSITION = keccak256("diamond.standard.traderjoe_v2_bins_1685370112"); //TODO: maybe we should keep here a tuple[tokenId, factory] to account for multiple Uniswap V3 deployments bytes32 constant OWNED_UNISWAP_V3_TOKEN_IDS_POSITION = keccak256("diamond.standard.uniswap_v3_token_ids_1685370112"); struct FacetAddressAndPosition { address facetAddress; uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array } struct FacetFunctionSelectors { bytes4[] functionSelectors; uint256 facetAddressPosition; // position of facetAddress in facetAddresses array } struct DiamondStorage { // ----------- DIAMOND-SPECIFIC VARIABLES -------------- // maps function selector to the facet address and // the position of the selector in the facetFunctionSelectors.selectors array mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition; // maps facet addresses to function selectors mapping(address => FacetFunctionSelectors) facetFunctionSelectors; // facet addresses address[] facetAddresses; // Used to query if a contract implements an interface. // Used to implement ERC-165. mapping(bytes4 => bool) supportedInterfaces; // Used to select methods that can be executed even when Diamond is paused mapping(bytes4 => bool) canBeExecutedWhenPaused; bool _initialized; bool _active; uint256 _lastBorrowTimestamp; } struct SmartLoanStorage { // PauseAdmin has the power to pause/unpause the contract without the timelock delay in case of a critical bug/exploit address pauseAdmin; // Owner of the contract address contractOwner; // Proposed owner of the contract address proposedOwner; // Proposed pauseAdmin of the contract address proposedPauseAdmin; // Is contract initialized? bool _initialized; // TODO: mock staking tokens until redstone oracle supports them EnumerableMap.Bytes32ToAddressMap ownedAssets; // Staked positions of the contract IStakingPositions.StakedPosition[] currentStakedPositions; } struct TraderJoeV2Storage { // TJ v2 bins of the contract ITraderJoeV2Facet.TraderJoeV2Bin[] ownedTjV2Bins; } struct UniswapV3Storage { // UniswapV3 token IDs of the contract uint256[] ownedUniswapV3TokenIds; } struct LiquidationStorage { // Mapping controlling addresses that can execute the liquidation methods mapping(address=>bool) canLiquidate; } struct ReentrancyGuardStorage { uint256 _status; } function reentrancyGuardStorage() internal pure returns (ReentrancyGuardStorage storage rgs) { bytes32 position = REENTRANCY_GUARD_STORAGE_POSITION; assembly { rgs.slot := position } } function traderJoeV2Storage() internal pure returns (TraderJoeV2Storage storage tjv2s) { bytes32 position = OWNED_TRADERJOE_V2_BINS_POSITION; assembly { tjv2s.slot := position } } function uniswapV3Storage() internal pure returns (UniswapV3Storage storage uv3s) { bytes32 position = OWNED_UNISWAP_V3_TOKEN_IDS_POSITION; assembly { uv3s.slot := position } } function diamondStorage() internal pure returns (DiamondStorage storage ds) { bytes32 position = DIAMOND_STORAGE_POSITION; assembly { ds.slot := position } } function liquidationStorage() internal pure returns (LiquidationStorage storage ls) { bytes32 position = LIQUIDATION_STORAGE_POSITION; assembly { ls.slot := position } } function smartLoanStorage() internal pure returns (SmartLoanStorage storage sls) { bytes32 position = SMARTLOAN_STORAGE_POSITION; assembly { sls.slot := position } } event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); event PauseAdminOwnershipTransferred(address indexed previousPauseAdmin, address indexed newPauseAdmin); function setContractOwner(address _newOwner) internal { SmartLoanStorage storage sls = smartLoanStorage(); address previousOwner = sls.contractOwner; sls.contractOwner = _newOwner; emit OwnershipTransferred(previousOwner, _newOwner); } function getTjV2OwnedBins() internal returns(ITraderJoeV2Facet.TraderJoeV2Bin[] storage bins){ TraderJoeV2Storage storage tjv2s = traderJoeV2Storage(); bins = tjv2s.ownedTjV2Bins; } function getTjV2OwnedBinsView() internal view returns(ITraderJoeV2Facet.TraderJoeV2Bin[] storage bins){ TraderJoeV2Storage storage tjv2s = traderJoeV2Storage(); bins = tjv2s.ownedTjV2Bins; } function getUV3OwnedTokenIds() internal returns(uint256[] storage tokenIds){ UniswapV3Storage storage uv3s = uniswapV3Storage(); tokenIds = uv3s.ownedUniswapV3TokenIds; } function getUV3OwnedTokenIdsView() internal view returns(uint256[] storage tokenIds){ UniswapV3Storage storage uv3s = uniswapV3Storage(); tokenIds = uv3s.ownedUniswapV3TokenIds; } function setContractPauseAdmin(address _newPauseAdmin) internal { SmartLoanStorage storage sls = smartLoanStorage(); address previousPauseAdmin = sls.pauseAdmin; sls.pauseAdmin = _newPauseAdmin; emit PauseAdminOwnershipTransferred(previousPauseAdmin, _newPauseAdmin); } function contractOwner() internal view returns (address contractOwner_) { contractOwner_ = smartLoanStorage().contractOwner; } function pauseAdmin() internal view returns (address pauseAdmin) { pauseAdmin = smartLoanStorage().pauseAdmin; } function setProposedOwner(address _newOwner) internal { SmartLoanStorage storage sls = smartLoanStorage(); sls.proposedOwner = _newOwner; } function setProposedPauseAdmin(address _newPauseAdmin) internal { SmartLoanStorage storage sls = smartLoanStorage(); sls.proposedPauseAdmin = _newPauseAdmin; } function getPausedMethodExemption(bytes4 _methodSig) internal view returns (bool) { DiamondStorage storage ds = diamondStorage(); return ds.canBeExecutedWhenPaused[_methodSig]; } function proposedOwner() internal view returns (address proposedOwner_) { proposedOwner_ = smartLoanStorage().proposedOwner; } function proposedPauseAdmin() internal view returns (address proposedPauseAdmin) { proposedPauseAdmin = smartLoanStorage().proposedPauseAdmin; } function stakedPositions() internal view returns (IStakingPositions.StakedPosition[] storage _positions) { _positions = smartLoanStorage().currentStakedPositions; } function addStakedPosition(IStakingPositions.StakedPosition memory position) internal { IStakingPositions.StakedPosition[] storage positions = stakedPositions(); bool found; for (uint256 i; i < positions.length; i++) { if (positions[i].identifier == position.identifier) { found = true; break; } } if (!found) { positions.push(position); } } function removeStakedPosition(bytes32 identifier) internal { IStakingPositions.StakedPosition[] storage positions = stakedPositions(); for (uint256 i; i < positions.length; i++) { if (positions[i].identifier == identifier) { positions[i] = positions[positions.length - 1]; positions.pop(); } } } function addOwnedAsset(bytes32 _symbol, address _address) internal { require(_symbol != "", "Symbol cannot be empty"); require(_address != address(0), "Invalid AddressZero"); SmartLoanStorage storage sls = smartLoanStorage(); EnumerableMap.set(sls.ownedAssets, _symbol, _address); } function hasAsset(bytes32 _symbol) internal view returns (bool){ SmartLoanStorage storage sls = smartLoanStorage(); return sls.ownedAssets.contains(_symbol); } function removeOwnedAsset(bytes32 _symbol) internal { SmartLoanStorage storage sls = smartLoanStorage(); EnumerableMap.remove(sls.ownedAssets, _symbol); } function enforceIsContractOwner() internal view { require(msg.sender == smartLoanStorage().contractOwner, "DiamondStorageLib: Must be contract owner"); } function enforceIsPauseAdmin() internal view { require(msg.sender == smartLoanStorage().pauseAdmin, "DiamondStorageLib: Must be contract pauseAdmin"); } event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata); // Internal function version of diamondCut function diamondCut( IDiamondCut.FacetCut[] memory _diamondCut, address _init, bytes memory _calldata ) internal { for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) { IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action; if (action == IDiamondCut.FacetCutAction.Add) { addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else if (action == IDiamondCut.FacetCutAction.Replace) { replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else if (action == IDiamondCut.FacetCutAction.Remove) { removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else { revert("DiamondStorageLibCut: Incorrect FacetCutAction"); } } emit DiamondCut(_diamondCut, _init, _calldata); initializeDiamondCut(_init, _calldata); } function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "DiamondStorageLibCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); require(_facetAddress != address(0), "DiamondStorageLibCut: Add facet can't be address(0)"); uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length); // add new facet address if it does not exist if (selectorPosition == 0) { addFacet(ds, _facetAddress); } for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; require(oldFacetAddress == address(0), "DiamondStorageLibCut: Can't add function that already exists"); addFunction(ds, selector, selectorPosition, _facetAddress); selectorPosition++; } } function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "DiamondStorageLibCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); require(_facetAddress != address(0), "DiamondStorageLibCut: Add facet can't be address(0)"); uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length); // add new facet address if it does not exist if (selectorPosition == 0) { addFacet(ds, _facetAddress); } for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; require(oldFacetAddress != _facetAddress, "DiamondStorageLibCut: Can't replace function with same function"); removeFunction(ds, oldFacetAddress, selector); addFunction(ds, selector, selectorPosition, _facetAddress); selectorPosition++; } } function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "DiamondStorageLibCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); // if function does not exist then do nothing and return require(_facetAddress == address(0), "DiamondStorageLibCut: Remove facet address must be address(0)"); for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; removeFunction(ds, oldFacetAddress, selector); } } function addFacet(DiamondStorage storage ds, address _facetAddress) internal { enforceHasContractCode(_facetAddress, "DiamondStorageLibCut: New facet has no code"); ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length; ds.facetAddresses.push(_facetAddress); } function addFunction(DiamondStorage storage ds, bytes4 _selector, uint96 _selectorPosition, address _facetAddress) internal { ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition; ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector); ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress; } function removeFunction(DiamondStorage storage ds, address _facetAddress, bytes4 _selector) internal { require(_facetAddress != address(0), "DiamondStorageLibCut: Can't remove function that doesn't exist"); // an immutable function is a function defined directly in a diamond require(_facetAddress != address(this), "DiamondStorageLibCut: Can't remove immutable function"); // replace selector with last selector, then delete last selector uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition; uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1; // if not the same then replace _selector with lastSelector if (selectorPosition != lastSelectorPosition) { bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition]; ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector; ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition); } // delete the last selector ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop(); delete ds.selectorToFacetAndPosition[_selector]; // if no more selectors for facet address then delete the facet address if (lastSelectorPosition == 0) { // replace facet address with last facet address and delete last facet address uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1; uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition; if (facetAddressPosition != lastFacetAddressPosition) { address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition]; ds.facetAddresses[facetAddressPosition] = lastFacetAddress; ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition; } ds.facetAddresses.pop(); delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition; } } function initializeDiamondCut(address _init, bytes memory _calldata) internal { if (_init == address(0)) { require(_calldata.length == 0, "DiamondStorageLibCut: _init is address(0) but_calldata is not empty"); } else { require(_calldata.length > 0, "DiamondStorageLibCut: _calldata is empty but _init is not address(0)"); if (_init != address(this)) { enforceHasContractCode(_init, "DiamondStorageLibCut: _init address has no code"); } (bool success, bytes memory error) = _init.delegatecall(_calldata); if (!success) { if (error.length > 0) { // bubble up the error revert(string(error)); } else { revert("DiamondStorageLibCut: _init function reverted"); } } } } function enforceHasContractCode(address _contract, string memory _errorMessage) internal view { uint256 contractSize; assembly { contractSize := extcodesize(_contract) } require(contractSize > 0, _errorMessage); } }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
/**
* @title Liquidity Book Constants Library
* @author Trader Joe
* @notice Set of constants for Liquidity Book contracts
*/
library Constants {
uint8 internal constant SCALE_OFFSET = 128;
uint256 internal constant SCALE = 1 << SCALE_OFFSET;
uint256 internal constant PRECISION = 1e18;
uint256 internal constant SQUARED_PRECISION = PRECISION * PRECISION;
uint256 internal constant MAX_FEE = 0.1e18; // 10%
uint256 internal constant MAX_PROTOCOL_SHARE = 2_500; // 25% of the fee
uint256 internal constant BASIS_POINT_MAX = 10_000;
/// @dev The expected return after a successful flash loan
bytes32 internal constant CALLBACK_SUCCESS = keccak256("LBPair.onFlashLoan");
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
/**
* @title Liquidity Book Bit Math Library
* @author Trader Joe
* @notice Helper contract used for bit calculations
*/
library BitMath {
/**
* @dev Returns the index of the closest bit on the right of x that is non null
* @param x The value as a uint256
* @param bit The index of the bit to start searching at
* @return id The index of the closest non null bit on the right of x.
* If there is no closest bit, it returns max(uint256)
*/
function closestBitRight(uint256 x, uint8 bit) internal pure returns (uint256 id) {
unchecked {
uint256 shift = 255 - bit;
x <<= shift;
// can't overflow as it's non-zero and we shifted it by `_shift`
return (x == 0) ? type(uint256).max : mostSignificantBit(x) - shift;
}
}
/**
* @dev Returns the index of the closest bit on the left of x that is non null
* @param x The value as a uint256
* @param bit The index of the bit to start searching at
* @return id The index of the closest non null bit on the left of x.
* If there is no closest bit, it returns max(uint256)
*/
function closestBitLeft(uint256 x, uint8 bit) internal pure returns (uint256 id) {
unchecked {
x >>= bit;
return (x == 0) ? type(uint256).max : leastSignificantBit(x) + bit;
}
}
/**
* @dev Returns the index of the most significant bit of x
* This function returns 0 if x is 0
* @param x The value as a uint256
* @return msb The index of the most significant bit of x
*/
function mostSignificantBit(uint256 x) internal pure returns (uint8 msb) {
assembly {
if gt(x, 0xffffffffffffffffffffffffffffffff) {
x := shr(128, x)
msb := 128
}
if gt(x, 0xffffffffffffffff) {
x := shr(64, x)
msb := add(msb, 64)
}
if gt(x, 0xffffffff) {
x := shr(32, x)
msb := add(msb, 32)
}
if gt(x, 0xffff) {
x := shr(16, x)
msb := add(msb, 16)
}
if gt(x, 0xff) {
x := shr(8, x)
msb := add(msb, 8)
}
if gt(x, 0xf) {
x := shr(4, x)
msb := add(msb, 4)
}
if gt(x, 0x3) {
x := shr(2, x)
msb := add(msb, 2)
}
if gt(x, 0x1) { msb := add(msb, 1) }
}
}
/**
* @dev Returns the index of the least significant bit of x
* This function returns 255 if x is 0
* @param x The value as a uint256
* @return lsb The index of the least significant bit of x
*/
function leastSignificantBit(uint256 x) internal pure returns (uint8 lsb) {
assembly {
let sx := shl(128, x)
if iszero(iszero(sx)) {
lsb := 128
x := sx
}
sx := shl(64, x)
if iszero(iszero(sx)) {
x := sx
lsb := add(lsb, 64)
}
sx := shl(32, x)
if iszero(iszero(sx)) {
x := sx
lsb := add(lsb, 32)
}
sx := shl(16, x)
if iszero(iszero(sx)) {
x := sx
lsb := add(lsb, 16)
}
sx := shl(8, x)
if iszero(iszero(sx)) {
x := sx
lsb := add(lsb, 8)
}
sx := shl(4, x)
if iszero(iszero(sx)) {
x := sx
lsb := add(lsb, 4)
}
sx := shl(2, x)
if iszero(iszero(sx)) {
x := sx
lsb := add(lsb, 2)
}
if iszero(iszero(shl(1, x))) { lsb := add(lsb, 1) }
lsb := sub(255, lsb)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
/**
* @title Liquidity Book Safe Cast Library
* @author Trader Joe
* @notice This library contains functions to safely cast uint256 to different uint types.
*/
library SafeCast {
error SafeCast__Exceeds248Bits();
error SafeCast__Exceeds240Bits();
error SafeCast__Exceeds232Bits();
error SafeCast__Exceeds224Bits();
error SafeCast__Exceeds216Bits();
error SafeCast__Exceeds208Bits();
error SafeCast__Exceeds200Bits();
error SafeCast__Exceeds192Bits();
error SafeCast__Exceeds184Bits();
error SafeCast__Exceeds176Bits();
error SafeCast__Exceeds168Bits();
error SafeCast__Exceeds160Bits();
error SafeCast__Exceeds152Bits();
error SafeCast__Exceeds144Bits();
error SafeCast__Exceeds136Bits();
error SafeCast__Exceeds128Bits();
error SafeCast__Exceeds120Bits();
error SafeCast__Exceeds112Bits();
error SafeCast__Exceeds104Bits();
error SafeCast__Exceeds96Bits();
error SafeCast__Exceeds88Bits();
error SafeCast__Exceeds80Bits();
error SafeCast__Exceeds72Bits();
error SafeCast__Exceeds64Bits();
error SafeCast__Exceeds56Bits();
error SafeCast__Exceeds48Bits();
error SafeCast__Exceeds40Bits();
error SafeCast__Exceeds32Bits();
error SafeCast__Exceeds24Bits();
error SafeCast__Exceeds16Bits();
error SafeCast__Exceeds8Bits();
/**
* @dev Returns x on uint248 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint248
*/
function safe248(uint256 x) internal pure returns (uint248 y) {
if ((y = uint248(x)) != x) revert SafeCast__Exceeds248Bits();
}
/**
* @dev Returns x on uint240 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint240
*/
function safe240(uint256 x) internal pure returns (uint240 y) {
if ((y = uint240(x)) != x) revert SafeCast__Exceeds240Bits();
}
/**
* @dev Returns x on uint232 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint232
*/
function safe232(uint256 x) internal pure returns (uint232 y) {
if ((y = uint232(x)) != x) revert SafeCast__Exceeds232Bits();
}
/**
* @dev Returns x on uint224 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint224
*/
function safe224(uint256 x) internal pure returns (uint224 y) {
if ((y = uint224(x)) != x) revert SafeCast__Exceeds224Bits();
}
/**
* @dev Returns x on uint216 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint216
*/
function safe216(uint256 x) internal pure returns (uint216 y) {
if ((y = uint216(x)) != x) revert SafeCast__Exceeds216Bits();
}
/**
* @dev Returns x on uint208 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint208
*/
function safe208(uint256 x) internal pure returns (uint208 y) {
if ((y = uint208(x)) != x) revert SafeCast__Exceeds208Bits();
}
/**
* @dev Returns x on uint200 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint200
*/
function safe200(uint256 x) internal pure returns (uint200 y) {
if ((y = uint200(x)) != x) revert SafeCast__Exceeds200Bits();
}
/**
* @dev Returns x on uint192 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint192
*/
function safe192(uint256 x) internal pure returns (uint192 y) {
if ((y = uint192(x)) != x) revert SafeCast__Exceeds192Bits();
}
/**
* @dev Returns x on uint184 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint184
*/
function safe184(uint256 x) internal pure returns (uint184 y) {
if ((y = uint184(x)) != x) revert SafeCast__Exceeds184Bits();
}
/**
* @dev Returns x on uint176 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint176
*/
function safe176(uint256 x) internal pure returns (uint176 y) {
if ((y = uint176(x)) != x) revert SafeCast__Exceeds176Bits();
}
/**
* @dev Returns x on uint168 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint168
*/
function safe168(uint256 x) internal pure returns (uint168 y) {
if ((y = uint168(x)) != x) revert SafeCast__Exceeds168Bits();
}
/**
* @dev Returns x on uint160 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint160
*/
function safe160(uint256 x) internal pure returns (uint160 y) {
if ((y = uint160(x)) != x) revert SafeCast__Exceeds160Bits();
}
/**
* @dev Returns x on uint152 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint152
*/
function safe152(uint256 x) internal pure returns (uint152 y) {
if ((y = uint152(x)) != x) revert SafeCast__Exceeds152Bits();
}
/**
* @dev Returns x on uint144 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint144
*/
function safe144(uint256 x) internal pure returns (uint144 y) {
if ((y = uint144(x)) != x) revert SafeCast__Exceeds144Bits();
}
/**
* @dev Returns x on uint136 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint136
*/
function safe136(uint256 x) internal pure returns (uint136 y) {
if ((y = uint136(x)) != x) revert SafeCast__Exceeds136Bits();
}
/**
* @dev Returns x on uint128 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint128
*/
function safe128(uint256 x) internal pure returns (uint128 y) {
if ((y = uint128(x)) != x) revert SafeCast__Exceeds128Bits();
}
/**
* @dev Returns x on uint120 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint120
*/
function safe120(uint256 x) internal pure returns (uint120 y) {
if ((y = uint120(x)) != x) revert SafeCast__Exceeds120Bits();
}
/**
* @dev Returns x on uint112 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint112
*/
function safe112(uint256 x) internal pure returns (uint112 y) {
if ((y = uint112(x)) != x) revert SafeCast__Exceeds112Bits();
}
/**
* @dev Returns x on uint104 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint104
*/
function safe104(uint256 x) internal pure returns (uint104 y) {
if ((y = uint104(x)) != x) revert SafeCast__Exceeds104Bits();
}
/**
* @dev Returns x on uint96 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint96
*/
function safe96(uint256 x) internal pure returns (uint96 y) {
if ((y = uint96(x)) != x) revert SafeCast__Exceeds96Bits();
}
/**
* @dev Returns x on uint88 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint88
*/
function safe88(uint256 x) internal pure returns (uint88 y) {
if ((y = uint88(x)) != x) revert SafeCast__Exceeds88Bits();
}
/**
* @dev Returns x on uint80 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint80
*/
function safe80(uint256 x) internal pure returns (uint80 y) {
if ((y = uint80(x)) != x) revert SafeCast__Exceeds80Bits();
}
/**
* @dev Returns x on uint72 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint72
*/
function safe72(uint256 x) internal pure returns (uint72 y) {
if ((y = uint72(x)) != x) revert SafeCast__Exceeds72Bits();
}
/**
* @dev Returns x on uint64 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint64
*/
function safe64(uint256 x) internal pure returns (uint64 y) {
if ((y = uint64(x)) != x) revert SafeCast__Exceeds64Bits();
}
/**
* @dev Returns x on uint56 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint56
*/
function safe56(uint256 x) internal pure returns (uint56 y) {
if ((y = uint56(x)) != x) revert SafeCast__Exceeds56Bits();
}
/**
* @dev Returns x on uint48 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint48
*/
function safe48(uint256 x) internal pure returns (uint48 y) {
if ((y = uint48(x)) != x) revert SafeCast__Exceeds48Bits();
}
/**
* @dev Returns x on uint40 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint40
*/
function safe40(uint256 x) internal pure returns (uint40 y) {
if ((y = uint40(x)) != x) revert SafeCast__Exceeds40Bits();
}
/**
* @dev Returns x on uint32 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint32
*/
function safe32(uint256 x) internal pure returns (uint32 y) {
if ((y = uint32(x)) != x) revert SafeCast__Exceeds32Bits();
}
/**
* @dev Returns x on uint24 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint24
*/
function safe24(uint256 x) internal pure returns (uint24 y) {
if ((y = uint24(x)) != x) revert SafeCast__Exceeds24Bits();
}
/**
* @dev Returns x on uint16 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint16
*/
function safe16(uint256 x) internal pure returns (uint16 y) {
if ((y = uint16(x)) != x) revert SafeCast__Exceeds16Bits();
}
/**
* @dev Returns x on uint8 and check that it does not overflow
* @param x The value as an uint256
* @return y The value as an uint8
*/
function safe8(uint256 x) internal pure returns (uint8 y) {
if ((y = uint8(x)) != x) revert SafeCast__Exceeds8Bits();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {Constants} from "../Constants.sol";
import {BitMath} from "./BitMath.sol";
/**
* @title Liquidity Book Uint128x128 Math Library
* @author Trader Joe
* @notice Helper contract used for power and log calculations
*/
library Uint128x128Math {
using BitMath for uint256;
error Uint128x128Math__LogUnderflow();
error Uint128x128Math__PowUnderflow(uint256 x, int256 y);
uint256 constant LOG_SCALE_OFFSET = 127;
uint256 constant LOG_SCALE = 1 << LOG_SCALE_OFFSET;
uint256 constant LOG_SCALE_SQUARED = LOG_SCALE * LOG_SCALE;
/**
* @notice Calculates the binary logarithm of x.
* @dev Based on the iterative approximation algorithm.
* https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
* Requirements:
* - x must be greater than zero.
* Caveats:
* - The results are not perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation
* Also because x is converted to an unsigned 129.127-binary fixed-point number during the operation to optimize the multiplication
* @param x The unsigned 128.128-binary fixed-point number for which to calculate the binary logarithm.
* @return result The binary logarithm as a signed 128.128-binary fixed-point number.
*/
function log2(uint256 x) internal pure returns (int256 result) {
// Convert x to a unsigned 129.127-binary fixed-point number to optimize the multiplication.
// If we use an offset of 128 bits, y would need 129 bits and y**2 would would overflow and we would have to
// use mulDiv, by reducing x to 129.127-binary fixed-point number we assert that y will use 128 bits, and we
// can use the regular multiplication
if (x == 1) return -128;
if (x == 0) revert Uint128x128Math__LogUnderflow();
x >>= 1;
unchecked {
// This works because log2(x) = -log2(1/x).
int256 sign;
if (x >= LOG_SCALE) {
sign = 1;
} else {
sign = -1;
// Do the fixed-point inversion inline to save gas
x = LOG_SCALE_SQUARED / x;
}
// Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n).
uint256 n = (x >> LOG_SCALE_OFFSET).mostSignificantBit();
// The integer part of the logarithm as a signed 129.127-binary fixed-point number. The operation can't overflow
// because n is maximum 255, LOG_SCALE_OFFSET is 127 bits and sign is either 1 or -1.
result = int256(n) << LOG_SCALE_OFFSET;
// This is y = x * 2^(-n).
uint256 y = x >> n;
// If y = 1, the fractional part is zero.
if (y != LOG_SCALE) {
// Calculate the fractional part via the iterative approximation.
// The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster.
for (int256 delta = int256(1 << (LOG_SCALE_OFFSET - 1)); delta > 0; delta >>= 1) {
y = (y * y) >> LOG_SCALE_OFFSET;
// Is y^2 > 2 and so in the range [2,4)?
if (y >= 1 << (LOG_SCALE_OFFSET + 1)) {
// Add the 2^(-m) factor to the logarithm.
result += delta;
// Corresponds to z/2 on Wikipedia.
y >>= 1;
}
}
}
// Convert x back to unsigned 128.128-binary fixed-point number
result = (result * sign) << 1;
}
}
/**
* @notice Returns the value of x^y. It calculates `1 / x^abs(y)` if x is bigger than 2^128.
* At the end of the operations, we invert the result if needed.
* @param x The unsigned 128.128-binary fixed-point number for which to calculate the power
* @param y A relative number without any decimals, needs to be between ]2^21; 2^21[
*/
function pow(uint256 x, int256 y) internal pure returns (uint256 result) {
bool invert;
uint256 absY;
if (y == 0) return Constants.SCALE;
assembly {
absY := y
if slt(absY, 0) {
absY := sub(0, absY)
invert := iszero(invert)
}
}
if (absY < 0x100000) {
result = Constants.SCALE;
assembly {
let squared := x
if gt(x, 0xffffffffffffffffffffffffffffffff) {
squared := div(not(0), squared)
invert := iszero(invert)
}
if and(absY, 0x1) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x2) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x4) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x8) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x10) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x20) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x40) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x80) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x100) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x200) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x400) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x800) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x1000) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x2000) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x4000) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x8000) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x10000) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x20000) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x40000) { result := shr(128, mul(result, squared)) }
squared := shr(128, mul(squared, squared))
if and(absY, 0x80000) { result := shr(128, mul(result, squared)) }
}
}
// revert if y is too big or if x^y underflowed
if (result == 0) revert Uint128x128Math__PowUnderflow(x, y);
return invert ? type(uint256).max / result : result;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
/**
* @title Liquidity Book Uint256x256 Math Library
* @author Trader Joe
* @notice Helper contract used for full precision calculations
*/
library Uint256x256Math {
error Uint256x256Math__MulShiftOverflow();
error Uint256x256Math__MulDivOverflow();
/**
* @notice Calculates floor(x*y/denominator) with full precision
* The result will be rounded down
* @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
* Requirements:
* - The denominator cannot be zero
* - The result must fit within uint256
* Caveats:
* - This function does not work with fixed-point numbers
* @param x The multiplicand as an uint256
* @param y The multiplier as an uint256
* @param denominator The divisor as an uint256
* @return result The result as an uint256
*/
function mulDivRoundDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
(uint256 prod0, uint256 prod1) = _getMulProds(x, y);
return _getEndOfDivRoundDown(x, y, denominator, prod0, prod1);
}
/**
* @notice Calculates ceil(x*y/denominator) with full precision
* The result will be rounded up
* @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
* Requirements:
* - The denominator cannot be zero
* - The result must fit within uint256
* Caveats:
* - This function does not work with fixed-point numbers
* @param x The multiplicand as an uint256
* @param y The multiplier as an uint256
* @param denominator The divisor as an uint256
* @return result The result as an uint256
*/
function mulDivRoundUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
result = mulDivRoundDown(x, y, denominator);
if (mulmod(x, y, denominator) != 0) result += 1;
}
/**
* @notice Calculates floor(x * y / 2**offset) with full precision
* The result will be rounded down
* @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
* Requirements:
* - The offset needs to be strictly lower than 256
* - The result must fit within uint256
* Caveats:
* - This function does not work with fixed-point numbers
* @param x The multiplicand as an uint256
* @param y The multiplier as an uint256
* @param offset The offset as an uint256, can't be greater than 256
* @return result The result as an uint256
*/
function mulShiftRoundDown(uint256 x, uint256 y, uint8 offset) internal pure returns (uint256 result) {
(uint256 prod0, uint256 prod1) = _getMulProds(x, y);
if (prod0 != 0) result = prod0 >> offset;
if (prod1 != 0) {
// Make sure the result is less than 2^256.
if (prod1 >= 1 << offset) revert Uint256x256Math__MulShiftOverflow();
unchecked {
result += prod1 << (256 - offset);
}
}
}
/**
* @notice Calculates floor(x * y / 2**offset) with full precision
* The result will be rounded down
* @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
* Requirements:
* - The offset needs to be strictly lower than 256
* - The result must fit within uint256
* Caveats:
* - This function does not work with fixed-point numbers
* @param x The multiplicand as an uint256
* @param y The multiplier as an uint256
* @param offset The offset as an uint256, can't be greater than 256
* @return result The result as an uint256
*/
function mulShiftRoundUp(uint256 x, uint256 y, uint8 offset) internal pure returns (uint256 result) {
result = mulShiftRoundDown(x, y, offset);
if (mulmod(x, y, 1 << offset) != 0) result += 1;
}
/**
* @notice Calculates floor(x << offset / y) with full precision
* The result will be rounded down
* @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
* Requirements:
* - The offset needs to be strictly lower than 256
* - The result must fit within uint256
* Caveats:
* - This function does not work with fixed-point numbers
* @param x The multiplicand as an uint256
* @param offset The number of bit to shift x as an uint256
* @param denominator The divisor as an uint256
* @return result The result as an uint256
*/
function shiftDivRoundDown(uint256 x, uint8 offset, uint256 denominator) internal pure returns (uint256 result) {
uint256 prod0;
uint256 prod1;
prod0 = x << offset; // Least significant 256 bits of the product
unchecked {
prod1 = x >> (256 - offset); // Most significant 256 bits of the product
}
return _getEndOfDivRoundDown(x, 1 << offset, denominator, prod0, prod1);
}
/**
* @notice Calculates ceil(x << offset / y) with full precision
* The result will be rounded up
* @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
* Requirements:
* - The offset needs to be strictly lower than 256
* - The result must fit within uint256
* Caveats:
* - This function does not work with fixed-point numbers
* @param x The multiplicand as an uint256
* @param offset The number of bit to shift x as an uint256
* @param denominator The divisor as an uint256
* @return result The result as an uint256
*/
function shiftDivRoundUp(uint256 x, uint8 offset, uint256 denominator) internal pure returns (uint256 result) {
result = shiftDivRoundDown(x, offset, denominator);
if (mulmod(x, 1 << offset, denominator) != 0) result += 1;
}
/**
* @notice Helper function to return the result of `x * y` as 2 uint256
* @param x The multiplicand as an uint256
* @param y The multiplier as an uint256
* @return prod0 The least significant 256 bits of the product
* @return prod1 The most significant 256 bits of the product
*/
function _getMulProds(uint256 x, uint256 y) private pure returns (uint256 prod0, uint256 prod1) {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
}
/**
* @notice Helper function to return the result of `x * y / denominator` with full precision
* @param x The multiplicand as an uint256
* @param y The multiplier as an uint256
* @param denominator The divisor as an uint256
* @param prod0 The least significant 256 bits of the product
* @param prod1 The most significant 256 bits of the product
* @return result The result as an uint256
*/
function _getEndOfDivRoundDown(uint256 x, uint256 y, uint256 denominator, uint256 prod0, uint256 prod1)
private
pure
returns (uint256 result)
{
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
unchecked {
result = prod0 / denominator;
}
} else {
// Make sure the result is less than 2^256. Also prevents denominator == 0
if (prod1 >= denominator) revert Uint256x256Math__MulDivOverflow();
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1
// See https://cs.stackexchange.com/q/138556/92363
unchecked {
// Does not overflow because the denominator cannot be zero at this stage in the function
uint256 lpotdod = denominator & (~denominator + 1);
assembly {
// Divide denominator by lpotdod.
denominator := div(denominator, lpotdod)
// Divide [prod1 prod0] by lpotdod.
prod0 := div(prod0, lpotdod)
// Flip lpotdod such that it is 2^256 / lpotdod. If lpotdod is zero, then it becomes one
lpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
}
// Shift in bits from prod1 into prod0
prod0 |= prod1 * lpotdod;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;
import {Uint128x128Math} from "./math/Uint128x128Math.sol";
import {Uint256x256Math} from "./math/Uint256x256Math.sol";
import {SafeCast} from "./math/SafeCast.sol";
import {Constants} from "./Constants.sol";
/**
* @title Liquidity Book Price Helper Library
* @author Trader Joe
* @notice This library contains functions to calculate prices
*/
library PriceHelper {
using Uint128x128Math for uint256;
using Uint256x256Math for uint256;
using SafeCast for uint256;
int256 private constant REAL_ID_SHIFT = 1 << 23;
/**
* @dev Calculates the price from the id and the bin step
* @param id The id
* @param binStep The bin step
* @return price The price as a 128.128-binary fixed-point number
*/
function getPriceFromId(uint24 id, uint16 binStep) internal pure returns (uint256 price) {
uint256 base = getBase(binStep);
int256 exponent = getExponent(id);
price = base.pow(exponent);
}
/**
* @dev Calculates the id from the price and the bin step
* @param price The price as a 128.128-binary fixed-point number
* @param binStep The bin step
* @return id The id
*/
function getIdFromPrice(uint256 price, uint16 binStep) internal pure returns (uint24 id) {
uint256 base = getBase(binStep);
int256 realId = price.log2() / base.log2();
unchecked {
id = uint256(REAL_ID_SHIFT + realId).safe24();
}
}
/**
* @dev Calculates the base from the bin step, which is `1 + binStep / BASIS_POINT_MAX`
* @param binStep The bin step
* @return base The base
*/
function getBase(uint16 binStep) internal pure returns (uint256) {
unchecked {
return Constants.SCALE + (uint256(binStep) << Constants.SCALE_OFFSET) / Constants.BASIS_POINT_MAX;
}
}
/**
* @dev Calculates the exponent from the id, which is `id - REAL_ID_SHIFT`
* @param id The id
* @return exponent The exponent
*/
function getExponent(uint24 id) internal pure returns (int256) {
unchecked {
return int256(uint256(id)) - REAL_ID_SHIFT;
}
}
/**
* @dev Converts a price with 18 decimals to a 128.128-binary fixed-point number
* @param price The price with 18 decimals
* @return price128x128 The 128.128-binary fixed-point number
*/
function convertDecimalPriceTo128x128(uint256 price) internal pure returns (uint256) {
return price.shiftDivRoundDown(Constants.SCALE_OFFSET, Constants.PRECISION);
}
/**
* @dev Converts a 128.128-binary fixed-point number to a price with 18 decimals
* @param price128x128 The 128.128-binary fixed-point number
* @return price The price with 18 decimals
*/
function convert128x128PriceToDecimal(uint256 price128x128) internal pure returns (uint256) {
return price128x128.mulShiftRoundDown(Constants.PRECISION, Constants.SCALE_OFFSET);
}
}// SPDX-License-Identifier: BUSL-1.1
// Last deployed from commit: ;
pragma solidity 0.8.17;
import "@redstone-finance/evm-connector/contracts/core/ProxyConnector.sol";
import "../facets/SolvencyFacetProd.sol";
import "../facets/AssetsExposureController.sol";
import "../DiamondHelper.sol";
// TODO Rename to contract instead of lib
contract SolvencyMethods is DiamondHelper, ProxyConnector {
// This function executes SolvencyFacetProd.getDebt()
function _getDebt() internal virtual returns (uint256 debt) {
debt = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.getDebt.selector),
abi.encodeWithSelector(SolvencyFacetProd.getDebt.selector)
),
(uint256)
);
}
// This function executes SolvencyFacetProd.getDebtWithPrices()
function _getDebtWithPrices(SolvencyFacetProd.AssetPrice[] memory debtAssetsPrices) internal virtual returns (uint256 debt) {
debt = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.getDebtWithPrices.selector),
abi.encodeWithSelector(SolvencyFacetProd.getDebtWithPrices.selector, debtAssetsPrices)
),
(uint256)
);
}
// This function executes SolvencyFacetProd.isSolventWithPrices()
function _isSolventWithPrices(SolvencyFacetProd.CachedPrices memory cachedPrices) internal virtual returns (bool solvent){
solvent = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.isSolventWithPrices.selector),
abi.encodeWithSelector(SolvencyFacetProd.isSolventWithPrices.selector, cachedPrices)
),
(bool)
);
}
// This function executes SolvencyFacetProd.isSolvent()
function _isSolvent() internal virtual returns (bool solvent){
solvent = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.isSolvent.selector),
abi.encodeWithSelector(SolvencyFacetProd.isSolvent.selector)
),
(bool)
);
}
// This function executes SolvencyFacetProd.canRepayDebtFully()
function _canRepayDebtFully() internal virtual returns (bool solvent){
solvent = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.canRepayDebtFully.selector),
abi.encodeWithSelector(SolvencyFacetProd.canRepayDebtFully.selector)
),
(bool)
);
}
// This function executes SolvencyFacetProd.getTotalValue()
function _getTotalValue() internal virtual returns (uint256 totalValue) {
totalValue = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.getTotalValue.selector),
abi.encodeWithSelector(SolvencyFacetProd.getTotalValue.selector)
),
(uint256)
);
}
// This function executes SolvencyFacetProd.getTotalAssetsValue()
function _getTotalAssetsValue() internal virtual returns (uint256 assetsValue) {
assetsValue = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.getTotalAssetsValue.selector),
abi.encodeWithSelector(SolvencyFacetProd.getTotalAssetsValue.selector)
),
(uint256)
);
}
// This function executes SolvencyFacetProd.getHealthRatioWithPrices()
function _getHealthRatioWithPrices(SolvencyFacetProd.CachedPrices memory cachedPrices) public virtual returns (uint256 health) {
health = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.getHealthRatioWithPrices.selector),
abi.encodeWithSelector(SolvencyFacetProd.getHealthRatioWithPrices.selector, cachedPrices)
),
(uint256)
);
}
// This function executes SolvencyFacetProd.getHealthRatio()
function _getHealthRatio() public virtual returns (uint256 health) {
health = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.getHealthRatio.selector),
abi.encodeWithSelector(SolvencyFacetProd.getHealthRatio.selector)
),
(uint256)
);
}
// This function executes SolvencyFacetProd.getPrices()
function getPrices(bytes32[] memory symbols) public virtual returns (uint256[] memory prices) {
prices = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.getPrices.selector),
abi.encodeWithSelector(SolvencyFacetProd.getPrices.selector, symbols)
),
(uint256[])
);
}
// This function executes SolvencyFacetProd.getPrices()
function _getAllPricesForLiquidation(bytes32[] memory assetsToRepay) public virtual returns (SolvencyFacetProd.CachedPrices memory result) {
result = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.getAllPricesForLiquidation.selector),
abi.encodeWithSelector(SolvencyFacetProd.getAllPricesForLiquidation.selector, assetsToRepay)
),
(SolvencyFacetProd.CachedPrices)
);
}
// This function executes SolvencyFacetProd.getOwnedAssetsWithNativePrices()
function _getOwnedAssetsWithNativePrices() internal virtual returns (SolvencyFacetProd.AssetPrice[] memory ownedAssetsPrices) {
ownedAssetsPrices = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.getOwnedAssetsWithNativePrices.selector),
abi.encodeWithSelector(SolvencyFacetProd.getOwnedAssetsWithNativePrices.selector)
),
(SolvencyFacetProd.AssetPrice[])
);
}
// This function executes SolvencyFacetProd.getDebtAssetsPrices()
function _getDebtAssetsPrices() internal virtual returns (SolvencyFacetProd.AssetPrice[] memory debtAssetsPrices) {
debtAssetsPrices = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.getDebtAssetsPrices.selector),
abi.encodeWithSelector(SolvencyFacetProd.getDebtAssetsPrices.selector)
),
(SolvencyFacetProd.AssetPrice[])
);
}
// This function executes SolvencyFacetProd.getStakedPositionsPrices()
function _getStakedPositionsPrices() internal virtual returns (SolvencyFacetProd.AssetPrice[] memory stakedPositionsPrices) {
stakedPositionsPrices = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.getStakedPositionsPrices.selector),
abi.encodeWithSelector(SolvencyFacetProd.getStakedPositionsPrices.selector)
),
(SolvencyFacetProd.AssetPrice[])
);
}
// This function executes SolvencyFacetProd.getTotalAssetsValueWithPrices()
function _getTotalValueWithPrices(SolvencyFacetProd.AssetPrice[] memory ownedAssetsPrices, SolvencyFacetProd.AssetPrice[] memory stakedPositionsPrices) internal virtual returns (uint256 totalValue) {
totalValue = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.getTotalValueWithPrices.selector),
abi.encodeWithSelector(SolvencyFacetProd.getTotalValueWithPrices.selector, ownedAssetsPrices, stakedPositionsPrices)
),
(uint256)
);
}
// This function executes SolvencyFacetProd.getPrices()
function getPrice(bytes32 symbol) public virtual returns (uint256 price) {
price = abi.decode(
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(SolvencyFacetProd.getPrice.selector),
abi.encodeWithSelector(SolvencyFacetProd.getPrice.selector, symbol)
),
(uint256)
);
}
// This function executes AssetsExposureController.decreaseAssetsExposure()
function _resetPrimeAccountAssetsExposure() public {
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(AssetsExposureController.resetPrimeAccountAssetsExposure.selector),
abi.encodeWithSelector(AssetsExposureController.resetPrimeAccountAssetsExposure.selector)
);
}
// This function executes AssetsExposureController.increaseAssetsExposure()
function _setPrimeAccountAssetsExposure() public {
proxyDelegateCalldata(
DiamondHelper._getFacetAddress(AssetsExposureController.setPrimeAccountAssetsExposure.selector),
abi.encodeWithSelector(AssetsExposureController.setPrimeAccountAssetsExposure.selector)
);
}
/**
* Returns IERC20Metadata instance of a token
* @param _asset the code of an asset
**/
function getERC20TokenInstance(bytes32 _asset, bool allowInactive) internal view returns (IERC20Metadata) {
return IERC20Metadata(DeploymentConstants.getTokenManager().getAssetAddress(_asset, allowInactive));
}
modifier recalculateAssetsExposure() {
_resetPrimeAccountAssetsExposure();
_;
_setPrimeAccountAssetsExposure();
}
/**
* Checks whether account is solvent (health higher than 1)
* @dev This modifier uses the redstone-evm-connector
**/
modifier remainsSolvent() {
_;
require(_isSolvent(), "The action may cause an account to become insolvent");
}
modifier canRepayDebtFully() {
_;
require(_canRepayDebtFully(), "Insufficient assets to fully repay the debt");
}
modifier noBorrowInTheSameBlock() {
DiamondStorageLib.DiamondStorage storage ds = DiamondStorageLib.diamondStorage();
require(ds._lastBorrowTimestamp != block.timestamp, "Borrowing must happen in a standalone transaction");
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
/// @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.
//TODO: our change
//https://ethereum.stackexchange.com/questions/96642/unary-operator-minus-cannot-be-applied-to-type-uint256
// uint256 twos = -denominator & denominator;
uint256 twos = denominator & (~denominator + 1);
// 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 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(
//TODO: changed uint256 casting to uint160(uint)
uint160(uint(
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.8.17;
/// @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));
//TODO: conversion int24 -> int256: check
require(absTick <= uint256(int256(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: UNLICENSED
pragma solidity ^0.8.0;
import {FullMath} from "./FullMath.sol";
library UniswapV3IntegrationHelper {
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
//TODO: check what happens to signed
function sqrt(uint y) internal pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
//source: https://ethereum.stackexchange.com/questions/98685/computing-the-uniswap-v3-pair-price-from-q64-96-number
function sqrtPriceX96ToUint(uint160 sqrtPriceX96, uint8 decimalsToken0)
internal
view //TODO: pure
returns (uint256)
{
{
uint256 numerator1 = uint256(sqrtPriceX96) * uint256(sqrtPriceX96);
uint256 numerator2 = 10**decimalsToken0;
}
uint256 numerator1 = uint256(sqrtPriceX96);
uint256 numerator2 = 10**decimalsToken0;
return FullMath.mulDiv(numerator1, numerator2, 2 ** 96);
}
}// SPDX-License-Identifier: BUSL-1.1
// Last deployed from commit: b75e073cf23a3eb181f55a89a800ef040b7ba456;
pragma solidity 0.8.17;
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@uniswap/lib/contracts/libraries/TransferHelper.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "./interfaces/IIndex.sol";
import "./interfaces/IRatesCalculator.sol";
import "./interfaces/IBorrowersRegistry.sol";
import "./interfaces/IPoolRewarder.sol";
import "./VestingDistributor.sol";
/**
* @title Pool
* @dev Contract allowing user to deposit to and borrow from a dedicated user account
* Depositors are rewarded with the interest rates collected from borrowers.
* The interest rates calculation is delegated to an external calculator contract.
*/
contract Pool is OwnableUpgradeable, ReentrancyGuardUpgradeable, IERC20 {
using TransferHelper for address payable;
uint256 public totalSupplyCap;
mapping(address => mapping(address => uint256)) private _allowed;
mapping(address => uint256) internal _deposited;
mapping(address => uint256) public borrowed;
IRatesCalculator public ratesCalculator;
IBorrowersRegistry public borrowersRegistry;
IPoolRewarder public poolRewarder;
IIndex public depositIndex;
IIndex public borrowIndex;
address payable public tokenAddress;
VestingDistributor public vestingDistributor;
uint8 internal _decimals;
modifier onlyWhitelistedAccounts {
if(
msg.sender == 0x0E5Bad4108a6A5a8b06820f98026a7f3A77466b2 ||
msg.sender == 0x2fFA7E9624B923fA811d9B9995Aa34b715Db1945 ||
msg.sender == 0x0d7137feA34BC97819f05544Ec7DE5c98617989C ||
msg.sender == 0xC6ba6BB819f1Be84EFeB2E3f2697AD9818151e5D ||
msg.sender == 0x14f69F9C351b798dF31fC53E33c09dD29bFAb547 ||
msg.sender == 0x5C23Bd1BD272D22766eB3708B8f874CB93B75248 ||
msg.sender == 0x000000F406CA147030BE7069149e4a7423E3A264 ||
msg.sender == 0x5D80a1c0a5084163F1D2620c1B1F43209cd4dB12 ||
msg.sender == 0x6C21A841d6f029243AF87EF01f6772F05832144b
){
_;
} else {
revert("Not whitelisted");
}
}
function initialize(IRatesCalculator ratesCalculator_, IBorrowersRegistry borrowersRegistry_, IIndex depositIndex_, IIndex borrowIndex_, address payable tokenAddress_, IPoolRewarder poolRewarder_, uint256 _totalSupplyCap) public initializer {
require(AddressUpgradeable.isContract(address(ratesCalculator_))
&& AddressUpgradeable.isContract(address(borrowersRegistry_))
&& AddressUpgradeable.isContract(address(depositIndex_))
&& AddressUpgradeable.isContract(address(borrowIndex_))
&& (AddressUpgradeable.isContract(address(poolRewarder_)) || address(poolRewarder_) == address(0)), "Wrong init arguments");
borrowersRegistry = borrowersRegistry_;
ratesCalculator = ratesCalculator_;
depositIndex = depositIndex_;
borrowIndex = borrowIndex_;
poolRewarder = poolRewarder_;
tokenAddress = tokenAddress_;
totalSupplyCap = _totalSupplyCap;
_decimals = IERC20Metadata(tokenAddress_).decimals();
__Ownable_init();
__ReentrancyGuard_init();
_updateRates();
}
/* ========== SETTERS ========== */
/**
* Sets new totalSupplyCap limiting how much in total can be deposited to the Pool.
* Only the owner of the Contract can execute this function.
* @dev _newTotalSupplyCap new deposit cap
**/
function setTotalSupplyCap(uint256 _newTotalSupplyCap) external onlyOwner {
totalSupplyCap = _newTotalSupplyCap;
}
/**
* Sets the new Pool Rewarder.
* The IPoolRewarder that distributes additional token rewards to people having a stake in this pool proportionally to their stake and time of participance.
* Only the owner of the Contract can execute this function.
* @dev _poolRewarder the address of PoolRewarder
**/
function setPoolRewarder(IPoolRewarder _poolRewarder) external onlyOwner {
if(!AddressUpgradeable.isContract(address(_poolRewarder)) && address(_poolRewarder) != address(0)) revert NotAContract(address(poolRewarder));
poolRewarder = _poolRewarder;
emit PoolRewarderChanged(address(_poolRewarder), block.timestamp);
}
/**
* Sets the new rate calculator.
* The calculator is an external contract that contains the logic for calculating deposit and borrowing rates.
* Only the owner of the Contract can execute this function.
* @dev ratesCalculator the address of rates calculator
**/
function setRatesCalculator(IRatesCalculator ratesCalculator_) external onlyOwner {
// setting address(0) ratesCalculator_ freezes the pool
if(!AddressUpgradeable.isContract(address(ratesCalculator_)) && address(ratesCalculator_) != address(0)) revert NotAContract(address(ratesCalculator_));
ratesCalculator = ratesCalculator_;
if (address(ratesCalculator_) != address(0)) {
_updateRates();
}
emit RatesCalculatorChanged(address(ratesCalculator_), block.timestamp);
}
/**
* Sets the new borrowers registry contract.
* The borrowers registry decides if an account can borrow funds.
* Only the owner of the Contract can execute this function.
* @dev borrowersRegistry the address of borrowers registry
**/
function setBorrowersRegistry(IBorrowersRegistry borrowersRegistry_) external onlyOwner {
if(!AddressUpgradeable.isContract(address(borrowersRegistry_))) revert NotAContract(address(borrowersRegistry_));
borrowersRegistry = borrowersRegistry_;
emit BorrowersRegistryChanged(address(borrowersRegistry_), block.timestamp);
}
/**
* Sets the new Pool Rewarder.
* The IPoolRewarder that distributes additional token rewards to people having a stake in this pool proportionally to their stake and time of participance.
* Only the owner of the Contract can execute this function.
* @dev _poolRewarder the address of PoolRewarder
**/
function setVestingDistributor(address _distributor) external onlyOwner {
if(!AddressUpgradeable.isContract(_distributor) && _distributor != address(0)) revert NotAContract(_distributor);
vestingDistributor = VestingDistributor(_distributor);
emit VestingDistributorChanged(_distributor, block.timestamp);
}
/* ========== MUTATIVE FUNCTIONS ========== */
function transfer(address recipient, uint256 amount) external override returns (bool) {
if(recipient == address(0)) revert TransferToZeroAddress();
if(recipient == address(this)) revert TransferToPoolAddress();
address account = msg.sender;
_accumulateDepositInterest(account);
(uint256 lockedAmount, uint256 transferrableAmount) = _getAmounts(account);
if(amount > transferrableAmount) revert TransferAmountExceedsBalance(amount, transferrableAmount);
_updateWithdrawn(account, amount, lockedAmount);
// (this is verified in "require" above)
unchecked {
_deposited[account] -= amount;
}
_accumulateDepositInterest(recipient);
_deposited[recipient] += amount;
// Handle rewards
if(address(poolRewarder) != address(0) && amount != 0){
uint256 unstaked = poolRewarder.withdrawFor(amount, account);
if(unstaked > 0) {
poolRewarder.stakeFor(unstaked, recipient);
}
}
emit Transfer(account, recipient, amount);
return true;
}
function allowance(address owner, address spender) external view override returns (uint256) {
return _allowed[owner][spender];
}
function increaseAllowance(address spender, uint256 addedValue) external returns (bool) {
if(spender == address(0)) revert SpenderZeroAddress();
uint256 newAllowance = _allowed[msg.sender][spender] + addedValue;
_allowed[msg.sender][spender] = newAllowance;
emit Approval(msg.sender, spender, newAllowance);
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool) {
if(spender == address(0)) revert SpenderZeroAddress();
uint256 currentAllowance = _allowed[msg.sender][spender];
if(currentAllowance < subtractedValue) revert InsufficientAllowance(subtractedValue, currentAllowance);
uint256 newAllowance = currentAllowance - subtractedValue;
_allowed[msg.sender][spender] = newAllowance;
emit Approval(msg.sender, spender, newAllowance);
return true;
}
function approve(address spender, uint256 amount) external override returns (bool) {
if(spender == address(0)) revert SpenderZeroAddress();
_allowed[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) {
if(_allowed[sender][msg.sender] < amount) revert InsufficientAllowance(amount, _allowed[sender][msg.sender]);
if(recipient == address(0)) revert TransferToZeroAddress();
if(recipient == address(this)) revert TransferToPoolAddress();
_accumulateDepositInterest(sender);
(uint256 lockedAmount, uint256 transferrableAmount) = _getAmounts(sender);
if(amount > transferrableAmount) revert TransferAmountExceedsBalance(amount, transferrableAmount);
_updateWithdrawn(sender, amount, lockedAmount);
_deposited[sender] -= amount;
_allowed[sender][msg.sender] -= amount;
_accumulateDepositInterest(recipient);
_deposited[recipient] += amount;
// Handle rewards
if(address(poolRewarder) != address(0) && amount != 0){
uint256 unstaked = poolRewarder.withdrawFor(amount, sender);
if(unstaked > 0) {
poolRewarder.stakeFor(unstaked, recipient);
}
}
emit Transfer(sender, recipient, amount);
return true;
}
/**
* Deposits the amount
* It updates user deposited balance, total deposited and rates
**/
function deposit(uint256 _amount) public virtual onlyWhitelistedAccounts{
depositOnBehalf(_amount, msg.sender);
}
/**
* Deposits the amount on behalf of `_of` user.
* It updates `_of` user deposited balance, total deposited and rates
**/
function depositOnBehalf(uint256 _amount, address _of) public virtual nonReentrant onlyWhitelistedAccounts {
if(_amount == 0) revert ZeroDepositAmount();
require(_of != address(0), "Address zero");
require(_of != address(this), "Cannot deposit on behalf of pool");
_amount = Math.min(_amount, IERC20(tokenAddress).balanceOf(msg.sender));
_accumulateDepositInterest(_of);
if(totalSupplyCap != 0){
if(_deposited[address(this)] + _amount > totalSupplyCap) revert TotalSupplyCapBreached();
}
_transferToPool(msg.sender, _amount);
_mint(_of, _amount);
_deposited[address(this)] += _amount;
_updateRates();
if (address(poolRewarder) != address(0)) {
poolRewarder.stakeFor(_amount, _of);
}
emit DepositOnBehalfOf(msg.sender, _of, _amount, block.timestamp);
}
function _transferToPool(address from, uint256 amount) internal virtual {
tokenAddress.safeTransferFrom(from, address(this), amount);
}
function _transferFromPool(address to, uint256 amount) internal virtual {
tokenAddress.safeTransfer(to, amount);
}
/**
* Withdraws selected amount from the user deposits
* @dev _amount the amount to be withdrawn
**/
function withdraw(uint256 _amount) external nonReentrant onlyWhitelistedAccounts {
if(_amount > IERC20(tokenAddress).balanceOf(address(this))) revert InsufficientPoolFunds();
_accumulateDepositInterest(msg.sender);
if(_amount > _deposited[address(this)]) revert BurnAmountExceedsBalance();
// verified in "require" above
unchecked {
_deposited[address(this)] -= _amount;
}
_burn(msg.sender, _amount);
_updateRates();
_transferFromPool(msg.sender, _amount);
if (address(poolRewarder) != address(0)) {
poolRewarder.withdrawFor(_amount, msg.sender);
}
emit Withdrawal(msg.sender, _amount, block.timestamp);
}
/**
* Borrows the specified amount
* It updates user borrowed balance, total borrowed amount and rates
* @dev _amount the amount to be borrowed
* @dev It is only meant to be used by a SmartLoanDiamondProxy
**/
function borrow(uint256 _amount) public virtual canBorrow nonReentrant {
if (_amount > IERC20(tokenAddress).balanceOf(address(this))) revert InsufficientPoolFunds();
_accumulateBorrowingInterest(msg.sender);
borrowed[msg.sender] += _amount;
borrowed[address(this)] += _amount;
_transferFromPool(msg.sender, _amount);
_updateRates();
emit Borrowing(msg.sender, _amount, block.timestamp);
}
/**
* Repays the amount
* It updates user borrowed balance, total borrowed amount and rates
* @dev It is only meant to be used by a SmartLoanDiamondProxy
**/
function repay(uint256 amount) external nonReentrant {
_accumulateBorrowingInterest(msg.sender);
if(amount > borrowed[msg.sender]) revert RepayingMoreThanWasBorrowed();
_transferToPool(msg.sender, amount);
borrowed[msg.sender] -= amount;
borrowed[address(this)] -= amount;
_updateRates();
emit Repayment(msg.sender, amount, block.timestamp);
}
/* =========
/**
* Returns the current borrowed amount for the given user
* The value includes the interest rates owned at the current moment
* @dev _user the address of queried borrower
**/
function getBorrowed(address _user) public view returns (uint256) {
return borrowIndex.getIndexedValue(borrowed[_user], _user);
}
function name() public virtual pure returns(string memory _name){
_name = "";
}
function symbol() public virtual pure returns(string memory _symbol){
_symbol = "";
}
function decimals() public virtual view returns(uint8){
return _decimals;
}
function totalSupply() public view override returns (uint256) {
return balanceOf(address(this));
}
function totalBorrowed() public view returns (uint256) {
return getBorrowed(address(this));
}
// Calls the IPoolRewarder.getRewardsFor() that sends pending rewards to msg.sender
function getRewards() external {
poolRewarder.getRewardsFor(msg.sender);
}
// Returns number of pending rewards for msg.sender
function checkRewards() external view returns (uint256) {
return poolRewarder.earned(msg.sender);
}
// Returns max. acceptable pool utilisation after borrow action
function getMaxPoolUtilisationForBorrowing() virtual public view returns (uint256) {
return 0.9e18;
}
/**
* Returns the current deposited amount for the given user
* The value includes the interest rates earned at the current moment
* @dev _user the address of queried depositor
**/
function balanceOf(address user) public view override returns (uint256) {
return depositIndex.getIndexedValue(_deposited[user], user);
}
/**
* Returns the current interest rate for deposits
**/
function getDepositRate() public view returns (uint256) {
return ratesCalculator.calculateDepositRate(totalBorrowed(), totalSupply());
}
/**
* Returns the current interest rate for borrowings
**/
function getBorrowingRate() public view returns (uint256) {
return ratesCalculator.calculateBorrowingRate(totalBorrowed(), totalSupply());
}
/**
* Returns full pool status
*/
function getFullPoolStatus() public view returns (uint256[5] memory) {
return [
totalSupply(),
getDepositRate(),
getBorrowingRate(),
totalBorrowed(),
getMaxPoolUtilisationForBorrowing()
];
}
/**
* Recovers the surplus funds resultant from difference between deposit and borrowing rates
**/
function recoverSurplus(uint256 amount, address account) public onlyOwner nonReentrant {
uint256 balance = IERC20(tokenAddress).balanceOf(address(this));
uint256 surplus = balance + totalBorrowed() - totalSupply();
if(amount > balance) revert InsufficientPoolFunds();
if(surplus < amount) revert InsufficientSurplus();
_transferFromPool(account, amount);
}
/* ========== INTERNAL FUNCTIONS ========== */
function _mint(address to, uint256 amount) internal {
if(to == address(0)) revert MintToAddressZero();
_deposited[to] += amount;
emit Transfer(address(0), to, amount);
}
function _burn(address account, uint256 amount) internal {
if(amount > _deposited[account]) revert BurnAmountExceedsBalance();
(uint256 lockedAmount, uint256 transferrableAmount) = _getAmounts(account);
if(amount > transferrableAmount) revert BurnAmountExceedsAvailableForUser();
_updateWithdrawn(account, amount, lockedAmount);
// verified in "require" above
unchecked {
_deposited[account] -= amount;
}
emit Transfer(account, address(0), amount);
}
function _getAmounts(address account) internal view returns (uint256 lockedAmount, uint256 transferrableAmount) {
if (address(vestingDistributor) != address(0)) {
lockedAmount = vestingDistributor.locked(account);
transferrableAmount = _deposited[account] - (lockedAmount - vestingDistributor.availableToWithdraw(account));
} else {
transferrableAmount = _deposited[account];
}
}
function _updateWithdrawn(address account, uint256 amount, uint256 lockedAmount) internal {
uint256 availableUnvested = _deposited[account] - lockedAmount;
if (amount > availableUnvested && address(vestingDistributor) != address(0)) {
vestingDistributor.updateWithdrawn(account, amount - availableUnvested);
}
}
function _updateRates() internal {
uint256 _totalBorrowed = totalBorrowed();
uint256 _totalSupply = totalSupply();
if(address(ratesCalculator) == address(0)) revert PoolFrozen();
depositIndex.setRate(ratesCalculator.calculateDepositRate(_totalBorrowed, _totalSupply));
borrowIndex.setRate(ratesCalculator.calculateBorrowingRate(_totalBorrowed, _totalSupply));
}
function _accumulateDepositInterest(address user) internal {
uint256 interest = balanceOf(user) - _deposited[user];
_mint(user, interest);
_deposited[address(this)] = balanceOf(address(this));
emit InterestCollected(user, interest, block.timestamp);
depositIndex.updateUser(user);
depositIndex.updateUser(address(this));
}
function _accumulateBorrowingInterest(address user) internal {
borrowed[user] = getBorrowed(user);
borrowed[address(this)] = getBorrowed(address(this));
borrowIndex.updateUser(user);
borrowIndex.updateUser(address(this));
}
/* ========== OVERRIDDEN FUNCTIONS ========== */
function renounceOwnership() public virtual override {}
/* ========== MODIFIERS ========== */
modifier canBorrow() {
if(address(borrowersRegistry) == address(0)) revert BorrowersRegistryNotConfigured();
if(!borrowersRegistry.canBorrow(msg.sender)) revert NotAuthorizedToBorrow();
if(totalSupply() == 0) revert InsufficientPoolFunds();
_;
if((totalBorrowed() * 1e18) / totalSupply() > getMaxPoolUtilisationForBorrowing()) revert MaxPoolUtilisationBreached();
}
/* ========== EVENTS ========== */
/**
* @dev emitted after the user deposits funds
* @param user the address performing the deposit
* @param value the amount deposited
* @param timestamp of the deposit
**/
event Deposit(address indexed user, uint256 value, uint256 timestamp);
/**
* @dev emitted after the user deposits funds on behalf of other user
* @param user the address performing the deposit
* @param _of the address on behalf of which the deposit is being performed
* @param value the amount deposited
* @param timestamp of the deposit
**/
event DepositOnBehalfOf(address indexed user, address indexed _of, uint256 value, uint256 timestamp);
/**
* @dev emitted after the user withdraws funds
* @param user the address performing the withdrawal
* @param value the amount withdrawn
* @param timestamp of the withdrawal
**/
event Withdrawal(address indexed user, uint256 value, uint256 timestamp);
/**
* @dev emitted after the user borrows funds
* @param user the address that borrows
* @param value the amount borrowed
* @param timestamp time of the borrowing
**/
event Borrowing(address indexed user, uint256 value, uint256 timestamp);
/**
* @dev emitted after the user repays debt
* @param user the address that repays debt
* @param value the amount repaid
* @param timestamp of the repayment
**/
event Repayment(address indexed user, uint256 value, uint256 timestamp);
/**
* @dev emitted after accumulating deposit interest
* @param user the address that the deposit interest is accumulated for
* @param value the amount that interest is calculated from
* @param timestamp of the interest accumulation
**/
event InterestCollected(address indexed user, uint256 value, uint256 timestamp);
/**
* @dev emitted after changing borrowers registry
* @param registry an address of the newly set borrowers registry
* @param timestamp of the borrowers registry change
**/
event BorrowersRegistryChanged(address indexed registry, uint256 timestamp);
/**
* @dev emitted after changing rates calculator
* @param calculator an address of the newly set rates calculator
* @param timestamp of the borrowers registry change
**/
event RatesCalculatorChanged(address indexed calculator, uint256 timestamp);
/**
* @dev emitted after changing pool rewarder
* @param poolRewarder an address of the newly set pool rewarder
* @param timestamp of the pool rewarder change
**/
event PoolRewarderChanged(address indexed poolRewarder, uint256 timestamp);
/**
* @dev emitted after changing vesting distributor
* @param distributor an address of the newly set distributor
* @param timestamp of the distributor change
**/
event VestingDistributorChanged(address indexed distributor, uint256 timestamp);
/* ========== ERRORS ========== */
// Only authorized accounts may borrow
error NotAuthorizedToBorrow();
// Borrowers registry is not configured
error BorrowersRegistryNotConfigured();
// Pool is frozen
error PoolFrozen();
// Not enough funds in the pool.
error InsufficientPoolFunds();
// Insufficient pool surplus to cover the requested recover amount
error InsufficientSurplus();
// Address (`target`) must be a contract
// @param target target address that must be a contract
error NotAContract(address target);
// ERC20: Spender cannot be a zero address
error SpenderZeroAddress();
// ERC20: cannot transfer to the zero address
error TransferToZeroAddress();
// ERC20: cannot transfer to the pool address
error TransferToPoolAddress();
// ERC20: transfer amount (`amount`) exceeds balance (`balance`)
/// @param amount transfer amount
/// @param balance available balance
error TransferAmountExceedsBalance(uint256 amount, uint256 balance);
// ERC20: requested transfer amount (`requested`) exceeds current allowance (`allowance`)
/// @param requested requested transfer amount
/// @param allowance current allowance
error InsufficientAllowance(uint256 requested, uint256 allowance);
// This deposit operation would result in a breach of the totalSupplyCap
error TotalSupplyCapBreached();
// The deposit amount must be > 0
error ZeroDepositAmount();
// ERC20: cannot mint to the zero address
error MintToAddressZero();
// ERC20: burn amount exceeds current pool indexed balance
error BurnAmountExceedsBalance();
// ERC20: burn amount exceeds current amount available (including vesting)
error BurnAmountExceedsAvailableForUser();
// Trying to repay more than was borrowed
error RepayingMoreThanWasBorrowed();
// getMaxPoolUtilisationForBorrowing was breached
error MaxPoolUtilisationBreached();
}// SPDX-License-Identifier: MIT
// Modified version of Openzeppelin (OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)) ReentrancyGuard
// contract that uses keccak slots instead of the standard storage layout.
import {DiamondStorageLib} from "./lib/DiamondStorageLib.sol";
pragma solidity 0.8.17;
/**
* @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.
*/
abstract contract ReentrancyGuardKeccak {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
DiamondStorageLib.ReentrancyGuardStorage storage rgs = DiamondStorageLib.reentrancyGuardStorage();
// On the first call to nonReentrant, _notEntered will be true
require(rgs._status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
rgs._status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
rgs._status = _NOT_ENTERED;
}
}// SPDX-License-Identifier: BUSL-1.1
// Last deployed from commit: 9f1e1bba11316303810f35a4440e20bc5ad0ef86;
pragma solidity 0.8.17;
import "./Pool.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
/**
* @title VestingDistributor
* @dev Contract distributing pool's spread among vesting participants.
*/
contract VestingDistributor {
Pool pool;
IERC20Metadata poolToken;
address keeper;
address pendingKeeper;
uint256 totalLockedMultiplied;
address[] public participants;
mapping(address => uint256) public locked;
mapping(address => uint256) public withdrawn;
mapping(address => uint256) public unvestingTime;
mapping(address => uint256) public unlockTimestamp;
mapping(address => uint256) public multiplier;
mapping(uint256 => uint256) rewardAmount;
mapping(uint256 => mapping(address => bool)) rewardDistributed;
mapping(uint256 => uint256) numRewardDistributed;
uint256 lastUpdated;
uint256 updateInterval = 6 hours;
uint256 public constant ONE_DAY = 24 * 3600; // 24 hours * 3600 seconds
uint256 public constant MIN_VESTING_TIME = ONE_DAY; // 1 day * 24 hours * 3600 seconds
uint256 public constant MAX_VESTING_TIME = 30 * ONE_DAY; // 30 days * 24 hours * 3600 seconds
modifier onlyPool() {
require(msg.sender == address(pool), "Unauthorized: onlyPool");
_;
}
modifier onlyKeeper() {
require(msg.sender == keeper, "Unauthorized: onlyKeeper");
_;
}
modifier onlyPendingKeeper() {
require(msg.sender == pendingKeeper, "Unauthorized: onlyPendingKeeper");
_;
}
constructor(address poolAddress, address keeperAddress) {
pool = Pool(poolAddress);
poolToken = IERC20Metadata(pool.tokenAddress());
keeper = keeperAddress;
lastUpdated = block.timestamp;
}
function transferKeeper(address keeperAddress) external onlyKeeper {
pendingKeeper = keeperAddress;
}
function acceptKeeper() external onlyPendingKeeper {
keeper = pendingKeeper;
pendingKeeper = address(0);
}
/**
* Add vesting participant (msg.sender)
**/
function startVesting(uint256 amount, uint256 time) public {
if (time < MIN_VESTING_TIME || time > MAX_VESTING_TIME) revert InvalidVestingTime();
if (pool.balanceOf(msg.sender) < amount) revert InsufficientPoolBalance();
if (locked[msg.sender] > 0 || unvestingTime[msg.sender] > 0) revert AlreadyLocked();
participants.push(msg.sender);
locked[msg.sender] = amount;
unvestingTime[msg.sender] = time;
multiplier[msg.sender] = getMultiplier(time);
totalLockedMultiplied += amount * multiplier[msg.sender] / 1e18;
}
/**
* Increase vesting of msg.sender
**/
function increaseVesting(uint256 amount) public {
if (locked[msg.sender] == 0 || unvestingTime[msg.sender] == 0) revert UserNotLocked();
if (pool.balanceOf(msg.sender) < locked[msg.sender] + amount) revert InsufficientPoolBalance();
if (unlockTimestamp[msg.sender] > 0) revert TooLate();
locked[msg.sender] += amount;
totalLockedMultiplied += amount * multiplier[msg.sender] / 1e18;
}
/**
* Unlock funds - start of unvesting
**/
function unlock() public {
if (locked[msg.sender] == 0 || unvestingTime[msg.sender] == 0) revert UserNotLocked();
unlockTimestamp[msg.sender] = block.timestamp;
}
/**
* Check how much user can withdraw
**/
function availableToWithdraw(address account) public view returns (uint256) {
if (locked[account] == 0 || unvestingTime[account] == 0) revert UserNotLocked();
if (unlockTimestamp[account] == 0) revert UserLocked();
uint256 timeFromUnlock = block.timestamp - unlockTimestamp[account];
if (timeFromUnlock > unvestingTime[account]) timeFromUnlock = unvestingTime[account];
uint256 initialUnlock = ONE_DAY * locked[account] / (unvestingTime[account] + ONE_DAY); // 1D / vesting days * locked amount
return initialUnlock + timeFromUnlock * (locked[account] - initialUnlock) / unvestingTime[account];
}
/**
* Gets pool's spread and distributes among vesting participants.
* @dev _totalLoans total value of loans
* @dev _totalDeposits total value of deposits
**/
//TODO: run periodically by bots
function distributeRewards(uint256 fromIndex, uint256 toIndex) public onlyKeeper {
if (block.timestamp < lastUpdated + updateInterval) revert DistributeTooEarly();
(fromIndex, toIndex) = fromIndex < toIndex ? (fromIndex, toIndex) : (toIndex, fromIndex);
toIndex = toIndex < participants.length ? toIndex : participants.length - 1;
if (rewardAmount[lastUpdated] == 0) {
rewardAmount[lastUpdated] = pool.balanceOf(address(this));
}
uint256 rewards = rewardAmount[lastUpdated];
for (uint256 i = fromIndex; i <= toIndex; i++) {
address participant = participants[i];
if (rewardDistributed[lastUpdated][participant]) {
continue;
}
//TODO: right now we distribute rewards even when someone start withdrawing. The rewards should depend on the amount which is still locked.
uint256 participantReward = rewards * (locked[participant] - withdrawn[participant]) * multiplier[participant] / 1e18 / totalLockedMultiplied;
pool.transfer(participant, participantReward);
rewardDistributed[lastUpdated][participant] = true;
++numRewardDistributed[lastUpdated];
if (numRewardDistributed[lastUpdated] == participants.length) {
lastUpdated = block.timestamp;
}
}
}
//TODO: run periodically by bots
function updateParticipants(uint256 fromIndex, uint256 toIndex) public onlyKeeper {
(fromIndex, toIndex) = fromIndex < toIndex ? (fromIndex, toIndex) : (toIndex, fromIndex);
toIndex = toIndex < participants.length ? toIndex : participants.length - 1;
for (uint256 i = fromIndex; i <= toIndex;) {
address participant = participants[i];
if (unlockTimestamp[participant] > 0 && (block.timestamp - unlockTimestamp[participant]) > unvestingTime[participant]) {
totalLockedMultiplied -= (locked[participant] - withdrawn[participant]) * multiplier[participant] / 1e18;
unvestingTime[participant] = 0;
locked[participant] = 0;
unlockTimestamp[participant] = 0;
withdrawn[participant] = 0;
multiplier[participant] = 0;
participants[i] = participants[participants.length - 1];
participants.pop();
--toIndex;
} else {
++i;
}
}
}
function updateWithdrawn(address account, uint256 amount) public onlyPool {
withdrawn[account] += amount;
if (withdrawn[account] > locked[account]) {
revert WithdrawMoreThanLocked();
}
totalLockedMultiplied -= amount * multiplier[account] / 1e18;
}
function getMultiplier(uint256 time) public pure returns (uint256){
if (time >= 30 * ONE_DAY) return 2e18; // min. 30 days
if (time >= 29 * ONE_DAY) return 1.99e18; // min. 29 days
if (time >= 28 * ONE_DAY) return 1.98e18; // min. 28 days
if (time >= 27 * ONE_DAY) return 1.97e18; // min. 27 days
if (time >= 26 * ONE_DAY) return 1.96e18; // min. 26 days
if (time >= 25 * ONE_DAY) return 1.948e18; // min. 25 days
if (time >= 24 * ONE_DAY) return 1.936e18; // min. 24 days
if (time >= 23 * ONE_DAY) return 1.924e18; // min. 23 days
if (time >= 22 * ONE_DAY) return 1.912e18; // min. 22 days
if (time >= 21 * ONE_DAY) return 1.9e18; // min. 21 days
if (time >= 20 * ONE_DAY) return 1.885e18; // min. 20 days
if (time >= 19 * ONE_DAY) return 1.871e18; // min. 19 days
if (time >= 18 * ONE_DAY) return 1.856e18; // min. 18 days
if (time >= 17 * ONE_DAY) return 1.841e18; // min. 17 days
if (time >= 16 * ONE_DAY) return 1.824e18; // min. 16 days
if (time >= 15 * ONE_DAY) return 1.806e18; // min. 15 days
if (time >= 14 * ONE_DAY) return 1.788e18; // min. 14 days
if (time >= 13 * ONE_DAY) return 1.768e18; // min. 13 days
if (time >= 12 * ONE_DAY) return 1.746e18; // min. 12 days
if (time >= 11 * ONE_DAY) return 1.723e18; // min. 11 days
if (time >= 10 * ONE_DAY) return 1.698e18; // min. 10 days
if (time >= 9 * ONE_DAY) return 1.67e18; // min. 9 days
if (time >= 8 * ONE_DAY) return 1.64e18; // min. 8 days
if (time >= 7 * ONE_DAY) return 1.605e18; // min. 7 days
if (time >= 6 * ONE_DAY) return 1.566e18; // min. 6 days
if (time >= 5 * ONE_DAY) return 1.521e18; // min. 5 days
if (time >= 4 * ONE_DAY) return 1.468e18; // min. 4 days
if (time >= 3 * ONE_DAY) return 1.4e18; // min. 3 days
if (time >= 2 * ONE_DAY) return 1.32e18; // min. 2 days
if (time >= 1 * ONE_DAY) return 1.2e18; // min. 1 day
return 1;
}
// Trying to distribute before the update interval has been reached
error DistributeTooEarly();
// Already participates in vesting
error AlreadyLocked();
// Vesting time is out of range
error InvalidVestingTime();
// Insufficient user balance of pool's tokens
error InsufficientPoolBalance();
// User not locked
error UserNotLocked();
// User funds are locked
error UserLocked();
// Too late
error TooLate();
// Withdraw amount is more than locked
error WithdrawMoreThanLocked();
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"name":"CalldataMustHaveValidPayload","type":"error"},{"inputs":[],"name":"CalldataOverOrUnderFlow","type":"error"},{"inputs":[],"name":"EachSignerMustProvideTheSameValue","type":"error"},{"inputs":[],"name":"EmptyCalldataPointersArr","type":"error"},{"inputs":[],"name":"IncorrectUnsignedMetadataSize","type":"error"},{"inputs":[{"internalType":"uint256","name":"receviedSignersCount","type":"uint256"},{"internalType":"uint256","name":"requiredSignersCount","type":"uint256"}],"name":"InsufficientNumberOfUniqueSigners","type":"error"},{"inputs":[],"name":"InvalidCalldataPointer","type":"error"},{"inputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"name":"ProxyCalldataFailedWithCustomError","type":"error"},{"inputs":[{"internalType":"string","name":"message","type":"string"}],"name":"ProxyCalldataFailedWithStringMessage","type":"error"},{"inputs":[],"name":"ProxyCalldataFailedWithoutErrMsg","type":"error"},{"inputs":[{"internalType":"address","name":"receivedSigner","type":"address"}],"name":"SignerNotAuthorised","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"bytes32","name":"soldAsset","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"boughtAsset","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amountSold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountBought","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Swap","type":"event"},{"inputs":[{"internalType":"bytes32[]","name":"assetsToRepay","type":"bytes32[]"}],"name":"_getAllPricesForLiquidation","outputs":[{"components":[{"components":[{"internalType":"bytes32","name":"asset","type":"bytes32"},{"internalType":"uint256","name":"price","type":"uint256"}],"internalType":"struct SolvencyFacetProd.AssetPrice[]","name":"ownedAssetsPrices","type":"tuple[]"},{"components":[{"internalType":"bytes32","name":"asset","type":"bytes32"},{"internalType":"uint256","name":"price","type":"uint256"}],"internalType":"struct SolvencyFacetProd.AssetPrice[]","name":"debtAssetsPrices","type":"tuple[]"},{"components":[{"internalType":"bytes32","name":"asset","type":"bytes32"},{"internalType":"uint256","name":"price","type":"uint256"}],"internalType":"struct SolvencyFacetProd.AssetPrice[]","name":"stakedPositionsPrices","type":"tuple[]"},{"components":[{"internalType":"bytes32","name":"asset","type":"bytes32"},{"internalType":"uint256","name":"price","type":"uint256"}],"internalType":"struct SolvencyFacetProd.AssetPrice[]","name":"assetsToRepayPrices","type":"tuple[]"}],"internalType":"struct SolvencyFacetProd.CachedPrices","name":"result","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_getHealthRatio","outputs":[{"internalType":"uint256","name":"health","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"bytes32","name":"asset","type":"bytes32"},{"internalType":"uint256","name":"price","type":"uint256"}],"internalType":"struct SolvencyFacetProd.AssetPrice[]","name":"ownedAssetsPrices","type":"tuple[]"},{"components":[{"internalType":"bytes32","name":"asset","type":"bytes32"},{"internalType":"uint256","name":"price","type":"uint256"}],"internalType":"struct SolvencyFacetProd.AssetPrice[]","name":"debtAssetsPrices","type":"tuple[]"},{"components":[{"internalType":"bytes32","name":"asset","type":"bytes32"},{"internalType":"uint256","name":"price","type":"uint256"}],"internalType":"struct SolvencyFacetProd.AssetPrice[]","name":"stakedPositionsPrices","type":"tuple[]"},{"components":[{"internalType":"bytes32","name":"asset","type":"bytes32"},{"internalType":"uint256","name":"price","type":"uint256"}],"internalType":"struct SolvencyFacetProd.AssetPrice[]","name":"assetsToRepayPrices","type":"tuple[]"}],"internalType":"struct SolvencyFacetProd.CachedPrices","name":"cachedPrices","type":"tuple"}],"name":"_getHealthRatioWithPrices","outputs":[{"internalType":"uint256","name":"health","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_resetPrimeAccountAssetsExposure","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"_setPrimeAccountAssetsExposure","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"symbol","type":"bytes32"}],"name":"getPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"symbols","type":"bytes32[]"}],"name":"getPrices","outputs":[{"internalType":"uint256[]","name":"prices","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_amountOut","type":"uint256"},{"internalType":"address[]","name":"_path","type":"address[]"},{"internalType":"address[]","name":"_adapters","type":"address[]"}],"name":"yakSwap","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b506001600055611fe4806100256000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c80635b4fad9c1161005b5780635b4fad9c146100f45780636e3bfe9f146100fc578063b2d40e731461010f578063f8f185531461012f57600080fd5b80631f7f8b271461008d57806326ee4836146100a257806331d98b3f146100cb57806339e2719e146100ec575b600080fd5b6100a061009b366004611747565b610137565b005b6100b56100b0366004611881565b610840565b6040516100c29190611917565b60405180910390f35b6100de6100d936600461195b565b6108bf565b6040519081526020016100c2565b6100de610902565b6100a061095d565b6100de61010a3660046119fe565b6109a1565b61012261011d366004611881565b6109d0565b6040516100c29190611b1d565b6100a0610a3b565b7f1c85eba10a4cf2d29c83fc15f97931886b9fd3caeb70d8c377aeedc58b919e3b8054600119016101af5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b600281556101bb610a7c565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c1322547fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c904290036102685760405162461bcd60e51b815260206004820152603160248201527f426f72726f77696e67206d7573742068617070656e20696e2061207374616e6460448201527030b637b732903a3930b739b0b1ba34b7b760791b60648201526084016101a6565b61027061095d565b60006102d38787600081811061028857610288611b9b565b905060200201602081019061029d9190611bc6565b88886102aa600182611bf9565b8181106102b9576102b9611b9b565b90506020020160208101906102ce9190611bc6565b610b0c565b60408082015190516370a0823160e01b815230600482015291925061034e916001600160a01b03909116906370a0823190602401602060405180830381865afa158015610324573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103489190611c0c565b8a610e6c565b9850600089116103ba5760405162461bcd60e51b815260206004820152603160248201527f416d6f756e74206f6620746f6b656e7320746f2073656c6c2068617320746f20604482015270062652067726561746572207468616e203607c1b60648201526084016101a6565b6103e873b32c79a25291265ef240eb32e9fabbc6dcee3ce360408301516001600160a01b0316906000610e84565b61041573b32c79a25291265ef240eb32e9fabbc6dcee3ce360408301516001600160a01b0316908b610e84565b600073b32c79a25291265ef240eb32e9fabbc6dcee3ce39050600060405180608001604052808c81526020018b81526020018a8a808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505050908252506040805160208a810282810182019093528a82529283019290918b918b91829185019084908082843760009201829052509390945250506040516335f96fc360e11b81529293506001600160a01b03851692636bf2df8692506104e7918591309190600401611c5e565b600060405180830381600087803b15801561050157600080fd5b505af1158015610515573d6000803e3d6000fd5b5050505060608301516040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015610564573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105889190611c0c565b11156105a0576105a083602001518460600151610fb3565b60408084015190516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156105ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061060e9190611c0c565b6000036106205782516106209061109a565b60a083015160608401516040516370a0823160e01b8152306004820152600092916001600160a01b0316906370a0823190602401602060405180830381865afa158015610671573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106959190611c0c565b61069f9190611bf9565b90508a8110156106f15760405162461bcd60e51b815260206004820152601a60248201527f496e73756666696369656e74206f757470757420616d6f756e7400000000000060448201526064016101a6565b6020840151845160408087015190516370a0823160e01b815230600482015233917f4cb7ea834525f7e7be14143ffc68be9c6735559e936d23502fff335d39a2e8e2916001600160a01b03909116906370a0823190602401602060405180830381865afa158015610766573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078a9190611c0c565b88608001516107999190611bf9565b6040805191825260208201879052429082015260600160405180910390a4505050506107c36110ea565b61082b5760405162461bcd60e51b815260206004820152603360248201527f54686520616374696f6e206d617920636175736520616e206163636f756e74206044820152721d1bc8189958dbdb59481a5b9cdbdb1d995b9d606a1b60648201526084016101a6565b610833610a3b565b5060019055505050505050565b60606108a6610855631377241b60e11b611140565b604051631377241b60e11b9061086f908690602401611917565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526111cb565b8060200190518101906108b99190611cc8565b92915050565b60006108ef6108d46331d98b3f60e01b611140565b604051602481018590526331d98b3f60e01b9060440161086f565b8060200190518101906108b99190611c0c565b6000610945610917637914334f60e01b611140565b6040805160048152602481019091526020810180516001600160e01b0316637914334f60e01b1790526111cb565b8060200190518101906109589190611c0c565b905090565b61099e61097063b1c9fbc160e01b611140565b6040805160048152602481019091526020810180516001600160e01b031663b1c9fbc160e01b1790526111cb565b50565b60006108ef6109b663360398a360e01b611140565b60405163360398a360e01b9061086f908690602401611b1d565b6109fb6040518060800160405280606081526020016060815260200160608152602001606081525090565b610a28610a0e6351d5468f60e11b611140565b6040516351d5468f60e11b9061086f908690602401611917565b8060200190518101906108b99190611dcd565b61099e610a4e63e017e12560e01b611140565b6040805160048152602481019091526020810180516001600160e01b031663e017e12560e01b1790526111cb565b7f8d5bb42e0ac1496a2c326edc9c00758985246e6c2bb146d6c2f4a0d509e0960a600101546001600160a01b03163314610b0a5760405162461bcd60e51b815260206004820152602960248201527f4469616d6f6e6453746f726167654c69623a204d75737420626520636f6e747260448201526830b1ba1037bbb732b960b91b60648201526084016101a6565b565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152730a0d954d4b0f0b47a5990c0abd179a90ff74e2556001600160a01b03831673ae64d55a6f09e4263421737397d1fdfa71896a6903610b8f57739e295b5b976a184b14ad8cd72413ad846c29966092505b6001600160a01b038416739e295b5b976a184b14ad8cd72413ad846c29966003610bcb5773ae64d55a6f09e4263421737397d1fdfa71896a6993505b6040516307c11f8f60e51b81526001600160a01b0385811660048301526000919083169063f823f1e090602401602060405180830381865afa158015610c15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c399190611c0c565b6040516307c11f8f60e51b81526001600160a01b03868116600483015291925060009184169063f823f1e090602401602060405180830381865afa158015610c85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca99190611c0c565b6040516335923e0360e01b81526001600160a01b038781166004830152919250908416906335923e0390602401602060405180830381865afa158015610cf3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d179190611e97565b610d5a5760405162461bcd60e51b815260206004820152601460248201527320b9b9b2ba103737ba1039bab83837b93a32b21760611b60448201526064016101a6565b6040805160c081018252838152602081018390526001600160a01b03888116828401819052908816606083015291516370a0823160e01b815230600482015288928892916080830191906370a0823190602401602060405180830381865afa158015610dca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dee9190611c0c565b81526040516370a0823160e01b81523060048201526020909101906001600160a01b038416906370a0823190602401602060405180830381865afa158015610e3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5e9190611c0c565b905298975050505050505050565b6000818310610e7b5781610e7d565b825b9392505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b1790529151600092839290871691610ee09190611edd565b6000604051808303816000865af19150503d8060008114610f1d576040519150601f19603f3d011682016040523d82523d6000602084013e610f22565b606091505b5091509150818015610f4c575080511580610f4c575080806020019051810190610f4c9190611e97565b610fac5760405162461bcd60e51b815260206004820152602b60248201527f5472616e7366657248656c7065723a3a73616665417070726f76653a2061707060448201526a1c9bdd994819985a5b195960aa1b60648201526084016101a6565b5050505050565b81600003610ffc5760405162461bcd60e51b815260206004820152601660248201527553796d626f6c2063616e6e6f7420626520656d70747960501b60448201526064016101a6565b6001600160a01b0381166110485760405162461bcd60e51b8152602060048201526013602482015272496e76616c696420416464726573735a65726f60681b60448201526064016101a6565b7f8d5bb42e0ac1496a2c326edc9c00758985246e6c2bb146d6c2f4a0d509e0960a6110947f8d5bb42e0ac1496a2c326edc9c00758985246e6c2bb146d6c2f4a0d509e0960e848461124e565b50505050565b7f8d5bb42e0ac1496a2c326edc9c00758985246e6c2bb146d6c2f4a0d509e0960a6110e57f8d5bb42e0ac1496a2c326edc9c00758985246e6c2bb146d6c2f4a0d509e0960e8361126c565b505050565b600061112d6110ff6305ce239560e41b611140565b6040805160048152602481019091526020810180516001600160e01b03166305ce239560e41b1790526111cb565b8060200190518101906109589190611e97565b60007362cf82fb0484af382714cd09296260edc1dc0c6c604051630d74157760e01b81526001600160e01b0319841660048201526001600160a01b039190911690630d74157790602401602060405180830381865afa1580156111a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b99190611ef9565b606060006111d883611278565b9050600080856001600160a01b0316836040516111f59190611edd565b600060405180830381855af49150503d8060008114611230576040519150601f19603f3d011682016040523d82523d6000602084013e611235565b606091505b50915091506112448282611307565b9695505050505050565b600061126484846001600160a01b038516611384565b949350505050565b6000610e7d83836113a1565b805160609060006112876113be565b905060006112958284611f16565b9050368211156112b857604051632bcb7bc560e11b815260040160405180910390fd5b6060604051905081815285602001848101826020015b818310156112e65782518152602092830192016112ce565b50505082833603856020018301379190920181016020016040529392505050565b60608261137e57815160000361133057604051632b3ff13d60e11b815260040160405180910390fd5b602082015162461bcd60e51b148015611363576040516301efd04f60e31b815260448401906101a6908290600401611f55565b8260405163fd36fde360e01b81526004016101a69190611f55565b50919050565b600082815260028401602052604081208290556112648484611429565b60008181526002830160205260408120819055610e7d8383611435565b6000806113c9611441565b905060006113d6826114e8565b61ffff1690506113e7600283611f16565b915060005b818110156114215760006113ff84611522565b905061140b8185611f16565b935050808061141990611f68565b9150506113ec565b509092915050565b6000610e7d8383611556565b6000610e7d83836115a5565b60006602ed57011e0000601f1936013581161480611472576040516373bb264f60e11b815260040160405180910390fd5b6000366029111561149657604051632bcb7bc560e11b815260040160405180910390fd5b50602819360135600060096114b1600362ffffff8516611f16565b6114bb9190611f16565b9050366114c9600283611f16565b1115610e7d5760405163c30a7bd760e01b815260040160405180910390fd5b6000806114f6602084611f16565b90503681111561151957604051632bcb7bc560e11b815260040160405180910390fd5b36033592915050565b600080600061153084611698565b9092509050604e611542826020611f16565b61154c9084611f81565b6112649190611f16565b600081815260018301602052604081205461159d575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556108b9565b5060006108b9565b6000818152600183016020526040812054801561168e5760006115c9600183611bf9565b85549091506000906115dd90600190611bf9565b90508181146116425760008660000182815481106115fd576115fd611b9b565b906000526020600020015490508087600001848154811061162057611620611b9b565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061165357611653611f98565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506108b9565b60009150506108b9565b6000808080806116a9604187611f16565b905060006116c26116bb602084611f16565b36906116ef565b8035945090506116d38160036116ef565b62ffffff9490941697933563ffffffff16965092945050505050565b6000610e7d8284611bf9565b60008083601f84011261170d57600080fd5b50813567ffffffffffffffff81111561172557600080fd5b6020830191508360208260051b850101111561174057600080fd5b9250929050565b6000806000806000806080878903121561176057600080fd5b8635955060208701359450604087013567ffffffffffffffff8082111561178657600080fd5b6117928a838b016116fb565b909650945060608901359150808211156117ab57600080fd5b506117b889828a016116fb565b979a9699509497509295939492505050565b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715611803576118036117ca565b60405290565b6040516080810167ffffffffffffffff81118282101715611803576118036117ca565b604051601f8201601f1916810167ffffffffffffffff81118282101715611855576118556117ca565b604052919050565b600067ffffffffffffffff821115611877576118776117ca565b5060051b60200190565b6000602080838503121561189457600080fd5b823567ffffffffffffffff8111156118ab57600080fd5b8301601f810185136118bc57600080fd5b80356118cf6118ca8261185d565b61182c565b81815260059190911b820183019083810190878311156118ee57600080fd5b928401925b8284101561190c578335825292840192908401906118f3565b979650505050505050565b6020808252825182820181905260009190848201906040850190845b8181101561194f57835183529284019291840191600101611933565b50909695505050505050565b60006020828403121561196d57600080fd5b5035919050565b600082601f83011261198557600080fd5b813560206119956118ca8361185d565b82815260069290921b840181019181810190868411156119b457600080fd5b8286015b848110156119f357604081890312156119d15760008081fd5b6119d96117e0565b8135815284820135858201528352918301916040016119b8565b509695505050505050565b600060208284031215611a1057600080fd5b813567ffffffffffffffff80821115611a2857600080fd5b9083019060808286031215611a3c57600080fd5b611a44611809565b823582811115611a5357600080fd5b611a5f87828601611974565b825250602083013582811115611a7457600080fd5b611a8087828601611974565b602083015250604083013582811115611a9857600080fd5b611aa487828601611974565b604083015250606083013582811115611abc57600080fd5b611ac887828601611974565b60608301525095945050505050565b600081518084526020808501945080840160005b83811015611b12578151805188528301518388015260409096019590820190600101611aeb565b509495945050505050565b602081526000825160806020840152611b3960a0840182611ad7565b90506020840151601f1980858403016040860152611b578383611ad7565b92506040860151915080858403016060860152611b748383611ad7565b9250606086015191508085840301608086015250611b928282611ad7565b95945050505050565b634e487b7160e01b600052603260045260246000fd5b6001600160a01b038116811461099e57600080fd5b600060208284031215611bd857600080fd5b8135610e7d81611bb1565b634e487b7160e01b600052601160045260246000fd5b818103818111156108b9576108b9611be3565b600060208284031215611c1e57600080fd5b5051919050565b600081518084526020808501945080840160005b83811015611b125781516001600160a01b031687529582019590820190600101611c39565b60608152835160608201526020840151608082015260006040850151608060a0840152611c8e60e0840182611c25565b90506060860151605f198483030160c0850152611cab8282611c25565b6001600160a01b0396909616602085015250505060400152919050565b60006020808385031215611cdb57600080fd5b825167ffffffffffffffff811115611cf257600080fd5b8301601f81018513611d0357600080fd5b8051611d116118ca8261185d565b81815260059190911b82018301908381019087831115611d3057600080fd5b928401925b8284101561190c57835182529284019290840190611d35565b600082601f830112611d5f57600080fd5b81516020611d6f6118ca8361185d565b82815260069290921b84018101918181019086841115611d8e57600080fd5b8286015b848110156119f35760408189031215611dab5760008081fd5b611db36117e0565b815181528482015185820152835291830191604001611d92565b600060208284031215611ddf57600080fd5b815167ffffffffffffffff80821115611df757600080fd5b9083019060808286031215611e0b57600080fd5b611e13611809565b825182811115611e2257600080fd5b611e2e87828601611d4e565b825250602083015182811115611e4357600080fd5b611e4f87828601611d4e565b602083015250604083015182811115611e6757600080fd5b611e7387828601611d4e565b604083015250606083015182811115611e8b57600080fd5b611ac887828601611d4e565b600060208284031215611ea957600080fd5b81518015158114610e7d57600080fd5b60005b83811015611ed4578181015183820152602001611ebc565b50506000910152565b60008251611eef818460208701611eb9565b9190910192915050565b600060208284031215611f0b57600080fd5b8151610e7d81611bb1565b808201808211156108b9576108b9611be3565b60008151808452611f41816020860160208601611eb9565b601f01601f19169290920160200192915050565b602081526000610e7d6020830184611f29565b600060018201611f7a57611f7a611be3565b5060010190565b80820281158282048414176108b9576108b9611be3565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220316f0c497739e4d2e2798745aa1f296f98aa4629960a9205a38ccb00b9ea65f764736f6c63430008110033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100885760003560e01c80635b4fad9c1161005b5780635b4fad9c146100f45780636e3bfe9f146100fc578063b2d40e731461010f578063f8f185531461012f57600080fd5b80631f7f8b271461008d57806326ee4836146100a257806331d98b3f146100cb57806339e2719e146100ec575b600080fd5b6100a061009b366004611747565b610137565b005b6100b56100b0366004611881565b610840565b6040516100c29190611917565b60405180910390f35b6100de6100d936600461195b565b6108bf565b6040519081526020016100c2565b6100de610902565b6100a061095d565b6100de61010a3660046119fe565b6109a1565b61012261011d366004611881565b6109d0565b6040516100c29190611b1d565b6100a0610a3b565b7f1c85eba10a4cf2d29c83fc15f97931886b9fd3caeb70d8c377aeedc58b919e3b8054600119016101af5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b600281556101bb610a7c565b7fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c1322547fc8fcad8db84d3cc18b4c41d551ea0ee66dd599cde068d998e57d5e09332c131c904290036102685760405162461bcd60e51b815260206004820152603160248201527f426f72726f77696e67206d7573742068617070656e20696e2061207374616e6460448201527030b637b732903a3930b739b0b1ba34b7b760791b60648201526084016101a6565b61027061095d565b60006102d38787600081811061028857610288611b9b565b905060200201602081019061029d9190611bc6565b88886102aa600182611bf9565b8181106102b9576102b9611b9b565b90506020020160208101906102ce9190611bc6565b610b0c565b60408082015190516370a0823160e01b815230600482015291925061034e916001600160a01b03909116906370a0823190602401602060405180830381865afa158015610324573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906103489190611c0c565b8a610e6c565b9850600089116103ba5760405162461bcd60e51b815260206004820152603160248201527f416d6f756e74206f6620746f6b656e7320746f2073656c6c2068617320746f20604482015270062652067726561746572207468616e203607c1b60648201526084016101a6565b6103e873b32c79a25291265ef240eb32e9fabbc6dcee3ce360408301516001600160a01b0316906000610e84565b61041573b32c79a25291265ef240eb32e9fabbc6dcee3ce360408301516001600160a01b0316908b610e84565b600073b32c79a25291265ef240eb32e9fabbc6dcee3ce39050600060405180608001604052808c81526020018b81526020018a8a808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152505050908252506040805160208a810282810182019093528a82529283019290918b918b91829185019084908082843760009201829052509390945250506040516335f96fc360e11b81529293506001600160a01b03851692636bf2df8692506104e7918591309190600401611c5e565b600060405180830381600087803b15801561050157600080fd5b505af1158015610515573d6000803e3d6000fd5b5050505060608301516040516370a0823160e01b81523060048201526000916001600160a01b0316906370a0823190602401602060405180830381865afa158015610564573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105889190611c0c565b11156105a0576105a083602001518460600151610fb3565b60408084015190516370a0823160e01b81523060048201526001600160a01b03909116906370a0823190602401602060405180830381865afa1580156105ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061060e9190611c0c565b6000036106205782516106209061109a565b60a083015160608401516040516370a0823160e01b8152306004820152600092916001600160a01b0316906370a0823190602401602060405180830381865afa158015610671573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106959190611c0c565b61069f9190611bf9565b90508a8110156106f15760405162461bcd60e51b815260206004820152601a60248201527f496e73756666696369656e74206f757470757420616d6f756e7400000000000060448201526064016101a6565b6020840151845160408087015190516370a0823160e01b815230600482015233917f4cb7ea834525f7e7be14143ffc68be9c6735559e936d23502fff335d39a2e8e2916001600160a01b03909116906370a0823190602401602060405180830381865afa158015610766573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061078a9190611c0c565b88608001516107999190611bf9565b6040805191825260208201879052429082015260600160405180910390a4505050506107c36110ea565b61082b5760405162461bcd60e51b815260206004820152603360248201527f54686520616374696f6e206d617920636175736520616e206163636f756e74206044820152721d1bc8189958dbdb59481a5b9cdbdb1d995b9d606a1b60648201526084016101a6565b610833610a3b565b5060019055505050505050565b60606108a6610855631377241b60e11b611140565b604051631377241b60e11b9061086f908690602401611917565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526111cb565b8060200190518101906108b99190611cc8565b92915050565b60006108ef6108d46331d98b3f60e01b611140565b604051602481018590526331d98b3f60e01b9060440161086f565b8060200190518101906108b99190611c0c565b6000610945610917637914334f60e01b611140565b6040805160048152602481019091526020810180516001600160e01b0316637914334f60e01b1790526111cb565b8060200190518101906109589190611c0c565b905090565b61099e61097063b1c9fbc160e01b611140565b6040805160048152602481019091526020810180516001600160e01b031663b1c9fbc160e01b1790526111cb565b50565b60006108ef6109b663360398a360e01b611140565b60405163360398a360e01b9061086f908690602401611b1d565b6109fb6040518060800160405280606081526020016060815260200160608152602001606081525090565b610a28610a0e6351d5468f60e11b611140565b6040516351d5468f60e11b9061086f908690602401611917565b8060200190518101906108b99190611dcd565b61099e610a4e63e017e12560e01b611140565b6040805160048152602481019091526020810180516001600160e01b031663e017e12560e01b1790526111cb565b7f8d5bb42e0ac1496a2c326edc9c00758985246e6c2bb146d6c2f4a0d509e0960a600101546001600160a01b03163314610b0a5760405162461bcd60e51b815260206004820152602960248201527f4469616d6f6e6453746f726167654c69623a204d75737420626520636f6e747260448201526830b1ba1037bbb732b960b91b60648201526084016101a6565b565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152730a0d954d4b0f0b47a5990c0abd179a90ff74e2556001600160a01b03831673ae64d55a6f09e4263421737397d1fdfa71896a6903610b8f57739e295b5b976a184b14ad8cd72413ad846c29966092505b6001600160a01b038416739e295b5b976a184b14ad8cd72413ad846c29966003610bcb5773ae64d55a6f09e4263421737397d1fdfa71896a6993505b6040516307c11f8f60e51b81526001600160a01b0385811660048301526000919083169063f823f1e090602401602060405180830381865afa158015610c15573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c399190611c0c565b6040516307c11f8f60e51b81526001600160a01b03868116600483015291925060009184169063f823f1e090602401602060405180830381865afa158015610c85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ca99190611c0c565b6040516335923e0360e01b81526001600160a01b038781166004830152919250908416906335923e0390602401602060405180830381865afa158015610cf3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d179190611e97565b610d5a5760405162461bcd60e51b815260206004820152601460248201527320b9b9b2ba103737ba1039bab83837b93a32b21760611b60448201526064016101a6565b6040805160c081018252838152602081018390526001600160a01b03888116828401819052908816606083015291516370a0823160e01b815230600482015288928892916080830191906370a0823190602401602060405180830381865afa158015610dca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dee9190611c0c565b81526040516370a0823160e01b81523060048201526020909101906001600160a01b038416906370a0823190602401602060405180830381865afa158015610e3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e5e9190611c0c565b905298975050505050505050565b6000818310610e7b5781610e7d565b825b9392505050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663095ea7b360e01b1790529151600092839290871691610ee09190611edd565b6000604051808303816000865af19150503d8060008114610f1d576040519150601f19603f3d011682016040523d82523d6000602084013e610f22565b606091505b5091509150818015610f4c575080511580610f4c575080806020019051810190610f4c9190611e97565b610fac5760405162461bcd60e51b815260206004820152602b60248201527f5472616e7366657248656c7065723a3a73616665417070726f76653a2061707060448201526a1c9bdd994819985a5b195960aa1b60648201526084016101a6565b5050505050565b81600003610ffc5760405162461bcd60e51b815260206004820152601660248201527553796d626f6c2063616e6e6f7420626520656d70747960501b60448201526064016101a6565b6001600160a01b0381166110485760405162461bcd60e51b8152602060048201526013602482015272496e76616c696420416464726573735a65726f60681b60448201526064016101a6565b7f8d5bb42e0ac1496a2c326edc9c00758985246e6c2bb146d6c2f4a0d509e0960a6110947f8d5bb42e0ac1496a2c326edc9c00758985246e6c2bb146d6c2f4a0d509e0960e848461124e565b50505050565b7f8d5bb42e0ac1496a2c326edc9c00758985246e6c2bb146d6c2f4a0d509e0960a6110e57f8d5bb42e0ac1496a2c326edc9c00758985246e6c2bb146d6c2f4a0d509e0960e8361126c565b505050565b600061112d6110ff6305ce239560e41b611140565b6040805160048152602481019091526020810180516001600160e01b03166305ce239560e41b1790526111cb565b8060200190518101906109589190611e97565b60007362cf82fb0484af382714cd09296260edc1dc0c6c604051630d74157760e01b81526001600160e01b0319841660048201526001600160a01b039190911690630d74157790602401602060405180830381865afa1580156111a7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b99190611ef9565b606060006111d883611278565b9050600080856001600160a01b0316836040516111f59190611edd565b600060405180830381855af49150503d8060008114611230576040519150601f19603f3d011682016040523d82523d6000602084013e611235565b606091505b50915091506112448282611307565b9695505050505050565b600061126484846001600160a01b038516611384565b949350505050565b6000610e7d83836113a1565b805160609060006112876113be565b905060006112958284611f16565b9050368211156112b857604051632bcb7bc560e11b815260040160405180910390fd5b6060604051905081815285602001848101826020015b818310156112e65782518152602092830192016112ce565b50505082833603856020018301379190920181016020016040529392505050565b60608261137e57815160000361133057604051632b3ff13d60e11b815260040160405180910390fd5b602082015162461bcd60e51b148015611363576040516301efd04f60e31b815260448401906101a6908290600401611f55565b8260405163fd36fde360e01b81526004016101a69190611f55565b50919050565b600082815260028401602052604081208290556112648484611429565b60008181526002830160205260408120819055610e7d8383611435565b6000806113c9611441565b905060006113d6826114e8565b61ffff1690506113e7600283611f16565b915060005b818110156114215760006113ff84611522565b905061140b8185611f16565b935050808061141990611f68565b9150506113ec565b509092915050565b6000610e7d8383611556565b6000610e7d83836115a5565b60006602ed57011e0000601f1936013581161480611472576040516373bb264f60e11b815260040160405180910390fd5b6000366029111561149657604051632bcb7bc560e11b815260040160405180910390fd5b50602819360135600060096114b1600362ffffff8516611f16565b6114bb9190611f16565b9050366114c9600283611f16565b1115610e7d5760405163c30a7bd760e01b815260040160405180910390fd5b6000806114f6602084611f16565b90503681111561151957604051632bcb7bc560e11b815260040160405180910390fd5b36033592915050565b600080600061153084611698565b9092509050604e611542826020611f16565b61154c9084611f81565b6112649190611f16565b600081815260018301602052604081205461159d575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556108b9565b5060006108b9565b6000818152600183016020526040812054801561168e5760006115c9600183611bf9565b85549091506000906115dd90600190611bf9565b90508181146116425760008660000182815481106115fd576115fd611b9b565b906000526020600020015490508087600001848154811061162057611620611b9b565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061165357611653611f98565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506108b9565b60009150506108b9565b6000808080806116a9604187611f16565b905060006116c26116bb602084611f16565b36906116ef565b8035945090506116d38160036116ef565b62ffffff9490941697933563ffffffff16965092945050505050565b6000610e7d8284611bf9565b60008083601f84011261170d57600080fd5b50813567ffffffffffffffff81111561172557600080fd5b6020830191508360208260051b850101111561174057600080fd5b9250929050565b6000806000806000806080878903121561176057600080fd5b8635955060208701359450604087013567ffffffffffffffff8082111561178657600080fd5b6117928a838b016116fb565b909650945060608901359150808211156117ab57600080fd5b506117b889828a016116fb565b979a9699509497509295939492505050565b634e487b7160e01b600052604160045260246000fd5b6040805190810167ffffffffffffffff81118282101715611803576118036117ca565b60405290565b6040516080810167ffffffffffffffff81118282101715611803576118036117ca565b604051601f8201601f1916810167ffffffffffffffff81118282101715611855576118556117ca565b604052919050565b600067ffffffffffffffff821115611877576118776117ca565b5060051b60200190565b6000602080838503121561189457600080fd5b823567ffffffffffffffff8111156118ab57600080fd5b8301601f810185136118bc57600080fd5b80356118cf6118ca8261185d565b61182c565b81815260059190911b820183019083810190878311156118ee57600080fd5b928401925b8284101561190c578335825292840192908401906118f3565b979650505050505050565b6020808252825182820181905260009190848201906040850190845b8181101561194f57835183529284019291840191600101611933565b50909695505050505050565b60006020828403121561196d57600080fd5b5035919050565b600082601f83011261198557600080fd5b813560206119956118ca8361185d565b82815260069290921b840181019181810190868411156119b457600080fd5b8286015b848110156119f357604081890312156119d15760008081fd5b6119d96117e0565b8135815284820135858201528352918301916040016119b8565b509695505050505050565b600060208284031215611a1057600080fd5b813567ffffffffffffffff80821115611a2857600080fd5b9083019060808286031215611a3c57600080fd5b611a44611809565b823582811115611a5357600080fd5b611a5f87828601611974565b825250602083013582811115611a7457600080fd5b611a8087828601611974565b602083015250604083013582811115611a9857600080fd5b611aa487828601611974565b604083015250606083013582811115611abc57600080fd5b611ac887828601611974565b60608301525095945050505050565b600081518084526020808501945080840160005b83811015611b12578151805188528301518388015260409096019590820190600101611aeb565b509495945050505050565b602081526000825160806020840152611b3960a0840182611ad7565b90506020840151601f1980858403016040860152611b578383611ad7565b92506040860151915080858403016060860152611b748383611ad7565b9250606086015191508085840301608086015250611b928282611ad7565b95945050505050565b634e487b7160e01b600052603260045260246000fd5b6001600160a01b038116811461099e57600080fd5b600060208284031215611bd857600080fd5b8135610e7d81611bb1565b634e487b7160e01b600052601160045260246000fd5b818103818111156108b9576108b9611be3565b600060208284031215611c1e57600080fd5b5051919050565b600081518084526020808501945080840160005b83811015611b125781516001600160a01b031687529582019590820190600101611c39565b60608152835160608201526020840151608082015260006040850151608060a0840152611c8e60e0840182611c25565b90506060860151605f198483030160c0850152611cab8282611c25565b6001600160a01b0396909616602085015250505060400152919050565b60006020808385031215611cdb57600080fd5b825167ffffffffffffffff811115611cf257600080fd5b8301601f81018513611d0357600080fd5b8051611d116118ca8261185d565b81815260059190911b82018301908381019087831115611d3057600080fd5b928401925b8284101561190c57835182529284019290840190611d35565b600082601f830112611d5f57600080fd5b81516020611d6f6118ca8361185d565b82815260069290921b84018101918181019086841115611d8e57600080fd5b8286015b848110156119f35760408189031215611dab5760008081fd5b611db36117e0565b815181528482015185820152835291830191604001611d92565b600060208284031215611ddf57600080fd5b815167ffffffffffffffff80821115611df757600080fd5b9083019060808286031215611e0b57600080fd5b611e13611809565b825182811115611e2257600080fd5b611e2e87828601611d4e565b825250602083015182811115611e4357600080fd5b611e4f87828601611d4e565b602083015250604083015182811115611e6757600080fd5b611e7387828601611d4e565b604083015250606083015182811115611e8b57600080fd5b611ac887828601611d4e565b600060208284031215611ea957600080fd5b81518015158114610e7d57600080fd5b60005b83811015611ed4578181015183820152602001611ebc565b50506000910152565b60008251611eef818460208701611eb9565b9190910192915050565b600060208284031215611f0b57600080fd5b8151610e7d81611bb1565b808201808211156108b9576108b9611be3565b60008151808452611f41816020860160208601611eb9565b601f01601f19169290920160200192915050565b602081526000610e7d6020830184611f29565b600060018201611f7a57611f7a611be3565b5060010190565b80820281158282048414176108b9576108b9611be3565b634e487b7160e01b600052603160045260246000fdfea2646970667358221220316f0c497739e4d2e2798745aa1f296f98aa4629960a9205a38ccb00b9ea65f764736f6c63430008110033
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.