Contract Address Details

0x2560f53027FD1800174F842F071aBd52521F0807

FuseStakingV3 Last Balance Update: Block #14186716
Created by 0x5128–22e4bb at 0xa9cc–7365eb

Balance

0 Fuse

(@ /Fuse)

Fetching tokens...

Contract name:
FuseStakingV3




Optimization enabled
true
Compiler version
v0.6.12+commit.27d51765




Optimization runs
200
EVM Version
default

Contract source code

/**
* Submitted for verification at blockscout.com on 2021-07-07 11:12:08.633946Z
*/
// Sources flattened with hardhat v2.0.6 https://hardhat.org
// File @openzeppelin/contracts-upgradeable/proxy/Initializable.sol@v3.3.0
// SPDX-License-Identifier: MIT
// solhint-disable-next-line compiler-version
pragma solidity >=0.4.24 <0.8.0;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {UpgradeableProxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
*/
bool private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Modifier to protect an initializer function from being invoked twice.
*/
modifier initializer() {
require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
/// @dev Returns true if and only if the function is running in the constructor
function _isConstructor() private view returns (bool) {
// extcodesize checks the size of the code stored in an address, and
// address returns the current address. Since the code is still not
// deployed when running a constructor, any checks on its code size will
// yield zero, making it an effective way to detect if a contract is
// under construction or not.
address self = address(this);
uint256 cs;
// solhint-disable-next-line no-inline-assembly
assembly { cs := extcodesize(self) }
return cs == 0;
}
}
// File @openzeppelin/contracts-upgradeable/GSN/ContextUpgradeable.sol@v3.3.0
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal initializer {
__Context_init_unchained();
}
function __Context_init_unchained() internal initializer {
}
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
uint256[50] private __gap;
}
// File @openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol@v3.3.0
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal initializer {
__Context_init_unchained();
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal initializer {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view 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 {
emit OwnershipTransferred(_owner, address(0));
_owner = 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");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
uint256[49] private __gap;
}
// File @openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol@v3.3.0
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMathUpgradeable {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
// File contracts/utils/DSMath.sol
/// math.sol -- mixin for inline numerical wizardry
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
contract DSMath {
function WAD() internal pure returns (uint256) {
return 10**18;
}
function RAY() internal pure returns (uint256) {
return 10**27;
}
function add(uint256 x, uint256 y) public pure returns (uint256 z) {
require((z = x + y) >= x, "ds-math-add-overflow");
}
function sub(uint256 x, uint256 y) public pure returns (uint256 z) {
require((z = x - y) <= x, "ds-math-sub-underflow");
}
function mul(uint256 x, uint256 y) public pure returns (uint256 z) {
require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
}
function min(uint256 x, uint256 y) public pure returns (uint256 z) {
return x <= y ? x : y;
}
function max(uint256 x, uint256 y) public pure returns (uint256 z) {
return x >= y ? x : y;
}
function imin(int256 x, int256 y) public pure returns (int256 z) {
return x <= y ? x : y;
}
function imax(int256 x, int256 y) public pure returns (int256 z) {
return x >= y ? x : y;
}
function wmul(uint256 x, uint256 y) public pure returns (uint256 z) {
z = add(mul(x, y), WAD() / 2) / WAD();
}
function rmul(uint256 x, uint256 y) public pure returns (uint256 z) {
z = add(mul(x, y), RAY() / 2) / RAY();
}
function wdiv(uint256 x, uint256 y) public pure returns (uint256 z) {
z = add(mul(x, WAD()), y / 2) / y;
}
function rdiv(uint256 x, uint256 y) public pure returns (uint256 z) {
z = add(mul(x, RAY()), y / 2) / y;
}
// This famous algorithm is called "exponentiation by squaring"
// and calculates x^n with x as fixed-point and n as regular unsigned.
//
// It's O(log n), instead of O(n) for naive repeated multiplication.
//
// These facts are why it works:
//
// If n is even, then x^n = (x^2)^(n/2).
// If n is odd, then x^n = x * x^(n-1),
// and applying the equation for even x gives
// x^n = x * (x^2)^((n-1) / 2).
//
// Also, EVM division is flooring and
// floor[(n-1) / 2] = floor[n / 2].
//
function rpow(uint256 x, uint256 n) public pure returns (uint256 z) {
z = n % 2 != 0 ? x : RAY();
for (n /= 2; n != 0; n /= 2) {
x = rmul(x, x);
if (n % 2 != 0) {
z = rmul(z, x);
}
}
}
}
// File contracts/Interfaces.sol
pragma experimental ABIEncoderV2;
interface cERC20 {
function mint(uint256 mintAmount) external returns (uint256);
function redeemUnderlying(uint256 mintAmount) external returns (uint256);
function exchangeRateCurrent() external returns (uint256);
function exchangeRateStored() external view returns (uint256);
function balanceOf(address addr) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
}
interface GoodDollar is cERC20 {
function getFees(uint256 value) external view returns (uint256, bool);
function mint(address to, uint256 mintAmount) external returns (uint256);
}
interface Staking {
struct Staker {
// The staked DAI amount
uint256 stakedDAI;
// The latest block number which the
// staker has staked tokens
uint256 lastStake;
}
function stakeDAI(uint256 amount) external;
function withdrawStake() external;
function stakers(address staker) external view returns (Staker memory);
}
interface Uniswap {
function swapExactETHForTokens(
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external payable returns (uint256[] memory amounts);
function swapExactTokensForETH(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function WETH() external pure returns (address);
function factory() external pure returns (address);
function quote(
uint256 amountA,
uint256 reserveA,
uint256 reserveB
) external pure returns (uint256 amountB);
function getAmountIn(
uint256 amountOut,
uint256 reserveIn,
uint256 reserveOut
) external pure returns (uint256 amountIn);
function getAmountOut(
uint256 amountI,
uint256 reserveIn,
uint256 reserveOut
) external pure returns (uint256 amountOut);
}
interface UniswapFactory {
function getPair(address tokenA, address tokenB)
external
view
returns (address);
}
interface UniswapPair {
function getReserves()
external
view
returns (
uint112 reserve0,
uint112 reserve1,
uint32 blockTimestampLast
);
function kLast() external view returns (uint256);
}
interface Reserve {
function buy(
address _buyWith,
uint256 _tokenAmount,
uint256 _minReturn
) external returns (uint256);
}
interface IIdentity {
function isWhitelisted(address user) external view returns (bool);
function addWhitelistedWithDID(address account, string memory did) external;
function removeWhitelisted(address account) external;
function addIdentityAdmin(address account) external returns (bool);
function setAvatar(address _avatar) external;
}
interface UBIScheme {
function currentDay() external view returns (uint256);
}
interface ProxyAdmin {
function getProxyImplementation(address proxy)
external
view
returns (address);
function getProxyAdmin(address proxy) external view returns (address);
function upgrade(address proxy, address implementation) external;
function owner() external view returns (address);
function transferOwnership(address newOwner) external;
}
// File contracts/staking/FuseStakingV3.sol
// import "hardhat/console.sol";
interface IConsensus {
/**
* @dev delegate to a validator
* @param _validator the address of the validator msg.sender is delegating to
*/
function delegate(address _validator) external payable;
/**
* @dev Function to be called when a delegator whishes to withdraw some of his staked funds for a validator
* @param _validator the address of the validator msg.sender has delegating to
* @param _amount the amount msg.sender wishes to withdraw from the contract
*/
function withdraw(address _validator, uint256 _amount) external;
function delegatedAmount(address _address, address _validator)
external
view
returns (uint256);
function stakeAmount(address _address) external view returns (uint256);
function delegators(address _validator)
external
view
returns (address[] memory);
}
interface PegSwap {
/**
* @notice exchanges the source token for target token
* @param sourceAmount count of tokens being swapped
* @param source the token that is being given
* @param target the token that is being taken
*/
function swap(
uint256 sourceAmount,
address source,
address target
) external;
}
contract FuseStakingV3 is Initializable, OwnableUpgradeable, DSMath {
using SafeMathUpgradeable for uint256;
mapping(address => uint256) public stakers;
address[] public validators;
IConsensus public consensus;
Uniswap public uniswap;
GoodDollar public GD;
UBIScheme public ubischeme;
UniswapFactory public uniswapFactory;
UniswapPair public uniswapPair;
uint256 public lastDayCollected; //ubi day from ubischeme
uint256 public stakeBackRatio;
uint256 public maxSlippageRatio; //actually its max price impact ratio
uint256 public keeperFeeRatio;
uint256 public RATIO_BASE;
uint256 public communityPoolRatio; //out of G$ bought how much should goto pool
uint256 communityPoolBalance;
uint256 pendingFuseEarnings; //earnings not used because of slippage
address public USDC;
address public fUSD;
bool public paused;
address public guardian;
PegSwap pegSwap;
event UBICollected(
uint256 indexed currentDay,
uint256 ubi, //G$ sent to ubischeme
uint256 communityPool, //G$ added to pool
uint256 gdBought, //actual G$ we got out of swapping stakingRewards + pendingFuseEarnings
uint256 stakingRewards, //rewards earned since previous collection,
uint256 pendingFuseEarnings, //new balance of fuse pending to be swapped for G$
address keeper,
uint256 keeperGDFee
);
/**
* @dev initialize
*/
function initialize() public initializer {
__Ownable_init_unchained();
consensus = IConsensus(
address(0x3014ca10b91cb3D0AD85fEf7A3Cb95BCAc9c0f79)
);
validators.push(address(0xcb876A393F05a6677a8a029f1C6D7603B416C0A6));
}
modifier notPaused() {
require(paused == false, "ubi collection is pauased");
_;
}
modifier onlyGuardian() {
require(msg.sender == guardian, "not guardian");
_;
}
function upgrade0() public {
if (RATIO_BASE == 0) {
stakeBackRatio = 33333; //%33
communityPoolRatio = 33333; //%33
maxSlippageRatio = 3000; //3%
keeperFeeRatio = 30; //0.03%
RATIO_BASE = 100000; //100%
}
}
function upgrade1(
address _gd,
address _ubischeme,
address _uniswap
) public {
if (address(uniswapPair) == address(0)) {
uniswap = Uniswap(
_uniswap == address(0)
? 0xFB76e9E7d88E308aB530330eD90e84a952570319
: _uniswap
);
GD = GoodDollar(_gd);
ubischeme = UBIScheme(_ubischeme);
uniswapFactory = UniswapFactory(uniswap.factory());
uniswapPair = UniswapPair(
uniswapFactory.getPair(uniswap.WETH(), _gd)
);
upgrade0();
}
}
function upgrade2() public {
if (USDC == address(0)) {
USDC = address(0x620fd5fa44BE6af63715Ef4E65DDFA0387aD13F5);
fUSD = address(0x249BE57637D8B013Ad64785404b24aeBaE9B098B);
}
}
function upgrade3() public {
if (guardian == address(0)) {
paused = true;
guardian = address(0x5128E3C1f8846724cc1007Af9b4189713922E4BB);
}
}
function upgrade4() public {
if (address(pegSwap) == address(0)) {
pegSwap = PegSwap(0xdfE016328E7BcD6FA06614fE3AF3877E931F7e0a);
paused = false;
}
}
function upgrade5() public {
cERC20(fUSD).approve(address(pegSwap), type(uint256).max);
cERC20(USDC).approve(address(uniswap), type(uint256).max);
}
function setContracts(address _gd, address _ubischeme) public onlyOwner {
if (_gd != address(0)) {
GD = GoodDollar(_gd);
}
if (_ubischeme != address(0)) {
ubischeme = UBIScheme(_ubischeme);
}
}
function stake() public payable returns (bool) {
return stake(address(0));
}
function stake(address _validator) public payable returns (bool) {
require(msg.value > 0, "stake must be > 0");
require(validators.length > 0, "no approved validators");
bool found;
for (
uint256 i = 0;
_validator != address(0) && i < validators.length;
i++
) {
if (validators[i] != _validator) {
found = true;
break;
}
}
require(
_validator == address(0) || found,
"validator not in approved list"
);
bool staked = stakeNextValidator(msg.value, _validator);
stakers[msg.sender] = stakers[msg.sender].add(msg.value);
return staked;
}
function balanceOf(address _owner) public view returns (uint256) {
return stakers[_owner];
}
function withdraw(uint256 _value) public returns (uint256) {
uint256 effectiveBalance = balance(); //use only undelegated funds
uint256 toWithdraw = _value == 0 ? stakers[msg.sender] : _value;
uint256 toCollect = toWithdraw;
require(
toWithdraw > 0 && toWithdraw <= stakers[msg.sender],
"invalid withdraw amount"
);
uint256 perValidator = _value.div(validators.length);
for (uint256 i = 0; i < validators.length; i++) {
uint256 cur = consensus.delegatedAmount(
address(this),
validators[i]
);
if (cur == 0) continue;
if (cur <= perValidator) {
undelegateWithCatch(validators[i], cur);
toCollect = toCollect.sub(cur);
} else {
undelegateWithCatch(validators[i], perValidator);
toCollect = toCollect.sub(perValidator);
}
if (toCollect == 0) break;
}
effectiveBalance = balance().sub(effectiveBalance); //use only undelegated funds
// in case some funds where not withdrawn
if (toWithdraw > effectiveBalance) {
toWithdraw = effectiveBalance;
}
stakers[msg.sender] = stakers[msg.sender].sub(toWithdraw);
if (toWithdraw > 0) payable(msg.sender).transfer(toWithdraw);
return toWithdraw;
}
function stakeNextValidator(uint256 _value, address _validator)
internal
returns (bool)
{
if (validators.length == 0) return false;
if (_validator != address(0)) {
consensus.delegate{ value: _value }(_validator);
return true;
}
uint256 perValidator = totalDelegated().add(_value).div(
validators.length
);
uint256 left = _value;
for (uint256 i = 0; i < validators.length && left > 0; i++) {
uint256 cur = consensus.delegatedAmount(
address(this),
validators[i]
);
if (cur < perValidator) {
uint256 toDelegate = perValidator.sub(cur);
toDelegate = toDelegate < left ? toDelegate : left;
consensus.delegate{ value: toDelegate }(validators[i]);
left = left.sub(toDelegate);
}
}
return true;
}
function addValidator(address _v) public onlyOwner {
validators.push(_v);
}
function totalDelegated() public view returns (uint256) {
uint256 total = 0;
for (uint256 i = 0; i < validators.length; i++) {
uint256 cur = consensus.delegatedAmount(
address(this),
validators[i]
);
total = total.add(cur);
}
return total;
}
function removeValidator(address _validator) public onlyOwner {
uint256 delegated = consensus.delegatedAmount(
address(this),
_validator
);
if (delegated > 0) {
uint256 prevBalance = balance();
undelegateWithCatch(_validator, delegated);
// wasnt withdrawn because validator needs to be taken of active validators
if (balance() == prevBalance) {
// pendingValidators.push(_validator);
return;
}
}
for (uint256 i = 0; i < validators.length; i++) {
if (validators[i] == _validator) {
if (i < validators.length - 1)
validators[i] = validators[validators.length - 1];
validators.pop();
break;
}
}
}
function collectUBIInterest() public notPaused {
uint256 curDay = ubischeme.currentDay();
require(
curDay != lastDayCollected,
"can collect only once in a ubi cycle"
);
uint256 earnings = balance() - pendingFuseEarnings;
require(pendingFuseEarnings + earnings > 0, "no earnings to collect");
lastDayCollected = curDay;
uint256 fuseUBI = earnings.mul(RATIO_BASE - stakeBackRatio).div(
RATIO_BASE
);
uint256 stakeBack = earnings - fuseUBI;
uint256[] memory fuseswapResult = _buyGD(
fuseUBI.add(pendingFuseEarnings)
); //buy GD with X% of earnings
pendingFuseEarnings = fuseUBI.add(pendingFuseEarnings).sub(
fuseswapResult[0]
);
stakeNextValidator(stakeBack, address(0)); //stake back the rest of the earnings
uint256 gdBought = fuseswapResult[fuseswapResult.length - 1];
uint256 keeperFee = gdBought.mul(keeperFeeRatio).div(RATIO_BASE);
if (keeperFee > 0) GD.transfer(msg.sender, keeperFee);
uint256 communityPoolContribution = gdBought
.sub(keeperFee) //subtract fee
.mul(communityPoolRatio) // * ommunityPoolRatio
.div(RATIO_BASE); // = G$ after fee * communityPoolRatio%
uint256 ubiAfterFeeAndPool = gdBought.sub(communityPoolContribution);
GD.transfer(address(ubischeme), ubiAfterFeeAndPool); //transfer to ubischeme
communityPoolBalance = communityPoolBalance.add(
communityPoolContribution
);
emit UBICollected(
curDay,
ubiAfterFeeAndPool,
communityPoolContribution,
gdBought,
earnings,
pendingFuseEarnings,
msg.sender,
keeperFee
);
}
/**
* @dev internal method to buy GD from fuseswap
* @param _value fuse to be sold
* @return uniswap coversion results uint256[2]
*/
function _buyGD(uint256 _value) internal returns (uint256[] memory) {
//buy from uniwasp
require(_value > 0, "buy value should be > 0");
(uint256 maxFuse, uint256 fuseGDOut) = calcMaxFuseWithPriceImpact(
_value
);
(
uint256 maxFuseUSDC,
uint256 usdcGDOut
) = calcMaxFuseUSDCWithPriceImpact(_value);
address[] memory path;
if (maxFuse >= maxFuseUSDC) {
path = new address[](2);
path[1] = address(GD);
path[0] = uniswap.WETH();
return
uniswap.swapExactETHForTokens{ value: maxFuse }(
(fuseGDOut * 95) / 100,
path,
address(this),
now
);
} else {
(uint256 usdcAmount, uint256 usedFuse) = _buyUSDC(maxFuseUSDC);
path = new address[](2);
path[1] = address(GD);
path[0] = USDC;
uint256[] memory result = uniswap.swapExactTokensForTokens(
usdcAmount,
(usdcGDOut * 95) / 100,
path,
address(this),
now
);
//buyGD should return how much fuse was used in [0] and how much G$ we got in [1]
result[0] = usedFuse;
return result;
}
}
/**
* @dev internal method to buy USDC via fuse->fusd
* @param _fuseIn fuse to be sold
* @return usdcAmount and usedFuse how much usdc we got and how much fuse was used
*/
function _buyUSDC(uint256 _fuseIn)
internal
returns (uint256 usdcAmount, uint256 usedFuse)
{
//buy from uniwasp
require(_fuseIn > 0, "buy value should be > 0");
UniswapPair uniswapFUSEfUSDPair = UniswapPair(
uniswapFactory.getPair(uniswap.WETH(), fUSD)
); //fusd is pegged 1:1 to usdc
(uint256 r_fuse, uint256 r_fusd, ) = uniswapFUSEfUSDPair.getReserves();
(uint256 maxFuse, uint256 tokenOut) = calcMaxTokenWithPriceImpact(
r_fuse,
r_fusd,
_fuseIn
); //expect r_token to be in 18 decimals
address[] memory path = new address[](2);
path[1] = fUSD;
path[0] = uniswap.WETH();
uint256[] memory result = uniswap.swapExactETHForTokens{
value: maxFuse
}((tokenOut * 95) / 100, path, address(this), now);
pegSwap.swap(result[1], fUSD, USDC);
usedFuse = result[0];
usdcAmount = result[1] / 1e12; //convert fusd from 1e18 to usdc 1e6
}
function calcMaxFuseWithPriceImpact(uint256 _value)
public
view
returns (uint256 fuseAmount, uint256 tokenOut)
{
(uint256 r_fuse, uint256 r_gd, ) = uniswapPair.getReserves();
return calcMaxTokenWithPriceImpact(r_fuse, r_gd, _value);
}
function calcMaxFuseUSDCWithPriceImpact(uint256 _value)
public
view
returns (uint256 maxFuse, uint256 gdOut)
{
UniswapPair uniswapFUSEfUSDPair = UniswapPair(
uniswapFactory.getPair(uniswap.WETH(), fUSD)
); //fusd is pegged 1:1 to usdc
UniswapPair uniswapGDUSDCPair = UniswapPair(
uniswapFactory.getPair(address(GD), USDC)
);
(uint256 rg_gd, uint256 rg_usdc, ) = uniswapGDUSDCPair.getReserves();
(uint256 r_fuse, uint256 r_fusd, ) = uniswapFUSEfUSDPair.getReserves();
uint256 fusdPriceInFuse = r_fuse.mul(1e18).div(r_fusd); //fusd is 1e18 so to keep in original 1e18 precision we first multiply by 1e18
// console.log(
// "rgd: %s rusdc:%s usdcPriceInFuse: %s",
// rg_gd,
// rg_usdc,
// fusdPriceInFuse
// );
// console.log("rfuse: %s rusdc:%s", r_fuse, r_fusd);
//how many fusd we can get for fuse
uint256 fuseValueInfUSD = _value.mul(1e18).div(fusdPriceInFuse); //value and usdPriceInFuse are in 1e18, we mul by 1e18 to keep 18 decimals precision
// console.log("fuse fusd value: %s", fuseValueInfUSD);
(uint256 maxUSDC, uint256 tokenOut) = calcMaxTokenWithPriceImpact(
rg_usdc * 1e12,
rg_gd,
fuseValueInfUSD
); //expect r_token to be in 18 decimals
// console.log("max USDC: %s", maxUSDC);
gdOut = tokenOut;
maxFuse = maxUSDC.mul(fusdPriceInFuse).div(1e18); //both are in 1e18 precision, div by 1e18 to keep precision
}
/**
* uniswap amountOut helper
*/
function getAmountOut(
uint256 _amountIn,
uint256 _reserveIn,
uint256 _reserveOut
) internal pure returns (uint256 amountOut) {
uint256 amountInWithFee = _amountIn.mul(997);
uint256 numerator = amountInWithFee.mul(_reserveOut);
uint256 denominator = _reserveIn.mul(1000).add(amountInWithFee);
amountOut = numerator / denominator;
}
/**
* @dev use binary search to find quantity that will result with price impact < maxPriceImpactRatio
*/
function calcMaxTokenWithPriceImpact(
uint256 r_token,
uint256 r_gd,
uint256 _value
) public view returns (uint256 fuseAmount, uint256 tokenOut) {
uint256 start = 0;
uint256 end = _value.div(1e18); //save iterations by moving precision to whole Fuse quantity
// uint256 curPriceWei = uint256(1e18).mul(r_gd) / r_token; //uniswap quote formula UniswapV2Library.sol
uint256 gdForQuantity = getAmountOut(1e18, r_token, r_gd);
uint256 priceForQuantityWei = rdiv(1e18, gdForQuantity.mul(1e16)).div(
1e9
);
uint256 maxPriceWei = priceForQuantityWei
.mul(RATIO_BASE.add(maxSlippageRatio))
.div(RATIO_BASE);
// console.log(
// "curPrice: %s, maxPrice %s",
// priceForQuantityWei,
// maxPriceWei
// );
fuseAmount = _value;
tokenOut;
//Iterate while start not meets end
while (start <= end) {
// Find the mid index
uint256 midQuantityWei = start.add(end).mul(1e18).div(2); //restore quantity precision
if (midQuantityWei == 0) break;
gdForQuantity = getAmountOut(midQuantityWei, r_token, r_gd);
priceForQuantityWei = rdiv(midQuantityWei, gdForQuantity.mul(1e16))
.div(1e9);
// console.log(
// "gdForQuantity: %s, priceForQuantity: %s, midQuantity: %s",
// gdForQuantity,
// priceForQuantityWei,
// midQuantityWei
// );
if (priceForQuantityWei <= maxPriceWei) {
start = midQuantityWei.div(1e18) + 1; //reduce precision to whole quantity div 1e18
fuseAmount = midQuantityWei;
tokenOut = gdForQuantity;
} else end = midQuantityWei.div(1e18) - 1; //reduce precision to whole quantity div 1e18
}
}
function undelegateWithCatch(address _validator, uint256 _amount)
internal
returns (bool)
{
try consensus.withdraw(_validator, _amount) {
return true;
} catch Error(
string memory /*reason*/
) {
// This is executed in case
// revert was called inside getData
// and a reason string was provided.
return false;
} catch (
bytes memory /*lowLevelData*/
) {
// This is executed in case revert() was used
// or there was a failing assertion, division
// by zero, etc. inside getData.
return false;
}
}
function balance() internal view returns (uint256) {
return payable(address(this)).balance;
}
function setPaused(bool _paused) public onlyGuardian {
paused = _paused;
}
receive() external payable {}
}

Contract ABI

[{"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":"UBICollected","inputs":[{"type":"uint256","name":"currentDay","internalType":"uint256","indexed":true},{"type":"uint256","name":"ubi","internalType":"uint256","indexed":false},{"type":"uint256","name":"communityPool","internalType":"uint256","indexed":false},{"type":"uint256","name":"gdBought","internalType":"uint256","indexed":false},{"type":"uint256","name":"stakingRewards","internalType":"uint256","indexed":false},{"type":"uint256","name":"pendingFuseEarnings","internalType":"uint256","indexed":false},{"type":"address","name":"keeper","internalType":"address","indexed":false},{"type":"uint256","name":"keeperGDFee","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract GoodDollar"}],"name":"GD","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"RATIO_BASE","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"USDC","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"z","internalType":"uint256"}],"name":"add","inputs":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"addValidator","inputs":[{"type":"address","name":"_v","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"_owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"maxFuse","internalType":"uint256"},{"type":"uint256","name":"gdOut","internalType":"uint256"}],"name":"calcMaxFuseUSDCWithPriceImpact","inputs":[{"type":"uint256","name":"_value","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"fuseAmount","internalType":"uint256"},{"type":"uint256","name":"tokenOut","internalType":"uint256"}],"name":"calcMaxFuseWithPriceImpact","inputs":[{"type":"uint256","name":"_value","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"fuseAmount","internalType":"uint256"},{"type":"uint256","name":"tokenOut","internalType":"uint256"}],"name":"calcMaxTokenWithPriceImpact","inputs":[{"type":"uint256","name":"r_token","internalType":"uint256"},{"type":"uint256","name":"r_gd","internalType":"uint256"},{"type":"uint256","name":"_value","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"collectUBIInterest","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"communityPoolRatio","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IConsensus"}],"name":"consensus","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"fUSD","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"guardian","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"int256","name":"z","internalType":"int256"}],"name":"imax","inputs":[{"type":"int256","name":"x","internalType":"int256"},{"type":"int256","name":"y","internalType":"int256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"int256","name":"z","internalType":"int256"}],"name":"imin","inputs":[{"type":"int256","name":"x","internalType":"int256"},{"type":"int256","name":"y","internalType":"int256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"initialize","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"keeperFeeRatio","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"lastDayCollected","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"z","internalType":"uint256"}],"name":"max","inputs":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxSlippageRatio","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"z","internalType":"uint256"}],"name":"min","inputs":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"z","internalType":"uint256"}],"name":"mul","inputs":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"paused","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"z","internalType":"uint256"}],"name":"rdiv","inputs":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"removeValidator","inputs":[{"type":"address","name":"_validator","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"z","internalType":"uint256"}],"name":"rmul","inputs":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"z","internalType":"uint256"}],"name":"rpow","inputs":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"n","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setContracts","inputs":[{"type":"address","name":"_gd","internalType":"address"},{"type":"address","name":"_ubischeme","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setPaused","inputs":[{"type":"bool","name":"_paused","internalType":"bool"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"stake","inputs":[{"type":"address","name":"_validator","internalType":"address"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"stake","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stakeBackRatio","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"stakers","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"z","internalType":"uint256"}],"name":"sub","inputs":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalDelegated","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract UBIScheme"}],"name":"ubischeme","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract Uniswap"}],"name":"uniswap","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract UniswapFactory"}],"name":"uniswapFactory","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract UniswapPair"}],"name":"uniswapPair","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"upgrade0","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"upgrade1","inputs":[{"type":"address","name":"_gd","internalType":"address"},{"type":"address","name":"_ubischeme","internalType":"address"},{"type":"address","name":"_uniswap","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"upgrade2","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"upgrade3","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"upgrade4","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"upgrade5","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"validators","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"z","internalType":"uint256"}],"name":"wdiv","inputs":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"withdraw","inputs":[{"type":"uint256","name":"_value","internalType":"uint256"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"z","internalType":"uint256"}],"name":"wmul","inputs":[{"type":"uint256","name":"x","internalType":"uint256"},{"type":"uint256","name":"y","internalType":"uint256"}]},{"type":"receive","stateMutability":"payable"}]
            

