ERC-20
Overview
Max Total Supply
18,486.858665 ERC20 ***
Holders
2,431
Market
Price
$0.00 @ 0.000000 ETH
Onchain Market Cap
-
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 6 Decimals)
Balance
0.000001 ERC20 ***Value
$0.00Loading...
Loading
Loading...
Loading
Loading...
Loading
Contract Name:
RiseTokenVault
Compiler Version
v0.8.11+commit.d7f03943
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity)
/**
*Submitted for verification at Arbiscan.io on 2022-01-28
*/
// . .
// * . . . . * .
// . . . . . . .
// o . .
// . . . .
// 0 .
// . . , , ,
// . \ . .
// . \ ,
// . o . . . .
// . \ , . .
// #\##\# . . .
// # #O##\### . .
// . #*# #\##\### . ,
// . ##*# #\##\## . .
// . ##*# #o##\# . , .
// . *# #\# . . . ,
// \ . .
// ____^/\___^--____/\____O______________/\/\---/\___________---______________
// /\^ ^ ^ ^ ^^ ^ '\ ^ ^ ---
// -- - -- - - --- __ ^
// -- __ ___-- ^ ^ -- __
//
// The largest leveraged tokens market protocol.
//
// docs: https://docs.risedle.com
// twitter: @risedle
// github: risedle
// Verified using https://dapp.tools
// hevm: flattened sources of src/RiseTokenVault.sol
// SPDX-License-Identifier: MIT AND GPL-3.0-or-later
pragma solidity >=0.8.9 >=0.8.0 <0.9.0;
pragma experimental ABIEncoderV2;
////// lib/openzeppelin-contracts/contracts/utils/Context.sol
/* pragma solidity ^0.8.0; */
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
////// lib/openzeppelin-contracts/contracts/access/Ownable.sol
/* pragma solidity ^0.8.0; */
/* import "../utils/Context.sol"; */
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* 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 Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_setOwner(_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 {
_setOwner(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");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
////// lib/openzeppelin-contracts/contracts/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 make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
////// lib/openzeppelin-contracts/contracts/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 `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
////// lib/openzeppelin-contracts/contracts/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);
}
////// lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol
/* pragma solidity ^0.8.0; */
/* import "./IERC20.sol"; */
/* import "./extensions/IERC20Metadata.sol"; */
/* import "../../utils/Context.sol"; */
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* Requirements:
*
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][_msgSender()];
require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(sender, _msgSender(), currentAllowance - amount);
}
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
uint256 currentAllowance = _allowances[_msgSender()][spender];
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(_msgSender(), spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `sender` to `recipient`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(
address sender,
address recipient,
uint256 amount
) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
uint256 senderBalance = _balances[sender];
require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[sender] = senderBalance - amount;
}
_balances[recipient] += amount;
emit Transfer(sender, recipient, amount);
_afterTokenTransfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
////// lib/openzeppelin-contracts/contracts/utils/Address.sol
/* pragma solidity ^0.8.0; */
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(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 Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(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);
}
}
}
}
////// lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol
/* pragma solidity ^0.8.0; */
/* import "../IERC20.sol"; */
/* import "../../../utils/Address.sol"; */
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
////// src/RisedleVault.sol
// Risedle Vault Contract
// It implements money market for Risedle RISE tokens and DROP tokens.
//
// Copyright (c) 2021 Bayu - All rights reserved
// github: pyk
// email: [email protected]
/* pragma solidity >=0.8.9; */
/* pragma experimental ABIEncoderV2; */
/* import { ERC20 } from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; */
/* import { IERC20 } from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; */
/* import { IERC20Metadata } from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; */
/* import { SafeERC20 } from "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; */
/* import { Ownable } from "lib/openzeppelin-contracts/contracts/access/Ownable.sol"; */
/* import { ReentrancyGuard } from "lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol"; */
/// @title Risedle Vault
contract RisedleVault is ERC20, Ownable, ReentrancyGuard {
using SafeERC20 for IERC20;
/// @notice Vault's underlying token address
address internal underlyingToken;
/// @notice Optimal utilization rate in ether units
uint256 internal optimalUtilizationRateInEther = 0.9 ether; // 90% utilization
/// @notice Interest slope 1 in ether units
uint256 internal interestSlope1InEther = 0.2 ether; // 20% slope 1
/// @notice Interest slop 2 in ether units
uint256 internal interestSlope2InEther = 0.6 ether; // 60% slope 2
/// @notice Number of seconds in a year (approximation)
uint256 internal immutable totalSecondsInAYear = 31536000;
/// @notice Maximum borrow rate per second in ether units
uint256 internal maxBorrowRatePerSecondInEther = 50735667174; // 0.000000050735667174% Approx 393% APY
/// @notice Performance fee for the lender
uint256 internal performanceFeeInEther = 0.1 ether; // 10% performance fee
/// @notice Timestamp that interest was last accrued at
uint256 internal lastTimestampInterestAccrued;
/// @notice The total amount of principal borrowed plus interest accrued
uint256 public totalOutstandingDebt;
/// @notice The total amount of pending fees to be collected in the vault
uint256 public totalPendingFees;
/// @notice The total debt proportion issued by the vault, the usage is similar to the vault token supply. In order to track the outstanding debt of the RISE/DROP token
uint256 internal totalDebtProportion;
/// @notice Max vault's total deposit
uint256 public maxTotalDeposit;
/// @notice Fee recipient
address public FEE_RECIPIENT;
/// @notice Mapping RISE/DROP token to their debt proportion of totalOutstandingDebt
/// @dev debt = debtProportion[token] * debtProportionRate
mapping(address => uint256) internal debtProportion;
/// @notice Event emitted when the interest succesfully accrued
event InterestAccrued(uint256 previousTimestamp, uint256 currentTimestamp, uint256 previousVaultTotalOutstandingDebt, uint256 previousVaultTotalPendingFees, uint256 borrowRatePerSecondInEther, uint256 elapsedSeconds, uint256 interestAmount, uint256 totalOutstandingDebt, uint256 totalPendingFees);
/// @notice Event emitted when lender add supply to the vault
event SupplyAdded(address indexed account, uint256 amount, uint256 ExchangeRateInEther, uint256 mintedAmount);
/// @notice Event emitted when lender remove supply from the vault
event SupplyRemoved(address indexed account, uint256 amount, uint256 ExchangeRateInEther, uint256 redeemedAmount);
/// @notice Event emitted when vault parameters are updated
event ParametersUpdated(address indexed updater, uint256 u, uint256 s1, uint256 s2, uint256 mr, uint256 fee);
/// @notice Event emitted when the collected fees are withdrawn
event FeeCollected(address collector, uint256 total, address feeRecipient);
/// @notice Event emitted when the fee recipient is updated
event FeeRecipientUpdated(address updater, address newFeeRecipient);
/// @notice Construct new RisedleVault
constructor(
string memory name, // The name of the vault's token (e.g. Risedle USDC Vault)
string memory symbol, // The symbol of the vault's token (e.g rvUSDC)
address underlying, // The ERC20 address of the vault's underlying token (e.g. address of USDC token)
address feeRecipient // Fee recipient
) ERC20(name, symbol) {
underlyingToken = underlying; // Set the vault underlying token
lastTimestampInterestAccrued = block.timestamp; // Set the last timestamp accrued
totalOutstandingDebt = 0; // Set the initial state
totalPendingFees = 0;
FEE_RECIPIENT = feeRecipient;
maxTotalDeposit = 0;
}
/// @notice Vault's token use the same decimals as the underlying
function decimals() public view virtual override returns (uint8) {
return IERC20Metadata(underlyingToken).decimals();
}
/// @notice getUnderlying returns the underlying token of the vault
function getUnderlying() external view returns (address underlying) {
underlying = underlyingToken;
}
/// @notice getTotalAvailableCash returns the total amount of vault's underlying token that available to borrow
function getTotalAvailableCash() public view returns (uint256) {
uint256 vaultBalance = IERC20(underlyingToken).balanceOf(address(this));
if (totalPendingFees >= vaultBalance) return 0;
return vaultBalance - totalPendingFees;
}
/// @notice calculateUtilizationRateInEther calculates the utilization rate of the vault.
function calculateUtilizationRateInEther(uint256 available, uint256 outstandingDebt) internal pure returns (uint256) {
if (outstandingDebt == 0) return 0; // Utilization rate is 0% when there is no outstandingDebt
if (available == 0 && outstandingDebt > 0) return 1 ether; // Utilization rate is 100% when there is no cash available
uint256 rateInEther = (outstandingDebt * 1 ether) / (outstandingDebt + available); // utilization rate = amount outstanding debt / (amount available + amount outstanding debt)
return rateInEther;
}
/// @notice getUtilizationRateInEther for external use
function getUtilizationRateInEther() public view returns (uint256 utilizationRateInEther) {
uint256 totalAvailable = getTotalAvailableCash(); // Get total available asset
utilizationRateInEther = calculateUtilizationRateInEther(totalAvailable, totalOutstandingDebt);
}
/// @notice calculateBorrowRatePerSecondInEther calculates the borrow rate per second in ether units
function calculateBorrowRatePerSecondInEther(uint256 utilizationRateInEther) internal view returns (uint256) {
// utilizationRateInEther should in range [0, 1e18], Otherwise return max borrow rate
if (utilizationRateInEther >= 1 ether) return maxBorrowRatePerSecondInEther;
// Calculate the borrow rate
// See the formula here: https://observablehq.com/@pyk /ethrise
if (utilizationRateInEther <= optimalUtilizationRateInEther) {
// Borrow rate per year = (utilization rate/optimal utilization rate) * interest slope 1
// Borrow rate per seconds = Borrow rate per year / seconds in a year
uint256 rateInEther = (utilizationRateInEther * 1 ether) / optimalUtilizationRateInEther;
uint256 borrowRatePerYearInEther = (rateInEther * interestSlope1InEther) / 1 ether;
uint256 borrowRatePerSecondInEther = borrowRatePerYearInEther / totalSecondsInAYear;
return borrowRatePerSecondInEther;
} else {
// Borrow rate per year = interest slope 1 + ((utilization rate - optimal utilization rate)/(1-utilization rate)) * interest slope 2
// Borrow rate per seconds = Borrow rate per year / seconds in a year
uint256 aInEther = utilizationRateInEther - optimalUtilizationRateInEther;
uint256 bInEther = 1 ether - utilizationRateInEther;
uint256 cInEther = (aInEther * 1 ether) / bInEther;
uint256 dInEther = (cInEther * interestSlope2InEther) / 1 ether;
uint256 borrowRatePerYearInEther = interestSlope1InEther + dInEther;
uint256 borrowRatePerSecondInEther = borrowRatePerYearInEther / totalSecondsInAYear;
// Cap the borrow rate
if (borrowRatePerSecondInEther >= maxBorrowRatePerSecondInEther) {
return maxBorrowRatePerSecondInEther;
}
return borrowRatePerSecondInEther;
}
}
/// @notice getBorrowRatePerSecondInEther returns the current borrow rate per seconds
function getBorrowRatePerSecondInEther() public view returns (uint256 borrowRateInEther) {
uint256 utilizationRateInEther = getUtilizationRateInEther();
borrowRateInEther = calculateBorrowRatePerSecondInEther(utilizationRateInEther);
}
/// @notice getSupplyRatePerSecondInEther calculates the supply rate per second in ether units
function getSupplyRatePerSecondInEther() public view returns (uint256 supplyRateInEther) {
uint256 utilizationRateInEther = getUtilizationRateInEther();
uint256 borrowRateInEther = calculateBorrowRatePerSecondInEther(utilizationRateInEther);
uint256 nonFeeInEther = 1 ether - performanceFeeInEther;
uint256 rateForSupplyInEther = (borrowRateInEther * nonFeeInEther) / 1 ether;
supplyRateInEther = (utilizationRateInEther * rateForSupplyInEther) / 1 ether;
}
/// @notice getInterestAmount calculate amount of interest based on the total outstanding debt and borrow rate per second.
function getInterestAmount(
uint256 outstandingDebt, // Total of outstanding debt, in underlying decimals
uint256 borrowRatePerSecondInEther, // Borrow rates per second in ether units
uint256 elapsedSeconds // Number of seconds elapsed since last accrued
) internal pure returns (uint256) {
if (outstandingDebt == 0 || borrowRatePerSecondInEther == 0 || elapsedSeconds == 0) return 0;
uint256 interestAmount = (borrowRatePerSecondInEther * elapsedSeconds * outstandingDebt) / 1 ether; // Calculate the amount of interest
return interestAmount;
}
/// @notice setVaultStates update the totalOutstandingDebt and totalPendingFees
function setVaultStates(uint256 interestAmount, uint256 currentTimestamp) internal {
uint256 feeAmount = (performanceFeeInEther * interestAmount) / 1 ether; // Get the fee
totalOutstandingDebt += interestAmount; // Update the states
totalPendingFees += feeAmount;
lastTimestampInterestAccrued = currentTimestamp;
}
/// @notice accrueInterest accrues interest to totalOutstandingDebt and totalPendingFees
function accrueInterest() public {
uint256 currentTimestamp = block.timestamp; // Get the current timestamp, get last timestamp accrued and set the last time accrued
uint256 previousTimestamp = lastTimestampInterestAccrued;
if (currentTimestamp == previousTimestamp) return; // If currentTimestamp and previousTimestamp is similar then return early
uint256 previousVaultTotalOutstandingDebt = totalOutstandingDebt; // For event logging purpose
uint256 previousVaultTotalPendingFees = totalPendingFees;
uint256 borrowRatePerSecondInEther = getBorrowRatePerSecondInEther(); // Get borrow rate per second
uint256 elapsedSeconds = currentTimestamp - previousTimestamp; // Get time elapsed since last accrued
uint256 interestAmount = getInterestAmount(totalOutstandingDebt, borrowRatePerSecondInEther, elapsedSeconds); // Get the interest amount
setVaultStates(interestAmount, currentTimestamp); // Update the vault states based on the interest amount:
emit InterestAccrued(previousTimestamp, currentTimestamp, previousVaultTotalOutstandingDebt, previousVaultTotalPendingFees, borrowRatePerSecondInEther, elapsedSeconds, interestAmount, totalOutstandingDebt, totalPendingFees);
}
/// @notice getExchangeRateInEther get the current exchange rate of vault token in term of Vault's underlying token.
function getExchangeRateInEther() public view returns (uint256) {
uint256 totalSupply = totalSupply();
if (totalSupply == 0) {
// If there is no supply, exchange rate is 1:1
return 1 ether;
} else {
// Otherwise: exchangeRate = (totalAvailable + totalOutstandingDebt) / totalSupply
uint256 totalAvailable = getTotalAvailableCash();
uint256 totalAllUnderlyingAsset = totalAvailable + totalOutstandingDebt;
uint256 exchangeRateInEther = (totalAllUnderlyingAsset * 1 ether) / totalSupply;
return exchangeRateInEther;
}
}
/// @notice Lender supplies underlying token into the vault and receives vault tokens in exchange
function addSupply(uint256 amount) external nonReentrant {
accrueInterest(); // Accrue interest
if (maxTotalDeposit != 0) require(getTotalAvailableCash() + totalOutstandingDebt + amount < maxTotalDeposit, "!MCR"); // Max cap reached
uint256 exchangeRateInEther = getExchangeRateInEther(); // Get the exchange rate
uint256 mintedAmount = (amount * 1 ether) / exchangeRateInEther; // Calculate how much vault token we need to send to the lender
IERC20(underlyingToken).safeTransferFrom(msg.sender, address(this), amount); // Transfer asset from lender to the vault
_mint(msg.sender, mintedAmount); // Send vault token to the lender
emit SupplyAdded(msg.sender, amount, exchangeRateInEther, mintedAmount);
}
/// @notice Lender burn vault tokens and receives underlying tokens in exchange
function removeSupply(uint256 amount) external nonReentrant {
accrueInterest(); // Accrue interest
uint256 exchangeRateInEther = getExchangeRateInEther(); // Get the exchange rate
uint256 redeemedAmount = (exchangeRateInEther * amount) / 1 ether; // Calculate how much underlying token we need to send to the lender
IERC20(underlyingToken).safeTransfer(msg.sender, redeemedAmount); // Transfer Vault's underlying token from the vault to the lender
_burn(msg.sender, amount); // Burn the vault tokens from the lender
emit SupplyRemoved(msg.sender, amount, exchangeRateInEther, redeemedAmount);
}
/// @notice getDebtProportionRateInEther returns the proportion of borrow amount relative to the totalOutstandingDebt
function getDebtProportionRateInEther() internal view returns (uint256 debtProportionRateInEther) {
if (totalOutstandingDebt == 0 || totalDebtProportion == 0) {
return 1 ether;
}
debtProportionRateInEther = (totalOutstandingDebt * 1 ether) / totalDebtProportion;
}
/// @notice getOutstandingDebt returns the debt owed by the RISE/DROP tokens
function getOutstandingDebt(address token) public view returns (uint256) {
// If there is no debt, return 0
if (totalOutstandingDebt == 0) return 0;
// Calculate the outstanding debt
// outstanding debt = debtProportion * debtProportionRate
uint256 debtProportionRateInEther = getDebtProportionRateInEther();
uint256 a = (debtProportion[token] * debtProportionRateInEther);
uint256 b = 1 ether;
uint256 outstandingDebt = a / b + (a % b == 0 ? 0 : 1); // Rounds up instead of rounding down
return outstandingDebt;
}
/// @notice setBorrowStates sets the debt of the RISE/DROP token
function setBorrowStates(address token, uint256 borrowAmount) internal {
uint256 debtProportionRateInEther = getDebtProportionRateInEther();
totalOutstandingDebt += borrowAmount;
uint256 borrowProportion = (borrowAmount * 1 ether) / debtProportionRateInEther;
totalDebtProportion += borrowProportion;
debtProportion[token] = debtProportion[token] + borrowProportion;
}
/// @notice setRepayStates repay the debt of the RISE tokens
function setRepayStates(address token, uint256 repayAmount) internal {
uint256 debtProportionRateInEther = getDebtProportionRateInEther();
// Handle repay amount larger than existing total debt
if (repayAmount > totalOutstandingDebt) {
totalOutstandingDebt = 0;
} else {
totalOutstandingDebt -= repayAmount;
}
uint256 repayProportion = (repayAmount * 1 ether) / debtProportionRateInEther;
if (repayProportion > totalDebtProportion) {
totalDebtProportion = 0;
} else {
totalDebtProportion -= repayProportion;
}
if (repayProportion > debtProportion[token]) {
debtProportion[token] -= 0;
} else {
debtProportion[token] -= repayProportion;
}
}
/// @notice setVaultParameters updates the vault parameters.
function setVaultParameters(
uint256 u,
uint256 s1,
uint256 s2,
uint256 mr,
uint256 fee
) external onlyOwner {
// Update vault parameters
optimalUtilizationRateInEther = u;
interestSlope1InEther = s1;
interestSlope2InEther = s2;
maxBorrowRatePerSecondInEther = mr;
performanceFeeInEther = fee;
emit ParametersUpdated(msg.sender, u, s1, s2, mr, fee);
}
/// @notice getVaultParameters returns the current vault parameters.
function getVaultParameters()
external
view
returns (
uint256 _optimalUtilizationRateInEther,
uint256 _interestSlope1InEther,
uint256 _interestSlope2InEther,
uint256 _maxBorrowRatePerSecondInEther,
uint256 _performanceFeeInEther
)
{
_optimalUtilizationRateInEther = optimalUtilizationRateInEther;
_interestSlope1InEther = interestSlope1InEther;
_interestSlope2InEther = interestSlope2InEther;
_maxBorrowRatePerSecondInEther = maxBorrowRatePerSecondInEther;
_performanceFeeInEther = performanceFeeInEther;
}
/// @notice setFeeRecipient sets the fee recipient address.
function setFeeRecipient(address account) external onlyOwner {
FEE_RECIPIENT = account;
emit FeeRecipientUpdated(msg.sender, account);
}
/// @notice collectVaultPendingFees withdraws collected fees to the FEE_RECIPIENT address
function collectVaultPendingFees() external {
accrueInterest(); // Accrue interest
uint256 collectedFees = totalPendingFees;
IERC20(underlyingToken).safeTransfer(FEE_RECIPIENT, collectedFees);
totalPendingFees = 0;
emit FeeCollected(msg.sender, collectedFees, FEE_RECIPIENT);
}
/// @notice setVaultMaxTotalDeposit sets the max total deposit of the vault
function setVaultMaxTotalDeposit(uint256 amount) external onlyOwner {
maxTotalDeposit = amount;
}
}
////// src/interfaces/IRisedleERC20.sol
/* pragma solidity >=0.8.9; */
/* pragma experimental ABIEncoderV2; */
interface IRisedleERC20 {
function mint(address to, uint256 amount) external;
function burn(address from, uint256 amount) external;
}
////// src/interfaces/IRisedleOracle.sol
/* pragma solidity >=0.8.9; */
/* pragma experimental ABIEncoderV2; */
interface IRisedleOracle {
// Get price of the collateral based on the vault's underlying asset
// For example ETH that trade 4000 USDC is returned as 4000 * 1e6 because USDC have 6 decimals
function getPrice() external view returns (uint256 price);
}
////// src/interfaces/IRisedleSwap.sol
/* pragma solidity >=0.8.9; */
/* pragma experimental ABIEncoderV2; */
interface IRisedleSwap {
/**
* @notice Swap tokenIn to tokenOut
* @param tokenIn The ERC20 address of token that we want to swap
* @param tokenOut The ERC20 address of token that we want swap to
* @param maxAmountIn The maximum amount of tokenIn to get the tokenOut with amountOut
* @param amountOut The amount of tokenOut that we want to get
* @return amountIn The amount of tokenIn that we spend to get the amountOut of tokenOut
*/
function swap(
address tokenIn,
address tokenOut,
uint256 maxAmountIn,
uint256 amountOut
) external returns (uint256 amountIn);
}
////// src/interfaces/IWETH9.sol
/* pragma solidity >=0.8.9; */
/* pragma experimental ABIEncoderV2; */
/* import { IERC20 } from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; */
/// @title Interface for WETH9
/// @author bayu (github.com/pyk)
interface IWETH9 is IERC20 {
/// @notice Deposit ether to get wrapped ether
function deposit() external payable;
/// @notice Withdraw wrapped ether to get ether
function withdraw(uint256) external;
}
////// src/tokens/RisedleERC20.sol
// Risedle ERC20 Contract
// ERC20 contract to leverage and hedge token.
// It allows the owner to mint/burn token. On the production setup,
// only Risedle Vault can mint/burn this token.
// It's been validated using dapp tools HEVM verification.
//
// Copyright (c) 2021 Bayu - All rights reserved
// github: pyk
// email: [email protected]
/* pragma solidity >=0.8.9; */
/* pragma experimental ABIEncoderV2; */
/* import { ERC20 } from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; */
/* import { Ownable } from "lib/openzeppelin-contracts/contracts/access/Ownable.sol"; */
/// @notice Risedle ERC20 implementation
contract RisedleERC20 is ERC20, Ownable {
uint8 private _decimals;
/// @notice Construct new Risedle ERC20 token
/// @param name The ERC20 token name
/// @param symbol The ERC20 token symbol
/// @param owner The ERC20 owner contract
/// @param decimals_ The ERC20 token decimals
constructor(
string memory name,
string memory symbol,
address owner,
uint8 decimals_
) ERC20(name, symbol) {
// Set the owner
transferOwnership(owner);
// Set the decimals
_decimals = decimals_;
}
function decimals() public view virtual override returns (uint8) {
return _decimals;
}
/// @notice mint mints new token to the specified address
/// @dev Used when user deposit asset in the vault or mint new leverage/hedge
/// token. Only owner can call this function.
function mint(address to, uint256 amount) external onlyOwner {
_mint(to, amount);
}
/// @notice burn burns the token from the specified address
/// @dev Used when user withdraw asset in the vault or redeem leverage/hedge
/// token. Only owner can call this function.
function burn(address from, uint256 amount) external onlyOwner {
_burn(from, amount);
}
}
////// src/RiseTokenVault.sol
// Copyright (c) 2021 Bayu - All rights reserved
/* pragma solidity >=0.8.9; */
/* pragma experimental ABIEncoderV2; */
/* import { IERC20 } from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; */
/* import { SafeERC20 } from "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; */
/* import { IERC20Metadata } from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol"; */
/* import { RisedleVault } from "./RisedleVault.sol"; */
/* import { RisedleERC20 } from "./tokens/RisedleERC20.sol"; */
/* import { IRisedleOracle } from "./interfaces/IRisedleOracle.sol"; */
/* import { IRisedleSwap } from "./interfaces/IRisedleSwap.sol"; */
/* import { IRisedleERC20 } from "./interfaces/IRisedleERC20.sol"; */
/* import { IWETH9 } from "./interfaces/IWETH9.sol"; */
/// @title Rise Token Vault
/// @author bayu (github.com/pyk)
/// @dev It implements leveraged tokens. User can mint leveraged tokens, redeem leveraged tokens and trigger the rebalance. Rebalance only get execute when the criteria is met.
contract RiseTokenVault is RisedleVault {
using SafeERC20 for IERC20;
/// @notice RiseTokenMetadata contains the metadata of TOKENRISE
struct RiseTokenMetadata {
bool isETH; // True if the collateral is eth
address token; // Address of ETF token ERC20, make sure this vault can mint & burn this token
address collateral; // ETF underlying asset (e.g. WETH address)
address oracleContract; // Contract address that implement IRisedleOracle interface
address swapContract; // Contract address that implment IRisedleSwap interface
uint256 maxSwapSlippageInEther; // Maximum swap slippage for mint, redeem and rebalancing (e.g. 1% is 0.01 ether or 0.01 * 1e18)
uint256 initialPrice; // In term of vault's underlying asset (e.g. 100 USDC -> 100 * 1e6, coz is 6 decimals for USDC)
uint256 feeInEther; // Creation and redemption fee in ether units (e.g. 0.1% is 0.001 ether)
uint256 totalCollateralPlusFee; // Total amount of underlying managed by this ETF
uint256 totalPendingFees; // Total amount of creation and redemption pending fees in ETF underlying
uint256 minLeverageRatioInEther; // Minimum leverage ratio in ether units (e.g. 2x is 2 ether = 2*1e18)
uint256 maxLeverageRatioInEther; // Maximum leverage ratio in ether units (e.g. 3x is 3 ether = 3*1e18)
uint256 maxRebalancingValue; // The maximum value of buy/sell when rebalancing (e.g. 500K USDC is 500000 * 1e6)
uint256 rebalancingStepInEther; // The rebalancing step in ether units (e.g. 0.2 is 0.2 ether or 0.2 * 1e18)
uint256 maxTotalCollateral; // Limit the mint amount
}
/// @notice Mapping TOKENRISE to their metadata
mapping(address => RiseTokenMetadata) riseTokens;
event RiseTokenCreated(address indexed creator, address token); // Event emitted when new TOKENRISE is created
event RiseTokenMinted(address indexed user, address indexed riseToken, uint256 mintedAmount); // Event emitted when TOKENRISE is minted
event RiseTokenRebalanced(address indexed executor, uint256 previousLeverageRatioInEther); // Event emitted when TOKENRISE is successfully rebalanced
event RiseTokenBurned(address indexed user, address indexed riseToken, uint256 redeemedAmount); // Event emitted when TOKENRISE is burned
event MaxTotalCollateralUpdated(address indexed token, uint256 newMaxTotalCollateral); // Event emitted when max collateral is set
event OracleContractUpdated(address indexed token, address indexed oracle); // Event emitted when new oracle contract is set
event SwapContractUpdated(address indexed token, address indexed swap); // Event emitted when new swap contract is set
/// @notice Construct new RiseTokenVault
constructor(
string memory name, // The name of the vault's token (e.g. Risedle USDC Vault)
string memory symbol, // The symbol of the vault's token (e.g rvUSDC)
address underlying, // The ERC20 address of the vault's underlying token (e.g. address of USDC token)
address feeRecipient // Vault's fee recipient
) RisedleVault(name, symbol, underlying, feeRecipient) {}
/// @notice create creates new TOKENRISE
function create(
bool isETH, // True if the collateral is ETH
address tokenRiseAddress, // ERC20 token address that only RiseTokenVault can mint and burn
address collateral, // The underlying token of TOKENRISE (e.g. WBTC), it's WETH if the isETH is true
address oracleContract, // Contract address that implement IRisedleOracle interface
address swapContract, // Uniswap V3 like token swapper
uint256 maxSwapSlippageInEther, // Maximum slippage when mint, redeem and rebalancing (1% is 0.01 ether or 0.01*1e18)
uint256 initialPrice, // Initial price of the TOKENRISE based on the Vault's underlying asset (e.g. 100 USDC => 100 * 1e6)
uint256 feeInEther, // Creation and redemption fee in ether units (e.g. 0.001 ether = 0.1%)
uint256 minLeverageRatioInEther, // Minimum leverage ratio in ether units (e.g. 2x is 2 ether = 2*1e18)
uint256 maxLeverageRatioInEther, // Maximum leverage ratio in ether units (e.g. 3x is 3 ether = 3*1e18)
uint256 maxRebalancingValue, // The maximum value of buy/sell when rebalancing (e.g. 500K USDC is 500000 * 1e6)
uint256 rebalancingStepInEther // The rebalancing step in ether units (e.g. 0.2 is 0.2 ether or 0.2 * 1e18)
) external onlyOwner {
RiseTokenMetadata memory riseTokenMetadata = riseTokens[tokenRiseAddress];
require(riseTokenMetadata.feeInEther == 0, "!AE"); // Make sure token metadata is not exists
// Create new Rise metadata
riseTokens[tokenRiseAddress] = RiseTokenMetadata({
isETH: isETH,
token: tokenRiseAddress,
collateral: collateral,
oracleContract: oracleContract,
swapContract: swapContract,
maxSwapSlippageInEther: maxSwapSlippageInEther,
initialPrice: initialPrice,
feeInEther: feeInEther,
minLeverageRatioInEther: minLeverageRatioInEther,
maxLeverageRatioInEther: maxLeverageRatioInEther,
maxRebalancingValue: maxRebalancingValue,
rebalancingStepInEther: rebalancingStepInEther,
totalCollateralPlusFee: 0,
totalPendingFees: 0,
maxTotalCollateral: 0
});
// Emit event
emit RiseTokenCreated(msg.sender, tokenRiseAddress);
}
/// @notice getMetadata returns the metadata of the TOKENRISE
function getMetadata(address token) external view returns (RiseTokenMetadata memory) {
return riseTokens[token];
}
/// @notice calculateCollateralPerRiseToken returns the collateral shares per TOKENRISE
function calculateCollateralPerRiseToken(
uint256 riseTokenSupply, // The total supply of the TOKENRISE
uint256 totalCollateralPlusFee, // The total collateral managed by the TOKENRISE
uint256 totalPendingFees, // The total pending fees in the TOKENRISE
uint8 collateralDecimals // The collateral decimals (e.g. ETH is 18 decimals)
) internal pure returns (uint256 collateralPerRiseToken) {
if (riseTokenSupply == 0) return 0;
collateralPerRiseToken = ((totalCollateralPlusFee - totalPendingFees) * (10**collateralDecimals)) / riseTokenSupply; // Get collateral per TOKENRISE
}
/// @notice getCollateralPerRiseToken returns the collateral shares per TOKENRISE
function getCollateralPerRiseToken(address token) external view returns (uint256 collateralPerRiseToken) {
RiseTokenMetadata memory riseTokenMetadata = riseTokens[token];
if (riseTokenMetadata.feeInEther == 0) return 0; // Make sure the TOKENRISE is exists
uint256 riseTokenSupply = IERC20(riseTokenMetadata.token).totalSupply();
uint8 collateralDecimals = IERC20Metadata(riseTokenMetadata.token).decimals();
collateralPerRiseToken = calculateCollateralPerRiseToken(riseTokenSupply, riseTokenMetadata.totalCollateralPlusFee, riseTokenMetadata.totalPendingFees, collateralDecimals);
}
/// @notice calculateDebtPerRiseToken returns the debt shares per TOKENRISE
function calculateDebtPerRiseToken(
address token, // The address of TOKENRISE (ERC20)
uint256 totalSupply, // The current total supply of the TOKENRISE
uint8 collateralDecimals // The decimals of the collateral token (e.g. ETH have 18 decimals)
) internal view returns (uint256 debtPerRiseToken) {
if (totalSupply == 0) return 0;
uint256 totalDebt = getOutstandingDebt(token); // Get total TOKENRISE debt
if (totalDebt == 0) return 0;
uint256 a = (totalDebt * (10**collateralDecimals));
uint256 b = totalSupply;
debtPerRiseToken = a / b + (a % b == 0 ? 0 : 1); // Rounds up instead of rounding down
}
/// @notice getDebtPerRiseToken returns the debt shares per TOKENRISE
function getDebtPerRiseToken(address token) external view returns (uint256 debtPerRiseToken) {
RiseTokenMetadata memory riseTokenMetadata = riseTokens[token];
if (riseTokenMetadata.feeInEther == 0) return 0; // Make sure the TOKENRISE is exists
uint256 totalSupply = IERC20(riseTokenMetadata.token).totalSupply();
uint8 collateralDecimals = IERC20Metadata(riseTokenMetadata.token).decimals();
debtPerRiseToken = calculateDebtPerRiseToken(riseTokenMetadata.token, totalSupply, collateralDecimals);
}
/// @notice calculateNAV calculates the net-asset value of the ETF
function calculateNAV(
uint256 collateralPerRiseToken, // The amount of collateral per TOKENRISE (e.g 0.5 ETH is 0.5*1e18)
uint256 debtPerRiseToken, // The amount of debt per TOKENRISE (e.g. 50 USDC is 50*1e6)
uint256 collateralPrice, // The collateral price in term of supply asset (e.g 100 USDC is 100*1e6)
uint256 etfInitialPrice, // The initial price of the ETF in terms od supply asset (e.g. 100 USDC is 100*1e6)
uint8 collateralDecimals // The decimals of the collateral token
) internal pure returns (uint256 nav) {
if (collateralPerRiseToken == 0 || debtPerRiseToken == 0) return etfInitialPrice;
uint256 collateralValuePerRiseToken = (collateralPerRiseToken * collateralPrice) / (10**collateralDecimals); // Get the collateral value in term of the supply
nav = collateralValuePerRiseToken - debtPerRiseToken; // Calculate the NAV
}
/// @notice Get the net-asset value of the TOKENRISE
function getNAV(address token) public view returns (uint256 nav) {
RiseTokenMetadata memory riseTokenMetadata = riseTokens[token];
if (riseTokenMetadata.feeInEther == 0) return 0; // Make sure the TOKENRISE is exists
uint256 collateralPrice = IRisedleOracle(riseTokenMetadata.oracleContract).getPrice(); // For example WETH/USDC would trading around 4000 USDC (4000 * 1e6)
uint256 totalSupply = IERC20(riseTokenMetadata.token).totalSupply(); // Get collateral per TOKENRISE and debt per TOKENRISE
uint8 collateralDecimals = IERC20Metadata(riseTokenMetadata.token).decimals();
uint256 collateralPerRiseToken = calculateCollateralPerRiseToken(totalSupply, riseTokenMetadata.totalCollateralPlusFee, riseTokenMetadata.totalPendingFees, collateralDecimals);
uint256 debtPerRiseToken = calculateDebtPerRiseToken(riseTokenMetadata.token, totalSupply, collateralDecimals);
nav = calculateNAV(collateralPerRiseToken, debtPerRiseToken, collateralPrice, riseTokenMetadata.initialPrice, collateralDecimals);
}
/// @notice getCollateralAndFeeAmount splits collateral and fee amount
function getCollateralAndFeeAmount(uint256 amount, uint256 feeInEther) internal pure returns (uint256 collateralAmount, uint256 feeAmount) {
feeAmount = (amount * feeInEther) / 1 ether;
collateralAmount = amount - feeAmount;
}
/// @notice swap swaps the inputToken to outputToken
function swap(
address swapContract, // The address of swap contract
address inputToken, // The address of the token that we want to sell
address outputToken, // The address of the output token that we want to buy
uint256 maxInputAmount, // The maximum amount of input token that we want to sell
uint256 outputAmount // The amount of output token that we want to buy
) internal returns (uint256 inputTokenSold) {
IERC20(inputToken).safeApprove(swapContract, maxInputAmount); // Allow swap contract to spend the input token from the contract
inputTokenSold = IRisedleSwap(swapContract).swap(inputToken, outputToken, maxInputAmount, outputAmount); // Swap inputToken to outputToken
IERC20(inputToken).safeApprove(swapContract, 0); // Reset the approval
}
/// @notice getMintAmount returns the amount of TOKENRISE need to be minted
function getMintAmount(
uint256 nav, // The net asset value of TOKENRISE (e.g. 200 USDC is 200 * 1e6)
uint256 collateralAmount, // The amount of the collateral (e.g. 1 ETH is 1e18)
uint256 collateralPrice, // The price of the collateral (e.g. 4000 USDC is 4000 * 1e6)
uint256 borrowAmount, // The amount of borrow (e.g 200 USDC is 200 * 1e6)
uint8 collateralDecimals // The decimals of the collateral token (e.g. ETH have 18 decimals)
) internal pure returns (uint256 mintedAmount) {
// Calculate the total investment
uint256 totalInvestment = ((2 * collateralAmount * collateralPrice) / (10**collateralDecimals)) - borrowAmount; // totalInvestment = (2 x collateralValue) - borrowAmount
mintedAmount = (totalInvestment * (10**collateralDecimals)) / nav; // Get minted amount
}
/// @notice Mint new TOKENRISE
function mintRiseToken(
address token, // The address of TOKENRISE
address minter, // The minter address
address recipient, // The TOKENRISE recipient
uint256 amount // The Amount
) internal nonReentrant {
RiseTokenMetadata memory riseTokenMetadata = riseTokens[token];
require(riseTokenMetadata.feeInEther > 0, "!RTNE"); // Make sure the TOKENRISE is exists
if (riseTokenMetadata.maxTotalCollateral > 0) require(riseTokenMetadata.totalCollateralPlusFee + (2 * amount) < riseTokenMetadata.maxTotalCollateral, "!CIR"); // Cap is reached
accrueInterest(); // Accrue interest
uint256 nav = getNAV(token); // For example, If ETHRISE nav is 200 USDC, it will returns 200 * 1e6
if (minter != address(this)) IERC20(riseTokenMetadata.collateral).safeTransferFrom(minter, address(this), amount); // Don't get WETH from the user
(uint256 collateralAmount, uint256 feeAmount) = getCollateralAndFeeAmount(amount, riseTokenMetadata.feeInEther); // Get the collateral and fee amount
riseTokens[riseTokenMetadata.token].totalCollateralPlusFee += ((2 * collateralAmount) + feeAmount); // Update the TOKENRISE metadata
riseTokens[riseTokenMetadata.token].totalPendingFees += feeAmount;
uint256 collateralPrice = IRisedleOracle(riseTokenMetadata.oracleContract).getPrice(); // Get the current price of collateral in term of vault underlying asset
uint8 collateralDecimals = IERC20Metadata(riseTokenMetadata.collateral).decimals();
uint256 maxCollateralPrice = collateralPrice + ((riseTokenMetadata.maxSwapSlippageInEther * collateralPrice) / 1 ether); // Maximum slippage from the oracle price; It can be +X% from the oracle price
uint256 maxBorrowAmount = (collateralAmount * maxCollateralPrice) / (10**collateralDecimals); // Calculate the maximum borrow amount
require(getTotalAvailableCash() > maxBorrowAmount, "!NES"); // Make sure we do have enough vault's underlying available
uint256 borrowedAmount = swap(riseTokenMetadata.swapContract, underlyingToken, riseTokenMetadata.collateral, maxBorrowAmount, collateralAmount);
setBorrowStates(token, borrowedAmount); // Set TOKENRISE debt states
uint256 mintedAmount = getMintAmount(nav, collateralAmount, collateralPrice, borrowedAmount, collateralDecimals); // Calculate minted amount
IRisedleERC20(token).mint(recipient, mintedAmount); // Transfer TOKENRISE to the caller
emit RiseTokenMinted(recipient, token, mintedAmount);
}
/// @notice Mint new ETHRISE. The ETH will automatically wrapped to WETH first
function mint(address token) external payable {
RiseTokenMetadata memory riseTokenMetadata = riseTokens[token];
require(riseTokenMetadata.feeInEther > 0, "!RTNE"); // Make sure the TOKENRISE is exists
require(riseTokenMetadata.isETH, "!TRNE"); // TOKENRISE is not ETH enabled
require(msg.value > 0, "!EIZ"); // ETH is zero
IWETH9(riseTokenMetadata.collateral).deposit{ value: msg.value }(); // Wrap the ETH to WETH
mintRiseToken(token, address(this), msg.sender, msg.value); // Mint the ETHRISE token as the contract and send the ETHRISE to the user
}
/// @notice Mint new ETHRISE and sent minted token to the recipient
function mint(address token, address recipient) external payable {
RiseTokenMetadata memory riseTokenMetadata = riseTokens[token];
require(riseTokenMetadata.feeInEther > 0, "!RTNE"); // Make sure the TOKENRISE is exists
require(riseTokenMetadata.isETH, "!TRNE"); // TOKENRISE is not ETH enabled
require(msg.value > 0, "!EIZ"); // ETH is zero
IWETH9(riseTokenMetadata.collateral).deposit{ value: msg.value }(); // Wrap the ETH to WETH
mintRiseToken(token, address(this), recipient, msg.value); // Mint the ETHRISE token as the contract and send the ETHRISE to the user
}
/// @notice Mint new ERC20RISE
function mint(address token, uint256 amount) external {
mintRiseToken(token, msg.sender, msg.sender, amount);
}
/// @notice Mint new ERC20RISE with custom recipient
function mint(
address token,
address recipient,
uint256 amount
) external {
mintRiseToken(token, msg.sender, recipient, amount);
}
/// @notice calculateLeverageRatio calculates leverage ratio
function calculateLeverageRatio(
uint256 collateralPerRiseToken,
uint256 debtPerRiseToken,
uint256 collateralPrice,
uint256 etfInitialPrice,
uint8 collateralDecimals
) internal pure returns (uint256 leverageRatioInEther) {
uint256 collateralValuePerRiseToken = (collateralPerRiseToken * collateralPrice) / (10**collateralDecimals);
uint256 nav = calculateNAV(collateralPerRiseToken, debtPerRiseToken, collateralPrice, etfInitialPrice, collateralDecimals);
leverageRatioInEther = (collateralValuePerRiseToken * 1 ether) / nav;
}
/// @notice Get the leverage ratio
function getLeverageRatioInEther(address token) external view returns (uint256 leverageRatioInEther) {
RiseTokenMetadata memory riseTokenMetadata = riseTokens[token];
if (riseTokenMetadata.feeInEther == 0) return 0; // Make sure the TOKENRISE is exists
uint256 totalSupply = IERC20(riseTokenMetadata.token).totalSupply();
uint8 collateralDecimals = IERC20Metadata(riseTokenMetadata.collateral).decimals();
uint256 collateralPerRiseToken = calculateCollateralPerRiseToken(totalSupply, riseTokenMetadata.totalCollateralPlusFee, riseTokenMetadata.totalPendingFees, collateralDecimals);
uint256 debtPerRiseToken = calculateDebtPerRiseToken(riseTokenMetadata.token, totalSupply, collateralDecimals);
uint256 collateralPrice = IRisedleOracle(riseTokenMetadata.oracleContract).getPrice();
leverageRatioInEther = calculateLeverageRatio(collateralPerRiseToken, debtPerRiseToken, collateralPrice, riseTokenMetadata.initialPrice, collateralDecimals);
}
/// @notice Run the rebalancing
function rebalance(address token) external nonReentrant {
RiseTokenMetadata memory riseTokenMetadata = riseTokens[token];
require(riseTokenMetadata.feeInEther > 0, "!RTNE"); // Make sure the TOKENRISE is exists
accrueInterest(); // Accrue interest
// Otherwise get the current leverage ratio
uint256 totalSupply = IERC20(riseTokenMetadata.token).totalSupply();
uint256 collateralPrice = IRisedleOracle(riseTokenMetadata.oracleContract).getPrice();
uint8 collateralDecimals = IERC20Metadata(riseTokenMetadata.collateral).decimals();
uint256 collateralPerRiseToken = calculateCollateralPerRiseToken(totalSupply, riseTokenMetadata.totalCollateralPlusFee, riseTokenMetadata.totalPendingFees, collateralDecimals);
uint256 debtPerRiseToken = calculateDebtPerRiseToken(riseTokenMetadata.token, totalSupply, collateralDecimals);
uint256 leverageRatioInEther = calculateLeverageRatio(collateralPerRiseToken, debtPerRiseToken, collateralPrice, riseTokenMetadata.initialPrice, collateralDecimals);
uint256 nav = calculateNAV(collateralPerRiseToken, debtPerRiseToken, collateralPrice, riseTokenMetadata.initialPrice, collateralDecimals);
require(leverageRatioInEther < riseTokenMetadata.minLeverageRatioInEther || leverageRatioInEther > riseTokenMetadata.maxLeverageRatioInEther, "!LRIR"); // Leverage ratio in range
uint256 borrowOrRepayAmount = (riseTokenMetadata.rebalancingStepInEther * ((nav * totalSupply) / (10**collateralDecimals))) / 1 ether;
uint256 collateralAmount = (borrowOrRepayAmount * (10**collateralDecimals)) / collateralPrice;
// Leveraging up when: leverage ratio < min leverage ratio. Borrow more USDCa and Swap USDC to collateral token
if (leverageRatioInEther < riseTokenMetadata.minLeverageRatioInEther) {
uint256 maximumCollateralPrice = collateralPrice + ((riseTokenMetadata.maxSwapSlippageInEther * collateralPrice) / 1 ether);
uint256 maxBorrowAmount = (collateralAmount * maximumCollateralPrice) / (10**collateralDecimals);
if (maxBorrowAmount > riseTokenMetadata.maxRebalancingValue) {
maxBorrowAmount = riseTokenMetadata.maxRebalancingValue;
}
uint256 borrowedAmount = swap(riseTokenMetadata.swapContract, underlyingToken, riseTokenMetadata.collateral, maxBorrowAmount, collateralAmount);
setBorrowStates(token, borrowedAmount);
riseTokens[riseTokenMetadata.token].totalCollateralPlusFee += collateralAmount;
}
// Leveraging down when: leverage ratio > max leverage ratio. Swap collateral to USDC and Repay the debt
if (leverageRatioInEther > riseTokenMetadata.maxLeverageRatioInEther) {
uint256 minimumCollateralPrice = collateralPrice - ((riseTokenMetadata.maxSwapSlippageInEther * collateralPrice) / 1 ether);
uint256 maxCollateralAmount = (borrowOrRepayAmount * (10**collateralDecimals)) / minimumCollateralPrice;
if (borrowOrRepayAmount > riseTokenMetadata.maxRebalancingValue) {
maxCollateralAmount = (riseTokenMetadata.maxRebalancingValue * (10**collateralDecimals)) / minimumCollateralPrice;
}
uint256 collateralSoldAmount = swap(riseTokenMetadata.swapContract, riseTokenMetadata.collateral, underlyingToken, maxCollateralAmount, borrowOrRepayAmount);
setRepayStates(token, borrowOrRepayAmount);
riseTokens[riseTokenMetadata.token].totalCollateralPlusFee -= collateralSoldAmount;
}
emit RiseTokenRebalanced(msg.sender, leverageRatioInEther);
}
function updateRedeemStates(
address token, // TOKENRISE address
uint256 collateral, // Collateral amount
uint256 fee // Fee amount
) internal {
riseTokens[token].totalCollateralPlusFee -= collateral;
riseTokens[token].totalPendingFees += fee;
}
function calculateRedeemAmount(RiseTokenMetadata memory riseTokenMetadata, uint256 amount) internal returns (uint256 redeemAmount) {
uint256 totalSupply = IERC20(riseTokenMetadata.token).totalSupply();
uint8 collateralDecimals = IERC20Metadata(riseTokenMetadata.collateral).decimals();
uint256 collateralPrice = IRisedleOracle(riseTokenMetadata.oracleContract).getPrice();
uint256 collateralPerRiseToken = calculateCollateralPerRiseToken(totalSupply, riseTokenMetadata.totalCollateralPlusFee, riseTokenMetadata.totalPendingFees, collateralDecimals);
uint256 debtPerRiseToken = calculateDebtPerRiseToken(riseTokenMetadata.token, totalSupply, collateralDecimals);
uint256 repayAmount = (debtPerRiseToken * amount) / (10**collateralDecimals);
setRepayStates(riseTokenMetadata.token, repayAmount);
uint256 collateralOwnedByUser = (amount * collateralPerRiseToken) / (10**collateralDecimals);
uint256 minimumCollateralPrice = collateralPrice - ((riseTokenMetadata.maxSwapSlippageInEther * collateralPrice) / 1 ether);
uint256 maxCollateralAmount = (((repayAmount * (10**collateralDecimals)) / ((collateralOwnedByUser * minimumCollateralPrice) / (10**collateralDecimals))) * collateralOwnedByUser) / (10**collateralDecimals);
uint256 collateralSoldAmount = swap(riseTokenMetadata.swapContract, riseTokenMetadata.collateral, underlyingToken, maxCollateralAmount, repayAmount);
uint256 feeAmount;
(redeemAmount, feeAmount) = getCollateralAndFeeAmount(collateralOwnedByUser - collateralSoldAmount, riseTokenMetadata.feeInEther);
updateRedeemStates(riseTokenMetadata.token, (collateralOwnedByUser - feeAmount), feeAmount);
}
/// @notice redeem Burn the TOKENRISE then send the collateral token to the sender
function redeem(address token, uint256 amount) external nonReentrant {
accrueInterest(); // Accrue interest
RiseTokenMetadata memory riseTokenMetadata = riseTokens[token];
require(riseTokenMetadata.feeInEther > 0, "!RTNE"); // Make sure the TOKENRISE is exists
uint256 redeemAmount = calculateRedeemAmount(riseTokenMetadata, amount);
IRisedleERC20(token).burn(msg.sender, amount);
// Send the remaining collateral to the investor minus the fee
if (riseTokenMetadata.isETH) {
IWETH9(riseTokenMetadata.collateral).withdraw(redeemAmount);
(bool success, ) = msg.sender.call{ value: redeemAmount }("");
require(success, "!ERF"); // ETH Redeem failed
} else {
IERC20(riseTokenMetadata.collateral).safeTransfer(msg.sender, redeemAmount);
}
emit RiseTokenBurned(msg.sender, token, redeemAmount);
}
/// @notice collectPendingFees withdraws collected fees to the FEE_RECIPIENT address
function collectPendingFees(address token) external {
accrueInterest(); // Accrue interest
RiseTokenMetadata memory riseTokenMetadata = riseTokens[token];
require(riseTokenMetadata.feeInEther > 0, "!RTNE"); // Make sure the TOKENRISE is exists
IERC20(riseTokenMetadata.collateral).safeTransfer(FEE_RECIPIENT, riseTokenMetadata.totalPendingFees);
riseTokens[token].totalCollateralPlusFee -= riseTokenMetadata.totalPendingFees;
riseTokens[token].totalPendingFees = 0;
emit FeeCollected(msg.sender, riseTokenMetadata.totalPendingFees, FEE_RECIPIENT);
}
/// @notice Set the cap
function setMaxTotalCollateral(address token, uint256 maxTotalCollateral) external onlyOwner {
RiseTokenMetadata memory riseTokenMetadata = riseTokens[token];
require(riseTokenMetadata.feeInEther > 0, "!RTNE"); // Make sure the TOKENRISE is exists
riseTokens[token].maxTotalCollateral = maxTotalCollateral;
emit MaxTotalCollateralUpdated(token, maxTotalCollateral);
}
/// @notice Set the oracle contract
function setOracleContract(address token, address newOracle) external onlyOwner {
RiseTokenMetadata memory riseTokenMetadata = riseTokens[token];
require(riseTokenMetadata.feeInEther > 0, "!RTNE"); // Make sure the TOKENRISE is exists
riseTokens[token].oracleContract = newOracle;
emit OracleContractUpdated(token, newOracle);
}
/// @notice Set the swap contract
function setSwapContract(address token, address newSwap) external onlyOwner {
RiseTokenMetadata memory riseTokenMetadata = riseTokens[token];
require(riseTokenMetadata.feeInEther > 0, "!RTNE"); // Make sure the TOKENRISE is exists
riseTokens[token].swapContract = newSwap;
emit SwapContractUpdated(token, newSwap);
}
/// @notice Receive ETH
receive() external payable {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"underlying","type":"address"},{"internalType":"address","name":"feeRecipient","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"collector","type":"address"},{"indexed":false,"internalType":"uint256","name":"total","type":"uint256"},{"indexed":false,"internalType":"address","name":"feeRecipient","type":"address"}],"name":"FeeCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"updater","type":"address"},{"indexed":false,"internalType":"address","name":"newFeeRecipient","type":"address"}],"name":"FeeRecipientUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"currentTimestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"previousVaultTotalOutstandingDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"previousVaultTotalPendingFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowRatePerSecondInEther","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"elapsedSeconds","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalOutstandingDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalPendingFees","type":"uint256"}],"name":"InterestAccrued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"newMaxTotalCollateral","type":"uint256"}],"name":"MaxTotalCollateralUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"oracle","type":"address"}],"name":"OracleContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"updater","type":"address"},{"indexed":false,"internalType":"uint256","name":"u","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"s1","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"s2","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mr","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"ParametersUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"riseToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"redeemedAmount","type":"uint256"}],"name":"RiseTokenBurned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"RiseTokenCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"riseToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"mintedAmount","type":"uint256"}],"name":"RiseTokenMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"executor","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousLeverageRatioInEther","type":"uint256"}],"name":"RiseTokenRebalanced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ExchangeRateInEther","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mintedAmount","type":"uint256"}],"name":"SupplyAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ExchangeRateInEther","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redeemedAmount","type":"uint256"}],"name":"SupplyRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"swap","type":"address"}],"name":"SwapContractUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"FEE_RECIPIENT","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrueInterest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"collectPendingFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collectVaultPendingFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"isETH","type":"bool"},{"internalType":"address","name":"tokenRiseAddress","type":"address"},{"internalType":"address","name":"collateral","type":"address"},{"internalType":"address","name":"oracleContract","type":"address"},{"internalType":"address","name":"swapContract","type":"address"},{"internalType":"uint256","name":"maxSwapSlippageInEther","type":"uint256"},{"internalType":"uint256","name":"initialPrice","type":"uint256"},{"internalType":"uint256","name":"feeInEther","type":"uint256"},{"internalType":"uint256","name":"minLeverageRatioInEther","type":"uint256"},{"internalType":"uint256","name":"maxLeverageRatioInEther","type":"uint256"},{"internalType":"uint256","name":"maxRebalancingValue","type":"uint256"},{"internalType":"uint256","name":"rebalancingStepInEther","type":"uint256"}],"name":"create","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getBorrowRatePerSecondInEther","outputs":[{"internalType":"uint256","name":"borrowRateInEther","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getCollateralPerRiseToken","outputs":[{"internalType":"uint256","name":"collateralPerRiseToken","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getDebtPerRiseToken","outputs":[{"internalType":"uint256","name":"debtPerRiseToken","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getExchangeRateInEther","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getLeverageRatioInEther","outputs":[{"internalType":"uint256","name":"leverageRatioInEther","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getMetadata","outputs":[{"components":[{"internalType":"bool","name":"isETH","type":"bool"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"collateral","type":"address"},{"internalType":"address","name":"oracleContract","type":"address"},{"internalType":"address","name":"swapContract","type":"address"},{"internalType":"uint256","name":"maxSwapSlippageInEther","type":"uint256"},{"internalType":"uint256","name":"initialPrice","type":"uint256"},{"internalType":"uint256","name":"feeInEther","type":"uint256"},{"internalType":"uint256","name":"totalCollateralPlusFee","type":"uint256"},{"internalType":"uint256","name":"totalPendingFees","type":"uint256"},{"internalType":"uint256","name":"minLeverageRatioInEther","type":"uint256"},{"internalType":"uint256","name":"maxLeverageRatioInEther","type":"uint256"},{"internalType":"uint256","name":"maxRebalancingValue","type":"uint256"},{"internalType":"uint256","name":"rebalancingStepInEther","type":"uint256"},{"internalType":"uint256","name":"maxTotalCollateral","type":"uint256"}],"internalType":"struct RiseTokenVault.RiseTokenMetadata","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getNAV","outputs":[{"internalType":"uint256","name":"nav","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getOutstandingDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSupplyRatePerSecondInEther","outputs":[{"internalType":"uint256","name":"supplyRateInEther","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTotalAvailableCash","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUnderlying","outputs":[{"internalType":"address","name":"underlying","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUtilizationRateInEther","outputs":[{"internalType":"uint256","name":"utilizationRateInEther","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVaultParameters","outputs":[{"internalType":"uint256","name":"_optimalUtilizationRateInEther","type":"uint256"},{"internalType":"uint256","name":"_interestSlope1InEther","type":"uint256"},{"internalType":"uint256","name":"_interestSlope2InEther","type":"uint256"},{"internalType":"uint256","name":"_maxBorrowRatePerSecondInEther","type":"uint256"},{"internalType":"uint256","name":"_performanceFeeInEther","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxTotalDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"rebalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"removeSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"maxTotalCollateral","type":"uint256"}],"name":"setMaxTotalCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"newOracle","type":"address"}],"name":"setOracleContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"newSwap","type":"address"}],"name":"setSwapContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"setVaultMaxTotalDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"u","type":"uint256"},{"internalType":"uint256","name":"s1","type":"uint256"},{"internalType":"uint256","name":"s2","type":"uint256"},{"internalType":"uint256","name":"mr","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setVaultParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalOutstandingDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalPendingFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60a0604052670c7d713b49da00006008556702c68af0bb140000600955670853a0d2313c0000600a556301e13380608052640bd014d7e6600b5567016345785d8a0000600c553480156200005257600080fd5b50604051620054d3380380620054d3833981016040819052620000759162000301565b83838383838381600390805190602001906200009392919062000171565b508051620000a990600490602084019062000171565b505050620000c6620000c06200011b60201b60201c565b6200011f565b6001600655600780546001600160a01b03199081166001600160a01b039485161790915542600d556000600e819055600f819055601280549092169290931691909117905560115550620003cd945050505050565b3390565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b8280546200017f9062000390565b90600052602060002090601f016020900481019282620001a35760008555620001ee565b82601f10620001be57805160ff1916838001178555620001ee565b82800160010185558215620001ee579182015b82811115620001ee578251825591602001919060010190620001d1565b50620001fc92915062000200565b5090565b5b80821115620001fc576000815560010162000201565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200023f57600080fd5b81516001600160401b03808211156200025c576200025c62000217565b604051601f8301601f19908116603f0116810190828211818310171562000287576200028762000217565b81604052838152602092508683858801011115620002a457600080fd5b600091505b83821015620002c85785820183015181830184015290820190620002a9565b83821115620002da5760008385830101525b9695505050505050565b80516001600160a01b0381168114620002fc57600080fd5b919050565b600080600080608085870312156200031857600080fd5b84516001600160401b03808211156200033057600080fd5b6200033e888389016200022d565b955060208701519150808211156200035557600080fd5b5062000364878288016200022d565b9350506200037560408601620002e4565b91506200038560608601620002e4565b905092959194509250565b600181811c90821680620003a557607f821691505b60208210811415620003c757634e487b7160e01b600052602260045260246000fd5b50919050565b6080516150e3620003f060003960008181613d800152613e2c01526150e36000f3fe6080604052600436106102b25760003560e01c8063715018a611610175578063a927de78116100dc578063e74b981b11610095578063ee1fe2ad1161006f578063ee1fe2ad1461087f578063f2fde38b14610892578063f76f5db5146108b2578063fd1b8498146108c757600080fd5b8063e74b981b1461081f578063e7a357181461083f578063ebd090541461085f57600080fd5b8063a927de7814610744578063acd4b00a14610759578063c6b5f6e214610779578063c6c3bbe614610799578063dc54a8ee146107b9578063dd62ed3e146107d957600080fd5b806395d89b411161012e57806395d89b411461069c5780639816f473146106b1578063a2fcc7ec146106cf578063a457c2d7146106ef578063a6afed951461070f578063a9059cbb1461072457600080fd5b8063715018a6146105f457806371a6cab71461060957806373f2cd8e1461061f578063859203e31461063f5780638da5cb5b14610655578063942c7cb41461068757600080fd5b8063298cbf261161021957806340c10f19116101d257806340c10f19146105125780634f4c25561461053257806363d4f58714610576578063695b84c4146105965780636a627842146105ab57806370a08231146105be57600080fd5b8063298cbf26146104495780632a50c1461461045e5780632a65983a1461048b578063313ce567146104ab57806339509351146104d257806340753a76146104f257600080fd5b806318160ddd1161026b57806318160ddd146103945780631e9a6950146103a95780631ea6cabc146103c957806321c28191146103e957806323b872dd1461040957806326bb0e8e1461042957600080fd5b806306fdde03146102be578063095ea7b3146102e95780630d7c759114610319578063109e62ea1461033c57806311ebf36d14610352578063179ddcdd1461037457600080fd5b366102b957005b600080fd5b3480156102ca57600080fd5b506102d36108e7565b6040516102e09190614aec565b60405180910390f35b3480156102f557600080fd5b50610309610304366004614b36565b610979565b60405190151581526020016102e0565b34801561032557600080fd5b5061032e610990565b6040519081526020016102e0565b34801561034857600080fd5b5061032e600f5481565b34801561035e57600080fd5b5061037261036d366004614b60565b6109f8565b005b34801561038057600080fd5b5061032e61038f366004614b60565b610bc6565b3480156103a057600080fd5b5060025461032e565b3480156103b557600080fd5b506103726103c4366004614b36565b610e33565b3480156103d557600080fd5b5061032e6103e4366004614b60565b61111e565b3480156103f557600080fd5b50610372610404366004614b60565b6111ac565b34801561041557600080fd5b50610309610424366004614b7b565b61174a565b34801561043557600080fd5b50610372610444366004614bb7565b6117f6565b34801561045557600080fd5b5061032e61196e565b34801561046a57600080fd5b5061047e610479366004614b60565b61198a565b6040516102e09190614bea565b34801561049757600080fd5b506103726104a6366004614ce1565b611afe565b3480156104b757600080fd5b506104c0611e85565b60405160ff90911681526020016102e0565b3480156104de57600080fd5b506103096104ed366004614b36565b611ef8565b3480156104fe57600080fd5b5061037261050d366004614d89565b611f34565b34801561051e57600080fd5b5061037261052d366004614b36565b612063565b34801561053e57600080fd5b50600854600954600a54600b54600c54604080519586526020860194909452928401919091526060830152608082015260a0016102e0565b34801561058257600080fd5b50610372610591366004614da2565b612073565b3480156105a257600080fd5b5061037261210e565b6103726105b9366004614b60565b61218c565b3480156105ca57600080fd5b5061032e6105d9366004614b60565b6001600160a01b031660009081526020819052604090205490565b34801561060057600080fd5b50610372612352565b34801561061557600080fd5b5061032e60115481565b34801561062b57600080fd5b5061032e61063a366004614b60565b612388565b34801561064b57600080fd5b5061032e600e5481565b34801561066157600080fd5b506005546001600160a01b03165b6040516001600160a01b0390911681526020016102e0565b34801561069357600080fd5b5061032e6125e9565b3480156106a857600080fd5b506102d3612664565b3480156106bd57600080fd5b506007546001600160a01b031661066f565b3480156106db57600080fd5b5061032e6106ea366004614b60565b612673565b3480156106fb57600080fd5b5061030961070a366004614b36565b612842565b34801561071b57600080fd5b506103726128db565b34801561073057600080fd5b5061030961073f366004614b36565b6129a2565b34801561075057600080fd5b5061032e6129af565b34801561076557600080fd5b50610372610774366004614bb7565b612a40565b34801561078557600080fd5b5061032e610794366004614b60565b612bb8565b3480156107a557600080fd5b506103726107b4366004614b7b565b612d80565b3480156107c557600080fd5b506103726107d4366004614d89565b612d91565b3480156107e557600080fd5b5061032e6107f4366004614bb7565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b34801561082b57600080fd5b5061037261083a366004614b60565b612dc0565b34801561084b57600080fd5b5061037261085a366004614d89565b612e3f565b34801561086b57600080fd5b5060125461066f906001600160a01b031681565b61037261088d366004614bb7565b612efe565b34801561089e57600080fd5b506103726108ad366004614b60565b6130c4565b3480156108be57600080fd5b5061032e61315f565b3480156108d357600080fd5b506103726108e2366004614b36565b613178565b6060600380546108f690614ddd565b80601f016020809104026020016040519081016040528092919081815260200182805461092290614ddd565b801561096f5780601f106109445761010080835404028352916020019161096f565b820191906000526020600020905b81548152906001019060200180831161095257829003601f168201915b5050505050905090565b60006109863384846132f2565b5060015b92915050565b60008061099c60025490565b9050806109b257670de0b6b3a764000091505090565b60006109bc6129af565b90506000600e54826109ce9190614e2e565b90506000836109e583670de0b6b3a7640000614e46565b6109ef9190614e7b565b95945050505050565b610a006128db565b6001600160a01b0380821660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c082015290610afc5760405162461bcd60e51b8152600401610af390614e8f565b60405180910390fd5b6012546101208201516040830151610b22926001600160a01b0391821692911690613416565b6101208101516001600160a01b03831660009081526014602052604081206007018054909190610b53908490614eae565b90915550506001600160a01b0382811660009081526014602090815260408083206008019290925561012084015160125483513381529283019190915290921682820152517f36119f4f28ae3384ed31589f21ec2992cb0ebe53b11c79a24466ee74471764ed9181900360600190a15050565b6001600160a01b03808216600090815260146020908152604080832081516101e081018352815460ff81161515825261010090819004871694820194909452600182015486169281019290925260028101548516606083015260038101549094166080820152600484015460a0820152600584015460c0820152600684015460e0820181905260078501549282019290925260088401546101208201526009840154610140820152600a840154610160820152600b840154610180820152600c8401546101a0820152600d909301546101c0840152909190610cab5750600092915050565b600081606001516001600160a01b03166398d5fdca6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d139190614ec5565b9050600082602001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7d9190614ec5565b9050600083602001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dc3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610de79190614ede565b90506000610e018386610100015187610120015185613479565b90506000610e14866020015185856134bd565b9050610e278282878960c001518761353d565b98975050505050505050565b60026006541415610e565760405162461bcd60e51b8152600401610af390614f01565b6002600655610e636128db565b6001600160a01b0380831660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c082015290610f565760405162461bcd60e51b8152600401610af390614e8f565b6000610f628284613583565b604051632770a7eb60e21b8152336004820152602481018590529091506001600160a01b03851690639dc29fac90604401600060405180830381600087803b158015610fad57600080fd5b505af1158015610fc1573d6000803e3d6000fd5b505050508160000151156110ba5781604001516001600160a01b0316632e1a7d4d826040518263ffffffff1660e01b815260040161100191815260200190565b600060405180830381600087803b15801561101b57600080fd5b505af115801561102f573d6000803e3d6000fd5b50506040516000925033915083908381818185875af1925050503d8060008114611075576040519150601f19603f3d011682016040523d82523d6000602084013e61107a565b606091505b50509050806110b45760405162461bcd60e51b8152600401610af39060208082526004908201526310a2a92360e11b604082015260600190565b506110d3565b60408201516110d3906001600160a01b03163383613416565b6040518181526001600160a01b0385169033907f547518a7214f42d72dccc4ed54426ba50f889c24bb4d9766443e899a123381b39060200160405180910390a3505060016006555050565b6000600e546000141561113357506000919050565b600061113d61384b565b6001600160a01b03841660009081526013602052604081205491925090611165908390614e46565b9050670de0b6b3a7640000600061117c8284614f38565b1561118857600161118b565b60005b60ff166111988385614e7b565b6111a29190614e2e565b9695505050505050565b600260065414156111cf5760405162461bcd60e51b8152600401610af390614f01565b600260068181556001600160a01b0380841660009081526014602090815260409182902082516101e081018452815460ff811615158252610100908190048616938201939093526001820154851693810193909352948501548316606083015260038501549092166080820152600484015460a0820152600584015460c08201529183015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c0820152906112c75760405162461bcd60e51b8152600401610af390614e8f565b6112cf6128db565b600081602001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611313573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113379190614ec5565b9050600082606001516001600160a01b03166398d5fdca6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561137d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a19190614ec5565b9050600083604001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061140b9190614ede565b905060006114258486610100015187610120015185613479565b90506000611438866020015186856134bd565b9050600061144d8383878a60c0015188613892565b905060006114628484888b60c001518961353d565b905087610140015182108061147b575087610160015182115b6114af5760405162461bcd60e51b815260206004820152600560248201526410a62924a960d91b6044820152606401610af3565b6000670de0b6b3a76400006114c587600a615030565b6114cf8a85614e46565b6114d99190614e7b565b8a6101a001516114e99190614e46565b6114f39190614e7b565b905060008761150388600a615030565b61150d9084614e46565b6115179190614e7b565b9050896101400151841015611600576000670de0b6b3a7640000898c60a001516115419190614e46565b61154b9190614e7b565b611555908a614e2e565b9050600061156489600a615030565b61156e8385614e46565b6115789190614e7b565b90508b610180015181111561158f57506101808b01515b60006115bb8d60800151600760009054906101000a90046001600160a01b03168f6040015185886138e4565b90506115c78e82613993565b6020808e01516001600160a01b0316600090815260149091526040812060070180548692906115f7908490614e2e565b90915550505050505b896101600151841115611703576000670de0b6b3a7640000898c60a001516116289190614e46565b6116329190614e7b565b61163c908a614eae565b905060008161164c8a600a615030565b6116569086614e46565b6116609190614e7b565b90508b6101800151841115611698578161167b8a600a615030565b8d610180015161168b9190614e46565b6116959190614e7b565b90505b60808c015160408d01516007546000926116be9290916001600160a01b031685896138e4565b90506116ca8e86613a35565b6020808e01516001600160a01b0316600090815260149091526040812060070180548392906116fa908490614eae565b90915550505050505b60405184815233907fdf85796b4c3419ec22e630b81a8766863bb75964edc1c7869986126e66f633449060200160405180910390a250506001600655505050505050505050565b6000611757848484613b40565b6001600160a01b0384166000908152600160209081526040808320338452909152902054828110156117dc5760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b6064820152608401610af3565b6117e985338584036132f2565b60019150505b9392505050565b6005546001600160a01b031633146118205760405162461bcd60e51b8152600401610af39061503f565b6001600160a01b0380831660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c0820152906119135760405162461bcd60e51b8152600401610af390614e8f565b6001600160a01b0383811660008181526014602052604080822060030180546001600160a01b0319169487169485179055517f28c4fd18fb488de9f00ed376c22a3a7d2bbadabfaec7ab26651c824676f6fd4b9190a3505050565b60008061197961315f565b905061198481613d0e565b91505090565b611a29604051806101e0016040528060001515815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b506001600160a01b0390811660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e083015260078301549082015260088201546101208201526009820154610140820152600a820154610160820152600b820154610180820152600c8201546101a0820152600d909101546101c082015290565b6005546001600160a01b03163314611b285760405162461bcd60e51b8152600401610af39061503f565b6001600160a01b03808c1660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c08201529015611c325760405162461bcd60e51b815260206004820152600360248201526221414560e81b6044820152606401610af3565b604051806101e001604052808e151581526020018d6001600160a01b031681526020018c6001600160a01b031681526020018b6001600160a01b031681526020018a6001600160a01b0316815260200189815260200188815260200187815260200160008152602001600081526020018681526020018581526020018481526020018381526020016000815250601460008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060608201518160020160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060808201518160030160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a0820151816004015560c0820151816005015560e0820151816006015561010082015181600701556101208201518160080155610140820151816009015561016082015181600a015561018082015181600b01556101a082015181600c01556101c082015181600d0155905050336001600160a01b03167f2a2b0348f99631bd5cb9665e0b2341476d6e9725bc1cc07c04f3c78fa958ad1f8d604051611e6e91906001600160a01b0391909116815260200190565b60405180910390a250505050505050505050505050565b6007546040805163313ce56760e01b815290516000926001600160a01b03169163313ce5679160048083019260209291908290030181865afa158015611ecf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ef39190614ede565b905090565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610986918590611f2f908690614e2e565b6132f2565b60026006541415611f575760405162461bcd60e51b8152600401610af390614f01565b6002600655611f646128db565b60115415611fc55760115481600e54611f7b6129af565b611f859190614e2e565b611f8f9190614e2e565b10611fc55760405162461bcd60e51b8152600401610af39060208082526004908201526310a6a1a960e11b604082015260600190565b6000611fcf610990565b9050600081611fe684670de0b6b3a7640000614e46565b611ff09190614e7b565b60075490915061200b906001600160a01b0316333086613e70565b6120153382613ea8565b604080518481526020810184905290810182905233907f85a85f897cc05e4f2226d4f0a00a86d8c091e7b9af9155d98f54cf9bdcb96934906060015b60405180910390a25050600160065550565b61206f82333384613f87565b5050565b6005546001600160a01b0316331461209d5760405162461bcd60e51b8152600401610af39061503f565b60088590556009849055600a839055600b829055600c8190556040805186815260208101869052908101849052606081018390526080810182905233907f307727c989f3c87a872f36c0c665dec093dfee07d03e13be7d3d624637f436819060a00160405180910390a25050505050565b6121166128db565b600f54601254600754612136916001600160a01b03918216911683613416565b6000600f5560125460408051338152602081018490526001600160a01b03909216908201527f36119f4f28ae3384ed31589f21ec2992cb0ebe53b11c79a24466ee74471764ed906060015b60405180910390a150565b6001600160a01b0380821660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c08201529061227f5760405162461bcd60e51b8152600401610af390614e8f565b80516122b55760405162461bcd60e51b81526020600482015260056024820152642154524e4560d81b6044820152606401610af3565b600034116122ee5760405162461bcd60e51b8152600401610af39060208082526004908201526310a2a4ad60e11b604082015260600190565b80604001516001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561232d57600080fd5b505af1158015612341573d6000803e3d6000fd5b505050505061206f82303334613f87565b6005546001600160a01b0316331461237c5760405162461bcd60e51b8152600401610af39061503f565b6123866000614451565b565b6001600160a01b03808216600090815260146020908152604080832081516101e081018352815460ff81161515825261010090819004871694820194909452600182015486169281019290925260028101548516606083015260038101549094166080820152600484015460a0820152600584015460c0820152600684015460e0820181905260078501549282019290925260088401546101208201526009840154610140820152600a840154610160820152600b840154610180820152600c8401546101a0820152600d909301546101c084015290919061246d5750600092915050565b600081602001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124d59190614ec5565b9050600082604001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561251b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061253f9190614ede565b905060006125598385610100015186610120015185613479565b9050600061256c856020015185856134bd565b9050600085606001516001600160a01b03166398d5fdca6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125d69190614ec5565b9050610e278383838960c0015188613892565b6000806125f461315f565b9050600061260182613d0e565b90506000600c54670de0b6b3a764000061261b9190614eae565b90506000670de0b6b3a76400006126328385614e46565b61263c9190614e7b565b9050670de0b6b3a76400006126518286614e46565b61265b9190614e7b565b94505050505090565b6060600480546108f690614ddd565b6001600160a01b03808216600090815260146020908152604080832081516101e081018352815460ff81161515825261010090819004871694820194909452600182015486169281019290925260028101548516606083015260038101549094166080820152600484015460a0820152600584015460c0820152600684015460e0820181905260078501549282019290925260088401546101208201526009840154610140820152600a840154610160820152600b840154610180820152600c8401546101a0820152600d909301546101c08401529091906127585750600092915050565b600081602001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561279c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127c09190614ec5565b9050600082602001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612806573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061282a9190614ede565b90506109ef8284610100015185610120015184613479565b3360009081526001602090815260408083206001600160a01b0386168452909152812054828110156128c45760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610af3565b6128d133858584036132f2565b5060019392505050565b600d544290808214156128ec575050565b600e54600f5460006128fc61196e565b9050600061290a8587614eae565b9050600061291b600e5484846144a3565b905061292781886144e6565b600e54600f5460408051898152602081018b9052908101889052606081018790526080810186905260a0810185905260c0810184905260e08101929092526101008201527f6359a1d821ddb244007da3e8ee241ce7b45d540a1896549f82566c50c68d7c60906101200160405180910390a150505050505050565b6000610986338484613b40565b6007546040516370a0823160e01b815230600482015260009182916001600160a01b03909116906370a0823190602401602060405180830381865afa1580156129fc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a209190614ec5565b905080600f5410612a3357600091505090565b600f546119849082614eae565b6005546001600160a01b03163314612a6a5760405162461bcd60e51b8152600401610af39061503f565b6001600160a01b0380831660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c082015290612b5d5760405162461bcd60e51b8152600401610af390614e8f565b6001600160a01b0383811660008181526014602052604080822060020180546001600160a01b0319169487169485179055517fbf7ec739eb17989ec37b8022950e7d1780f24bd2f029fe363f8320b6b6042f7c9190a3505050565b6001600160a01b03808216600090815260146020908152604080832081516101e081018352815460ff81161515825261010090819004871694820194909452600182015486169281019290925260028101548516606083015260038101549094166080820152600484015460a0820152600584015460c0820152600684015460e0820181905260078501549282019290925260088401546101208201526009840154610140820152600a840154610160820152600b840154610180820152600c8401546101a0820152600d909301546101c0840152909190612c9d5750600092915050565b600081602001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ce1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d059190614ec5565b9050600082602001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d6f9190614ede565b90506109ef836020015183836134bd565b612d8c83338484613f87565b505050565b6005546001600160a01b03163314612dbb5760405162461bcd60e51b8152600401610af39061503f565b601155565b6005546001600160a01b03163314612dea5760405162461bcd60e51b8152600401610af39061503f565b601280546001600160a01b0319166001600160a01b0383169081179091556040805133815260208101929092527faaebcf1bfa00580e41d966056b48521fa9f202645c86d4ddf28113e617c1b1d39101612181565b60026006541415612e625760405162461bcd60e51b8152600401610af390614f01565b6002600655612e6f6128db565b6000612e79610990565b90506000670de0b6b3a7640000612e908484614e46565b612e9a9190614e7b565b600754909150612eb4906001600160a01b03163383613416565b612ebe3384614542565b604080518481526020810184905290810182905233907fc30dcbcc543321ea79e9955ec689be197278979aa6117ad1b66bebf48d0f204090606001612051565b6001600160a01b0380831660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c082015290612ff15760405162461bcd60e51b8152600401610af390614e8f565b80516130275760405162461bcd60e51b81526020600482015260056024820152642154524e4560d81b6044820152606401610af3565b600034116130605760405162461bcd60e51b8152600401610af39060208082526004908201526310a2a4ad60e11b604082015260600190565b80604001516001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561309f57600080fd5b505af11580156130b3573d6000803e3d6000fd5b5050505050612d8c83308434613f87565b6005546001600160a01b031633146130ee5760405162461bcd60e51b8152600401610af39061503f565b6001600160a01b0381166131535760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610af3565b61315c81614451565b50565b60008061316a6129af565b905061198481600e54614690565b6005546001600160a01b031633146131a25760405162461bcd60e51b8152600401610af39061503f565b6001600160a01b0380831660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c0820152906132955760405162461bcd60e51b8152600401610af390614e8f565b6001600160a01b03831660008181526014602052604090819020600d01849055517f5084e158024653e5c1fb3043afcfdfb3265f039c5bb0a81eace8ad704445002d906132e59085815260200190565b60405180910390a2505050565b6001600160a01b0383166133545760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610af3565b6001600160a01b0382166133b55760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610af3565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6040516001600160a01b038316602482015260448101829052612d8c90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526146e9565b600084613488575060006134b5565b8461349483600a615030565b61349e8587614eae565b6134a89190614e46565b6134b29190614e7b565b90505b949350505050565b6000826134cc575060006117ef565b60006134d78561111e565b9050806134e85760009150506117ef565b60006134f584600a615030565b6134ff9083614e46565b90508461350c8183614f38565b1561351857600161351b565b60005b60ff166135288284614e7b565b6135329190614e2e565b979650505050505050565b600085158061354a575084155b156135565750816109ef565b600061356383600a615030565b61356d8689614e46565b6135779190614e7b565b90506135328682614eae565b60008083602001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156135c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135ec9190614ec5565b9050600084604001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613632573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136569190614ede565b9050600085606001516001600160a01b03166398d5fdca6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561369c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136c09190614ec5565b905060006136da8488610100015189610120015186613479565b905060006136ed886020015186866134bd565b905060006136fc85600a615030565b6137068984614e46565b6137109190614e7b565b9050613720896020015182613a35565b600061372d86600a615030565b613737858b614e46565b6137419190614e7b565b90506000670de0b6b3a7640000868c60a0015161375e9190614e46565b6137689190614e7b565b6137729087614eae565b9050600061378188600a615030565b8361378d8a600a615030565b6137978587614e46565b6137a19190614e7b565b6137ac8b600a615030565b6137b69088614e46565b6137c09190614e7b565b6137ca9190614e46565b6137d49190614e7b565b60808d015160408e01516007549293506000926137fd9291906001600160a01b031685896138e4565b9050600061381861380e8387614eae565b8f60e001516147bb565b809250819d50505061383a8e6020015182876138349190614eae565b836147f0565b505050505050505050505092915050565b6000600e546000148061385e5750601054155b156138705750670de0b6b3a764000090565b601054600e5461388890670de0b6b3a7640000614e46565b611ef39190614e7b565b6000806138a083600a615030565b6138aa8689614e46565b6138b49190614e7b565b905060006138c5888888888861353d565b9050806138da83670de0b6b3a7640000614e46565b610e279190614e7b565b60006138fa6001600160a01b0386168785614855565b604051637f0148ab60e11b81526001600160a01b0386811660048301528581166024830152604482018590526064820184905287169063fe029156906084016020604051808303816000875af1158015613958573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061397c9190614ec5565b90506109ef6001600160a01b038616876000614855565b600061399d61384b565b905081600e60008282546139b19190614e2e565b9091555060009050816139cc84670de0b6b3a7640000614e46565b6139d69190614e7b565b905080601060008282546139ea9190614e2e565b90915550506001600160a01b038416600090815260136020526040902054613a13908290614e2e565b6001600160a01b03909416600090815260136020526040902093909355505050565b6000613a3f61384b565b9050600e54821115613a55576000600e55613a6d565b81600e6000828254613a679190614eae565b90915550505b600081613a8284670de0b6b3a7640000614e46565b613a8c9190614e7b565b9050601054811115613aa2576000601055613aba565b8060106000828254613ab49190614eae565b90915550505b6001600160a01b038416600090815260136020526040902054811115613b0c576001600160a01b038416600090815260136020526040812080548290613b01908290614eae565b90915550613b3a9050565b6001600160a01b03841660009081526013602052604081208054839290613b34908490614eae565b90915550505b50505050565b6001600160a01b038316613ba45760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610af3565b6001600160a01b038216613c065760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610af3565b6001600160a01b03831660009081526020819052604090205481811015613c7e5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610af3565b6001600160a01b03808516600090815260208190526040808220858503905591851681529081208054849290613cb5908490614e2e565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051613d0191815260200190565b60405180910390a3613b3a565b6000670de0b6b3a76400008210613d27575050600b5490565b6008548211613da557600854600090613d4884670de0b6b3a7640000614e46565b613d529190614e7b565b90506000670de0b6b3a764000060095483613d6d9190614e46565b613d779190614e7b565b905060006109ef7f000000000000000000000000000000000000000000000000000000000000000083614e7b565b600060085483613db59190614eae565b90506000613dcb84670de0b6b3a7640000614eae565b9050600081613de284670de0b6b3a7640000614e46565b613dec9190614e7b565b90506000670de0b6b3a7640000600a5483613e079190614e46565b613e119190614e7b565b9050600081600954613e239190614e2e565b90506000613e517f000000000000000000000000000000000000000000000000000000000000000083614e7b565b9050600b548110613532575050600b549695505050505050565b919050565b6040516001600160a01b0380851660248301528316604482015260648101829052613b3a9085906323b872dd60e01b90608401613442565b6001600160a01b038216613efe5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610af3565b8060026000828254613f109190614e2e565b90915550506001600160a01b03821660009081526020819052604081208054839290613f3d908490614e2e565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b60026006541415613faa5760405162461bcd60e51b8152600401610af390614f01565b600260068181556001600160a01b0380871660009081526014602090815260409182902082516101e081018452815460ff811615158252610100908190048616938201939093526001820154851693810193909352948501548316606083015260038501549092166080820152600484015460a0820152600584015460c08201529183015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c0820152906140a25760405162461bcd60e51b8152600401610af390614e8f565b6101c081015115614104576101c08101516140be836002614e46565b8261010001516140ce9190614e2e565b106141045760405162461bcd60e51b8152600401610af39060208082526004908201526310a1a4a960e11b604082015260600190565b61410c6128db565b600061411786610bc6565b90506001600160a01b0385163014614143576040820151614143906001600160a01b0316863086613e70565b600080614154858560e001516147bb565b909250905080614165836002614e46565b61416f9190614e2e565b6020808601516001600160a01b03166000908152601490915260408120600701805490919061419f908490614e2e565b90915550506020808501516001600160a01b0316600090815260149091526040812060080180548392906141d4908490614e2e565b92505081905550600084606001516001600160a01b03166398d5fdca6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561421f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142439190614ec5565b9050600085604001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015614289573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142ad9190614ede565b90506000670de0b6b3a7640000838860a001516142ca9190614e46565b6142d49190614e7b565b6142de9084614e2e565b905060006142ed83600a615030565b6142f78388614e46565b6143019190614e7b565b90508061430c6129af565b116143425760405162461bcd60e51b8152600401610af390602080825260049082015263214e455360e01b604082015260600190565b600061436e8960800151600760009054906101000a90046001600160a01b03168b60400151858b6138e4565b905061437a8d82613993565b6000614389898988858961496a565b6040516340c10f1960e01b81526001600160a01b038e8116600483015260248201839052919250908f16906340c10f1990604401600060405180830381600087803b1580156143d757600080fd5b505af11580156143eb573d6000803e3d6000fd5b505050508d6001600160a01b03168c6001600160a01b03167fede1f310f6f94790fc59194108fa63c31d34618eb55c66f142a8eb2088770c4c8360405161443491815260200190565b60405180910390a350506001600655505050505050505050505050565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60008315806144b0575082155b806144b9575081155b156144c6575060006117ef565b6000670de0b6b3a7640000856144dc8587614e46565b6109e59190614e46565b6000670de0b6b3a764000083600c546144ff9190614e46565b6145099190614e7b565b905082600e600082825461451d9190614e2e565b9250508190555080600f60008282546145369190614e2e565b909155505050600d5550565b6001600160a01b0382166145a25760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610af3565b6001600160a01b038216600090815260208190526040902054818110156146165760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610af3565b6001600160a01b0383166000908152602081905260408120838303905560028054849290614645908490614eae565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b60008161469f5750600061098a565b821580156146ad5750600082115b156146c15750670de0b6b3a764000061098a565b60006146cd8484614e2e565b6146df84670de0b6b3a7640000614e46565b6134b59190614e7b565b600061473e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166149c59092919063ffffffff16565b805190915015612d8c578080602001905181019061475c9190615074565b612d8c5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610af3565b600080670de0b6b3a76400006147d18486614e46565b6147db9190614e7b565b90506147e78185614eae565b91509250929050565b6001600160a01b0383166000908152601460205260408120600701805484929061481b908490614eae565b90915550506001600160a01b0383166000908152601460205260408120600801805483929061484b908490614e2e565b9091555050505050565b8015806148cf5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa1580156148a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148cd9190614ec5565b155b61493a5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610af3565b6040516001600160a01b038316602482015260448101829052612d8c90849063095ea7b360e01b90606401613442565b6000808361497984600a615030565b86614985896002614e46565b61498f9190614e46565b6149999190614e7b565b6149a39190614eae565b9050866149b184600a615030565b6149bb9083614e46565b6135329190614e7b565b60606134b5848460008585843b614a1e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610af3565b600080866001600160a01b03168587604051614a3a9190615091565b60006040518083038185875af1925050503d8060008114614a77576040519150601f19603f3d011682016040523d82523d6000602084013e614a7c565b606091505b509150915061353282828660608315614a965750816117ef565b825115614aa65782518084602001fd5b8160405162461bcd60e51b8152600401610af39190614aec565b60005b83811015614adb578181015183820152602001614ac3565b83811115613b3a5750506000910152565b6020815260008251806020840152614b0b816040850160208701614ac0565b601f01601f19169190910160400192915050565b80356001600160a01b0381168114613e6b57600080fd5b60008060408385031215614b4957600080fd5b614b5283614b1f565b946020939093013593505050565b600060208284031215614b7257600080fd5b6117ef82614b1f565b600080600060608486031215614b9057600080fd5b614b9984614b1f565b9250614ba760208501614b1f565b9150604084013590509250925092565b60008060408385031215614bca57600080fd5b614bd383614b1f565b9150614be160208401614b1f565b90509250929050565b8151151581526101e081016020830151614c0f60208401826001600160a01b03169052565b506040830151614c2a60408401826001600160a01b03169052565b506060830151614c4560608401826001600160a01b03169052565b506080830151614c6060808401826001600160a01b03169052565b5060a0838101519083015260c0808401519083015260e08084015190830152610100808401519083015261012080840151908301526101408084015190830152610160808401519083015261018080840151908301526101a080840151908301526101c092830151929091019190915290565b801515811461315c57600080fd5b6000806000806000806000806000806000806101808d8f031215614d0457600080fd5b8c35614d0f81614cd3565b9b50614d1d60208e01614b1f565b9a50614d2b60408e01614b1f565b9950614d3960608e01614b1f565b9850614d4760808e01614b1f565b9b9e9a9d50989b979a9960a0890135995060c08901359860e0810135985061010081013597506101208101359650610140810135955061016001359350915050565b600060208284031215614d9b57600080fd5b5035919050565b600080600080600060a08688031215614dba57600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b600181811c90821680614df157607f821691505b60208210811415614e1257634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b60008219821115614e4157614e41614e18565b500190565b6000816000190483118215151615614e6057614e60614e18565b500290565b634e487b7160e01b600052601260045260246000fd5b600082614e8a57614e8a614e65565b500490565b6020808252600590820152642152544e4560d81b604082015260600190565b600082821015614ec057614ec0614e18565b500390565b600060208284031215614ed757600080fd5b5051919050565b600060208284031215614ef057600080fd5b815160ff811681146117ef57600080fd5b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b600082614f4757614f47614e65565b500690565b600181815b80851115614f87578160001904821115614f6d57614f6d614e18565b80851615614f7a57918102915b93841c9390800290614f51565b509250929050565b600082614f9e5750600161098a565b81614fab5750600061098a565b8160018114614fc15760028114614fcb57614fe7565b600191505061098a565b60ff841115614fdc57614fdc614e18565b50506001821b61098a565b5060208310610133831016604e8410600b841016171561500a575081810a61098a565b6150148383614f4c565b806000190482111561502857615028614e18565b029392505050565b60006117ef60ff841683614f8f565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60006020828403121561508657600080fd5b81516117ef81614cd3565b600082516150a3818460208701614ac0565b919091019291505056fea264697066735822122087643f065a980a4a954bc2572705531040ff2c5f36f37db464c1f0ea5bd01b6764736f6c634300080b0033000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000056b4a9675c52144c99f676835e83d5625cb47202000000000000000000000000000000000000000000000000000000000000001552697365646c65205661756c742045544855534443000000000000000000000000000000000000000000000000000000000000000000000000000000000000097276455448555344430000000000000000000000000000000000000000000000
Deployed Bytecode
0x6080604052600436106102b25760003560e01c8063715018a611610175578063a927de78116100dc578063e74b981b11610095578063ee1fe2ad1161006f578063ee1fe2ad1461087f578063f2fde38b14610892578063f76f5db5146108b2578063fd1b8498146108c757600080fd5b8063e74b981b1461081f578063e7a357181461083f578063ebd090541461085f57600080fd5b8063a927de7814610744578063acd4b00a14610759578063c6b5f6e214610779578063c6c3bbe614610799578063dc54a8ee146107b9578063dd62ed3e146107d957600080fd5b806395d89b411161012e57806395d89b411461069c5780639816f473146106b1578063a2fcc7ec146106cf578063a457c2d7146106ef578063a6afed951461070f578063a9059cbb1461072457600080fd5b8063715018a6146105f457806371a6cab71461060957806373f2cd8e1461061f578063859203e31461063f5780638da5cb5b14610655578063942c7cb41461068757600080fd5b8063298cbf261161021957806340c10f19116101d257806340c10f19146105125780634f4c25561461053257806363d4f58714610576578063695b84c4146105965780636a627842146105ab57806370a08231146105be57600080fd5b8063298cbf26146104495780632a50c1461461045e5780632a65983a1461048b578063313ce567146104ab57806339509351146104d257806340753a76146104f257600080fd5b806318160ddd1161026b57806318160ddd146103945780631e9a6950146103a95780631ea6cabc146103c957806321c28191146103e957806323b872dd1461040957806326bb0e8e1461042957600080fd5b806306fdde03146102be578063095ea7b3146102e95780630d7c759114610319578063109e62ea1461033c57806311ebf36d14610352578063179ddcdd1461037457600080fd5b366102b957005b600080fd5b3480156102ca57600080fd5b506102d36108e7565b6040516102e09190614aec565b60405180910390f35b3480156102f557600080fd5b50610309610304366004614b36565b610979565b60405190151581526020016102e0565b34801561032557600080fd5b5061032e610990565b6040519081526020016102e0565b34801561034857600080fd5b5061032e600f5481565b34801561035e57600080fd5b5061037261036d366004614b60565b6109f8565b005b34801561038057600080fd5b5061032e61038f366004614b60565b610bc6565b3480156103a057600080fd5b5060025461032e565b3480156103b557600080fd5b506103726103c4366004614b36565b610e33565b3480156103d557600080fd5b5061032e6103e4366004614b60565b61111e565b3480156103f557600080fd5b50610372610404366004614b60565b6111ac565b34801561041557600080fd5b50610309610424366004614b7b565b61174a565b34801561043557600080fd5b50610372610444366004614bb7565b6117f6565b34801561045557600080fd5b5061032e61196e565b34801561046a57600080fd5b5061047e610479366004614b60565b61198a565b6040516102e09190614bea565b34801561049757600080fd5b506103726104a6366004614ce1565b611afe565b3480156104b757600080fd5b506104c0611e85565b60405160ff90911681526020016102e0565b3480156104de57600080fd5b506103096104ed366004614b36565b611ef8565b3480156104fe57600080fd5b5061037261050d366004614d89565b611f34565b34801561051e57600080fd5b5061037261052d366004614b36565b612063565b34801561053e57600080fd5b50600854600954600a54600b54600c54604080519586526020860194909452928401919091526060830152608082015260a0016102e0565b34801561058257600080fd5b50610372610591366004614da2565b612073565b3480156105a257600080fd5b5061037261210e565b6103726105b9366004614b60565b61218c565b3480156105ca57600080fd5b5061032e6105d9366004614b60565b6001600160a01b031660009081526020819052604090205490565b34801561060057600080fd5b50610372612352565b34801561061557600080fd5b5061032e60115481565b34801561062b57600080fd5b5061032e61063a366004614b60565b612388565b34801561064b57600080fd5b5061032e600e5481565b34801561066157600080fd5b506005546001600160a01b03165b6040516001600160a01b0390911681526020016102e0565b34801561069357600080fd5b5061032e6125e9565b3480156106a857600080fd5b506102d3612664565b3480156106bd57600080fd5b506007546001600160a01b031661066f565b3480156106db57600080fd5b5061032e6106ea366004614b60565b612673565b3480156106fb57600080fd5b5061030961070a366004614b36565b612842565b34801561071b57600080fd5b506103726128db565b34801561073057600080fd5b5061030961073f366004614b36565b6129a2565b34801561075057600080fd5b5061032e6129af565b34801561076557600080fd5b50610372610774366004614bb7565b612a40565b34801561078557600080fd5b5061032e610794366004614b60565b612bb8565b3480156107a557600080fd5b506103726107b4366004614b7b565b612d80565b3480156107c557600080fd5b506103726107d4366004614d89565b612d91565b3480156107e557600080fd5b5061032e6107f4366004614bb7565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b34801561082b57600080fd5b5061037261083a366004614b60565b612dc0565b34801561084b57600080fd5b5061037261085a366004614d89565b612e3f565b34801561086b57600080fd5b5060125461066f906001600160a01b031681565b61037261088d366004614bb7565b612efe565b34801561089e57600080fd5b506103726108ad366004614b60565b6130c4565b3480156108be57600080fd5b5061032e61315f565b3480156108d357600080fd5b506103726108e2366004614b36565b613178565b6060600380546108f690614ddd565b80601f016020809104026020016040519081016040528092919081815260200182805461092290614ddd565b801561096f5780601f106109445761010080835404028352916020019161096f565b820191906000526020600020905b81548152906001019060200180831161095257829003601f168201915b5050505050905090565b60006109863384846132f2565b5060015b92915050565b60008061099c60025490565b9050806109b257670de0b6b3a764000091505090565b60006109bc6129af565b90506000600e54826109ce9190614e2e565b90506000836109e583670de0b6b3a7640000614e46565b6109ef9190614e7b565b95945050505050565b610a006128db565b6001600160a01b0380821660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c082015290610afc5760405162461bcd60e51b8152600401610af390614e8f565b60405180910390fd5b6012546101208201516040830151610b22926001600160a01b0391821692911690613416565b6101208101516001600160a01b03831660009081526014602052604081206007018054909190610b53908490614eae565b90915550506001600160a01b0382811660009081526014602090815260408083206008019290925561012084015160125483513381529283019190915290921682820152517f36119f4f28ae3384ed31589f21ec2992cb0ebe53b11c79a24466ee74471764ed9181900360600190a15050565b6001600160a01b03808216600090815260146020908152604080832081516101e081018352815460ff81161515825261010090819004871694820194909452600182015486169281019290925260028101548516606083015260038101549094166080820152600484015460a0820152600584015460c0820152600684015460e0820181905260078501549282019290925260088401546101208201526009840154610140820152600a840154610160820152600b840154610180820152600c8401546101a0820152600d909301546101c0840152909190610cab5750600092915050565b600081606001516001600160a01b03166398d5fdca6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610cef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d139190614ec5565b9050600082602001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7d9190614ec5565b9050600083602001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dc3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610de79190614ede565b90506000610e018386610100015187610120015185613479565b90506000610e14866020015185856134bd565b9050610e278282878960c001518761353d565b98975050505050505050565b60026006541415610e565760405162461bcd60e51b8152600401610af390614f01565b6002600655610e636128db565b6001600160a01b0380831660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c082015290610f565760405162461bcd60e51b8152600401610af390614e8f565b6000610f628284613583565b604051632770a7eb60e21b8152336004820152602481018590529091506001600160a01b03851690639dc29fac90604401600060405180830381600087803b158015610fad57600080fd5b505af1158015610fc1573d6000803e3d6000fd5b505050508160000151156110ba5781604001516001600160a01b0316632e1a7d4d826040518263ffffffff1660e01b815260040161100191815260200190565b600060405180830381600087803b15801561101b57600080fd5b505af115801561102f573d6000803e3d6000fd5b50506040516000925033915083908381818185875af1925050503d8060008114611075576040519150601f19603f3d011682016040523d82523d6000602084013e61107a565b606091505b50509050806110b45760405162461bcd60e51b8152600401610af39060208082526004908201526310a2a92360e11b604082015260600190565b506110d3565b60408201516110d3906001600160a01b03163383613416565b6040518181526001600160a01b0385169033907f547518a7214f42d72dccc4ed54426ba50f889c24bb4d9766443e899a123381b39060200160405180910390a3505060016006555050565b6000600e546000141561113357506000919050565b600061113d61384b565b6001600160a01b03841660009081526013602052604081205491925090611165908390614e46565b9050670de0b6b3a7640000600061117c8284614f38565b1561118857600161118b565b60005b60ff166111988385614e7b565b6111a29190614e2e565b9695505050505050565b600260065414156111cf5760405162461bcd60e51b8152600401610af390614f01565b600260068181556001600160a01b0380841660009081526014602090815260409182902082516101e081018452815460ff811615158252610100908190048616938201939093526001820154851693810193909352948501548316606083015260038501549092166080820152600484015460a0820152600584015460c08201529183015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c0820152906112c75760405162461bcd60e51b8152600401610af390614e8f565b6112cf6128db565b600081602001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611313573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113379190614ec5565b9050600082606001516001600160a01b03166398d5fdca6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561137d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113a19190614ec5565b9050600083604001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113e7573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061140b9190614ede565b905060006114258486610100015187610120015185613479565b90506000611438866020015186856134bd565b9050600061144d8383878a60c0015188613892565b905060006114628484888b60c001518961353d565b905087610140015182108061147b575087610160015182115b6114af5760405162461bcd60e51b815260206004820152600560248201526410a62924a960d91b6044820152606401610af3565b6000670de0b6b3a76400006114c587600a615030565b6114cf8a85614e46565b6114d99190614e7b565b8a6101a001516114e99190614e46565b6114f39190614e7b565b905060008761150388600a615030565b61150d9084614e46565b6115179190614e7b565b9050896101400151841015611600576000670de0b6b3a7640000898c60a001516115419190614e46565b61154b9190614e7b565b611555908a614e2e565b9050600061156489600a615030565b61156e8385614e46565b6115789190614e7b565b90508b610180015181111561158f57506101808b01515b60006115bb8d60800151600760009054906101000a90046001600160a01b03168f6040015185886138e4565b90506115c78e82613993565b6020808e01516001600160a01b0316600090815260149091526040812060070180548692906115f7908490614e2e565b90915550505050505b896101600151841115611703576000670de0b6b3a7640000898c60a001516116289190614e46565b6116329190614e7b565b61163c908a614eae565b905060008161164c8a600a615030565b6116569086614e46565b6116609190614e7b565b90508b6101800151841115611698578161167b8a600a615030565b8d610180015161168b9190614e46565b6116959190614e7b565b90505b60808c015160408d01516007546000926116be9290916001600160a01b031685896138e4565b90506116ca8e86613a35565b6020808e01516001600160a01b0316600090815260149091526040812060070180548392906116fa908490614eae565b90915550505050505b60405184815233907fdf85796b4c3419ec22e630b81a8766863bb75964edc1c7869986126e66f633449060200160405180910390a250506001600655505050505050505050565b6000611757848484613b40565b6001600160a01b0384166000908152600160209081526040808320338452909152902054828110156117dc5760405162461bcd60e51b815260206004820152602860248201527f45524332303a207472616e7366657220616d6f756e74206578636565647320616044820152676c6c6f77616e636560c01b6064820152608401610af3565b6117e985338584036132f2565b60019150505b9392505050565b6005546001600160a01b031633146118205760405162461bcd60e51b8152600401610af39061503f565b6001600160a01b0380831660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c0820152906119135760405162461bcd60e51b8152600401610af390614e8f565b6001600160a01b0383811660008181526014602052604080822060030180546001600160a01b0319169487169485179055517f28c4fd18fb488de9f00ed376c22a3a7d2bbadabfaec7ab26651c824676f6fd4b9190a3505050565b60008061197961315f565b905061198481613d0e565b91505090565b611a29604051806101e0016040528060001515815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b0316815260200160006001600160a01b03168152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b506001600160a01b0390811660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e083015260078301549082015260088201546101208201526009820154610140820152600a820154610160820152600b820154610180820152600c8201546101a0820152600d909101546101c082015290565b6005546001600160a01b03163314611b285760405162461bcd60e51b8152600401610af39061503f565b6001600160a01b03808c1660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c08201529015611c325760405162461bcd60e51b815260206004820152600360248201526221414560e81b6044820152606401610af3565b604051806101e001604052808e151581526020018d6001600160a01b031681526020018c6001600160a01b031681526020018b6001600160a01b031681526020018a6001600160a01b0316815260200189815260200188815260200187815260200160008152602001600081526020018681526020018581526020018481526020018381526020016000815250601460008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008201518160000160006101000a81548160ff02191690831515021790555060208201518160000160016101000a8154816001600160a01b0302191690836001600160a01b0316021790555060408201518160010160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060608201518160020160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060808201518160030160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a0820151816004015560c0820151816005015560e0820151816006015561010082015181600701556101208201518160080155610140820151816009015561016082015181600a015561018082015181600b01556101a082015181600c01556101c082015181600d0155905050336001600160a01b03167f2a2b0348f99631bd5cb9665e0b2341476d6e9725bc1cc07c04f3c78fa958ad1f8d604051611e6e91906001600160a01b0391909116815260200190565b60405180910390a250505050505050505050505050565b6007546040805163313ce56760e01b815290516000926001600160a01b03169163313ce5679160048083019260209291908290030181865afa158015611ecf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ef39190614ede565b905090565b3360008181526001602090815260408083206001600160a01b03871684529091528120549091610986918590611f2f908690614e2e565b6132f2565b60026006541415611f575760405162461bcd60e51b8152600401610af390614f01565b6002600655611f646128db565b60115415611fc55760115481600e54611f7b6129af565b611f859190614e2e565b611f8f9190614e2e565b10611fc55760405162461bcd60e51b8152600401610af39060208082526004908201526310a6a1a960e11b604082015260600190565b6000611fcf610990565b9050600081611fe684670de0b6b3a7640000614e46565b611ff09190614e7b565b60075490915061200b906001600160a01b0316333086613e70565b6120153382613ea8565b604080518481526020810184905290810182905233907f85a85f897cc05e4f2226d4f0a00a86d8c091e7b9af9155d98f54cf9bdcb96934906060015b60405180910390a25050600160065550565b61206f82333384613f87565b5050565b6005546001600160a01b0316331461209d5760405162461bcd60e51b8152600401610af39061503f565b60088590556009849055600a839055600b829055600c8190556040805186815260208101869052908101849052606081018390526080810182905233907f307727c989f3c87a872f36c0c665dec093dfee07d03e13be7d3d624637f436819060a00160405180910390a25050505050565b6121166128db565b600f54601254600754612136916001600160a01b03918216911683613416565b6000600f5560125460408051338152602081018490526001600160a01b03909216908201527f36119f4f28ae3384ed31589f21ec2992cb0ebe53b11c79a24466ee74471764ed906060015b60405180910390a150565b6001600160a01b0380821660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c08201529061227f5760405162461bcd60e51b8152600401610af390614e8f565b80516122b55760405162461bcd60e51b81526020600482015260056024820152642154524e4560d81b6044820152606401610af3565b600034116122ee5760405162461bcd60e51b8152600401610af39060208082526004908201526310a2a4ad60e11b604082015260600190565b80604001516001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561232d57600080fd5b505af1158015612341573d6000803e3d6000fd5b505050505061206f82303334613f87565b6005546001600160a01b0316331461237c5760405162461bcd60e51b8152600401610af39061503f565b6123866000614451565b565b6001600160a01b03808216600090815260146020908152604080832081516101e081018352815460ff81161515825261010090819004871694820194909452600182015486169281019290925260028101548516606083015260038101549094166080820152600484015460a0820152600584015460c0820152600684015460e0820181905260078501549282019290925260088401546101208201526009840154610140820152600a840154610160820152600b840154610180820152600c8401546101a0820152600d909301546101c084015290919061246d5750600092915050565b600081602001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156124b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124d59190614ec5565b9050600082604001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561251b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061253f9190614ede565b905060006125598385610100015186610120015185613479565b9050600061256c856020015185856134bd565b9050600085606001516001600160a01b03166398d5fdca6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125b2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125d69190614ec5565b9050610e278383838960c0015188613892565b6000806125f461315f565b9050600061260182613d0e565b90506000600c54670de0b6b3a764000061261b9190614eae565b90506000670de0b6b3a76400006126328385614e46565b61263c9190614e7b565b9050670de0b6b3a76400006126518286614e46565b61265b9190614e7b565b94505050505090565b6060600480546108f690614ddd565b6001600160a01b03808216600090815260146020908152604080832081516101e081018352815460ff81161515825261010090819004871694820194909452600182015486169281019290925260028101548516606083015260038101549094166080820152600484015460a0820152600584015460c0820152600684015460e0820181905260078501549282019290925260088401546101208201526009840154610140820152600a840154610160820152600b840154610180820152600c8401546101a0820152600d909301546101c08401529091906127585750600092915050565b600081602001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561279c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127c09190614ec5565b9050600082602001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612806573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061282a9190614ede565b90506109ef8284610100015185610120015184613479565b3360009081526001602090815260408083206001600160a01b0386168452909152812054828110156128c45760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610af3565b6128d133858584036132f2565b5060019392505050565b600d544290808214156128ec575050565b600e54600f5460006128fc61196e565b9050600061290a8587614eae565b9050600061291b600e5484846144a3565b905061292781886144e6565b600e54600f5460408051898152602081018b9052908101889052606081018790526080810186905260a0810185905260c0810184905260e08101929092526101008201527f6359a1d821ddb244007da3e8ee241ce7b45d540a1896549f82566c50c68d7c60906101200160405180910390a150505050505050565b6000610986338484613b40565b6007546040516370a0823160e01b815230600482015260009182916001600160a01b03909116906370a0823190602401602060405180830381865afa1580156129fc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a209190614ec5565b905080600f5410612a3357600091505090565b600f546119849082614eae565b6005546001600160a01b03163314612a6a5760405162461bcd60e51b8152600401610af39061503f565b6001600160a01b0380831660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c082015290612b5d5760405162461bcd60e51b8152600401610af390614e8f565b6001600160a01b0383811660008181526014602052604080822060020180546001600160a01b0319169487169485179055517fbf7ec739eb17989ec37b8022950e7d1780f24bd2f029fe363f8320b6b6042f7c9190a3505050565b6001600160a01b03808216600090815260146020908152604080832081516101e081018352815460ff81161515825261010090819004871694820194909452600182015486169281019290925260028101548516606083015260038101549094166080820152600484015460a0820152600584015460c0820152600684015460e0820181905260078501549282019290925260088401546101208201526009840154610140820152600a840154610160820152600b840154610180820152600c8401546101a0820152600d909301546101c0840152909190612c9d5750600092915050565b600081602001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612ce1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d059190614ec5565b9050600082602001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d6f9190614ede565b90506109ef836020015183836134bd565b612d8c83338484613f87565b505050565b6005546001600160a01b03163314612dbb5760405162461bcd60e51b8152600401610af39061503f565b601155565b6005546001600160a01b03163314612dea5760405162461bcd60e51b8152600401610af39061503f565b601280546001600160a01b0319166001600160a01b0383169081179091556040805133815260208101929092527faaebcf1bfa00580e41d966056b48521fa9f202645c86d4ddf28113e617c1b1d39101612181565b60026006541415612e625760405162461bcd60e51b8152600401610af390614f01565b6002600655612e6f6128db565b6000612e79610990565b90506000670de0b6b3a7640000612e908484614e46565b612e9a9190614e7b565b600754909150612eb4906001600160a01b03163383613416565b612ebe3384614542565b604080518481526020810184905290810182905233907fc30dcbcc543321ea79e9955ec689be197278979aa6117ad1b66bebf48d0f204090606001612051565b6001600160a01b0380831660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c082015290612ff15760405162461bcd60e51b8152600401610af390614e8f565b80516130275760405162461bcd60e51b81526020600482015260056024820152642154524e4560d81b6044820152606401610af3565b600034116130605760405162461bcd60e51b8152600401610af39060208082526004908201526310a2a4ad60e11b604082015260600190565b80604001516001600160a01b031663d0e30db0346040518263ffffffff1660e01b81526004016000604051808303818588803b15801561309f57600080fd5b505af11580156130b3573d6000803e3d6000fd5b5050505050612d8c83308434613f87565b6005546001600160a01b031633146130ee5760405162461bcd60e51b8152600401610af39061503f565b6001600160a01b0381166131535760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610af3565b61315c81614451565b50565b60008061316a6129af565b905061198481600e54614690565b6005546001600160a01b031633146131a25760405162461bcd60e51b8152600401610af39061503f565b6001600160a01b0380831660009081526014602090815260409182902082516101e081018452815460ff81161515825261010090819004861693820193909352600182015485169381019390935260028101548416606084015260038101549093166080830152600483015460a0830152600583015460c0830152600683015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c0820152906132955760405162461bcd60e51b8152600401610af390614e8f565b6001600160a01b03831660008181526014602052604090819020600d01849055517f5084e158024653e5c1fb3043afcfdfb3265f039c5bb0a81eace8ad704445002d906132e59085815260200190565b60405180910390a2505050565b6001600160a01b0383166133545760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610af3565b6001600160a01b0382166133b55760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610af3565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6040516001600160a01b038316602482015260448101829052612d8c90849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526146e9565b600084613488575060006134b5565b8461349483600a615030565b61349e8587614eae565b6134a89190614e46565b6134b29190614e7b565b90505b949350505050565b6000826134cc575060006117ef565b60006134d78561111e565b9050806134e85760009150506117ef565b60006134f584600a615030565b6134ff9083614e46565b90508461350c8183614f38565b1561351857600161351b565b60005b60ff166135288284614e7b565b6135329190614e2e565b979650505050505050565b600085158061354a575084155b156135565750816109ef565b600061356383600a615030565b61356d8689614e46565b6135779190614e7b565b90506135328682614eae565b60008083602001516001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156135c8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135ec9190614ec5565b9050600084604001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613632573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136569190614ede565b9050600085606001516001600160a01b03166398d5fdca6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561369c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136c09190614ec5565b905060006136da8488610100015189610120015186613479565b905060006136ed886020015186866134bd565b905060006136fc85600a615030565b6137068984614e46565b6137109190614e7b565b9050613720896020015182613a35565b600061372d86600a615030565b613737858b614e46565b6137419190614e7b565b90506000670de0b6b3a7640000868c60a0015161375e9190614e46565b6137689190614e7b565b6137729087614eae565b9050600061378188600a615030565b8361378d8a600a615030565b6137978587614e46565b6137a19190614e7b565b6137ac8b600a615030565b6137b69088614e46565b6137c09190614e7b565b6137ca9190614e46565b6137d49190614e7b565b60808d015160408e01516007549293506000926137fd9291906001600160a01b031685896138e4565b9050600061381861380e8387614eae565b8f60e001516147bb565b809250819d50505061383a8e6020015182876138349190614eae565b836147f0565b505050505050505050505092915050565b6000600e546000148061385e5750601054155b156138705750670de0b6b3a764000090565b601054600e5461388890670de0b6b3a7640000614e46565b611ef39190614e7b565b6000806138a083600a615030565b6138aa8689614e46565b6138b49190614e7b565b905060006138c5888888888861353d565b9050806138da83670de0b6b3a7640000614e46565b610e279190614e7b565b60006138fa6001600160a01b0386168785614855565b604051637f0148ab60e11b81526001600160a01b0386811660048301528581166024830152604482018590526064820184905287169063fe029156906084016020604051808303816000875af1158015613958573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061397c9190614ec5565b90506109ef6001600160a01b038616876000614855565b600061399d61384b565b905081600e60008282546139b19190614e2e565b9091555060009050816139cc84670de0b6b3a7640000614e46565b6139d69190614e7b565b905080601060008282546139ea9190614e2e565b90915550506001600160a01b038416600090815260136020526040902054613a13908290614e2e565b6001600160a01b03909416600090815260136020526040902093909355505050565b6000613a3f61384b565b9050600e54821115613a55576000600e55613a6d565b81600e6000828254613a679190614eae565b90915550505b600081613a8284670de0b6b3a7640000614e46565b613a8c9190614e7b565b9050601054811115613aa2576000601055613aba565b8060106000828254613ab49190614eae565b90915550505b6001600160a01b038416600090815260136020526040902054811115613b0c576001600160a01b038416600090815260136020526040812080548290613b01908290614eae565b90915550613b3a9050565b6001600160a01b03841660009081526013602052604081208054839290613b34908490614eae565b90915550505b50505050565b6001600160a01b038316613ba45760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610af3565b6001600160a01b038216613c065760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610af3565b6001600160a01b03831660009081526020819052604090205481811015613c7e5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610af3565b6001600160a01b03808516600090815260208190526040808220858503905591851681529081208054849290613cb5908490614e2e565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051613d0191815260200190565b60405180910390a3613b3a565b6000670de0b6b3a76400008210613d27575050600b5490565b6008548211613da557600854600090613d4884670de0b6b3a7640000614e46565b613d529190614e7b565b90506000670de0b6b3a764000060095483613d6d9190614e46565b613d779190614e7b565b905060006109ef7f0000000000000000000000000000000000000000000000000000000001e1338083614e7b565b600060085483613db59190614eae565b90506000613dcb84670de0b6b3a7640000614eae565b9050600081613de284670de0b6b3a7640000614e46565b613dec9190614e7b565b90506000670de0b6b3a7640000600a5483613e079190614e46565b613e119190614e7b565b9050600081600954613e239190614e2e565b90506000613e517f0000000000000000000000000000000000000000000000000000000001e1338083614e7b565b9050600b548110613532575050600b549695505050505050565b919050565b6040516001600160a01b0380851660248301528316604482015260648101829052613b3a9085906323b872dd60e01b90608401613442565b6001600160a01b038216613efe5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610af3565b8060026000828254613f109190614e2e565b90915550506001600160a01b03821660009081526020819052604081208054839290613f3d908490614e2e565b90915550506040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b60026006541415613faa5760405162461bcd60e51b8152600401610af390614f01565b600260068181556001600160a01b0380871660009081526014602090815260409182902082516101e081018452815460ff811615158252610100908190048616938201939093526001820154851693810193909352948501548316606083015260038501549092166080820152600484015460a0820152600584015460c08201529183015460e0830181905260078401549183019190915260088301546101208301526009830154610140830152600a830154610160830152600b830154610180830152600c8301546101a0830152600d909201546101c0820152906140a25760405162461bcd60e51b8152600401610af390614e8f565b6101c081015115614104576101c08101516140be836002614e46565b8261010001516140ce9190614e2e565b106141045760405162461bcd60e51b8152600401610af39060208082526004908201526310a1a4a960e11b604082015260600190565b61410c6128db565b600061411786610bc6565b90506001600160a01b0385163014614143576040820151614143906001600160a01b0316863086613e70565b600080614154858560e001516147bb565b909250905080614165836002614e46565b61416f9190614e2e565b6020808601516001600160a01b03166000908152601490915260408120600701805490919061419f908490614e2e565b90915550506020808501516001600160a01b0316600090815260149091526040812060080180548392906141d4908490614e2e565b92505081905550600084606001516001600160a01b03166398d5fdca6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561421f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142439190614ec5565b9050600085604001516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015614289573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906142ad9190614ede565b90506000670de0b6b3a7640000838860a001516142ca9190614e46565b6142d49190614e7b565b6142de9084614e2e565b905060006142ed83600a615030565b6142f78388614e46565b6143019190614e7b565b90508061430c6129af565b116143425760405162461bcd60e51b8152600401610af390602080825260049082015263214e455360e01b604082015260600190565b600061436e8960800151600760009054906101000a90046001600160a01b03168b60400151858b6138e4565b905061437a8d82613993565b6000614389898988858961496a565b6040516340c10f1960e01b81526001600160a01b038e8116600483015260248201839052919250908f16906340c10f1990604401600060405180830381600087803b1580156143d757600080fd5b505af11580156143eb573d6000803e3d6000fd5b505050508d6001600160a01b03168c6001600160a01b03167fede1f310f6f94790fc59194108fa63c31d34618eb55c66f142a8eb2088770c4c8360405161443491815260200190565b60405180910390a350506001600655505050505050505050505050565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60008315806144b0575082155b806144b9575081155b156144c6575060006117ef565b6000670de0b6b3a7640000856144dc8587614e46565b6109e59190614e46565b6000670de0b6b3a764000083600c546144ff9190614e46565b6145099190614e7b565b905082600e600082825461451d9190614e2e565b9250508190555080600f60008282546145369190614e2e565b909155505050600d5550565b6001600160a01b0382166145a25760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610af3565b6001600160a01b038216600090815260208190526040902054818110156146165760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610af3565b6001600160a01b0383166000908152602081905260408120838303905560028054849290614645908490614eae565b90915550506040518281526000906001600160a01b038516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3505050565b60008161469f5750600061098a565b821580156146ad5750600082115b156146c15750670de0b6b3a764000061098a565b60006146cd8484614e2e565b6146df84670de0b6b3a7640000614e46565b6134b59190614e7b565b600061473e826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166149c59092919063ffffffff16565b805190915015612d8c578080602001905181019061475c9190615074565b612d8c5760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610af3565b600080670de0b6b3a76400006147d18486614e46565b6147db9190614e7b565b90506147e78185614eae565b91509250929050565b6001600160a01b0383166000908152601460205260408120600701805484929061481b908490614eae565b90915550506001600160a01b0383166000908152601460205260408120600801805483929061484b908490614e2e565b9091555050505050565b8015806148cf5750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa1580156148a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906148cd9190614ec5565b155b61493a5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b6064820152608401610af3565b6040516001600160a01b038316602482015260448101829052612d8c90849063095ea7b360e01b90606401613442565b6000808361497984600a615030565b86614985896002614e46565b61498f9190614e46565b6149999190614e7b565b6149a39190614eae565b9050866149b184600a615030565b6149bb9083614e46565b6135329190614e7b565b60606134b5848460008585843b614a1e5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610af3565b600080866001600160a01b03168587604051614a3a9190615091565b60006040518083038185875af1925050503d8060008114614a77576040519150601f19603f3d011682016040523d82523d6000602084013e614a7c565b606091505b509150915061353282828660608315614a965750816117ef565b825115614aa65782518084602001fd5b8160405162461bcd60e51b8152600401610af39190614aec565b60005b83811015614adb578181015183820152602001614ac3565b83811115613b3a5750506000910152565b6020815260008251806020840152614b0b816040850160208701614ac0565b601f01601f19169190910160400192915050565b80356001600160a01b0381168114613e6b57600080fd5b60008060408385031215614b4957600080fd5b614b5283614b1f565b946020939093013593505050565b600060208284031215614b7257600080fd5b6117ef82614b1f565b600080600060608486031215614b9057600080fd5b614b9984614b1f565b9250614ba760208501614b1f565b9150604084013590509250925092565b60008060408385031215614bca57600080fd5b614bd383614b1f565b9150614be160208401614b1f565b90509250929050565b8151151581526101e081016020830151614c0f60208401826001600160a01b03169052565b506040830151614c2a60408401826001600160a01b03169052565b506060830151614c4560608401826001600160a01b03169052565b506080830151614c6060808401826001600160a01b03169052565b5060a0838101519083015260c0808401519083015260e08084015190830152610100808401519083015261012080840151908301526101408084015190830152610160808401519083015261018080840151908301526101a080840151908301526101c092830151929091019190915290565b801515811461315c57600080fd5b6000806000806000806000806000806000806101808d8f031215614d0457600080fd5b8c35614d0f81614cd3565b9b50614d1d60208e01614b1f565b9a50614d2b60408e01614b1f565b9950614d3960608e01614b1f565b9850614d4760808e01614b1f565b9b9e9a9d50989b979a9960a0890135995060c08901359860e0810135985061010081013597506101208101359650610140810135955061016001359350915050565b600060208284031215614d9b57600080fd5b5035919050565b600080600080600060a08688031215614dba57600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b600181811c90821680614df157607f821691505b60208210811415614e1257634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b60008219821115614e4157614e41614e18565b500190565b6000816000190483118215151615614e6057614e60614e18565b500290565b634e487b7160e01b600052601260045260246000fd5b600082614e8a57614e8a614e65565b500490565b6020808252600590820152642152544e4560d81b604082015260600190565b600082821015614ec057614ec0614e18565b500390565b600060208284031215614ed757600080fd5b5051919050565b600060208284031215614ef057600080fd5b815160ff811681146117ef57600080fd5b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b600082614f4757614f47614e65565b500690565b600181815b80851115614f87578160001904821115614f6d57614f6d614e18565b80851615614f7a57918102915b93841c9390800290614f51565b509250929050565b600082614f9e5750600161098a565b81614fab5750600061098a565b8160018114614fc15760028114614fcb57614fe7565b600191505061098a565b60ff841115614fdc57614fdc614e18565b50506001821b61098a565b5060208310610133831016604e8410600b841016171561500a575081810a61098a565b6150148383614f4c565b806000190482111561502857615028614e18565b029392505050565b60006117ef60ff841683614f8f565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60006020828403121561508657600080fd5b81516117ef81614cd3565b600082516150a3818460208701614ac0565b919091019291505056fea264697066735822122087643f065a980a4a954bc2572705531040ff2c5f36f37db464c1f0ea5bd01b6764736f6c634300080b0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc800000000000000000000000056b4a9675c52144c99f676835e83d5625cb47202000000000000000000000000000000000000000000000000000000000000001552697365646c65205661756c742045544855534443000000000000000000000000000000000000000000000000000000000000000000000000000000000000097276455448555344430000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : name (string): Risedle Vault ETHUSDC
Arg [1] : symbol (string): rvETHUSDC
Arg [2] : underlying (address): 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8
Arg [3] : feeRecipient (address): 0x56b4a9675c52144C99F676835e83d5625CB47202
-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [2] : 000000000000000000000000ff970a61a04b1ca14834a43f5de4533ebddb5cc8
Arg [3] : 00000000000000000000000056b4a9675c52144c99f676835e83d5625cb47202
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000015
Arg [5] : 52697365646c65205661756c7420455448555344430000000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000009
Arg [7] : 7276455448555344430000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.
Add Token to MetaMask (Web3)