Transactions
Token Transfers
Tokens
Internal Transactions
Coin Balance History
Logs
Code
Read Contract
Write Contract
- Contract name:
- wstVLXLendingV100
- Optimization enabled
- true
- Compiler version
- v0.8.21+commit.d9974bed
- Optimization runs
- 10
- EVM Version
- berlin
- Verified at
- 2025-05-08T10:37:34.269757Z
Constructor Arguments
0000000000000000000000008f0ecda9679ad16e30be3d83d183c482821f5325
Arg [0] (address) : 0x8f0ecda9679ad16e30be3d83d183c482821f5325
Contract source code
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.20;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
require(address(token).code.length != 0, "token does not exist");
bool success;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
IERC20 token,
address to,
uint256 amount
) internal {
require(address(token).code.length != 0, "token does not exist");
bool success;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
IERC20 token,
address to,
uint256 amount
) internal {
require(address(token).code.length != 0, "token does not exist");
bool success;
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
/**
* @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;
}
}
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
/**
* @dev 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() {
_transferOwnership(_msgSender());
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
/**
* @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);
}
/**
* @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}.
*/
abstract 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;
uint8 private _decimals;
/**
* @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_, uint8 decimals_) {
_name = name_;
_symbol = symbol_;
_decimals = decimals_;
}
/**
* @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 _decimals;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, 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}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, 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}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, 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) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, 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) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, 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:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, 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 Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - 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 {}
}
// ERC20 interface
interface IERC4626 is IERC20Metadata {
function pricePerShare() external view returns (uint256); // Asset value per share (used in BaseLending)
}
/**
* @title BaseLending
* @notice Abstract contract for a lending protocol with asset borrowing against ERC4626 collateral
* @dev Inherits Ownable, ReentrancyGuard, and ERC20 for pool tokens
*/
abstract contract BaseLending is Ownable, ReentrancyGuard, ERC20 {
using SafeTransferLib for IERC4626;
string public constant VERSION = "v1.0.0";
string public LENDING_TYPE = "base";
IERC4626 public immutable collateral; // ERC4626 collateral token
uint256 public totalDebtShares; // Total debt shares across all users
uint256 public totalAssets; // Total asset deposited by suppliers (includes interest)
uint256 public totalCollateral; // Total collateral deposited by borrowers
uint256 public assetsCap; // Maximum allowed asset deposits (0 = no cap)
mapping(address => uint256) internal userCollateral; // User's collateral (in wstTokens)
mapping(address => uint256) internal userDebtShares; // User's debt shares
uint256 internal debtPricePerShare = 10**18; // Price per debt share, increases with interest
uint256 public scaleFactor; // Scaling factor for pool decimals
uint256 public constant PPS_SCALE_FACTOR = 10**18; // Scaling factor for pricePerShare (18 decimals)
uint256 public constant SECONDS_PER_YEAR = 31_536_000; // Seconds in a year
uint256 public lastUpdateTimestamp; // Last interest update timestamp
uint256 public constant BPS_DENOMINATOR = 10000; // Basis points denominator (100% = 10000 bps)
// LTV in basis points
uint256 public constant MAX_LTV = 9900; // 99% = 9900 bps
uint256 public ltv = 9500; // Default 95%, in bps
// Borrowing rate params in bps
uint256 public minBorrowingRate = 0;
uint256 public vertexBorrowingRate = 1000;
uint256 public maxBorrowingRate = 10000;
uint256 public vertexUtilization = 9000;
// Stability fee
uint256 public stabilityFees;
// Stability fee params
uint256 public constant MAX_STABILITY_FEE = 4500;
uint256 public stabilityFee = 3000;
// Liquidation params
address private liquidator; // Authorized liquidator address
uint256 public liquidationBonus = 500; // 5% = 500 bps
uint256 public constant MAX_LIQUIDATION_BONUS = 1000; // 10% = 1000 bps
uint256 public constant LIQUIDATION_THRESHOLD = 1e18; // Health factor < 1
// Base balances for rebasing
mapping(address => uint256) internal baseBalances; // Unscaled balances
uint256 internal baseTotalSupply; // Unscaled total supply
// Events
event Deposit(address indexed caller, address indexed receiver, uint256 assets, uint256 shares);
event Withdraw(address indexed caller, address indexed receiver, uint256 assets, uint256 shares);
event DepositCollateral(address indexed user, uint256 amount);
event WithdrawCollateral(address indexed user, uint256 amount);
event Borrow(address indexed user, uint256 amount);
event Repay(address indexed user, uint256 amount);
event UpdateLTV(uint256 newLTV);
event UpdateBorrowingRateParams(uint256 minRate, uint256 vertexRate, uint256 maxRate, uint256 vertexUtilization);
event Recover(address indexed receiver, uint256 amount);
event UpdateStabilityFee(uint256 newFee);
event CollectStabilityFees(address indexed receiver, uint256 amount);
event UpdateAssetsCap(uint256 newCap);
event Liquidation(address indexed user, address indexed liquidator, uint256 debtCovered, uint256 collateralSeized);
event UpdateLiquidator(address indexed newLiquidator);
event UpdateLiquidationBonus(uint256 newBonus);
event InterestUpdated(uint256 newDebtPricePerShare, uint256 lenderInterest, uint256 stabilityFee);
// Modifier for liquidator-only access
modifier onlyLiquidator() {
require(msg.sender == liquidator, "NotLiquidator");
_;
}
/**
* @notice Initializes the lending pool with an ERC4626 collateral token
* @param _collateralToken The ERC4626 token used as collateral
*/
constructor(IERC4626 _collateralToken)
ERC20(
string(abi.encodePacked("AF ", _collateralToken.symbol(), " Lending")),
string(abi.encodePacked("afl", _collateralToken.symbol())),
_collateralToken.decimals()
)
{
collateral = _collateralToken;
scaleFactor = 10**_collateralToken.decimals();
lastUpdateTimestamp = block.timestamp;
liquidator = msg.sender;
}
/**
* @notice Computes scaled LTV for consistent calculations
* @return Scaled LTV (ltv * SCALE_FACTOR / BPS_DENOMINATOR)
*/
function _getScaledLtv() internal view returns (uint256) {
return (ltv * PPS_SCALE_FACTOR) / BPS_DENOMINATOR;
}
/**
* @notice Calculates pending interest for a given number of debt shares
* @param shares Number of debt shares
* @return Pending interest in assets
*/
function _getPendingInterest(uint256 shares) private view returns (uint256) {
if (shares == 0) return 0;
uint256 currentDebtValue = (shares * getPricePerShareDebt()) / PPS_SCALE_FACTOR;
uint256 principal = shares; // Initial debtPricePerShare was 1 * PPS_SCALE_FACTOR
return currentDebtValue > principal ? currentDebtValue - principal : 0;
}
/**
* @notice Returns the total pending interest across all debt shares
* @return Total pending interest in 18-decimal fixed-point
*/
function getTotalPendingInterest() public view returns (uint256) {
return _getPendingInterest(totalDebtShares);
}
/**
* @notice Returns the pending interest for a user's debt shares
* @param user Address of the user
* @return Pending interest in 18-decimal fixed-point
*/
function getUserPendingInterest(address user) public view returns (uint256) {
return _getPendingInterest(userDebtShares[user]);
}
/**
* @notice Calculates price per share (liquidity index) for deposits
* @return Price per share in 18-decimal fixed-point
*/
function getPricePerShare() public view returns (uint256) {
if (baseTotalSupply == 0) return PPS_SCALE_FACTOR; // 1:1 initially
uint256 grossInterest = getTotalPendingInterest();
uint256 totalValue = totalAssets;
if (grossInterest > 0) {
uint256 stabilityFeeRate = _getStabilityFeeRate();
uint256 fee = (grossInterest * stabilityFeeRate) / BPS_DENOMINATOR;
totalValue += grossInterest - fee;
}
return (totalValue * PPS_SCALE_FACTOR) / baseTotalSupply;
}
/**
* @notice Calculates price per debt share
* @return Price per debt share in 18-decimal fixed-point
*/
function getPricePerShareDebt() public view returns (uint256) {
if (totalDebtShares == 0) return PPS_SCALE_FACTOR; // 1:1 initially
uint256 timeElapsed = block.timestamp - lastUpdateTimestamp;
if (timeElapsed == 0) return debtPricePerShare;
uint256 rate = getBorrowingRate();
uint256 scaledRate = (rate * PPS_SCALE_FACTOR) / BPS_DENOMINATOR;
uint256 interestFactor = (scaledRate * timeElapsed) / SECONDS_PER_YEAR;
return debtPricePerShare + (debtPricePerShare * interestFactor) / PPS_SCALE_FACTOR;
}
/**
* @notice Returns the balance of pool tokens for an account, adjusted for rebasing
* @param account Address to query balance for
* @return Balance of pool tokens in pool tokens decimals
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return (baseBalances[account] * getPricePerShare()) / PPS_SCALE_FACTOR;
}
/**
* @notice Returns the total supply of pool tokens, adjusted for rebasing
* @return Total supply of pool tokens in pool tokens decimals
*/
function totalSupply() public view virtual override returns (uint256) {
return (baseTotalSupply * getPricePerShare()) / PPS_SCALE_FACTOR;
}
/**
* @notice Transfers pool tokens to a recipient, adjusting for rebasing
* @param recipient Address to receive the tokens
* @param amount Amount of tokens to transfer in 18-decimal fixed-point
* @return True if the transfer succeeds
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
uint256 baseAmount = (amount * PPS_SCALE_FACTOR) / getPricePerShare();
require(baseAmount <= baseBalances[msg.sender], "InsufficientBalance");
baseBalances[msg.sender] -= baseAmount;
baseBalances[recipient] += baseAmount;
emit Transfer(msg.sender, recipient, amount);
return true;
}
/**
* @notice Transfers pool tokens from a sender to a receiver, adjusting for rebasing
* @param sender Address to transfer tokens from
* @param receiver Address to receive the tokens
* @param amount Amount of tokens to transfer in 18-decimal fixed-point
* @return True if the transfer succeeds
*/
function transferFrom(address sender, address receiver, uint256 amount) public virtual override returns (bool) {
uint256 baseAmount = (amount * PPS_SCALE_FACTOR) / getPricePerShare();
require(baseAmount <= baseBalances[sender], "InsufficientBalance");
uint256 currentAllowance = allowance(sender, msg.sender);
require(currentAllowance >= amount, "InsufficientAllowance");
_approve(sender, msg.sender, currentAllowance - amount);
baseBalances[sender] -= baseAmount;
baseBalances[receiver] += baseAmount;
emit Transfer(sender, receiver, amount);
return true;
}
/**
* @notice Mints rebasing pool tokens
* @param account Recipient of the tokens
* @param amount Amount of unscaled tokens to mint
*/
function _mint(address account, uint256 amount) internal virtual override {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
baseTotalSupply += amount;
baseBalances[account] += amount;
emit Transfer(address(0), account, (amount * getPricePerShare()) / PPS_SCALE_FACTOR);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @notice Burns rebasing pool tokens
* @param account Account to burn tokens from
* @param amount Amount of unscaled tokens to burn
*/
function _burn(address account, uint256 amount) internal virtual override {
require(account != address(0), "ERC20: burn from the zero address");
require(baseBalances[account] >= amount, "ERC20: burn amount exceeds balance");
_beforeTokenTransfer(account, address(0), amount);
unchecked {
baseTotalSupply -= amount;
baseBalances[account] -= amount;
}
emit Transfer(account, address(0), (amount * getPricePerShare()) / PPS_SCALE_FACTOR);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @notice Returns the version and type of the lending protocol
* @return Version string in the format "vX.Y.Z:type"
*/
function getVersion() public view virtual returns (string memory) {
return string(abi.encodePacked(VERSION, ":", LENDING_TYPE));
}
/**
* @notice Calculates the value of a user's collateral
* @param user Address of the user
* @return Collateral value in 18-decimal fixed-point
*/
function _getCollateralValue(address user) internal view returns (uint256) {
return _getCollateralValueFromShares(userCollateral[user]);
}
/**
* @notice Calculates the value of a given number of collateral shares
* @param shares Number of collateral shares in collateral decimals
* @return Collateral value in 18-decimal fixed-point
*/
function _getCollateralValueFromShares(uint256 shares) internal view returns (uint256) {
return (shares * collateral.pricePerShare()) / PPS_SCALE_FACTOR;
}
/**
* @notice Computes maximum debt for a given collateral amount
* @param collateralAmount Amount of collateral shares
* @return Maximum debt value
*/
function getMaxDebtForCollateral(uint256 collateralAmount) public view returns (uint256) {
uint256 collateralValue = _getCollateralValueFromShares(collateralAmount);
uint256 scaledLtv = _getScaledLtv();
return (collateralValue * scaledLtv) / PPS_SCALE_FACTOR;
}
/**
* @notice Calculates the current utilization rate of the lending pool
* @return Utilization rate in basis points (10000 = 100%)
*/
function getUtilizationRate() public view returns (uint256) {
if (totalAssets == 0) return 0;
uint256 totalDebtValue = (totalDebtShares * debtPricePerShare) / PPS_SCALE_FACTOR;
uint256 utilization = (totalDebtValue * BPS_DENOMINATOR) / totalAssets;
return utilization > BPS_DENOMINATOR ? BPS_DENOMINATOR : utilization;
}
/**
* @notice Calculates the current borrowing rate based on utilization
* @return Borrowing rate in basis points (10000 = 100%)
*/
function getBorrowingRate() public view returns (uint256) {
uint256 utilization = getUtilizationRate();
if (utilization == vertexUtilization) {
return vertexBorrowingRate;
} else if (utilization < vertexUtilization) {
uint256 rateDiff = vertexBorrowingRate - minBorrowingRate;
return minBorrowingRate + (utilization * rateDiff) / vertexUtilization;
} else {
uint256 rateDiff = maxBorrowingRate - vertexBorrowingRate;
uint256 utilDiff = utilization - vertexUtilization;
uint256 utilRange = BPS_DENOMINATOR - vertexUtilization;
return vertexBorrowingRate + (utilDiff * rateDiff) / utilRange;
}
}
/**
* @notice Returns the borrowing rate parameters
* @return minRate Minimum borrowing rate in basis points
* @return vertexRate Borrowing rate at vertex utilization in basis points
* @return maxRate Maximum borrowing rate in basis points
* @return vertexUtil Vertex utilization rate in basis points
*/
function getBorrowingRateParams() public view returns (
uint256 minRate,
uint256 vertexRate,
uint256 maxRate,
uint256 vertexUtil
) {
return (minBorrowingRate, vertexBorrowingRate, maxBorrowingRate, vertexUtilization);
}
/**
* @notice Calculates the current stability fee rate based on utilization
* @return Stability fee rate in basis points (10000 = 100%)
*/
function _getStabilityFeeRate() internal view returns (uint256) {
uint256 utilization = getUtilizationRate();
if (utilization <= vertexUtilization) {
return stabilityFee;
} else {
uint256 maxFee = stabilityFee * 2;
uint256 feeDiff = maxFee - stabilityFee;
uint256 utilDiff = utilization - vertexUtilization;
uint256 utilRange = BPS_DENOMINATOR - vertexUtilization;
return stabilityFee + (utilDiff * feeDiff) / utilRange;
}
}
/**
* @notice Calculates the lending rate for liquidity providers
* @return Lending rate in basis points (10000 = 100%)
*/
function getLendingRate() public view returns (uint256) {
uint256 borrowingRate = getBorrowingRate();
uint256 stabilityFeeRateInBps = _getStabilityFeeRate();
uint256 utilization = getUtilizationRate();
uint256 feeFactor = BPS_DENOMINATOR - stabilityFeeRateInBps;
return (borrowingRate * utilization * feeFactor) / (BPS_DENOMINATOR * BPS_DENOMINATOR);
}
/**
* @notice Returns the amount of collateral shares held by a user
* @param user Address of the user
* @return Amount of collateral shares in 18-decimal fixed-point
*/
function getUserCollateral(address user) public view returns (uint256) {
return userCollateral[user];
}
/**
* @notice Returns the number of debt shares held by a user
* @param user Address of the user
* @return Number of debt shares in 18-decimal fixed-point
*/
function getUserDebtShares(address user) public view returns (uint256) {
return userDebtShares[user];
}
/**
* @notice Returns the user's debt value
* @param user User address
* @return Debt value in tokens
*/
function getUserDebtValue(address user) public view returns (uint256) {
return (userDebtShares[user] * getPricePerShareDebt()) / PPS_SCALE_FACTOR;
}
/**
* @notice Updates interest and protocol fees
*/
function _updateInterest() internal virtual {
uint256 timeElapsed = block.timestamp - lastUpdateTimestamp;
if (timeElapsed > 0 && totalDebtShares > 0) {
uint256 borrowingRate = getBorrowingRate();
uint256 scaledRate = (borrowingRate * scaleFactor) / BPS_DENOMINATOR;
uint256 totalDebtValue = (totalDebtShares * debtPricePerShare) / PPS_SCALE_FACTOR;
uint256 borrowingInterest = (totalDebtValue * scaledRate * timeElapsed) / (SECONDS_PER_YEAR * scaleFactor);
uint256 interestFactor = (borrowingInterest * PPS_SCALE_FACTOR) / totalDebtValue;
debtPricePerShare += (debtPricePerShare * interestFactor) / PPS_SCALE_FACTOR;
uint256 stabilityFeeRate = _getStabilityFeeRate();
uint256 fee = (borrowingInterest * stabilityFeeRate) / BPS_DENOMINATOR;
uint256 lenderInterest = borrowingInterest - fee;
totalAssets += lenderInterest;
stabilityFees += fee;
lastUpdateTimestamp = block.timestamp;
emit InterestUpdated(debtPricePerShare, lenderInterest, fee);
}
}
/**
* @notice Deposits ERC4626 collateral
* @param amount Amount of collateral to deposit
*/
function depositCollateral(uint256 amount) public virtual nonReentrant {
_updateInterest();
require(amount > 0, "ZeroAmount");
collateral.safeTransferFrom(msg.sender, address(this), amount);
userCollateral[msg.sender] += amount;
totalCollateral += amount;
emit DepositCollateral(msg.sender, amount);
}
/**
* @notice Withdraws collateral if not undercollateralized
* @param amount Amount of collateral to withdraw
*/
function withdrawCollateral(uint256 amount) public virtual nonReentrant {
_updateInterest();
require(amount > 0, "ZeroAmount");
uint256 currentCollateral = userCollateral[msg.sender];
require(currentCollateral >= amount, "InsufficientCollateral");
uint256 remainingCollateral = currentCollateral - amount;
uint256 userDebtValue = getUserDebtValue(msg.sender);
require(userDebtValue <= getMaxDebtForCollateral(remainingCollateral), "InsufficientCollateral");
userCollateral[msg.sender] = remainingCollateral;
totalCollateral -= amount;
collateral.safeTransfer(msg.sender, amount);
emit WithdrawCollateral(msg.sender, amount);
}
/**
* @notice Returns the value of a user's collateral
* @param user Address of the user
* @return Collateral value in 18-decimal fixed-point
*/
function getUserCollateralValue(address user) public view returns (uint256) {
return _getCollateralValue(user);
}
/**
* @notice Calculates user health factor
* @param user User address
* @return Health factor (1e18 = 1, < 1e18 = liquidatable)
*/
function getUserHealth(address user) public view returns (uint256) {
// Health factor = (Collateral Value * LTV) / Debt
uint256 borrowed = getUserDebtValue(user);
if (borrowed == 0) return type(uint256).max;
uint256 collateralValue = _getCollateralValue(user);
uint256 scaledLtv = _getScaledLtv();
return (collateralValue * scaledLtv) / borrowed;
}
/**
* @notice Checks if a user's position is liquidatable
* @param user Address of the user
* @return True if the user's health factor is below the liquidation threshold
*/
function isLiquidatable(address user) public view returns (bool) {
return getUserHealth(user) < LIQUIDATION_THRESHOLD;
}
/**
* @notice Calculates the maximum collateral a user can withdraw
* @param user Address of the user
* @return Maximum withdrawable collateral shares in collateral decimals
*/
function getUserMaxWithdrawCollateral(address user) public view returns (uint256) {
uint256 borrowed = getUserDebtValue(user);
if (borrowed == 0) return userCollateral[user];
uint256 collateralShares = userCollateral[user];
if (collateralShares == 0) return 0;
uint256 scaledLtv = _getScaledLtv();
if (scaledLtv == 0) return 0;
uint256 minCollateralValue = (borrowed * PPS_SCALE_FACTOR) / scaledLtv;
uint256 pricePerShare = collateral.pricePerShare();
require(pricePerShare > 0, "InvalidPrice");
uint256 minCollateralShares = (minCollateralValue * PPS_SCALE_FACTOR) / pricePerShare;
return minCollateralShares >= collateralShares ? 0 : collateralShares - minCollateralShares;
}
/**
* @notice Returns assets required to liquidate debt shares
* @param user User address
* @param debtSharesToCover Debt shares to liquidate
* @return Required assets
*/
function getRequiredAmountForLiquidation(address user, uint256 debtSharesToCover) public view returns (uint256) {
require(debtSharesToCover <= userDebtShares[user], "InvalidDebtSharesAmount");
return (debtSharesToCover * getPricePerShareDebt()) / PPS_SCALE_FACTOR;
}
/**
* @notice Updates the loan-to-value ratio
* @param newLTV New LTV value in basis points (10000 = 100%)
*/
function updateLTV(uint256 newLTV) public onlyOwner {
require(newLTV <= MAX_LTV, "LTVExceedsMax");
ltv = newLTV;
emit UpdateLTV(newLTV);
}
/**
* @notice Updates the borrowing rate parameters
* @param newMinRate New minimum borrowing rate in basis points
* @param newVertexRate New borrowing rate at vertex utilization in basis points
* @param newMaxRate New maximum borrowing rate in basis points
* @param newVertexUtilization New vertex utilization rate in basis points
*/
function updateBorrowingRateParams(
uint256 newMinRate,
uint256 newVertexRate,
uint256 newMaxRate,
uint256 newVertexUtilization
) public onlyOwner {
_updateInterest();
require(newMinRate <= newVertexRate && newVertexRate <= newMaxRate, "InvalidRateOrder");
require(newVertexUtilization > 0 && newVertexUtilization < BPS_DENOMINATOR, "InvalidVertexUtilization");
minBorrowingRate = newMinRate;
vertexBorrowingRate = newVertexRate;
maxBorrowingRate = newMaxRate;
vertexUtilization = newVertexUtilization;
emit UpdateBorrowingRateParams(newMinRate, newVertexRate, newMaxRate, newVertexUtilization);
}
/**
* @notice Updates the stability fee
* @param newFee New stability fee in basis points (10000 = 100%)
*/
function updateStabilityFee(uint256 newFee) public onlyOwner {
_updateInterest();
require(newFee <= MAX_STABILITY_FEE, "FeeExceedsMax");
stabilityFee = newFee;
emit UpdateStabilityFee(newFee);
}
/**
* @notice Updates the maximum asset cap
* @param newCap New asset cap in 18-decimal fixed-point (0 = no cap)
*/
function updateAssetsCap(uint256 newCap) public onlyOwner {
require(newCap >= totalAssets, "NewMaxBelowCurrentAssets");
assetsCap = newCap;
emit UpdateAssetsCap(newCap);
}
/**
* @notice Updates the liquidator address
* @param newLiquidator New liquidator address
*/
function updateLiquidator(address newLiquidator) public onlyOwner {
require(newLiquidator != address(0), "InvalidAddress");
liquidator = newLiquidator;
emit UpdateLiquidator(newLiquidator);
}
/**
* @notice Updates the liquidation bonus
* @param newBonus New liquidation bonus in basis points (10000 = 100%)
*/
function updateLiquidationBonus(uint256 newBonus) public onlyOwner {
require(newBonus <= MAX_LIQUIDATION_BONUS, "BonusExceedsMax");
liquidationBonus = newBonus;
emit UpdateLiquidationBonus(newBonus);
}
}
/**
* @title NativeLending
* @notice Lending protocol accepting native tokens (e.g., ETH) as borrowable assets
* @dev Inherits from BaseLending
*/
abstract contract NativeLending is BaseLending {
using SafeTransferLib for IERC4626;
/**
* @notice Initializes the lending pool with an ERC4626 collateral token
* @param _collateralToken The ERC4626 token used as collateral
*/
constructor(IERC4626 _collateralToken) BaseLending(_collateralToken) {
LENDING_TYPE = "native";
}
/**
* @notice Calculates the maximum assets a user can withdraw
* @param user Address of the user
* @return Maximum withdrawable assets in 18-decimal fixed-point
*/
function getUserMaxWithdraw(address user) public view returns (uint256) {
uint256 userBalance = balanceOf(user);
uint256 maxAvailable = address(this).balance;
return userBalance < maxAvailable ? userBalance : maxAvailable;
}
/**
* @notice Returns the maximum additional assets a user can borrow
* @param user User address
* @return Maximum additional borrowable assets
*/
function getUserMaxBorrow(address user) public view returns (uint256) {
uint256 userDebtValue = getUserDebtValue(user);
uint256 maxDebt = getMaxDebtForCollateral(userCollateral[user]);
uint256 maxAvailable = address(this).balance;
uint256 maxBorrow = maxDebt > userDebtValue ? maxDebt - userDebtValue : 0;
return maxBorrow < maxAvailable ? maxBorrow : maxAvailable;
}
/**
* @notice Deposits asset to supply liquidity, minting pool tokens
* @param receiver Address to receive pool tokens
*/
function deposit(address receiver) public payable virtual nonReentrant {
_updateInterest();
require(msg.value > 0, "ZeroAmount");
require(totalAssets + msg.value <= assetsCap, "ExceedsAssetsCap");
uint256 baseTokens = (msg.value * PPS_SCALE_FACTOR) / getPricePerShare();
require(baseTokens > 0, "InsufficientShares");
totalAssets += msg.value;
_mint(receiver, baseTokens);
emit Deposit(msg.sender, receiver, msg.value, baseTokens);
}
/**
* @notice Withdraws assets, burning pool tokens
* @param amount Amount of assets to withdraw in asset decimals
* @param receiver Recipient of the assets
*/
function withdraw(uint256 amount, address receiver) public virtual nonReentrant {
_updateInterest();
require(amount > 0, "ZeroAmount");
require(amount <= balanceOf(msg.sender), "InsufficientBalance");
require(totalAssets >= amount, "InsufficientPoolAssets");
uint256 baseTokens = (amount * PPS_SCALE_FACTOR) / getPricePerShare();
require(address(this).balance >= amount, "InsufficientContractBalance");
totalAssets -= amount;
_burn(msg.sender, baseTokens);
SafeTransferLib.safeTransferETH(receiver, amount);
emit Withdraw(msg.sender, receiver, amount, baseTokens);
}
/**
* @notice Borrows asset against collateral
* @param amount Amount of asset to borrow
*/
function borrow(uint256 amount) public virtual nonReentrant {
_updateInterest();
require(amount > 0, "ZeroAmount");
require(address(this).balance >= amount, "InsufficientBalance");
require(amount <= getUserMaxBorrow(msg.sender), "InsufficientCollateral");
uint256 newDebtShares = (amount * PPS_SCALE_FACTOR) / debtPricePerShare;
userDebtShares[msg.sender] += newDebtShares;
totalDebtShares += newDebtShares;
SafeTransferLib.safeTransferETH(msg.sender, amount);
emit Borrow(msg.sender, amount);
}
/**
* @notice Repays debt with native tokens
*/
function repay() public payable virtual nonReentrant {
_updateInterest();
require(msg.value > 0, "ZeroAmount");
uint256 shares = userDebtShares[msg.sender];
require(shares > 0, "NoDebt");
uint256 dpps = debtPricePerShare;
uint256 totalDebtValue = (shares * dpps) / PPS_SCALE_FACTOR;
uint256 repayment = msg.value > totalDebtValue ? totalDebtValue : msg.value;
if (repayment == totalDebtValue) {
totalDebtShares -= shares;
userDebtShares[msg.sender] = 0;
} else {
uint256 sharesRepaid = (repayment * PPS_SCALE_FACTOR) / dpps;
totalDebtShares -= sharesRepaid;
userDebtShares[msg.sender] -= sharesRepaid;
}
if (msg.value > totalDebtValue) {
SafeTransferLib.safeTransferETH(msg.sender, msg.value - totalDebtValue);
}
emit Repay(msg.sender, repayment);
}
/**
* @notice Liquidates a user's position
* @param user User to liquidate
* @param debtSharesToCover Debt shares to cover
*/
function liquidate(address user, uint256 debtSharesToCover) public payable virtual onlyLiquidator nonReentrant {
_updateInterest();
require(isLiquidatable(user), "PositionNotLiquidatable");
require(debtSharesToCover > 0 && debtSharesToCover <= userDebtShares[user], "InvalidDebtSharesAmount");
uint256 debtToCover = (debtSharesToCover * getPricePerShareDebt()) / PPS_SCALE_FACTOR;
uint256 collateralPricePerShare = collateral.pricePerShare();
require(collateralPricePerShare > 0, "InvalidPrice");
uint256 collateralValue = (userCollateral[user] * collateralPricePerShare) / PPS_SCALE_FACTOR;
uint256 bonusAmount = (debtToCover * liquidationBonus) / BPS_DENOMINATOR;
uint256 maxBonus = collateralValue > debtToCover ? collateralValue - debtToCover : 0;
bonusAmount = bonusAmount > maxBonus ? maxBonus : bonusAmount;
uint256 totalValueToSeize = debtToCover + bonusAmount;
require(totalValueToSeize <= collateralValue, "InsufficientCollateralValue");
uint256 collateralSharesToSeize = (totalValueToSeize * PPS_SCALE_FACTOR) / collateralPricePerShare;
require(collateralSharesToSeize <= userCollateral[user], "InsufficientCollateralShares");
require(msg.value >= debtToCover, "InsufficientAmount");
if (msg.value > debtToCover) {
uint256 refund = msg.value - debtToCover;
SafeTransferLib.safeTransferETH(msg.sender, refund);
}
userDebtShares[user] -= debtSharesToCover;
totalDebtShares -= debtSharesToCover;
userCollateral[user] -= collateralSharesToSeize;
totalCollateral -= collateralSharesToSeize;
collateral.safeTransfer(msg.sender, collateralSharesToSeize);
emit Liquidation(user, msg.sender, debtToCover, collateralSharesToSeize);
}
/**
* @notice Collects accumulated stability fees
* @param receiver Address to receive the fees
*/
function collectStabilityFees(address receiver) public onlyOwner {
_updateInterest();
require(stabilityFees > 0, "NoFeesToCollect");
uint256 contractBalance = address(this).balance;
uint256 amountToCollect = stabilityFees > contractBalance ? contractBalance : stabilityFees;
stabilityFees -= amountToCollect;
SafeTransferLib.safeTransferETH(receiver, amountToCollect);
emit CollectStabilityFees(receiver, amountToCollect);
}
/**
* @notice Returns the amount of tokens available for recovery
* @return The excess balance that can be recovered
*/
function getRecoverableAmount() public view returns (uint256) {
uint256 totalDebtValue = (totalDebtShares * getPricePerShareDebt()) / PPS_SCALE_FACTOR;
uint256 requiredBalance = totalAssets > totalDebtValue ? totalAssets - totalDebtValue : 0;
uint256 reservedBalance = requiredBalance + stabilityFees + getTotalPendingInterest();
return address(this).balance > reservedBalance ? address(this).balance - reservedBalance : 0;
}
/**
* @notice Recovers excess assets not tracked in totalAssets or reserved for protocol fees
* @param amount Amount to recover
* @param receiver Recipient of the recovered assets
*/
function recover(uint256 amount, address receiver) public virtual onlyOwner {
_updateInterest();
require(amount > 0, "ZeroAmount");
uint256 excessBalance = getRecoverableAmount();
require(amount <= excessBalance, "AmountExceedsExcess");
SafeTransferLib.safeTransferETH(receiver, amount);
emit Recover(receiver, amount);
}
}
/**
* @title ERC20Lending
* @notice Lending protocol accepting ERC20 tokens as borrowable assets
* @dev Inherits from BaseLending
*/
abstract contract ERC20Lending is BaseLending {
using SafeTransferLib for IERC4626;
using SafeTransferLib for IERC20;
IERC20 public immutable asset; // ERC20 asset token
/**
* @notice Initializes the lending pool with an ERC20 asset and ERC4626 collateral token
* @param _assetToken The ERC20 token used as the borrowable asset
* @param _collateralToken The ERC4626 token used as collateral
*/
constructor(IERC20 _assetToken, IERC4626 _collateralToken) BaseLending(_collateralToken) {
LENDING_TYPE = "erc20";
asset = IERC20(_assetToken);
}
/**
* @notice Calculates the maximum assets a user can withdraw
* @param user Address of the user
* @return Maximum withdrawable assets in 18-decimal fixed-point
*/
function getUserMaxWithdraw(address user) public view returns (uint256) {
uint256 userBalance = balanceOf(user);
uint256 maxAvailable = asset.balanceOf(address(this));
return userBalance < maxAvailable ? userBalance : maxAvailable;
}
/**
* @notice Returns the maximum additional assets a user can borrow
* @param user User address
* @return Maximum additional borrowable assets
*/
function getUserMaxBorrow(address user) public view returns (uint256) {
uint256 userDebtValue = getUserDebtValue(user);
uint256 maxDebt = getMaxDebtForCollateral(userCollateral[user]);
uint256 maxAvailable = asset.balanceOf(address(this));
uint256 maxBorrow = maxDebt > userDebtValue ? maxDebt - userDebtValue : 0;
return maxBorrow < maxAvailable ? maxBorrow : maxAvailable;
}
/**
* @notice Deposits asset to supply liquidity, minting pool tokens
* @param amount Deposit amount in asset decimals
* @param receiver Address to receive pool tokens
*/
function deposit(uint256 amount, address receiver) public virtual nonReentrant {
_updateInterest();
require(amount > 0, "ZeroAmount");
require(totalAssets + amount <= assetsCap, "ExceedsAssetsCap");
uint256 baseTokens = (amount * PPS_SCALE_FACTOR) / getPricePerShare();
require(baseTokens > 0, "InsufficientShares");
asset.safeTransferFrom(address(msg.sender), address(this), amount);
totalAssets += amount;
_mint(receiver, baseTokens);
emit Deposit(msg.sender, receiver, amount, baseTokens);
}
/**
* @notice Withdraws assets, burning pool tokens
* @param amount Amount of assets to withdraw in asset decimals
* @param receiver Recipient of the assets
*/
function withdraw(uint256 amount, address receiver) public virtual nonReentrant {
_updateInterest();
require(amount > 0, "ZeroAmount");
require(amount <= balanceOf(msg.sender), "InsufficientBalance");
require(totalAssets >= amount, "InsufficientPoolAssets");
uint256 baseTokens = (amount * PPS_SCALE_FACTOR) / getPricePerShare();
require(asset.balanceOf(address(this)) >= amount, "InsufficientContractBalance");
totalAssets -= amount;
_burn(msg.sender, baseTokens);
asset.safeTransfer(receiver, amount);
emit Withdraw(msg.sender, receiver, amount, baseTokens);
}
/**
* @notice Borrows asset against collateral
* @param amount Amount of asset to borrow
*/
function borrow(uint256 amount) public virtual nonReentrant {
_updateInterest();
require(amount > 0, "ZeroAmount");
require(asset.balanceOf(address(this)) >= amount, "InsufficientBalance");
require(amount <= getUserMaxBorrow(msg.sender), "InsufficientCollateral");
uint256 newDebtShares = (amount * PPS_SCALE_FACTOR) / debtPricePerShare;
userDebtShares[msg.sender] += newDebtShares;
totalDebtShares += newDebtShares;
asset.safeTransfer(msg.sender, amount);
emit Borrow(msg.sender, amount);
}
/**
* @notice Repays debt with ERC20 tokens
* @param amount Amount of tokens to repay in 18-decimal fixed-point
*/
function repay(uint256 amount) public virtual nonReentrant {
_updateInterest();
require(amount > 0, "ZeroAmount");
asset.safeTransferFrom(address(msg.sender), address(this), amount);
uint256 shares = userDebtShares[msg.sender];
require(shares > 0, "NoDebt");
uint256 dpps = debtPricePerShare;
uint256 totalDebtValue = (shares * dpps) / PPS_SCALE_FACTOR;
uint256 repayment = amount > totalDebtValue ? totalDebtValue : amount;
if (repayment == totalDebtValue) {
totalDebtShares -= shares;
userDebtShares[msg.sender] = 0;
} else {
uint256 sharesRepaid = (repayment * PPS_SCALE_FACTOR) / dpps;
totalDebtShares -= sharesRepaid;
userDebtShares[msg.sender] -= sharesRepaid;
}
if (amount > totalDebtValue) {
asset.safeTransfer(msg.sender, amount - totalDebtValue);
}
emit Repay(msg.sender, repayment);
}
/**
* @notice Liquidates a user's position
* @param user User to liquidate
* @param debtSharesToCover Debt shares to cover
* @param amount Amount of assets to cover the debt in asset decimals
*/
function liquidate(address user, uint256 debtSharesToCover, uint256 amount) public virtual onlyLiquidator nonReentrant {
_updateInterest();
require(isLiquidatable(user), "PositionNotLiquidatable");
require(debtSharesToCover > 0 && debtSharesToCover <= userDebtShares[user], "InvalidDebtSharesAmount");
asset.safeTransferFrom(address(msg.sender), address(this), amount);
uint256 debtToCover = (debtSharesToCover * getPricePerShareDebt()) / PPS_SCALE_FACTOR;
uint256 collateralPricePerShare = collateral.pricePerShare();
require(collateralPricePerShare > 0, "InvalidPrice");
uint256 collateralValue = (userCollateral[user] * collateralPricePerShare) / PPS_SCALE_FACTOR;
uint256 bonusAmount = (debtToCover * liquidationBonus) / BPS_DENOMINATOR;
uint256 maxBonus = collateralValue > debtToCover ? collateralValue - debtToCover : 0;
bonusAmount = bonusAmount > maxBonus ? maxBonus : bonusAmount;
uint256 totalValueToSeize = debtToCover + bonusAmount;
require(totalValueToSeize <= collateralValue, "InsufficientCollateralValue");
uint256 collateralSharesToSeize = (totalValueToSeize * PPS_SCALE_FACTOR) / collateralPricePerShare;
require(collateralSharesToSeize <= userCollateral[user], "InsufficientCollateralShares");
require(amount >= debtToCover, "InsufficientAmount");
if (amount > debtToCover) {
uint256 refund = amount - debtToCover;
asset.safeTransfer(msg.sender, refund);
}
userDebtShares[user] -= debtSharesToCover;
totalDebtShares -= debtSharesToCover;
userCollateral[user] -= collateralSharesToSeize;
totalCollateral -= collateralSharesToSeize;
collateral.safeTransfer(msg.sender, collateralSharesToSeize);
emit Liquidation(user, msg.sender, debtToCover, collateralSharesToSeize);
}
/**
* @notice Collects accumulated stability fees
* @param receiver Address to receive the fees
*/
function collectStabilityFees(address receiver) public onlyOwner {
_updateInterest();
require(stabilityFees > 0, "NoFeesToCollect");
uint256 contractBalance = asset.balanceOf(address(this));
uint256 amountToCollect = stabilityFees > contractBalance ? contractBalance : stabilityFees;
stabilityFees -= amountToCollect;
asset.safeTransfer(receiver, amountToCollect);
emit CollectStabilityFees(receiver, amountToCollect);
}
/**
* @notice Returns the amount of tokens available for recovery
* @return The excess balance that can be recovered
*/
function getRecoverableAmount() public view returns (uint256) {
uint256 totalDebtValue = (totalDebtShares * getPricePerShareDebt()) / PPS_SCALE_FACTOR;
uint256 requiredBalance = totalAssets > totalDebtValue ? totalAssets - totalDebtValue : 0;
uint256 reservedBalance = requiredBalance + stabilityFees + getTotalPendingInterest();
return asset.balanceOf(address(this)) > reservedBalance ? asset.balanceOf(address(this)) - reservedBalance : 0;
}
/**
* @notice Recovers excess assets not tracked in totalAssets or reserved for protocol fees
* @param amount Amount to recover
* @param receiver Recipient of the recovered assets
*/
function recover(uint256 amount, address receiver) public virtual onlyOwner {
_updateInterest();
require(amount > 0, "ZeroAmount");
uint256 excessBalance = getRecoverableAmount();
require(amount <= excessBalance, "AmountExceedsExcess");
asset.safeTransfer(receiver, amount);
emit Recover(receiver, amount);
}
}
contract wstVLXLendingV100 is NativeLending {
constructor(IERC4626 _collateralToken) NativeLending(_collateralToken) {}
}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_collateralToken","internalType":"contract IERC4626"}]},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Borrow","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"CollectStabilityFees","inputs":[{"type":"address","name":"receiver","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Deposit","inputs":[{"type":"address","name":"caller","internalType":"address","indexed":true},{"type":"address","name":"receiver","internalType":"address","indexed":true},{"type":"uint256","name":"assets","internalType":"uint256","indexed":false},{"type":"uint256","name":"shares","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"DepositCollateral","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"InterestUpdated","inputs":[{"type":"uint256","name":"newDebtPricePerShare","internalType":"uint256","indexed":false},{"type":"uint256","name":"lenderInterest","internalType":"uint256","indexed":false},{"type":"uint256","name":"stabilityFee","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Liquidation","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"address","name":"liquidator","internalType":"address","indexed":true},{"type":"uint256","name":"debtCovered","internalType":"uint256","indexed":false},{"type":"uint256","name":"collateralSeized","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Recover","inputs":[{"type":"address","name":"receiver","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Repay","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"UpdateAssetsCap","inputs":[{"type":"uint256","name":"newCap","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"UpdateBorrowingRateParams","inputs":[{"type":"uint256","name":"minRate","internalType":"uint256","indexed":false},{"type":"uint256","name":"vertexRate","internalType":"uint256","indexed":false},{"type":"uint256","name":"maxRate","internalType":"uint256","indexed":false},{"type":"uint256","name":"vertexUtilization","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"UpdateLTV","inputs":[{"type":"uint256","name":"newLTV","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"UpdateLiquidationBonus","inputs":[{"type":"uint256","name":"newBonus","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"UpdateLiquidator","inputs":[{"type":"address","name":"newLiquidator","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"UpdateStabilityFee","inputs":[{"type":"uint256","name":"newFee","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"Withdraw","inputs":[{"type":"address","name":"caller","internalType":"address","indexed":true},{"type":"address","name":"receiver","internalType":"address","indexed":true},{"type":"uint256","name":"assets","internalType":"uint256","indexed":false},{"type":"uint256","name":"shares","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"WithdrawCollateral","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BPS_DENOMINATOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"LENDING_TYPE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"LIQUIDATION_THRESHOLD","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_LIQUIDATION_BONUS","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_LTV","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"MAX_STABILITY_FEE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"PPS_SCALE_FACTOR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"SECONDS_PER_YEAR","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"VERSION","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"owner","internalType":"address"},{"type":"address","name":"spender","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"assetsCap","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"borrow","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC4626"}],"name":"collateral","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"collectStabilityFees","inputs":[{"type":"address","name":"receiver","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"decreaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"subtractedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[],"name":"deposit","inputs":[{"type":"address","name":"receiver","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"depositCollateral","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getBorrowingRate","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"minRate","internalType":"uint256"},{"type":"uint256","name":"vertexRate","internalType":"uint256"},{"type":"uint256","name":"maxRate","internalType":"uint256"},{"type":"uint256","name":"vertexUtil","internalType":"uint256"}],"name":"getBorrowingRateParams","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getLendingRate","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getMaxDebtForCollateral","inputs":[{"type":"uint256","name":"collateralAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getPricePerShare","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getPricePerShareDebt","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getRecoverableAmount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getRequiredAmountForLiquidation","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"uint256","name":"debtSharesToCover","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getTotalPendingInterest","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUserCollateral","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUserCollateralValue","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUserDebtShares","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUserDebtValue","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUserHealth","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUserMaxBorrow","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUserMaxWithdraw","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUserMaxWithdrawCollateral","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUserPendingInterest","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getUtilizationRate","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"getVersion","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"increaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"addedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isLiquidatable","inputs":[{"type":"address","name":"user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"lastUpdateTimestamp","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"liquidate","inputs":[{"type":"address","name":"user","internalType":"address"},{"type":"uint256","name":"debtSharesToCover","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"liquidationBonus","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"ltv","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxBorrowingRate","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"minBorrowingRate","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"recover","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"address","name":"receiver","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"payable","outputs":[],"name":"repay","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"scaleFactor","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stabilityFee","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stabilityFees","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalAssets","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalCollateral","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalDebtShares","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"sender","internalType":"address"},{"type":"address","name":"receiver","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateAssetsCap","inputs":[{"type":"uint256","name":"newCap","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateBorrowingRateParams","inputs":[{"type":"uint256","name":"newMinRate","internalType":"uint256"},{"type":"uint256","name":"newVertexRate","internalType":"uint256"},{"type":"uint256","name":"newMaxRate","internalType":"uint256"},{"type":"uint256","name":"newVertexUtilization","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateLTV","inputs":[{"type":"uint256","name":"newLTV","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateLiquidationBonus","inputs":[{"type":"uint256","name":"newBonus","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateLiquidator","inputs":[{"type":"address","name":"newLiquidator","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateStabilityFee","inputs":[{"type":"uint256","name":"newFee","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"vertexBorrowingRate","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"vertexUtilization","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdraw","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"address","name":"receiver","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawCollateral","inputs":[{"type":"uint256","name":"amount","internalType":"uint256"}]}]
Contract Creation Code
0x60e0604052600460a0908152636261736560e01b60c0526008906200002590826200040b565b50670de0b6b3a7640000600f5561251c60125560006013556103e8601455612710601555612328601655610bb86018556101f4601a553480156200006857600080fd5b5060405162004184380380620041848339810160408190526200008b91620004d7565b8080806001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa158015620000cc573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620000f691908101906200052f565b604051602001620001089190620005e7565b604051602081830303815290604052816001600160a01b03166395d89b416040518163ffffffff1660e01b8152600401600060405180830381865afa15801562000156573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526200018091908101906200052f565b60405160200162000192919062000626565b604051602081830303815290604052826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001e0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000206919062000653565b620002113362000316565b6001805560056200022384826200040b565b5060066200023283826200040b565b506007805460ff191660ff9290921691909117905550506001600160a01b03811660808190526040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa15801562000294573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620002ba919062000653565b620002c790600a6200078d565b6010555042601155601980546001600160a01b031916331790556040805180820190915260068152656e617469766560d01b60208201526008906200030d90826200040b565b5050506200079e565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200039157607f821691505b602082108103620003b257634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200040657600081815260208120601f850160051c81016020861015620003e15750805b601f850160051c820191505b818110156200040257828155600101620003ed565b5050505b505050565b81516001600160401b0381111562000427576200042762000366565b6200043f816200043884546200037c565b84620003b8565b602080601f8311600181146200047757600084156200045e5750858301515b600019600386901b1c1916600185901b17855562000402565b600085815260208120601f198616915b82811015620004a85788860151825594840194600190910190840162000487565b5085821015620004c75787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215620004ea57600080fd5b81516001600160a01b03811681146200050257600080fd5b9392505050565b60005b83811015620005265781810151838201526020016200050c565b50506000910152565b6000602082840312156200054257600080fd5b81516001600160401b03808211156200055a57600080fd5b818401915084601f8301126200056f57600080fd5b81518181111562000584576200058462000366565b604051601f8201601f19908116603f01168101908382118183101715620005af57620005af62000366565b81604052828152876020848701011115620005c957600080fd5b620005dc83602083016020880162000509565b979650505050505050565b62020a3160ed1b8152600082516200060781600385016020870162000509565b67204c656e64696e6760c01b6003939091019283015250600b01919050565b6218599b60ea1b8152600082516200064681600385016020870162000509565b9190910160030192915050565b6000602082840312156200066657600080fd5b815160ff811681146200050257600080fd5b634e487b7160e01b600052601160045260246000fd5b600181815b80851115620006cf578160001904821115620006b357620006b362000678565b80851615620006c157918102915b93841c939080029062000693565b509250929050565b600082620006e85750600162000787565b81620006f75750600062000787565b81600181146200071057600281146200071b576200073b565b600191505062000787565b60ff8411156200072f576200072f62000678565b50506001821b62000787565b5060208310610133831016604e8410600b841016171562000760575081810a62000787565b6200076c83836200068e565b806000190482111562000783576200078362000678565b0290505b92915050565b60006200050260ff841683620006d7565b6080516139a0620007e460003960008181610a460152818161189e01528181611ae401528181611e74015281816120310152818161233f015261314701526139a06000f3fe6080604052600436106103505760003560e01c8063778fd81e116101b9578063778fd81e146107725780637b7c1f4c1461078757806381bf8d3d1461079d5780638339918e146107b3578063855c55f3146107d357806387309f8b146107e85780638da5cb5b146107fe57806390a8ae9b146106b557806395d89b411461082b5780639e1b99f914610840578063a1df3b4014610860578063a3371d1214610876578063a457c2d714610896578063a9059cbb146108b6578063ad3a8537146108d6578063afc50c2f146108ec578063b6f7132f14610901578063b72b5efd14610937578063b92a49721461094d578063bad4a01f14610976578063bcbaf48714610996578063c5ebeaec146109a9578063cd4a4861146109c9578063d23c7aed146109df578063d5d8cf4a146109ff578063d7d6cc4714610a14578063d8dfeb4514610a34578063dd62ed3e14610a68578063e1a4521814610a88578063e6a69ab814610a9e578063eb2313a414610ab6578063f2d6e5d514610ad6578063f2fde38b14610af6578063f340fa0114610b16578063f36b242514610b29578063f9d6c03b14610b3e578063ffa1ad7414610b5e57600080fd5b8062f714ce1461035557806301e1d11414610377578063042e02cf146103a057806306fdde03146103d0578063095ea7b3146103f25780630be56d34146104125780630d6a4077146104275780630d8e6e2c146104475780631326851e1461045c57806313f9cca21461047c57806314bcec9f1461049c57806318160ddd146104b25780631b0187f9146104c75780631b113dfb146104dd5780631b859e41146104f357806323b872dd146105095780632878fff214610529578063313ce5671461054957806332319fda1461056b578063361416e11461058b57806336fde70b146105a157806339509351146105c15780633d68175c146105e1578063402d8883146105f65780634a417a53146105fe5780634ac8eb5f146106135780634e6657bc146106295780634eea48e21461063f5780635b1e9c111461065f5780635f0cf1bc1461067f578063606ae9ea146106b55780636112fe2e146106d1578063683dd191146106f15780636fb49d731461070757806370a082311461071d578063715018a61461073d57806375417da614610752575b600080fd5b34801561036157600080fd5b506103756103703660046134f0565b610b90565b005b34801561038357600080fd5b5061038d600a5481565b6040519081526020015b60405180910390f35b3480156103ac57600080fd5b506103c06103bb36600461351c565b610d32565b6040519015158152602001610397565b3480156103dc57600080fd5b506103e5610d4d565b604051610397919061355b565b3480156103fe57600080fd5b506103c061040d36600461358e565b610ddf565b34801561041e57600080fd5b5061038d610df9565b34801561043357600080fd5b5061038d61044236600461358e565b610e62565b34801561045357600080fd5b506103e5610ec6565b34801561046857600080fd5b506103756104773660046135b8565b610f0e565b34801561048857600080fd5b506103756104973660046135b8565b610fbb565b3480156104a857600080fd5b5061038d60115481565b3480156104be57600080fd5b5061038d611069565b3480156104d357600080fd5b5061038d60165481565b3480156104e957600080fd5b5061038d60145481565b3480156104ff57600080fd5b5061038d60095481565b34801561051557600080fd5b506103c06105243660046135d1565b611098565b34801561053557600080fd5b506103756105443660046135b8565b611207565b34801561055557600080fd5b5060075460405160ff9091168152602001610397565b34801561057757600080fd5b5061038d6105863660046135b8565b6112b8565b34801561059757600080fd5b5061038d60185481565b3480156105ad57600080fd5b506103756105bc3660046135b8565b6112f7565b3480156105cd57600080fd5b506103c06105dc36600461358e565b61139f565b3480156105ed57600080fd5b5061038d6113bc565b610375611451565b34801561060a57600080fd5b5061038d6115ee565b34801561061f57600080fd5b5061038d600b5481565b34801561063557600080fd5b5061038d61119481565b34801561064b57600080fd5b5061037561065a3660046134f0565b61165f565b34801561066b57600080fd5b5061038d61067a36600461351c565b61175a565b34801561068b57600080fd5b5061038d61069a36600461351c565b6001600160a01b03166000908152600e602052604090205490565b3480156106c157600080fd5b5061038d670de0b6b3a764000081565b3480156106dd57600080fd5b506103756106ec3660046135b8565b6117c4565b3480156106fd57600080fd5b5061038d60105481565b34801561071357600080fd5b5061038d60125481565b34801561072957600080fd5b5061038d61073836600461351c565b611909565b34801561074957600080fd5b50610375611949565b34801561075e57600080fd5b5061038d61076d36600461351c565b611982565b34801561077e57600080fd5b5061038d61198d565b34801561079357600080fd5b5061038d601a5481565b3480156107a957600080fd5b5061038d6126ac81565b3480156107bf57600080fd5b5061038d6107ce36600461351c565b611a42565b3480156107df57600080fd5b5061038d611bcd565b3480156107f457600080fd5b5061038d6103e881565b34801561080a57600080fd5b50610813611bda565b6040516001600160a01b039091168152602001610397565b34801561083757600080fd5b506103e5611be9565b34801561084c57600080fd5b5061038d61085b36600461351c565b611bf8565b34801561086c57600080fd5b5061038d60175481565b34801561088257600080fd5b5061038d61089136600461351c565b611c1a565b3480156108a257600080fd5b506103c06108b136600461358e565b611c3d565b3480156108c257600080fd5b506103c06108d136600461358e565b611cc3565b3480156108e257600080fd5b5061038d60155481565b3480156108f857600080fd5b5061038d611da5565b34801561090d57600080fd5b5061038d61091c36600461351c565b6001600160a01b03166000908152600d602052604090205490565b34801561094357600080fd5b5061038d60135481565b34801561095957600080fd5b50601354601454601554601654604051610397949392919061360d565b34801561098257600080fd5b506103756109913660046135b8565b611e37565b6103756109a436600461358e565b611f17565b3480156109b557600080fd5b506103756109c43660046135b8565b6123bc565b3480156109d557600080fd5b5061038d600c5481565b3480156109eb57600080fd5b506103756109fa366004613628565b6124e2565b348015610a0b57600080fd5b506103e5612618565b348015610a2057600080fd5b50610375610a2f36600461351c565b6126a6565b348015610a4057600080fd5b506108137f000000000000000000000000000000000000000000000000000000000000000081565b348015610a7457600080fd5b5061038d610a8336600461365a565b61279a565b348015610a9457600080fd5b5061038d61271081565b348015610aaa57600080fd5b5061038d6301e1338081565b348015610ac257600080fd5b50610375610ad136600461351c565b6127c5565b348015610ae257600080fd5b5061038d610af136600461351c565b612885565b348015610b0257600080fd5b50610375610b1136600461351c565b6128bb565b610375610b2436600461351c565b612958565b348015610b3557600080fd5b5061038d612ab9565b348015610b4a57600080fd5b5061038d610b5936600461351c565b612b76565b348015610b6a57600080fd5b506103e560405180604001604052806006815260200165076312e302e360d41b81525081565b610b98612bcd565b610ba0612c26565b60008211610bc95760405162461bcd60e51b8152600401610bc090613684565b60405180910390fd5b610bd233611909565b821115610bf15760405162461bcd60e51b8152600401610bc0906136a8565b81600a541015610c3c5760405162461bcd60e51b8152602060048201526016602482015275496e73756666696369656e74506f6f6c41737365747360501b6044820152606401610bc0565b6000610c466113bc565b610c58670de0b6b3a7640000856136eb565b610c629190613702565b905082471015610cb25760405162461bcd60e51b815260206004820152601b60248201527a496e73756666696369656e74436f6e747261637442616c616e636560281b6044820152606401610bc0565b82600a6000828254610cc49190613724565b90915550610cd490503382612de6565b610cde8284612f2b565b60408051848152602081018390526001600160a01b0384169133917ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567910160405180910390a350610d2e60018055565b5050565b6000670de0b6b3a7640000610d4683612b76565b1092915050565b606060058054610d5c90613737565b80601f0160208091040260200160405190810160405280929190818152602001828054610d8890613737565b8015610dd55780601f10610daa57610100808354040283529160200191610dd5565b820191906000526020600020905b815481529060010190602001808311610db857829003601f168201915b5050505050905090565b600033610ded818585612f81565b60019150505b92915050565b600080610e04612ab9565b90506000610e106130a5565b90506000610e1c6115ee565b90506000610e2c83612710613724565b9050610e3a612710806136eb565b81610e4584876136eb565b610e4f91906136eb565b610e599190613702565b94505050505090565b6001600160a01b0382166000908152600e6020526040812054821115610e9a5760405162461bcd60e51b8152600401610bc090613771565b670de0b6b3a7640000610eab61198d565b610eb590846136eb565b610ebf9190613702565b9392505050565b606060405180604001604052806006815260200165076312e302e360d41b8152506008604051602001610efa9291906137a2565b604051602081830303815290604052905090565b33610f17611bda565b6001600160a01b031614610f3d5760405162461bcd60e51b8152600401610bc090613865565b6126ac811115610f7f5760405162461bcd60e51b815260206004820152600d60248201526c098a8ac8af0c6cacac8e69ac2f609b1b6044820152606401610bc0565b60128190556040518181527f425ae18279be27670b7d1aca7ed22f09b42d8987aa5f7f2daa207a1b40344dc4906020015b60405180910390a150565b33610fc4611bda565b6001600160a01b031614610fea5760405162461bcd60e51b8152600401610bc090613865565b610ff2612c26565b6111948111156110345760405162461bcd60e51b815260206004820152600d60248201526c08ccaca8af0c6cacac8e69ac2f609b1b6044820152606401610bc0565b60188190556040518181527fc29a54be08294e48a4890512ce07766ec561083c51666bb17db641006638afaf90602001610fb0565b6000670de0b6b3a764000061107c6113bc565b601c5461108991906136eb565b6110939190613702565b905090565b6000806110a36113bc565b6110b5670de0b6b3a7640000856136eb565b6110bf9190613702565b6001600160a01b0386166000908152601b60205260409020549091508111156110fa5760405162461bcd60e51b8152600401610bc0906136a8565b6000611106863361279a565b9050838110156111505760405162461bcd60e51b8152602060048201526015602482015274496e73756666696369656e74416c6c6f77616e636560581b6044820152606401610bc0565b611164863361115f8785613724565b612f81565b6001600160a01b0386166000908152601b60205260408120805484929061118c908490613724565b90915550506001600160a01b0385166000908152601b6020526040812080548492906111b990849061389a565b92505081905550846001600160a01b0316866001600160a01b031660008051602061394b833981519152866040516111f391815260200190565b60405180910390a350600195945050505050565b33611210611bda565b6001600160a01b0316146112365760405162461bcd60e51b8152600401610bc090613865565b600a548110156112835760405162461bcd60e51b81526020600482015260186024820152774e65774d617842656c6f7743757272656e7441737365747360401b6044820152606401610bc0565b600c8190556040518181527f777c9c2fe9518de2709d793343a1dab8140fbeebb3d0865418ccae3bda81c92a90602001610fb0565b6000806112c48361313a565b905060006112d06131d1565b9050670de0b6b3a76400006112e582846136eb565b6112ef9190613702565b949350505050565b33611300611bda565b6001600160a01b0316146113265760405162461bcd60e51b8152600401610bc090613865565b6103e881111561136a5760405162461bcd60e51b815260206004820152600f60248201526e084dedceae68af0c6cacac8e69ac2f608b1b6044820152606401610bc0565b601a8190556040518181527f3f2e00bbe653f6beb30377cecbddb09eeaf1c2c60e50aca27764412d39d1592d90602001610fb0565b600033610ded8185856113b2838361279a565b61115f919061389a565b6000601c546000036113d55750670de0b6b3a764000090565b60006113df611bcd565b600a54909150811561142b5760006113f56130a5565b9050600061271061140683866136eb565b6114109190613702565b905061141c8185613724565b611426908461389a565b925050505b601c54611440670de0b6b3a7640000836136eb565b61144a9190613702565b9250505090565b611459612bcd565b611461612c26565b600034116114815760405162461bcd60e51b8152600401610bc090613684565b336000908152600e6020526040902054806114c75760405162461bcd60e51b8152602060048201526006602482015265139bd119589d60d21b6044820152606401610bc0565b600f546000670de0b6b3a76400006114df83856136eb565b6114e99190613702565b905060008134116114fa57346114fc565b815b90508181036115315783600960008282546115179190613724565b9091555050336000908152600e602052604081205561158f565b600083611546670de0b6b3a7640000846136eb565b6115509190613702565b905080600960008282546115649190613724565b9091555050336000908152600e602052604081208054839290611588908490613724565b9091555050505b813411156115aa576115aa336115a58434613724565b612f2b565b60405181815233907f5c16de4f8b59bd9caf0f49a545f25819a895ed223294290b408242e72a5942319060200160405180910390a2505050506115ec60018055565b565b6000600a546000036116005750600090565b6000670de0b6b3a7640000600f5460095461161b91906136eb565b6116259190613702565b90506000600a546127108361163a91906136eb565b6116449190613702565b90506127108111611655578061144a565b6127109250505090565b33611668611bda565b6001600160a01b03161461168e5760405162461bcd60e51b8152600401610bc090613865565b611696612c26565b600082116116b65760405162461bcd60e51b8152600401610bc090613684565b60006116c0611da5565b9050808311156117085760405162461bcd60e51b8152602060048201526013602482015272416d6f756e744578636565647345786365737360681b6044820152606401610bc0565b6117128284612f2b565b816001600160a01b03167f817c5912299b2d8eea4d9429e557c7b42c96a31499b4229932d1f070f068e37a8460405161174d91815260200190565b60405180910390a2505050565b60008061176683612885565b6001600160a01b0384166000908152600d60205260408120549192509061178c906112b8565b905047600083831161179f5760006117a9565b6117a98484613724565b90508181106117b857816117ba565b805b9695505050505050565b6117cc612bcd565b6117d4612c26565b600081116117f45760405162461bcd60e51b8152600401610bc090613684565b336000908152600d6020526040902054818110156118245760405162461bcd60e51b8152600401610bc0906138ad565b60006118308383613724565b9050600061183d33612885565b9050611848826112b8565b8111156118675760405162461bcd60e51b8152600401610bc0906138ad565b336000908152600d60205260408120839055600b805486929061188b908490613724565b909155506118c590506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633866131ec565b60405184815233907fa8e76b822fc682be77f3b1c822ea81f6bda5aed92ba82e6873bfd889f328d1d29060200160405180910390a250505061190660018055565b50565b6000670de0b6b3a764000061191c6113bc565b6001600160a01b0384166000908152601b602052604090205461193f91906136eb565b610df39190613702565b33611952611bda565b6001600160a01b0316146119785760405162461bcd60e51b8152600401610bc090613865565b6115ec6000613294565b6000610df3826132e4565b60006009546000036119a65750670de0b6b3a764000090565b6000601154426119b69190613724565b9050806000036119c8575050600f5490565b60006119d2612ab9565b905060006127106119eb670de0b6b3a7640000846136eb565b6119f59190613702565b905060006301e13380611a0885846136eb565b611a129190613702565b9050670de0b6b3a764000081600f54611a2b91906136eb565b611a359190613702565b600f54610e59919061389a565b600080611a4e83612885565b905080600003611a755750506001600160a01b03166000908152600d602052604090205490565b6001600160a01b0383166000908152600d602052604081205490819003611aa0575060009392505050565b6000611aaa6131d1565b905080600003611abf57506000949350505050565b600081611ad4670de0b6b3a7640000866136eb565b611ade9190613702565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166399530b066040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b6491906138dd565b905060008111611b865760405162461bcd60e51b8152600401610bc0906138f6565b600081611b9b670de0b6b3a7640000856136eb565b611ba59190613702565b905084811015611bbe57611bb98186613724565b611bc1565b60005b98975050505050505050565b6000611093600954613306565b6000546001600160a01b031690565b606060068054610d5c90613737565b6001600160a01b0381166000908152600e6020526040812054610df390613306565b600080611c2683611909565b905047808210611c3657806112ef565b5092915050565b60003381611c4b828661279a565b905083811015611cab5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610bc0565b611cb88286868403612f81565b506001949350505050565b600080611cce6113bc565b611ce0670de0b6b3a7640000856136eb565b611cea9190613702565b336000908152601b6020526040902054909150811115611d1c5760405162461bcd60e51b8152600401610bc0906136a8565b336000908152601b602052604081208054839290611d3b908490613724565b90915550506001600160a01b0384166000908152601b602052604081208054839290611d6890849061389a565b90915550506040518381526001600160a01b03851690339060008051602061394b8339815191529060200160405180910390a35060019392505050565b600080670de0b6b3a7640000611db961198d565b600954611dc691906136eb565b611dd09190613702565b9050600081600a5411611de4576000611df2565b81600a54611df29190613724565b90506000611dfe611bcd565b601754611e0b908461389a565b611e15919061389a565b9050804711611e25576000611e2f565b611e2f8147613724565b935050505090565b611e3f612bcd565b611e47612c26565b60008111611e675760405162461bcd60e51b8152600401610bc090613684565b611e9c6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633308461335a565b336000908152600d602052604081208054839290611ebb90849061389a565b9250508190555080600b6000828254611ed4919061389a565b909155505060405181815233907f8c66c9368f6312eb21bf56acbd3a20448f7d1589d024c30482c3d0a33125523e9060200160405180910390a261190660018055565b6019546001600160a01b03163314611f615760405162461bcd60e51b815260206004820152600d60248201526c2737ba2634b8bab4b230ba37b960991b6044820152606401610bc0565b611f69612bcd565b611f71612c26565b611f7a82610d32565b611fc05760405162461bcd60e51b8152602060048201526017602482015276506f736974696f6e4e6f744c6971756964617461626c6560481b6044820152606401610bc0565b600081118015611fe857506001600160a01b0382166000908152600e60205260409020548111155b6120045760405162461bcd60e51b8152600401610bc090613771565b6000670de0b6b3a764000061201761198d565b61202190846136eb565b61202b9190613702565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166399530b066040518163ffffffff1660e01b8152600401602060405180830381865afa15801561208d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120b191906138dd565b9050600081116120d35760405162461bcd60e51b8152600401610bc0906138f6565b6001600160a01b0384166000908152600d6020526040812054670de0b6b3a7640000906121019084906136eb565b61210b9190613702565b90506000612710601a548561212091906136eb565b61212a9190613702565b9050600084831161213c576000612146565b6121468584613724565b90508082116121555781612157565b805b91506000612165838761389a565b9050838111156121b55760405162461bcd60e51b815260206004820152601b60248201527a496e73756666696369656e74436f6c6c61746572616c56616c756560281b6044820152606401610bc0565b6000856121ca670de0b6b3a7640000846136eb565b6121d49190613702565b6001600160a01b038a166000908152600d602052604090205490915081111561223e5760405162461bcd60e51b815260206004820152601c60248201527b496e73756666696369656e74436f6c6c61746572616c53686172657360201b6044820152606401610bc0565b863410156122835760405162461bcd60e51b8152602060048201526012602482015271125b9cdd59999a58da595b9d105b5bdd5b9d60721b6044820152606401610bc0565b863411156122a55760006122978834613724565b90506122a33382612f2b565b505b6001600160a01b0389166000908152600e6020526040812080548a92906122cd908490613724565b9250508190555087600960008282546122e69190613724565b90915550506001600160a01b0389166000908152600d602052604081208054839290612313908490613724565b9250508190555080600b600082825461232c9190613724565b9091555061236690506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633836131ec565b604080518881526020810183905233916001600160a01b038c16917f4ecae3269f800df64b16cb9f6f8b0b507018888521d1cff0841823e44bc0b00d910160405180910390a350505050505050610d2e60018055565b6123c4612bcd565b6123cc612c26565b600081116123ec5760405162461bcd60e51b8152600401610bc090613684565b8047101561240c5760405162461bcd60e51b8152600401610bc0906136a8565b6124153361175a565b8111156124345760405162461bcd60e51b8152600401610bc0906138ad565b600f5460009061244c670de0b6b3a7640000846136eb565b6124569190613702565b336000908152600e602052604081208054929350839290919061247a90849061389a565b925050819055508060096000828254612493919061389a565b909155506124a390503383612f2b565b60405182815233907fcbc04eca7e9da35cb1393a6135a199ca52e450d5e9251cbd99f7847d33a367509060200160405180910390a25061190660018055565b336124eb611bda565b6001600160a01b0316146125115760405162461bcd60e51b8152600401610bc090613865565b612519612c26565b8284111580156125295750818311155b6125685760405162461bcd60e51b815260206004820152601060248201526f24b73b30b634b22930ba32a7b93232b960811b6044820152606401610bc0565b600081118015612579575061271081105b6125c05760405162461bcd60e51b815260206004820152601860248201527724b73b30b634b22b32b93a32bc2aba34b634bd30ba34b7b760411b6044820152606401610bc0565b60138490556014839055601582905560168190556040517f1de93074d10c2db97d8f7408c79e46e730dc195fbce690a9284b3d7d82f1431a9061260a90869086908690869061360d565b60405180910390a150505050565b6008805461262590613737565b80601f016020809104026020016040519081016040528092919081815260200182805461265190613737565b801561269e5780601f106126735761010080835404028352916020019161269e565b820191906000526020600020905b81548152906001019060200180831161268157829003601f168201915b505050505081565b336126af611bda565b6001600160a01b0316146126d55760405162461bcd60e51b8152600401610bc090613865565b6126dd612c26565b6000601754116127215760405162461bcd60e51b815260206004820152600f60248201526e139bd199595cd51bd0dbdb1b1958dd608a1b6044820152606401610bc0565b6000479050600081601754116127395760175461273b565b815b9050806017600082825461274f9190613724565b9091555061275f90508382612f2b565b826001600160a01b03167f4fb36193ec6d40f372342e3af9ed712452955eb5f5264b82f735d3aa02cb5b1c8260405161174d91815260200190565b6001600160a01b03918216600090815260036020908152604080832093909416825291909152205490565b336127ce611bda565b6001600160a01b0316146127f45760405162461bcd60e51b8152600401610bc090613865565b6001600160a01b03811661283b5760405162461bcd60e51b815260206004820152600e60248201526d496e76616c69644164647265737360901b6044820152606401610bc0565b601980546001600160a01b0319166001600160a01b0383169081179091556040517f9d28480b7af53e0edce895a64e65347ebb01bde60bb65af97c46f78d6d89cafe90600090a250565b6000670de0b6b3a764000061289861198d565b6001600160a01b0384166000908152600e602052604090205461193f91906136eb565b336128c4611bda565b6001600160a01b0316146128ea5760405162461bcd60e51b8152600401610bc090613865565b6001600160a01b03811661294f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610bc0565b61190681613294565b612960612bcd565b612968612c26565b600034116129885760405162461bcd60e51b8152600401610bc090613684565b600c5434600a54612999919061389a565b11156129da5760405162461bcd60e51b815260206004820152601060248201526f0457863656564734173736574734361760841b6044820152606401610bc0565b60006129e46113bc565b6129f6670de0b6b3a7640000346136eb565b612a009190613702565b905060008111612a475760405162461bcd60e51b8152602060048201526012602482015271496e73756666696369656e7453686172657360701b6044820152606401610bc0565b34600a6000828254612a59919061389a565b90915550612a699050828261340e565b60408051348152602081018390526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a35061190660018055565b600080612ac46115ee565b90506016548103612ad757505060145490565b601654811015612b1a576000601354601454612af39190613724565b601654909150612b0382846136eb565b612b0d9190613702565b60135461144a919061389a565b6000601454601554612b2c9190613724565b9050600060165483612b3e9190613724565b90506000601654612710612b529190613724565b905080612b5f84846136eb565b612b699190613702565b601454610e59919061389a565b600080612b8283612885565b905080600003612b96575060001992915050565b6000612ba1846132e4565b90506000612bad6131d1565b905082612bba82846136eb565b612bc49190613702565b95945050505050565b600260015403612c1f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610bc0565b6002600155565b600060115442612c369190613724565b9050600081118015612c4a57506000600954115b15611906576000612c59612ab9565b9050600061271060105483612c6e91906136eb565b612c789190613702565b90506000670de0b6b3a7640000600f54600954612c9591906136eb565b612c9f9190613702565b905060006010546301e13380612cb591906136eb565b85612cc085856136eb565b612cca91906136eb565b612cd49190613702565b9050600082612ceb670de0b6b3a7640000846136eb565b612cf59190613702565b9050670de0b6b3a764000081600f54612d0e91906136eb565b612d189190613702565b600f6000828254612d29919061389a565b9091555060009050612d396130a5565b90506000612710612d4a83866136eb565b612d549190613702565b90506000612d628286613724565b905080600a6000828254612d76919061389a565b925050819055508160176000828254612d8f919061389a565b909155505042601155600f54604080519182526020820183905281018390527f1f42ca9fe8250d2ef16b6a18292adc74cf5a88f41875baabc5c247098c4b1e3f9060600160405180910390a1505050505050505050565b6001600160a01b038216612e465760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610bc0565b6001600160a01b0382166000908152601b6020526040902054811115612eb95760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610bc0565b601c805482900390556001600160a01b0382166000818152601b60205260408120805484900390559060008051602061394b833981519152670de0b6b3a7640000612f026113bc565b612f0c90866136eb565b612f169190613702565b60405190815260200160405180910390a35050565b600080600080600085875af1905080612f7c5760405162461bcd60e51b815260206004820152601360248201527211551217d514905394d1915497d19052531151606a1b6044820152606401610bc0565b505050565b6001600160a01b038316612fe35760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610bc0565b6001600160a01b0382166130445760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610bc0565b6001600160a01b0383811660008181526003602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6000806130b06115ee565b905060165481116130c357505060185490565b600060185460026130d491906136eb565b90506000601854826130e69190613724565b90506000601654846130f89190613724565b9050600060165461271061310c9190613724565b90508061311984846136eb565b6131239190613702565b601854613130919061389a565b9550505050505090565b6000670de0b6b3a76400007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166399530b066040518163ffffffff1660e01b8152600401602060405180830381865afa1580156131a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131c791906138dd565b61193f90846136eb565b6000612710670de0b6b3a764000060125461108991906136eb565b826001600160a01b03163b6000036132165760405162461bcd60e51b8152600401610bc09061391c565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061328e5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610bc0565b50505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b0381166000908152600d6020526040812054610df39061313a565b60008160000361331857506000919050565b6000670de0b6b3a764000061332b61198d565b61333590856136eb565b61333f9190613702565b9050828082116133505760006112ef565b6112ef8183613724565b836001600160a01b03163b6000036133845760405162461bcd60e51b8152600401610bc09061391c565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806134075760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610bc0565b5050505050565b6001600160a01b0382166134645760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610bc0565b80601c6000828254613476919061389a565b90915550506001600160a01b0382166000908152601b6020526040812080548392906134a390849061389a565b90915550506001600160a01b038216600060008051602061394b833981519152670de0b6b3a7640000612f026113bc565b80356001600160a01b03811681146134eb57600080fd5b919050565b6000806040838503121561350357600080fd5b82359150613513602084016134d4565b90509250929050565b60006020828403121561352e57600080fd5b610ebf826134d4565b60005b8381101561355257818101518382015260200161353a565b50506000910152565b602081526000825180602084015261357a816040850160208701613537565b601f01601f19169190910160400192915050565b600080604083850312156135a157600080fd5b6135aa836134d4565b946020939093013593505050565b6000602082840312156135ca57600080fd5b5035919050565b6000806000606084860312156135e657600080fd5b6135ef846134d4565b92506135fd602085016134d4565b9150604084013590509250925092565b93845260208401929092526040830152606082015260800190565b6000806000806080858703121561363e57600080fd5b5050823594602084013594506040840135936060013592509050565b6000806040838503121561366d57600080fd5b613676836134d4565b9150613513602084016134d4565b6020808252600a908201526916995c9bd05b5bdd5b9d60b21b604082015260600190565b602080825260139082015272496e73756666696369656e7442616c616e636560681b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610df357610df36136d5565b60008261371f57634e487b7160e01b600052601260045260246000fd5b500490565b81810381811115610df357610df36136d5565b600181811c9082168061374b57607f821691505b60208210810361376b57634e487b7160e01b600052602260045260246000fd5b50919050565b602080825260179082015276125b9d985b1a591119589d14da185c995cd05b5bdd5b9d604a1b604082015260600190565b6000835160206137b58285838901613537565b601d60f91b918401918252845460019060009080831c818416806137da57607f821691505b85821081036137f757634e487b7160e01b84526022600452602484fd5b80801561380b576001811461382457613855565b60ff198416888701528215158302880186019450613855565b60008b81526020902060005b8481101561384b5781548a8201890152908701908801613830565b5050858389010194505b50929a9950505050505050505050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b80820180821115610df357610df36136d5565b602080825260169082015275125b9cdd59999a58da595b9d10dbdb1b185d195c985b60521b604082015260600190565b6000602082840312156138ef57600080fd5b5051919050565b6020808252600c908201526b496e76616c6964507269636560a01b604082015260600190565b6020808252601490820152731d1bdad95b88191bd95cc81b9bdd08195e1a5cdd60621b60408201526060019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122013706cdb2fa1b35465818679a3ae056c59e304a06e2bbcba2ab57bbde170a7d964736f6c634300081500330000000000000000000000008f0ecda9679ad16e30be3d83d183c482821f5325
Deployed ByteCode
0x6080604052600436106103505760003560e01c8063778fd81e116101b9578063778fd81e146107725780637b7c1f4c1461078757806381bf8d3d1461079d5780638339918e146107b3578063855c55f3146107d357806387309f8b146107e85780638da5cb5b146107fe57806390a8ae9b146106b557806395d89b411461082b5780639e1b99f914610840578063a1df3b4014610860578063a3371d1214610876578063a457c2d714610896578063a9059cbb146108b6578063ad3a8537146108d6578063afc50c2f146108ec578063b6f7132f14610901578063b72b5efd14610937578063b92a49721461094d578063bad4a01f14610976578063bcbaf48714610996578063c5ebeaec146109a9578063cd4a4861146109c9578063d23c7aed146109df578063d5d8cf4a146109ff578063d7d6cc4714610a14578063d8dfeb4514610a34578063dd62ed3e14610a68578063e1a4521814610a88578063e6a69ab814610a9e578063eb2313a414610ab6578063f2d6e5d514610ad6578063f2fde38b14610af6578063f340fa0114610b16578063f36b242514610b29578063f9d6c03b14610b3e578063ffa1ad7414610b5e57600080fd5b8062f714ce1461035557806301e1d11414610377578063042e02cf146103a057806306fdde03146103d0578063095ea7b3146103f25780630be56d34146104125780630d6a4077146104275780630d8e6e2c146104475780631326851e1461045c57806313f9cca21461047c57806314bcec9f1461049c57806318160ddd146104b25780631b0187f9146104c75780631b113dfb146104dd5780631b859e41146104f357806323b872dd146105095780632878fff214610529578063313ce5671461054957806332319fda1461056b578063361416e11461058b57806336fde70b146105a157806339509351146105c15780633d68175c146105e1578063402d8883146105f65780634a417a53146105fe5780634ac8eb5f146106135780634e6657bc146106295780634eea48e21461063f5780635b1e9c111461065f5780635f0cf1bc1461067f578063606ae9ea146106b55780636112fe2e146106d1578063683dd191146106f15780636fb49d731461070757806370a082311461071d578063715018a61461073d57806375417da614610752575b600080fd5b34801561036157600080fd5b506103756103703660046134f0565b610b90565b005b34801561038357600080fd5b5061038d600a5481565b6040519081526020015b60405180910390f35b3480156103ac57600080fd5b506103c06103bb36600461351c565b610d32565b6040519015158152602001610397565b3480156103dc57600080fd5b506103e5610d4d565b604051610397919061355b565b3480156103fe57600080fd5b506103c061040d36600461358e565b610ddf565b34801561041e57600080fd5b5061038d610df9565b34801561043357600080fd5b5061038d61044236600461358e565b610e62565b34801561045357600080fd5b506103e5610ec6565b34801561046857600080fd5b506103756104773660046135b8565b610f0e565b34801561048857600080fd5b506103756104973660046135b8565b610fbb565b3480156104a857600080fd5b5061038d60115481565b3480156104be57600080fd5b5061038d611069565b3480156104d357600080fd5b5061038d60165481565b3480156104e957600080fd5b5061038d60145481565b3480156104ff57600080fd5b5061038d60095481565b34801561051557600080fd5b506103c06105243660046135d1565b611098565b34801561053557600080fd5b506103756105443660046135b8565b611207565b34801561055557600080fd5b5060075460405160ff9091168152602001610397565b34801561057757600080fd5b5061038d6105863660046135b8565b6112b8565b34801561059757600080fd5b5061038d60185481565b3480156105ad57600080fd5b506103756105bc3660046135b8565b6112f7565b3480156105cd57600080fd5b506103c06105dc36600461358e565b61139f565b3480156105ed57600080fd5b5061038d6113bc565b610375611451565b34801561060a57600080fd5b5061038d6115ee565b34801561061f57600080fd5b5061038d600b5481565b34801561063557600080fd5b5061038d61119481565b34801561064b57600080fd5b5061037561065a3660046134f0565b61165f565b34801561066b57600080fd5b5061038d61067a36600461351c565b61175a565b34801561068b57600080fd5b5061038d61069a36600461351c565b6001600160a01b03166000908152600e602052604090205490565b3480156106c157600080fd5b5061038d670de0b6b3a764000081565b3480156106dd57600080fd5b506103756106ec3660046135b8565b6117c4565b3480156106fd57600080fd5b5061038d60105481565b34801561071357600080fd5b5061038d60125481565b34801561072957600080fd5b5061038d61073836600461351c565b611909565b34801561074957600080fd5b50610375611949565b34801561075e57600080fd5b5061038d61076d36600461351c565b611982565b34801561077e57600080fd5b5061038d61198d565b34801561079357600080fd5b5061038d601a5481565b3480156107a957600080fd5b5061038d6126ac81565b3480156107bf57600080fd5b5061038d6107ce36600461351c565b611a42565b3480156107df57600080fd5b5061038d611bcd565b3480156107f457600080fd5b5061038d6103e881565b34801561080a57600080fd5b50610813611bda565b6040516001600160a01b039091168152602001610397565b34801561083757600080fd5b506103e5611be9565b34801561084c57600080fd5b5061038d61085b36600461351c565b611bf8565b34801561086c57600080fd5b5061038d60175481565b34801561088257600080fd5b5061038d61089136600461351c565b611c1a565b3480156108a257600080fd5b506103c06108b136600461358e565b611c3d565b3480156108c257600080fd5b506103c06108d136600461358e565b611cc3565b3480156108e257600080fd5b5061038d60155481565b3480156108f857600080fd5b5061038d611da5565b34801561090d57600080fd5b5061038d61091c36600461351c565b6001600160a01b03166000908152600d602052604090205490565b34801561094357600080fd5b5061038d60135481565b34801561095957600080fd5b50601354601454601554601654604051610397949392919061360d565b34801561098257600080fd5b506103756109913660046135b8565b611e37565b6103756109a436600461358e565b611f17565b3480156109b557600080fd5b506103756109c43660046135b8565b6123bc565b3480156109d557600080fd5b5061038d600c5481565b3480156109eb57600080fd5b506103756109fa366004613628565b6124e2565b348015610a0b57600080fd5b506103e5612618565b348015610a2057600080fd5b50610375610a2f36600461351c565b6126a6565b348015610a4057600080fd5b506108137f0000000000000000000000008f0ecda9679ad16e30be3d83d183c482821f532581565b348015610a7457600080fd5b5061038d610a8336600461365a565b61279a565b348015610a9457600080fd5b5061038d61271081565b348015610aaa57600080fd5b5061038d6301e1338081565b348015610ac257600080fd5b50610375610ad136600461351c565b6127c5565b348015610ae257600080fd5b5061038d610af136600461351c565b612885565b348015610b0257600080fd5b50610375610b1136600461351c565b6128bb565b610375610b2436600461351c565b612958565b348015610b3557600080fd5b5061038d612ab9565b348015610b4a57600080fd5b5061038d610b5936600461351c565b612b76565b348015610b6a57600080fd5b506103e560405180604001604052806006815260200165076312e302e360d41b81525081565b610b98612bcd565b610ba0612c26565b60008211610bc95760405162461bcd60e51b8152600401610bc090613684565b60405180910390fd5b610bd233611909565b821115610bf15760405162461bcd60e51b8152600401610bc0906136a8565b81600a541015610c3c5760405162461bcd60e51b8152602060048201526016602482015275496e73756666696369656e74506f6f6c41737365747360501b6044820152606401610bc0565b6000610c466113bc565b610c58670de0b6b3a7640000856136eb565b610c629190613702565b905082471015610cb25760405162461bcd60e51b815260206004820152601b60248201527a496e73756666696369656e74436f6e747261637442616c616e636560281b6044820152606401610bc0565b82600a6000828254610cc49190613724565b90915550610cd490503382612de6565b610cde8284612f2b565b60408051848152602081018390526001600160a01b0384169133917ff341246adaac6f497bc2a656f546ab9e182111d630394f0c57c710a59a2cb567910160405180910390a350610d2e60018055565b5050565b6000670de0b6b3a7640000610d4683612b76565b1092915050565b606060058054610d5c90613737565b80601f0160208091040260200160405190810160405280929190818152602001828054610d8890613737565b8015610dd55780601f10610daa57610100808354040283529160200191610dd5565b820191906000526020600020905b815481529060010190602001808311610db857829003601f168201915b5050505050905090565b600033610ded818585612f81565b60019150505b92915050565b600080610e04612ab9565b90506000610e106130a5565b90506000610e1c6115ee565b90506000610e2c83612710613724565b9050610e3a612710806136eb565b81610e4584876136eb565b610e4f91906136eb565b610e599190613702565b94505050505090565b6001600160a01b0382166000908152600e6020526040812054821115610e9a5760405162461bcd60e51b8152600401610bc090613771565b670de0b6b3a7640000610eab61198d565b610eb590846136eb565b610ebf9190613702565b9392505050565b606060405180604001604052806006815260200165076312e302e360d41b8152506008604051602001610efa9291906137a2565b604051602081830303815290604052905090565b33610f17611bda565b6001600160a01b031614610f3d5760405162461bcd60e51b8152600401610bc090613865565b6126ac811115610f7f5760405162461bcd60e51b815260206004820152600d60248201526c098a8ac8af0c6cacac8e69ac2f609b1b6044820152606401610bc0565b60128190556040518181527f425ae18279be27670b7d1aca7ed22f09b42d8987aa5f7f2daa207a1b40344dc4906020015b60405180910390a150565b33610fc4611bda565b6001600160a01b031614610fea5760405162461bcd60e51b8152600401610bc090613865565b610ff2612c26565b6111948111156110345760405162461bcd60e51b815260206004820152600d60248201526c08ccaca8af0c6cacac8e69ac2f609b1b6044820152606401610bc0565b60188190556040518181527fc29a54be08294e48a4890512ce07766ec561083c51666bb17db641006638afaf90602001610fb0565b6000670de0b6b3a764000061107c6113bc565b601c5461108991906136eb565b6110939190613702565b905090565b6000806110a36113bc565b6110b5670de0b6b3a7640000856136eb565b6110bf9190613702565b6001600160a01b0386166000908152601b60205260409020549091508111156110fa5760405162461bcd60e51b8152600401610bc0906136a8565b6000611106863361279a565b9050838110156111505760405162461bcd60e51b8152602060048201526015602482015274496e73756666696369656e74416c6c6f77616e636560581b6044820152606401610bc0565b611164863361115f8785613724565b612f81565b6001600160a01b0386166000908152601b60205260408120805484929061118c908490613724565b90915550506001600160a01b0385166000908152601b6020526040812080548492906111b990849061389a565b92505081905550846001600160a01b0316866001600160a01b031660008051602061394b833981519152866040516111f391815260200190565b60405180910390a350600195945050505050565b33611210611bda565b6001600160a01b0316146112365760405162461bcd60e51b8152600401610bc090613865565b600a548110156112835760405162461bcd60e51b81526020600482015260186024820152774e65774d617842656c6f7743757272656e7441737365747360401b6044820152606401610bc0565b600c8190556040518181527f777c9c2fe9518de2709d793343a1dab8140fbeebb3d0865418ccae3bda81c92a90602001610fb0565b6000806112c48361313a565b905060006112d06131d1565b9050670de0b6b3a76400006112e582846136eb565b6112ef9190613702565b949350505050565b33611300611bda565b6001600160a01b0316146113265760405162461bcd60e51b8152600401610bc090613865565b6103e881111561136a5760405162461bcd60e51b815260206004820152600f60248201526e084dedceae68af0c6cacac8e69ac2f608b1b6044820152606401610bc0565b601a8190556040518181527f3f2e00bbe653f6beb30377cecbddb09eeaf1c2c60e50aca27764412d39d1592d90602001610fb0565b600033610ded8185856113b2838361279a565b61115f919061389a565b6000601c546000036113d55750670de0b6b3a764000090565b60006113df611bcd565b600a54909150811561142b5760006113f56130a5565b9050600061271061140683866136eb565b6114109190613702565b905061141c8185613724565b611426908461389a565b925050505b601c54611440670de0b6b3a7640000836136eb565b61144a9190613702565b9250505090565b611459612bcd565b611461612c26565b600034116114815760405162461bcd60e51b8152600401610bc090613684565b336000908152600e6020526040902054806114c75760405162461bcd60e51b8152602060048201526006602482015265139bd119589d60d21b6044820152606401610bc0565b600f546000670de0b6b3a76400006114df83856136eb565b6114e99190613702565b905060008134116114fa57346114fc565b815b90508181036115315783600960008282546115179190613724565b9091555050336000908152600e602052604081205561158f565b600083611546670de0b6b3a7640000846136eb565b6115509190613702565b905080600960008282546115649190613724565b9091555050336000908152600e602052604081208054839290611588908490613724565b9091555050505b813411156115aa576115aa336115a58434613724565b612f2b565b60405181815233907f5c16de4f8b59bd9caf0f49a545f25819a895ed223294290b408242e72a5942319060200160405180910390a2505050506115ec60018055565b565b6000600a546000036116005750600090565b6000670de0b6b3a7640000600f5460095461161b91906136eb565b6116259190613702565b90506000600a546127108361163a91906136eb565b6116449190613702565b90506127108111611655578061144a565b6127109250505090565b33611668611bda565b6001600160a01b03161461168e5760405162461bcd60e51b8152600401610bc090613865565b611696612c26565b600082116116b65760405162461bcd60e51b8152600401610bc090613684565b60006116c0611da5565b9050808311156117085760405162461bcd60e51b8152602060048201526013602482015272416d6f756e744578636565647345786365737360681b6044820152606401610bc0565b6117128284612f2b565b816001600160a01b03167f817c5912299b2d8eea4d9429e557c7b42c96a31499b4229932d1f070f068e37a8460405161174d91815260200190565b60405180910390a2505050565b60008061176683612885565b6001600160a01b0384166000908152600d60205260408120549192509061178c906112b8565b905047600083831161179f5760006117a9565b6117a98484613724565b90508181106117b857816117ba565b805b9695505050505050565b6117cc612bcd565b6117d4612c26565b600081116117f45760405162461bcd60e51b8152600401610bc090613684565b336000908152600d6020526040902054818110156118245760405162461bcd60e51b8152600401610bc0906138ad565b60006118308383613724565b9050600061183d33612885565b9050611848826112b8565b8111156118675760405162461bcd60e51b8152600401610bc0906138ad565b336000908152600d60205260408120839055600b805486929061188b908490613724565b909155506118c590506001600160a01b037f0000000000000000000000008f0ecda9679ad16e30be3d83d183c482821f53251633866131ec565b60405184815233907fa8e76b822fc682be77f3b1c822ea81f6bda5aed92ba82e6873bfd889f328d1d29060200160405180910390a250505061190660018055565b50565b6000670de0b6b3a764000061191c6113bc565b6001600160a01b0384166000908152601b602052604090205461193f91906136eb565b610df39190613702565b33611952611bda565b6001600160a01b0316146119785760405162461bcd60e51b8152600401610bc090613865565b6115ec6000613294565b6000610df3826132e4565b60006009546000036119a65750670de0b6b3a764000090565b6000601154426119b69190613724565b9050806000036119c8575050600f5490565b60006119d2612ab9565b905060006127106119eb670de0b6b3a7640000846136eb565b6119f59190613702565b905060006301e13380611a0885846136eb565b611a129190613702565b9050670de0b6b3a764000081600f54611a2b91906136eb565b611a359190613702565b600f54610e59919061389a565b600080611a4e83612885565b905080600003611a755750506001600160a01b03166000908152600d602052604090205490565b6001600160a01b0383166000908152600d602052604081205490819003611aa0575060009392505050565b6000611aaa6131d1565b905080600003611abf57506000949350505050565b600081611ad4670de0b6b3a7640000866136eb565b611ade9190613702565b905060007f0000000000000000000000008f0ecda9679ad16e30be3d83d183c482821f53256001600160a01b03166399530b066040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b40573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b6491906138dd565b905060008111611b865760405162461bcd60e51b8152600401610bc0906138f6565b600081611b9b670de0b6b3a7640000856136eb565b611ba59190613702565b905084811015611bbe57611bb98186613724565b611bc1565b60005b98975050505050505050565b6000611093600954613306565b6000546001600160a01b031690565b606060068054610d5c90613737565b6001600160a01b0381166000908152600e6020526040812054610df390613306565b600080611c2683611909565b905047808210611c3657806112ef565b5092915050565b60003381611c4b828661279a565b905083811015611cab5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610bc0565b611cb88286868403612f81565b506001949350505050565b600080611cce6113bc565b611ce0670de0b6b3a7640000856136eb565b611cea9190613702565b336000908152601b6020526040902054909150811115611d1c5760405162461bcd60e51b8152600401610bc0906136a8565b336000908152601b602052604081208054839290611d3b908490613724565b90915550506001600160a01b0384166000908152601b602052604081208054839290611d6890849061389a565b90915550506040518381526001600160a01b03851690339060008051602061394b8339815191529060200160405180910390a35060019392505050565b600080670de0b6b3a7640000611db961198d565b600954611dc691906136eb565b611dd09190613702565b9050600081600a5411611de4576000611df2565b81600a54611df29190613724565b90506000611dfe611bcd565b601754611e0b908461389a565b611e15919061389a565b9050804711611e25576000611e2f565b611e2f8147613724565b935050505090565b611e3f612bcd565b611e47612c26565b60008111611e675760405162461bcd60e51b8152600401610bc090613684565b611e9c6001600160a01b037f0000000000000000000000008f0ecda9679ad16e30be3d83d183c482821f53251633308461335a565b336000908152600d602052604081208054839290611ebb90849061389a565b9250508190555080600b6000828254611ed4919061389a565b909155505060405181815233907f8c66c9368f6312eb21bf56acbd3a20448f7d1589d024c30482c3d0a33125523e9060200160405180910390a261190660018055565b6019546001600160a01b03163314611f615760405162461bcd60e51b815260206004820152600d60248201526c2737ba2634b8bab4b230ba37b960991b6044820152606401610bc0565b611f69612bcd565b611f71612c26565b611f7a82610d32565b611fc05760405162461bcd60e51b8152602060048201526017602482015276506f736974696f6e4e6f744c6971756964617461626c6560481b6044820152606401610bc0565b600081118015611fe857506001600160a01b0382166000908152600e60205260409020548111155b6120045760405162461bcd60e51b8152600401610bc090613771565b6000670de0b6b3a764000061201761198d565b61202190846136eb565b61202b9190613702565b905060007f0000000000000000000000008f0ecda9679ad16e30be3d83d183c482821f53256001600160a01b03166399530b066040518163ffffffff1660e01b8152600401602060405180830381865afa15801561208d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120b191906138dd565b9050600081116120d35760405162461bcd60e51b8152600401610bc0906138f6565b6001600160a01b0384166000908152600d6020526040812054670de0b6b3a7640000906121019084906136eb565b61210b9190613702565b90506000612710601a548561212091906136eb565b61212a9190613702565b9050600084831161213c576000612146565b6121468584613724565b90508082116121555781612157565b805b91506000612165838761389a565b9050838111156121b55760405162461bcd60e51b815260206004820152601b60248201527a496e73756666696369656e74436f6c6c61746572616c56616c756560281b6044820152606401610bc0565b6000856121ca670de0b6b3a7640000846136eb565b6121d49190613702565b6001600160a01b038a166000908152600d602052604090205490915081111561223e5760405162461bcd60e51b815260206004820152601c60248201527b496e73756666696369656e74436f6c6c61746572616c53686172657360201b6044820152606401610bc0565b863410156122835760405162461bcd60e51b8152602060048201526012602482015271125b9cdd59999a58da595b9d105b5bdd5b9d60721b6044820152606401610bc0565b863411156122a55760006122978834613724565b90506122a33382612f2b565b505b6001600160a01b0389166000908152600e6020526040812080548a92906122cd908490613724565b9250508190555087600960008282546122e69190613724565b90915550506001600160a01b0389166000908152600d602052604081208054839290612313908490613724565b9250508190555080600b600082825461232c9190613724565b9091555061236690506001600160a01b037f0000000000000000000000008f0ecda9679ad16e30be3d83d183c482821f53251633836131ec565b604080518881526020810183905233916001600160a01b038c16917f4ecae3269f800df64b16cb9f6f8b0b507018888521d1cff0841823e44bc0b00d910160405180910390a350505050505050610d2e60018055565b6123c4612bcd565b6123cc612c26565b600081116123ec5760405162461bcd60e51b8152600401610bc090613684565b8047101561240c5760405162461bcd60e51b8152600401610bc0906136a8565b6124153361175a565b8111156124345760405162461bcd60e51b8152600401610bc0906138ad565b600f5460009061244c670de0b6b3a7640000846136eb565b6124569190613702565b336000908152600e602052604081208054929350839290919061247a90849061389a565b925050819055508060096000828254612493919061389a565b909155506124a390503383612f2b565b60405182815233907fcbc04eca7e9da35cb1393a6135a199ca52e450d5e9251cbd99f7847d33a367509060200160405180910390a25061190660018055565b336124eb611bda565b6001600160a01b0316146125115760405162461bcd60e51b8152600401610bc090613865565b612519612c26565b8284111580156125295750818311155b6125685760405162461bcd60e51b815260206004820152601060248201526f24b73b30b634b22930ba32a7b93232b960811b6044820152606401610bc0565b600081118015612579575061271081105b6125c05760405162461bcd60e51b815260206004820152601860248201527724b73b30b634b22b32b93a32bc2aba34b634bd30ba34b7b760411b6044820152606401610bc0565b60138490556014839055601582905560168190556040517f1de93074d10c2db97d8f7408c79e46e730dc195fbce690a9284b3d7d82f1431a9061260a90869086908690869061360d565b60405180910390a150505050565b6008805461262590613737565b80601f016020809104026020016040519081016040528092919081815260200182805461265190613737565b801561269e5780601f106126735761010080835404028352916020019161269e565b820191906000526020600020905b81548152906001019060200180831161268157829003601f168201915b505050505081565b336126af611bda565b6001600160a01b0316146126d55760405162461bcd60e51b8152600401610bc090613865565b6126dd612c26565b6000601754116127215760405162461bcd60e51b815260206004820152600f60248201526e139bd199595cd51bd0dbdb1b1958dd608a1b6044820152606401610bc0565b6000479050600081601754116127395760175461273b565b815b9050806017600082825461274f9190613724565b9091555061275f90508382612f2b565b826001600160a01b03167f4fb36193ec6d40f372342e3af9ed712452955eb5f5264b82f735d3aa02cb5b1c8260405161174d91815260200190565b6001600160a01b03918216600090815260036020908152604080832093909416825291909152205490565b336127ce611bda565b6001600160a01b0316146127f45760405162461bcd60e51b8152600401610bc090613865565b6001600160a01b03811661283b5760405162461bcd60e51b815260206004820152600e60248201526d496e76616c69644164647265737360901b6044820152606401610bc0565b601980546001600160a01b0319166001600160a01b0383169081179091556040517f9d28480b7af53e0edce895a64e65347ebb01bde60bb65af97c46f78d6d89cafe90600090a250565b6000670de0b6b3a764000061289861198d565b6001600160a01b0384166000908152600e602052604090205461193f91906136eb565b336128c4611bda565b6001600160a01b0316146128ea5760405162461bcd60e51b8152600401610bc090613865565b6001600160a01b03811661294f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610bc0565b61190681613294565b612960612bcd565b612968612c26565b600034116129885760405162461bcd60e51b8152600401610bc090613684565b600c5434600a54612999919061389a565b11156129da5760405162461bcd60e51b815260206004820152601060248201526f0457863656564734173736574734361760841b6044820152606401610bc0565b60006129e46113bc565b6129f6670de0b6b3a7640000346136eb565b612a009190613702565b905060008111612a475760405162461bcd60e51b8152602060048201526012602482015271496e73756666696369656e7453686172657360701b6044820152606401610bc0565b34600a6000828254612a59919061389a565b90915550612a699050828261340e565b60408051348152602081018390526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a35061190660018055565b600080612ac46115ee565b90506016548103612ad757505060145490565b601654811015612b1a576000601354601454612af39190613724565b601654909150612b0382846136eb565b612b0d9190613702565b60135461144a919061389a565b6000601454601554612b2c9190613724565b9050600060165483612b3e9190613724565b90506000601654612710612b529190613724565b905080612b5f84846136eb565b612b699190613702565b601454610e59919061389a565b600080612b8283612885565b905080600003612b96575060001992915050565b6000612ba1846132e4565b90506000612bad6131d1565b905082612bba82846136eb565b612bc49190613702565b95945050505050565b600260015403612c1f5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610bc0565b6002600155565b600060115442612c369190613724565b9050600081118015612c4a57506000600954115b15611906576000612c59612ab9565b9050600061271060105483612c6e91906136eb565b612c789190613702565b90506000670de0b6b3a7640000600f54600954612c9591906136eb565b612c9f9190613702565b905060006010546301e13380612cb591906136eb565b85612cc085856136eb565b612cca91906136eb565b612cd49190613702565b9050600082612ceb670de0b6b3a7640000846136eb565b612cf59190613702565b9050670de0b6b3a764000081600f54612d0e91906136eb565b612d189190613702565b600f6000828254612d29919061389a565b9091555060009050612d396130a5565b90506000612710612d4a83866136eb565b612d549190613702565b90506000612d628286613724565b905080600a6000828254612d76919061389a565b925050819055508160176000828254612d8f919061389a565b909155505042601155600f54604080519182526020820183905281018390527f1f42ca9fe8250d2ef16b6a18292adc74cf5a88f41875baabc5c247098c4b1e3f9060600160405180910390a1505050505050505050565b6001600160a01b038216612e465760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610bc0565b6001600160a01b0382166000908152601b6020526040902054811115612eb95760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610bc0565b601c805482900390556001600160a01b0382166000818152601b60205260408120805484900390559060008051602061394b833981519152670de0b6b3a7640000612f026113bc565b612f0c90866136eb565b612f169190613702565b60405190815260200160405180910390a35050565b600080600080600085875af1905080612f7c5760405162461bcd60e51b815260206004820152601360248201527211551217d514905394d1915497d19052531151606a1b6044820152606401610bc0565b505050565b6001600160a01b038316612fe35760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610bc0565b6001600160a01b0382166130445760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610bc0565b6001600160a01b0383811660008181526003602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6000806130b06115ee565b905060165481116130c357505060185490565b600060185460026130d491906136eb565b90506000601854826130e69190613724565b90506000601654846130f89190613724565b9050600060165461271061310c9190613724565b90508061311984846136eb565b6131239190613702565b601854613130919061389a565b9550505050505090565b6000670de0b6b3a76400007f0000000000000000000000008f0ecda9679ad16e30be3d83d183c482821f53256001600160a01b03166399530b066040518163ffffffff1660e01b8152600401602060405180830381865afa1580156131a3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131c791906138dd565b61193f90846136eb565b6000612710670de0b6b3a764000060125461108991906136eb565b826001600160a01b03163b6000036132165760405162461bcd60e51b8152600401610bc09061391c565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d116001600051141617169150508061328e5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610bc0565b50505050565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b0381166000908152600d6020526040812054610df39061313a565b60008160000361331857506000919050565b6000670de0b6b3a764000061332b61198d565b61333590856136eb565b61333f9190613702565b9050828082116133505760006112ef565b6112ef8183613724565b836001600160a01b03163b6000036133845760405162461bcd60e51b8152600401610bc09061391c565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806134075760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610bc0565b5050505050565b6001600160a01b0382166134645760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610bc0565b80601c6000828254613476919061389a565b90915550506001600160a01b0382166000908152601b6020526040812080548392906134a390849061389a565b90915550506001600160a01b038216600060008051602061394b833981519152670de0b6b3a7640000612f026113bc565b80356001600160a01b03811681146134eb57600080fd5b919050565b6000806040838503121561350357600080fd5b82359150613513602084016134d4565b90509250929050565b60006020828403121561352e57600080fd5b610ebf826134d4565b60005b8381101561355257818101518382015260200161353a565b50506000910152565b602081526000825180602084015261357a816040850160208701613537565b601f01601f19169190910160400192915050565b600080604083850312156135a157600080fd5b6135aa836134d4565b946020939093013593505050565b6000602082840312156135ca57600080fd5b5035919050565b6000806000606084860312156135e657600080fd5b6135ef846134d4565b92506135fd602085016134d4565b9150604084013590509250925092565b93845260208401929092526040830152606082015260800190565b6000806000806080858703121561363e57600080fd5b5050823594602084013594506040840135936060013592509050565b6000806040838503121561366d57600080fd5b613676836134d4565b9150613513602084016134d4565b6020808252600a908201526916995c9bd05b5bdd5b9d60b21b604082015260600190565b602080825260139082015272496e73756666696369656e7442616c616e636560681b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610df357610df36136d5565b60008261371f57634e487b7160e01b600052601260045260246000fd5b500490565b81810381811115610df357610df36136d5565b600181811c9082168061374b57607f821691505b60208210810361376b57634e487b7160e01b600052602260045260246000fd5b50919050565b602080825260179082015276125b9d985b1a591119589d14da185c995cd05b5bdd5b9d604a1b604082015260600190565b6000835160206137b58285838901613537565b601d60f91b918401918252845460019060009080831c818416806137da57607f821691505b85821081036137f757634e487b7160e01b84526022600452602484fd5b80801561380b576001811461382457613855565b60ff198416888701528215158302880186019450613855565b60008b81526020902060005b8481101561384b5781548a8201890152908701908801613830565b5050858389010194505b50929a9950505050505050505050565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b80820180821115610df357610df36136d5565b602080825260169082015275125b9cdd59999a58da595b9d10dbdb1b185d195c985b60521b604082015260600190565b6000602082840312156138ef57600080fd5b5051919050565b6020808252600c908201526b496e76616c6964507269636560a01b604082015260600190565b6020808252601490820152731d1bdad95b88191bd95cc81b9bdd08195e1a5cdd60621b60408201526060019056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122013706cdb2fa1b35465818679a3ae056c59e304a06e2bbcba2ab57bbde170a7d964736f6c63430008150033