Contract Byte Code

0x60806040526004361061031e5760003560e01c80637ae2b5c7116101ab578063a5a90636116100f7578063c816841b11610095578063d22e9b061161006f578063d22e9b061461085d578063d8952a4914610872578063f2fde38b14610892578063ff39f259146108b257610325565b8063c816841b14610808578063c8a4ac9c1461081d578063c8e6c7d91461083d57610325565b8063b67d77c5116100d1578063b67d77c51461079e578063bf79c67f146107be578063c11a5630146107de578063c25673d5146107f357610325565b8063a5a9063614610754578063ae94e44814610769578063aff44caf1461078957610325565b806389a30271116101645780638da5cb5b1161013e5780638da5cb5b146106ea5780638ef3f761146106ff5780638f907195146107145780639168ae721461073457610325565b806389a30271146106a05780638b9af5c1146106b55780638bdb2afa146106d557610325565b80637ae2b5c71461060c5780637c5570461461062c57806380d04de81461064c57806381028f67146106615780638129fc1c146106765780638918c4a21461068b57610325565b8063452a93201161026a5780636021a9d5116102235780636d5433e6116101fd5780636d5433e61461059757806370a08231146105b7578063715018a6146105d7578063771602f7146105ec57610325565b80636021a9d51461054d5780636711281c14610562578063674570221461057757610325565b8063452a9320146104ae57806347a1671a146104c35780634cabdbfb146104e35780634d238c8e146104f85780635c975abb146105185780635fde731c1461052d57610325565b806326476204116102d757806335aa2e44116102b157806335aa2e44146104515780633a4b66f1146104715780633fb1ccf11461047957806340a141ff1461048e57610325565b806326476204146103fc5780632681f7e41461041c5780632e1a7d4d1461043157610325565b806304d26ebf1461032a578063094e23f9146103555780630e2286d314610377578063150d7e471461039757806316c38b3c146103ac57806317dfb94d146103ce57610325565b3661032557005b600080fd5b34801561033657600080fd5b5061033f6108c7565b60405161034c91906131fb565b60405180910390f35b34801561036157600080fd5b5061036a6108cd565b60405161034c91906131a9565b34801561038357600080fd5b5061033f6103923660046130a1565b6108dc565b3480156103a357600080fd5b5061033f610907565b3480156103b857600080fd5b506103cc6103c7366004613069565b61090d565b005b3480156103da57600080fd5b506103ee6103e936600461310b565b61095e565b60405161034c929190613648565b61040f61040a366004612f1a565b610a17565b60405161034c91906131f0565b34801561042857600080fd5b5061036a610b2d565b34801561043d57600080fd5b5061033f61044c36600461310b565b610b3c565b34801561045d57600080fd5b5061036a61046c36600461310b565b610d9c565b61040f610dc3565b34801561048557600080fd5b5061036a610dd5565b34801561049a57600080fd5b506103cc6104a9366004612f1a565b610de4565b3480156104ba57600080fd5b5061036a610fc7565b3480156104cf57600080fd5b506103cc6104de366004612f8a565b610fd6565b3480156104ef57600080fd5b506103cc6111ee565b34801561050457600080fd5b506103cc610513366004612f1a565b611249565b34801561052457600080fd5b5061040f6112d0565b34801561053957600080fd5b5061033f6105483660046130a1565b6112e0565b34801561055957600080fd5b5061033f6112f2565b34801561056e57600080fd5b506103cc6112f8565b34801561058357600080fd5b5061033f6105923660046130a1565b61131e565b3480156105a357600080fd5b5061033f6105b23660046130a1565b611346565b3480156105c357600080fd5b5061033f6105d2366004612f1a565b611361565b3480156105e357600080fd5b506103cc61137c565b3480156105f857600080fd5b5061033f6106073660046130a1565b6113fb565b34801561061857600080fd5b5061033f6106273660046130a1565b61141e565b34801561063857600080fd5b506103ee61064736600461310b565b61142e565b34801561065857600080fd5b5061033f61175d565b34801561066d57600080fd5b5061036a611837565b34801561068257600080fd5b506103cc611846565b34801561069757600080fd5b5061033f611948565b3480156106ac57600080fd5b5061036a61194e565b3480156106c157600080fd5b5061033f6106d03660046130a1565b61195d565b3480156106e157600080fd5b5061036a61197e565b3480156106f657600080fd5b5061036a61198d565b34801561070b57600080fd5b5061036a61199c565b34801561072057600080fd5b5061033f61072f3660046130a1565b6119ab565b34801561074057600080fd5b5061033f61074f366004612f1a565b611a00565b34801561076057600080fd5b506103cc611a12565b34801561077557600080fd5b5061033f6107843660046130a1565b611d80565b34801561079557600080fd5b506103cc611d90565b3480156107aa57600080fd5b5061033f6107b93660046130a1565b611ea5565b3480156107ca57600080fd5b506103ee6107d936600461313b565b611ec8565b3480156107ea57600080fd5b5061033f612001565b3480156107ff57600080fd5b506103cc612007565b34801561081457600080fd5b5061036a612052565b34801561082957600080fd5b5061033f6108383660046130a1565b612061565b34801561084957600080fd5b5061033f6108583660046130a1565b612098565b34801561086957600080fd5b5061033f6120a8565b34801561087e57600080fd5b506103cc61088d366004612f52565b6120ae565b34801561089e57600080fd5b506103cc6108ad366004612f1a565b61213d565b3480156108be57600080fd5b506103cc6121f4565b606f5481565b6076546001600160a01b031681565b6000816108f86108ee85610838612239565b6002855b046113fb565b816108ff57fe5b049392505050565b60705481565b6077546001600160a01b031633146109405760405162461bcd60e51b81526004016109379061348f565b60405180910390fd5b60768054911515600160a01b0260ff60a01b19909216919091179055565b600080600080606c60009054906101000a90046001600160a01b03166001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b1580156109b257600080fd5b505afa1580156109c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ea91906130c2565b506001600160701b031691506001600160701b03169150610a0c828287611ec8565b935093505050915091565b6000803411610a385760405162461bcd60e51b815260040161093790613562565b606654610a575760405162461bcd60e51b81526004016109379061358d565b6000805b6001600160a01b03841615801590610a74575060665481105b15610abb57836001600160a01b031660668281548110610a9057fe5b6000918252602090912001546001600160a01b031614610ab35760019150610abb565b600101610a5b565b506001600160a01b0383161580610acf5750805b610aeb5760405162461bcd60e51b8152600401610937906135bd565b6000610af73485612249565b33600090815260656020526040902054909150610b1490346124a0565b336000908152606560205260409020559150505b919050565b6068546001600160a01b031681565b600080610b476124c5565b905060008315610b575783610b68565b336000908152606560205260409020545b9050808015801590610b895750336000908152606560205260409020548211155b610ba55760405162461bcd60e51b8152600401610937906134f6565b606654600090610bb69087906124ca565b905060005b606654811015610d1257606754606680546000926001600160a01b0316916321429e609130919086908110610bec57fe5b6000918252602090912001546040516001600160e01b031960e085901b168152610c2392916001600160a01b0316906004016131bd565b60206040518083038186803b158015610c3b57600080fd5b505afa158015610c4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c739190613123565b905080610c805750610d0a565b828111610cc457610cb260668381548110610c9757fe5b6000918252602090912001546001600160a01b03168261250c565b50610cbd84826125cd565b9350610cfd565b610cef60668381548110610cd457fe5b6000918252602090912001546001600160a01b03168461250c565b50610cfa84846125cd565b93505b83610d085750610d12565b505b600101610bbb565b50610d2584610d1f6124c5565b906125cd565b935083831115610d33578392505b33600090815260656020526040902054610d4d90846125cd565b336000908152606560205260409020558215610d9257604051339084156108fc029085906000818181858888f19350505050158015610d90573d6000803e3d6000fd5b505b5090949350505050565b60668181548110610da957fe5b6000918252602090912001546001600160a01b0316905081565b6000610dcf6000610a17565b90505b90565b6069546001600160a01b031681565b610dec61260f565b6033546001600160a01b03908116911614610e195760405162461bcd60e51b81526004016109379061352d565b60675460405163010a14f360e51b81526000916001600160a01b0316906321429e6090610e4c90309086906004016131bd565b60206040518083038186803b158015610e6457600080fd5b505afa158015610e78573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e9c9190613123565b90508015610ed3576000610eae6124c5565b9050610eba838361250c565b5080610ec46124c5565b1415610ed1575050610fc4565b505b60005b606654811015610fc157826001600160a01b031660668281548110610ef757fe5b6000918252602090912001546001600160a01b03161415610fb95760665460001901811015610f8757606680546000198101908110610f3257fe5b600091825260209091200154606680546001600160a01b039092169183908110610f5857fe5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b6066805480610f9257fe5b600082815260209020810160001990810180546001600160a01b0319169055019055610fc1565b600101610ed6565b50505b50565b6077546001600160a01b031681565b606c546001600160a01b0316610fc1576001600160a01b03811615610ffb5780611011565b73fb76e9e7d88e308ab530330ed90e84a9525703195b606880546001600160a01b03199081166001600160a01b039384161791829055606980548216878516179055606a80549091168584161790556040805163c45a015560e01b81529051919092169163c45a0155916004808301926020929190829003018186803b15801561108457600080fd5b505afa158015611098573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110bc9190612f36565b606b80546001600160a01b0319166001600160a01b039283161790819055606854604080516315ab88c960e31b815290519284169363e6a439059392169163ad5c464891600480820192602092909190829003018186803b15801561112057600080fd5b505afa158015611134573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111589190612f36565b856040518363ffffffff1660e01b81526004016111769291906131bd565b60206040518083038186803b15801561118e57600080fd5b505afa1580156111a2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111c69190612f36565b606c80546001600160a01b0319166001600160a01b0392909216919091179055610fc16112f8565b6075546001600160a01b031661124757607580546001600160a01b031990811673620fd5fa44be6af63715ef4e65ddfa0387ad13f5179091556076805490911673249be57637d8b013ad64785404b24aebae9b098b1790555b565b61125161260f565b6033546001600160a01b0390811691161461127e5760405162461bcd60e51b81526004016109379061352d565b606680546001810182556000919091527f46501879b8ca8525e8c2fd519e2fbfcfa2ebea26501294aa02cbfcfb12e943540180546001600160a01b0319166001600160a01b0392909216919091179055565b607654600160a01b900460ff1681565b6000816108f86108ee85610838612613565b606e5481565b60715461124757618235606e819055607255610bb8606f55601e607055620186a0607155565b6000611328612239565b6108f86113358585612061565b600261133f612239565b816108f257fe5b6000818310156113565781611358565b825b90505b92915050565b6001600160a01b031660009081526065602052604090205490565b61138461260f565b6033546001600160a01b039081169116146113b15760405162461bcd60e51b81526004016109379061352d565b6033546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3603380546001600160a01b0319169055565b8082018281101561135b5760405162461bcd60e51b81526004016109379061339f565b6000818311156113565781611358565b606b54606854604080516315ab88c960e31b81529051600093849384936001600160a01b039283169363e6a43905939092169163ad5c464891600480820192602092909190829003018186803b15801561148757600080fd5b505afa15801561149b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114bf9190612f36565b6076546040516001600160e01b031960e085901b1681526114ed92916001600160a01b0316906004016131bd565b60206040518083038186803b15801561150557600080fd5b505afa158015611519573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061153d9190612f36565b606b5460695460755460405163e6a4390560e01b81529394506000936001600160a01b039384169363e6a439059361157e93908216929116906004016131bd565b60206040518083038186803b15801561159657600080fd5b505afa1580156115aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115ce9190612f36565b9050600080826001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561160c57600080fd5b505afa158015611620573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061164491906130c2565b506001600160701b031691506001600160701b03169150600080856001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b15801561169757600080fd5b505afa1580156116ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116cf91906130c2565b506001600160701b03918216935016905060006116fe826116f885670de0b6b3a764000061261f565b906124ca565b90506000611718826116f88d670de0b6b3a764000061261f565b905060008061172f8764e8d4a51000028985611ec8565b9b5091508a905061174c670de0b6b3a76400006116f8848761261f565b9b5050505050505050505050915091565b600080805b60665481101561183157606754606680546000926001600160a01b0316916321429e60913091908690811061179357fe5b6000918252602090912001546040516001600160e01b031960e085901b1681526117ca92916001600160a01b0316906004016131bd565b60206040518083038186803b1580156117e257600080fd5b505afa1580156117f6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061181a9190613123565b905061182683826124a0565b925050600101611762565b50905090565b606a546001600160a01b031681565b600054610100900460ff168061185f575061185f612659565b8061186d575060005460ff16155b6118895760405162461bcd60e51b8152600401610937906133fd565b600054610100900460ff161580156118b4576000805460ff1961ff0019909116610100171660011790555b6118bc61265f565b606780546001600160a01b0319908116733014ca10b91cb3d0ad85fef7a3cb95bcac9c0f7917909155606680546001810182556000919091527f46501879b8ca8525e8c2fd519e2fbfcfa2ebea26501294aa02cbfcfb12e9435401805490911673cb876a393f05a6677a8a029f1c6d7603b416c0a61790558015610fc4576000805461ff001916905550565b606d5481565b6075546001600160a01b031681565b6000611967612613565b6108f86119748585612061565b600261133f612613565b606b546001600160a01b031681565b6033546001600160a01b031690565b6067546001600160a01b031681565b6000600282066119c2576119bd612239565b6119c4565b825b90506002820491505b811561135b576119dd838461131e565b925060028206156119f5576119f2818461131e565b90505b6002820491506119cd565b60656020526000908152604090205481565b607654600160a01b900460ff1615611a3c5760405162461bcd60e51b815260040161093790613257565b606a5460408051635c9302c960e01b815290516000926001600160a01b031691635c9302c9916004808301926020929190829003018186803b158015611a8157600080fd5b505afa158015611a95573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ab99190613123565b9050606d54811415611add5760405162461bcd60e51b81526004016109379061344b565b6000607454611aea6124c5565b0390506000816074540111611b115760405162461bcd60e51b8152600401610937906133cd565b606d829055607154606e54600091611b30916116f8908590830361261f565b9050600081830390506060611b58611b53607454856124a090919063ffffffff16565b612739565b9050611b8781600081518110611b6a57fe5b6020026020010151610d1f607454866124a090919063ffffffff16565b607455611b95826000612249565b50600081600183510381518110611ba857fe5b602002602001015190506000611bcf6071546116f86070548561261f90919063ffffffff16565b90508015611c5d5760695460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb90611c0990339085906004016131d7565b602060405180830381600087803b158015611c2357600080fd5b505af1158015611c37573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c5b9190613085565b505b6000611c846071546116f8607254611c7e86886125cd90919063ffffffff16565b9061261f565b90506000611c9284836125cd565b606954606a5460405163a9059cbb60e01b81529293506001600160a01b039182169263a9059cbb92611cca92169085906004016131d7565b602060405180830381600087803b158015611ce457600080fd5b505af1158015611cf8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d1c9190613085565b50607354611d2a90836124a0565b6073556074546040518a917f49a079c90e07ed02ae8283e86eaeb643c637381d30aa7ba06d155e5b744a854391611d6d91859187918a918f919033908c90613692565b60405180910390a2505050505050505050565b6000818312156113565781611358565b60765460785460405163095ea7b360e01b81526001600160a01b039283169263095ea7b392611dc892911690600019906004016131d7565b602060405180830381600087803b158015611de257600080fd5b505af1158015611df6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e1a9190613085565b5060755460685460405163095ea7b360e01b81526001600160a01b039283169263095ea7b392611e5392911690600019906004016131d7565b602060405180830381600087803b158015611e6d57600080fd5b505af1158015611e81573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fc49190613085565b8082038281111561135b5760405162461bcd60e51b81526004016109379061328e565b6000808080611edf85670de0b6b3a76400006124ca565b90506000611ef6670de0b6b3a76400008989612a4a565b90506000611f1f633b9aca006116f8670de0b6b3a764000061039286662386f26fc1000061261f565b90506000611f4a6071546116f8611f43606f546071546124a090919063ffffffff16565b859061261f565b90508796505b838511611ff4576000611f7460026116f8670de0b6b3a7640000611c7e8a8a6124a0565b905080611f815750611ff4565b611f8c818c8c612a4a565b9350611fab633b9aca006116f88361039288662386f26fc1000061261f565b9250818311611fd657611fc681670de0b6b3a76400006124ca565b6001019550809750839650611fee565b6001611fea82670de0b6b3a76400006124ca565b0394505b50611f50565b5050505050935093915050565b60715481565b6077546001600160a01b0316611247576076805460ff60a01b1916600160a01b179055607780546001600160a01b031916735128e3c1f8846724cc1007af9b4189713922e4bb179055565b606c546001600160a01b031681565b600081158061207c5750508082028282828161207957fe5b04145b61135b5760405162461bcd60e51b81526004016109379061333a565b6000818313156113565781611358565b60725481565b6120b661260f565b6033546001600160a01b039081169116146120e35760405162461bcd60e51b81526004016109379061352d565b6001600160a01b0382161561210e57606980546001600160a01b0319166001600160a01b0384161790555b6001600160a01b0381161561213957606a80546001600160a01b0319166001600160a01b0383161790555b5050565b61214561260f565b6033546001600160a01b039081169116146121725760405162461bcd60e51b81526004016109379061352d565b6001600160a01b0381166121985760405162461bcd60e51b8152600401610937906132f4565b6033546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3603380546001600160a01b0319166001600160a01b0392909216919091179055565b6078546001600160a01b031661124757607880546001600160a01b03191673dfe016328e7bcd6fa06614fe3af3877e931f7e0a1790556076805460ff60a01b19169055565b6b033b2e3c9fd0803ce800000090565b60665460009061225b5750600061135b565b6001600160a01b038216156122d7576067546040516317066a5760e21b81526001600160a01b0390911690635c19a95c90859061229c9086906004016131a9565b6000604051808303818588803b1580156122b557600080fd5b505af11580156122c9573d6000803e3d6000fd5b50505050506001905061135b565b6066546000906122f3906116f8866122ed61175d565b906124a0565b90508360005b6066548110801561230a5750600082115b1561249457606754606680546000926001600160a01b0316916321429e60913091908690811061233657fe5b6000918252602090912001546040516001600160e01b031960e085901b16815261236d92916001600160a01b0316906004016131bd565b60206040518083038186803b15801561238557600080fd5b505afa158015612399573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123bd9190613123565b90508381101561248b5760006123d385836125cd565b90508381106123e257836123e4565b805b606754606680549293506001600160a01b0390911691635c19a95c9184918790811061240c57fe5b6000918252602090912001546040516001600160e01b031960e085901b168152612442916001600160a01b0316906004016131a9565b6000604051808303818588803b15801561245b57600080fd5b505af115801561246f573d6000803e3d6000fd5b505050505061248781856125cd90919063ffffffff16565b9350505b506001016122f9565b50600195945050505050565b6000828201838110156113585760405162461bcd60e51b815260040161093790613368565b303190565b600061135883836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f000000000000815250612a92565b60675460405163f3fef3a360e01b81526000916001600160a01b03169063f3fef3a39061253f90869086906004016131d7565b600060405180830381600087803b15801561255957600080fd5b505af192505050801561256a575060015b6125c557612576613718565b80612581575061258b565b600091505061135b565b3d8080156125b5576040519150601f19603f3d011682016040523d82523d6000602084013e6125ba565b606091505b50600091505061135b565b50600161135b565b600061135883836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250612ac9565b3390565b670de0b6b3a764000090565b60008261262e5750600061135b565b8282028284828161263b57fe5b04146113585760405162461bcd60e51b8152600401610937906134b5565b303b1590565b600054610100900460ff16806126785750612678612659565b80612686575060005460ff16155b6126a25760405162461bcd60e51b8152600401610937906133fd565b600054610100900460ff161580156126cd576000805460ff1961ff0019909116610100171660011790555b60006126d761260f565b603380546001600160a01b0319166001600160a01b038316908117909155604051919250906000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3508015610fc4576000805461ff001916905550565b60606000821161275b5760405162461bcd60e51b8152600401610937906132bd565b6000806127678461095e565b915091506000806127778661142e565b915091506060828510612904576040805160028082526060820183529091602083019080368337505060695482519293506001600160a01b03169183915060019081106127c057fe5b6001600160a01b03928316602091820292909201810191909152606854604080516315ab88c960e31b81529051919093169263ad5c4648926004808301939192829003018186803b15801561281457600080fd5b505afa158015612828573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061284c9190612f36565b8160008151811061285957fe5b6001600160a01b03928316602091820292909201015260685416637ff36ab5866064605f8802048430426040518663ffffffff1660e01b81526004016128a29493929190613613565b6000604051808303818588803b1580156128bb57600080fd5b505af11580156128cf573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f191682016040526128f89190810190612fd4565b95505050505050610b28565b60008061291085612af5565b60408051600280825260608201909252929450909250816020016020820280368337505060695482519295506001600160a01b031691859150600190811061295457fe5b6001600160a01b0392831660209182029290920101526075548451911690849060009061297d57fe5b6001600160a01b039283166020918202929092010152606854606091166338ed1739846064605f8902048730426040518663ffffffff1660e01b81526004016129ca959493929190613656565b600060405180830381600087803b1580156129e457600080fd5b505af11580156129f8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612a209190810190612fd4565b90508181600081518110612a3057fe5b60209081029190910101529750610b289650505050505050565b600080612a59856103e561261f565b90506000612a67828561261f565b90506000612a7b836122ed886103e861261f565b9050808281612a8657fe5b04979650505050505050565b60008183612ab35760405162461bcd60e51b81526004016109379190613204565b506000838581612abf57fe5b0495945050505050565b60008184841115612aed5760405162461bcd60e51b81526004016109379190613204565b505050900390565b60008060008311612b185760405162461bcd60e51b8152600401610937906132bd565b606b54606854604080516315ab88c960e31b815290516000936001600160a01b039081169363e6a439059391169163ad5c464891600480820192602092909190829003018186803b158015612b6c57600080fd5b505afa158015612b80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ba49190612f36565b6076546040516001600160e01b031960e085901b168152612bd292916001600160a01b0316906004016131bd565b60206040518083038186803b158015612bea57600080fd5b505afa158015612bfe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c229190612f36565b9050600080826001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160606040518083038186803b158015612c6057600080fd5b505afa158015612c74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c9891906130c2565b506001600160701b031691506001600160701b03169150600080612cbd84848a611ec8565b604080516002808252606080830184529496509294509190602083019080368337505060765482519293506001600160a01b0316918391506001908110612d0057fe5b6001600160a01b03928316602091820292909201810191909152606854604080516315ab88c960e31b81529051919093169263ad5c4648926004808301939192829003018186803b158015612d5457600080fd5b505afa158015612d68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d8c9190612f36565b81600081518110612d9957fe5b6001600160a01b03928316602091820292909201015260685460609116637ff36ab5856064605f8702048530426040518663ffffffff1660e01b8152600401612de59493929190613613565b6000604051808303818588803b158015612dfe57600080fd5b505af1158015612e12573d6000803e3d6000fd5b50505050506040513d6000823e601f3d908101601f19168201604052612e3b9190810190612fd4565b60785481519192506001600160a01b031690632b7f09239083906001908110612e6057fe5b60209081029190910101516076546075546040516001600160e01b031960e086901b168152612ea093926001600160a01b039081169216906004016135f4565b600060405180830381600087803b158015612eba57600080fd5b505af1158015612ece573d6000803e3d6000fd5b5050505080600081518110612edf57fe5b6020026020010151975064e8d4a5100081600181518110612efc57fe5b602002602001015181612f0b57fe5b04985050505050505050915091565b600060208284031215612f2b578081fd5b8135611358816137bd565b600060208284031215612f47578081fd5b8151611358816137bd565b60008060408385031215612f64578081fd5b8235612f6f816137bd565b91506020830135612f7f816137bd565b809150509250929050565b600080600060608486031215612f9e578081fd5b8335612fa9816137bd565b92506020840135612fb9816137bd565b91506040840135612fc9816137bd565b809150509250925092565b60006020808385031215612fe6578182fd5b825167ffffffffffffffff811115612ffc578283fd5b8301601f8101851361300c578283fd5b805161301f61301a826136f2565b6136cb565b818152838101908385018584028501860189101561303b578687fd5b8694505b8385101561305d57805183526001949094019391850191850161303f565b50979650505050505050565b60006020828403121561307a578081fd5b8135611358816137d2565b600060208284031215613096578081fd5b8151611358816137d2565b600080604083850312156130b3578182fd5b50508035926020909101359150565b6000806000606084860312156130d6578283fd5b83516130e1816137e0565b60208501519093506130f2816137e0565b604085015190925063ffffffff81168114612fc9578182fd5b60006020828403121561311c578081fd5b5035919050565b600060208284031215613134578081fd5b5051919050565b60008060006060848603121561314f578283fd5b505081359360208301359350604090920135919050565b6000815180845260208085019450808401835b8381101561319e5781516001600160a01b031687529582019590820190600101613179565b509495945050505050565b6001600160a01b0391909116815260200190565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b03929092168252602082015260400190565b901515815260200190565b90815260200190565b6000602080835283518082850152825b8181101561323057858101830151858201604001528201613214565b818111156132415783604083870101525b50601f01601f1916929092016040019392505050565b60208082526019908201527f75626920636f6c6c656374696f6e206973207061756173656400000000000000604082015260600190565b60208082526015908201527464732d6d6174682d7375622d756e646572666c6f7760581b604082015260600190565b60208082526017908201527f6275792076616c75652073686f756c64206265203e2030000000000000000000604082015260600190565b60208082526026908201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160408201526564647265737360d01b606082015260800190565b60208082526014908201527364732d6d6174682d6d756c2d6f766572666c6f7760601b604082015260600190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b60208082526014908201527364732d6d6174682d6164642d6f766572666c6f7760601b604082015260600190565b6020808252601690820152751b9bc819585c9b9a5b99dcc81d1bc818dbdb1b1958dd60521b604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b60208082526024908201527f63616e20636f6c6c656374206f6e6c79206f6e636520696e206120756269206360408201526379636c6560e01b606082015260800190565b6020808252600c908201526b3737ba1033bab0b93234b0b760a11b604082015260600190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f6040820152607760f81b606082015260800190565b60208082526017908201527f696e76616c696420776974686472617720616d6f756e74000000000000000000604082015260600190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b60208082526011908201527007374616b65206d757374206265203e203607c1b604082015260600190565b6020808252601690820152756e6f20617070726f7665642076616c696461746f727360501b604082015260600190565b6020808252601e908201527f76616c696461746f72206e6f7420696e20617070726f766564206c6973740000604082015260600190565b9283526001600160a01b03918216602084015216604082015260600190565b60008582526080602083015261362c6080830186613166565b6001600160a01b03949094166040830152506060015292915050565b918252602082015260400190565b600086825285602083015260a0604083015261367560a0830186613166565b6001600160a01b0394909416606083015250608001529392505050565b96875260208701959095526040860193909352606085019190915260808401526001600160a01b031660a083015260c082015260e00190565b60405181810167ffffffffffffffff811182821017156136ea57600080fd5b604052919050565b600067ffffffffffffffff821115613708578081fd5b5060209081020190565b60e01c90565b600060443d101561372857610dd2565b600481823e6308c379a061373c8251613712565b1461374657610dd2565b6040513d600319016004823e80513d67ffffffffffffffff81602484011181841117156137765750505050610dd2565b828401925082519150808211156137905750505050610dd2565b503d830160208284010111156137a857505050610dd2565b601f01601f1916810160200160405291505090565b6001600160a01b0381168114610fc457600080fd5b8015158114610fc457600080fd5b6001600160701b0381168114610fc457600080fdfea2646970667358221220a32e66fa10487f462d8efdaa09782619363c7e8bc1b8db5873cc15036b392e4864736f6c634300060c0033