Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
StakedTokenBPT
Compiler Version
v0.8.6+commit.11564f7e
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
pragma abicoder v2;
import { StakedToken } from "./StakedToken.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IBVault, ExitPoolRequest } from "./interfaces/IBVault.sol";
import { IBalancerGauge } from "../../peripheral/Balancer/IBalancerGauge.sol";
/**
* @title StakedTokenBPT
* @dev Derives from StakedToken, and simply adds the ability to withdraw any unclaimed $BAL tokens
* that are at this address
**/
contract StakedTokenBPT is StakedToken {
using SafeERC20 for IERC20;
/// @notice Balancer token
IERC20 public immutable BAL;
/// @notice Balancer vault
IBVault public immutable balancerVault;
/// @notice Balancer pool Id
bytes32 public immutable poolId;
/// @notice Balancer Pool Token Gauge. eg mBPT Gauge (mBPT-gauge)
IBalancerGauge public immutable balancerGauge;
/// @notice contract that can redistribute the $BAL
/// @dev Deprecated as the $BAL recipient is now set in the BPT Gauge.
address private balRecipient;
/// @notice Keeper
/// @dev Deprecated as Keeper has been added as a module to the Nexus
address private keeper;
/// @notice Pending fees in BPT terms
uint256 public pendingBPTFees;
/// @notice Most recent PriceCoefficient
uint256 public priceCoefficient;
/// @notice Time of last priceCoefficient upgrade
uint256 public lastPriceUpdateTime;
event BalRecipientChanged(address newRecipient);
event PriceCoefficientUpdated(uint256 newPriceCoeff);
event FeesConverted(uint256 bpt, uint256 mta);
/***************************************
INIT
****************************************/
/**
* @param _nexus System nexus
* @param _rewardsToken Token that is being distributed as a reward. eg Meta (MTA)
* @param _questManager Centralised manager of quests
* @param _stakedToken Core token that is staked and tracked e.g. mStable MTA/WETH Staking BPT (mBPT)
* @param _cooldownSeconds Seconds a user must wait after she initiates her cooldown before withdrawal is possible
* @param _bal Balancer addresses, [0] = $BAL addr, [1] = BAL vault
* @param _poolId Balancer Pool identifier
* @param _balancerGauge Address of the Balancer Pool Token Gauge. eg mBPT Gauge (mBPT-gauge)
*/
constructor(
address _nexus,
address _rewardsToken,
address _questManager,
address _stakedToken,
uint256 _cooldownSeconds,
address[2] memory _bal,
bytes32 _poolId,
address _balancerGauge
)
StakedToken(
_nexus,
_rewardsToken,
_questManager,
_stakedToken,
_cooldownSeconds,
true
)
{
BAL = IERC20(_bal[0]);
balancerVault = IBVault(_bal[1]);
poolId = _poolId;
balancerGauge = IBalancerGauge(_balancerGauge);
}
/***************************************
BAL incentives
****************************************/
/**
* @dev Sets the recipient for any potential $BAL earnings
*/
function setBalRecipient(address _newRecipient) external onlyGovernor {
balancerGauge.set_rewards_receiver(_newRecipient);
emit BalRecipientChanged(_newRecipient);
}
/***************************************
FEES
****************************************/
/**
* @dev Converts redemption fees accrued in mBPT into MTA, before depositing to the rewards contract.
*/
function convertFees() external nonReentrant {
uint256 pendingBPT = pendingBPTFees;
require(pendingBPT > 1, "no fees");
pendingBPTFees = 1;
// 1. Sell the mBPT
uint256 stakingBalBefore = balancerGauge.balanceOf(address(this));
uint256 mtaBalBefore = REWARDS_TOKEN.balanceOf(address(this));
(address[] memory tokens, , ) = balancerVault.getPoolTokens(poolId);
require(tokens[0] == address(REWARDS_TOKEN), "not MTA");
// 1.1. Calculate minimum output amount
uint256[] memory minOut = new uint256[](2);
{
// 10% discount from the latest pcoeff
// e.g. 1e18 * 42000 / 11000 = 3.81e18
minOut[0] = (pendingBPT * priceCoefficient) / 11000;
}
// 1.2 Withdraw pending mBPT fees from the mBPT Gauge back to this mBPT staking contract
balancerGauge.withdraw(pendingBPT - 1);
// 1.3. Exits rewards (MTA) to this staking contract for mBPT from this staking contract.
// Assumes rewards token (MTA) is in position 0
balancerVault.exitPool(
poolId,
address(this),
payable(address(this)),
ExitPoolRequest(tokens, minOut, bytes(abi.encode(0, pendingBPT - 1, 0)), false)
);
// 2. Verify and update state
uint256 stakingBalAfter = balancerGauge.balanceOf(address(this));
require(
stakingBalAfter == (stakingBalBefore - pendingBPT + 1),
"< min BPT"
);
// 3. Inform HeadlessRewards about the new MTA rewards
uint256 received = REWARDS_TOKEN.balanceOf(address(this)) - mtaBalBefore;
require(received >= minOut[0], "< min MTA");
super._notifyAdditionalReward(received);
emit FeesConverted(pendingBPT, received);
}
/**
* @dev Called by `StakedToken._withdraw` to add early withdrawal fee charged in the staking token mBPT.
* @param _fees Units of staking token mBPT.
*/
function _notifyAdditionalReward(uint256 _fees) internal override {
require(_fees < 1e24, "> mil");
pendingBPTFees += _fees;
}
/***************************************
PRICE
****************************************/
/**
* @dev Allows the governor or keeper to update the price coeff
*/
function fetchPriceCoefficient() external onlyKeeperOrGovernor {
require(block.timestamp > lastPriceUpdateTime + 14 days, "< 14 days");
uint256 newPriceCoeff = getProspectivePriceCoefficient();
uint256 oldPriceCoeff = priceCoefficient;
uint256 diff = newPriceCoeff > oldPriceCoeff
? newPriceCoeff - oldPriceCoeff
: oldPriceCoeff - newPriceCoeff;
// e.g. 500 * 10000 / 35000 = 5000000 / 35000 = 142
require((diff * 10000) / oldPriceCoeff > 500, "< 5% diff");
require(newPriceCoeff > 15000 && newPriceCoeff < 75000, "Out of bounds");
priceCoefficient = newPriceCoeff;
lastPriceUpdateTime = block.timestamp;
emit PriceCoefficientUpdated(newPriceCoeff);
}
/**
* @dev Fetches most recent priceCoeff from the balancer pool.
* PriceCoeff = units of MTA per BPT, scaled to 1:1 = 10000
* Assuming an 80/20 BPT, it is possible to calculate
* PriceCoeff (p) = balanceOfMTA in pool (b) / bpt supply (s) / 0.8
* p = b * 1.25 / s
*/
function getProspectivePriceCoefficient() public view returns (uint256 newPriceCoeff) {
(address[] memory tokens, uint256[] memory balances, ) = balancerVault.getPoolTokens(
poolId
);
require(tokens[0] == address(REWARDS_TOKEN), "not MTA");
// Calculate units of MTA per BPT
// e.g. 800e18 * 125e16 / 1000e18 = 1e18
// e.g. 1280e18 * 125e16 / 1000e18 = 16e17
uint256 unitsPerToken = (balances[0] * 125e16) / STAKED_TOKEN.totalSupply();
// e.g. 1e18 / 1e14 = 10000
// e.g. 16e17 / 1e14 = 16000
newPriceCoeff = unitsPerToken / 1e14;
}
/**
* @dev Get the current priceCoeff
*/
function _getPriceCoeff() internal view override returns (uint256 priceCoeff) {
priceCoeff = priceCoefficient;
}
/***************************************
BALANCER POOL GAUGE
****************************************/
/**
* @dev Transfers staked tokens from sender to this staking token contract,
* deposits staked tokens in the Balancer Pool Gauge to earn rewards and
* finally call `_settleStake`.
*/
function _transferAndStake(
uint256 _amount,
address _delegatee,
bool _exitCooldown
) internal override {
STAKED_TOKEN.safeTransferFrom(_msgSender(), address(this), _amount);
balancerGauge.deposit(_amount);
_settleStake(_amount, _delegatee, _exitCooldown);
}
function _withdrawStakedTokens(
address _recipient,
uint256 userWithdrawal
) internal override {
balancerGauge.withdraw(userWithdrawal);
STAKED_TOKEN.safeTransfer(_recipient, userWithdrawal);
}
function _balanceOfStakedTokens() internal override view returns (uint256 stakedTokens) {
stakedTokens = balancerGauge.balanceOf(address(this));
}
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
pragma abicoder v2;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IStakedToken } from "./interfaces/IStakedToken.sol";
import { GamifiedVotingToken } from "./GamifiedVotingToken.sol";
import { Root } from "../../shared/Root.sol";
import { InitializableReentrancyGuard } from "../../shared/InitializableReentrancyGuard.sol";
import "./deps/GamifiedTokenStructs.sol";
/**
* @title StakedToken
* @notice StakedToken is a non-transferrable ERC20 token that allows users to stake and withdraw, earning voting rights.
* Scaled balance is determined by quests a user completes, and the length of time they keep the raw balance wrapped.
* Stakers can unstake, after the elapsed cooldown period, and before the end of the unstake window. Users voting/earning
* power is slashed during this time, and they may face a redemption fee if they leave early.
* The reason for this unstake window is that this StakedToken acts as a source of insurance value for the mStable system,
* which can access the funds via the Recollateralisation module, up to the amount defined in `safetyData`.
* Voting power can be used for a number of things: voting in the mStable DAO/emission dials, boosting rewards, earning
* rewards here. While a users "balance" is unique to themselves, they can choose to delegate their voting power (which will apply
* to voting in the mStable DAO and emission dials).
* @author mStable
* @dev Only whitelisted contracts can communicate with this contract, in order to avoid having tokenised wrappers that
* could potentially circumvent our unstaking procedure.
**/
contract StakedToken is GamifiedVotingToken, InitializableReentrancyGuard {
using SafeERC20 for IERC20;
/// @notice Core token that is staked and tracked (e.g. MTA)
IERC20 public immutable STAKED_TOKEN;
/// @notice Seconds a user must wait after she initiates her cooldown before withdrawal is possible
uint256 public immutable COOLDOWN_SECONDS;
/// @notice Window in which it is possible to withdraw, following the cooldown period
uint256 public constant UNSTAKE_WINDOW = 14 days;
/// @notice A week
uint256 private constant ONE_WEEK = 7 days;
struct SafetyData {
/// Percentage of collateralisation where 100% = 1e18
uint128 collateralisationRatio;
/// Slash % where 100% = 1e18
uint128 slashingPercentage;
}
/// @notice Data relating to the re-collateralisation safety module
SafetyData public safetyData;
/// @notice Whitelisted smart contract integrations
mapping(address => bool) public whitelistedWrappers;
event Staked(address indexed user, uint256 amount, address delegatee);
event Withdraw(address indexed user, address indexed to, uint256 amount);
event Cooldown(address indexed user, uint256 percentage);
event CooldownExited(address indexed user);
event SlashRateChanged(uint256 newRate);
event Recollateralised();
event WrapperWhitelisted(address wallet);
event WrapperBlacklisted(address wallet);
/***************************************
INIT
****************************************/
/**
* @param _nexus System nexus
* @param _rewardsToken Token that is being distributed as a reward. eg MTA
* @param _questManager Centralised manager of quests
* @param _stakedToken Core token that is staked and tracked (e.g. MTA)
* @param _cooldownSeconds Seconds a user must wait after she initiates her cooldown before withdrawal is possible
* @param _hasPriceCoeff true if raw staked amount is multiplied by price coeff to get staked amount. eg BPT Staked Token
*/
constructor(
address _nexus,
address _rewardsToken,
address _questManager,
address _stakedToken,
uint256 _cooldownSeconds,
bool _hasPriceCoeff
) GamifiedVotingToken(_nexus, _rewardsToken, _questManager, _hasPriceCoeff) {
STAKED_TOKEN = IERC20(_stakedToken);
COOLDOWN_SECONDS = _cooldownSeconds;
}
/**
* @param _nameArg Token name
* @param _symbolArg Token symbol
* @param _rewardsDistributorArg mStable Rewards Distributor
*/
function __StakedToken_init(
bytes32 _nameArg,
bytes32 _symbolArg,
address _rewardsDistributorArg
) internal {
__GamifiedToken_init(_nameArg, _symbolArg, _rewardsDistributorArg);
_initializeReentrancyGuard();
safetyData = SafetyData({ collateralisationRatio: 1e18, slashingPercentage: 0 });
}
/**
* @dev Only the recollateralisation module, as specified in the mStable Nexus, can execute this
*/
modifier onlyRecollateralisationModule() {
require(_msgSender() == _recollateraliser(), "Only Recollateralisation");
_;
}
/**
* @dev This protects against fn's being called after a recollateralisation event, when the contract is essentially finished
*/
modifier onlyBeforeRecollateralisation() {
_onlyBeforeRecollateralisation();
_;
}
function _onlyBeforeRecollateralisation() internal view {
require(safetyData.collateralisationRatio == 1e18, "Only while collateralised");
}
/**
* @dev Only whitelisted contracts can call core fns. mStable governors can whitelist and de-whitelist wrappers.
* Access may be given to yield optimisers to boost rewards, but creating unlimited and ungoverned wrappers is unadvised.
*/
modifier assertNotContract() {
_assertNotContract();
_;
}
function _assertNotContract() internal view {
if (_msgSender() != tx.origin) {
require(whitelistedWrappers[_msgSender()], "Not whitelisted");
}
}
/***************************************
ACTIONS
****************************************/
/**
* @dev Stake an `_amount` of STAKED_TOKEN in the system. This amount is added to the users stake and
* boosts their voting power.
* @param _amount Units of STAKED_TOKEN to stake
*/
function stake(uint256 _amount) external {
_transferAndStake(_amount, address(0), false);
}
/**
* @dev Stake an `_amount` of STAKED_TOKEN in the system. This amount is added to the users stake and
* boosts their voting power.
* @param _amount Units of STAKED_TOKEN to stake
* @param _exitCooldown Bool signalling whether to take this opportunity to end any outstanding cooldown and
* return the user back to their full voting power
*/
function stake(uint256 _amount, bool _exitCooldown) external {
_transferAndStake(_amount, address(0), _exitCooldown);
}
/**
* @dev Stake an `_amount` of STAKED_TOKEN in the system. This amount is added to the users stake and
* boosts their voting power. Take the opportunity to change delegatee.
* @param _amount Units of STAKED_TOKEN to stake
* @param _delegatee Address of the user to whom the sender would like to delegate their voting power
*/
function stake(uint256 _amount, address _delegatee) external {
_transferAndStake(_amount, _delegatee, false);
}
/**
* @dev Transfers an `_amount` of staked tokens from sender to this staking contract
* before calling `_settleStake`.
* Can be overridden if the tokens are held elsewhere. eg in the Balancer Pool Gauge.
*/
function _transferAndStake(
uint256 _amount,
address _delegatee,
bool _exitCooldown
) internal virtual {
STAKED_TOKEN.safeTransferFrom(_msgSender(), address(this), _amount);
_settleStake(_amount, _delegatee, _exitCooldown);
}
/**
* @dev Gets the total number of staked tokens in this staking contract. eg MTA or mBPT.
* Can be overridden if the tokens are held elsewhere. eg in the Balancer Pool Gauge.
*/
function _balanceOfStakedTokens() internal view virtual returns (uint256 stakedTokens) {
stakedTokens = STAKED_TOKEN.balanceOf(address(this));
}
/**
* @dev Internal stake fn. Can only be called by whitelisted contracts/EOAs and only before a recollateralisation event.
* NOTE - Assumes tokens have already been transferred
* @param _amount Units of STAKED_TOKEN to stake
* @param _delegatee Address of the user to whom the sender would like to delegate their voting power
* @param _exitCooldown Bool signalling whether to take this opportunity to end any outstanding cooldown and
* return the user back to their full voting power
*/
function _settleStake(
uint256 _amount,
address _delegatee,
bool _exitCooldown
) internal onlyBeforeRecollateralisation assertNotContract {
require(_amount != 0, "INVALID_ZERO_AMOUNT");
// 1. Apply the delegate if it has been chosen (else it defaults to the sender)
if (_delegatee != address(0)) {
_delegate(_msgSender(), _delegatee);
}
// 2. Deal with cooldown
// If a user is currently in a cooldown period, re-calculate their cooldown timestamp
Balance memory oldBalance = _balances[_msgSender()];
// If we have missed the unstake window, or the user has chosen to exit the cooldown,
// then reset the timestamp to 0
bool exitCooldown = _exitCooldown ||
(oldBalance.cooldownTimestamp > 0 &&
block.timestamp >
(oldBalance.cooldownTimestamp + COOLDOWN_SECONDS + UNSTAKE_WINDOW));
if (exitCooldown) {
emit CooldownExited(_msgSender());
}
// 3. Settle the stake by depositing the STAKED_TOKEN and minting voting power
_mintRaw(_msgSender(), _amount, exitCooldown);
emit Staked(_msgSender(), _amount, _delegatee);
}
/**
* @dev Withdraw raw tokens from the system, following an elapsed cooldown period.
* Note - May be subject to a transfer fee, depending on the users weightedTimestamp
* @param _amount Units of raw staking token to withdraw. eg MTA or mBPT
* @param _recipient Address of beneficiary who will receive the raw tokens
* @param _amountIncludesFee Is the `_amount` specified inclusive of any applicable redemption fee?
* @param _exitCooldown Should we take this opportunity to exit the cooldown period?
**/
function withdraw(
uint256 _amount,
address _recipient,
bool _amountIncludesFee,
bool _exitCooldown
) external {
_withdraw(_amount, _recipient, _amountIncludesFee, _exitCooldown);
}
/**
* @dev Withdraw raw tokens from the system, following an elapsed cooldown period.
* Note - May be subject to a transfer fee, depending on the users weightedTimestamp
* @param _amount Units of raw staking token to withdraw. eg MTA or mBPT
* @param _recipient Address of beneficiary who will receive the raw tokens
* @param _amountIncludesFee Is the `_amount` specified inclusive of any applicable redemption fee?
* @param _exitCooldown Should we take this opportunity to exit the cooldown period?
**/
function _withdraw(
uint256 _amount,
address _recipient,
bool _amountIncludesFee,
bool _exitCooldown
) internal assertNotContract {
require(_amount != 0, "INVALID_ZERO_AMOUNT");
// Is the contract post-recollateralisation?
if (safetyData.collateralisationRatio != 1e18) {
// 1. If recollateralisation has occured, the contract is finished and we can skip all checks
_burnRaw(_msgSender(), _amount, false, true);
// 2. Return a proportionate amount of tokens, based on the collateralisation ratio
_withdrawStakedTokens(_recipient, (_amount * safetyData.collateralisationRatio) / 1e18);
emit Withdraw(_msgSender(), _recipient, _amount);
} else {
// 1. If no recollateralisation has occured, the user must be within their UNSTAKE_WINDOW period in order to withdraw
Balance memory oldBalance = _balances[_msgSender()];
require(
block.timestamp > oldBalance.cooldownTimestamp + COOLDOWN_SECONDS,
"INSUFFICIENT_COOLDOWN"
);
require(
block.timestamp - (oldBalance.cooldownTimestamp + COOLDOWN_SECONDS) <=
UNSTAKE_WINDOW,
"UNSTAKE_WINDOW_FINISHED"
);
// 2. Get current balance
Balance memory balance = _balances[_msgSender()];
// 3. Apply redemption fee
// e.g. (55e18 / 5e18) - 2e18 = 9e18 / 100 = 9e16
uint256 feeRate = calcRedemptionFeeRate(balance.weightedTimestamp);
// fee = amount * feeRate / 1e18
// totalAmount = amount + fee
uint256 totalWithdraw = _amountIncludesFee
? _amount
: (_amount * (1e18 + feeRate)) / 1e18;
uint256 userWithdrawal = (totalWithdraw * 1e18) / (1e18 + feeRate);
// Check for percentage withdrawal
uint256 maxWithdrawal = oldBalance.cooldownUnits;
require(totalWithdraw <= maxWithdrawal, "Exceeds max withdrawal");
// 4. Exit cooldown if the user has specified, or if they have withdrawn everything
// Otherwise, update the percentage remaining proportionately
bool exitCooldown = _exitCooldown || totalWithdraw == maxWithdrawal;
// 5. Settle the withdrawal by burning the voting tokens
_burnRaw(_msgSender(), totalWithdraw, exitCooldown, false);
// Log any redemption fee to the rewards contract if MTA or
// the staking token if mBPT.
_notifyAdditionalReward(totalWithdraw - userWithdrawal);
// Finally transfer staked tokens back to recipient
_withdrawStakedTokens(_recipient, userWithdrawal);
emit Withdraw(_msgSender(), _recipient, _amount);
}
}
/**
* @dev Transfers an `amount` of staked tokens to the withdraw `recipient`. eg MTA or mBPT.
* Can be overridden if the tokens are held elsewhere. eg in the Balancer Pool Gauge.
*/
function _withdrawStakedTokens(address _recipient, uint256 amount) internal virtual {
STAKED_TOKEN.safeTransfer(_recipient, amount);
}
/**
* @dev Enters a cooldown period, after which (and before the unstake window elapses) a user will be able
* to withdraw part or all of their staked tokens. Note, during this period, a users voting power is significantly reduced.
* If a user already has a cooldown period, then it will reset to the current block timestamp, so use wisely.
* @param _units Units of stake to cooldown for
**/
function startCooldown(uint256 _units) external {
_startCooldown(_units);
}
/**
* @dev Ends the cooldown of the sender and give them back their full voting power. This can be used to signal that
* the user no longer wishes to exit the system. Note, the cooldown can also be reset, more smoothly, as part of a stake or
* withdraw transaction.
**/
function endCooldown() external {
require(_balances[_msgSender()].cooldownTimestamp != 0, "No cooldown");
_exitCooldownPeriod(_msgSender());
emit CooldownExited(_msgSender());
}
/**
* @dev Enters a cooldown period, after which (and before the unstake window elapses) a user will be able
* to withdraw part or all of their staked tokens. Note, during this period, a users voting power is significantly reduced.
* If a user already has a cooldown period, then it will reset to the current block timestamp, so use wisely.
* @param _units Units of stake to cooldown for
**/
function _startCooldown(uint256 _units) internal {
require(balanceOf(_msgSender()) != 0, "INVALID_BALANCE_ON_COOLDOWN");
_enterCooldownPeriod(_msgSender(), _units);
emit Cooldown(_msgSender(), _units);
}
/***************************************
ADMIN
****************************************/
/**
* @dev This is a write function allowing the whitelisted recollateralisation module to slash stakers here and take
* the capital to use to recollateralise any lost value in the system. Trusting that the recollateralisation module has
* sufficient protections put in place. Note, once this has been executed, the contract is now finished, and undercollateralised,
* meaning that all users must withdraw, and will only receive a proportionate amount back relative to the colRatio.
**/
function emergencyRecollateralisation()
external
onlyRecollateralisationModule
onlyBeforeRecollateralisation
{
// 1. Change collateralisation rate
safetyData.collateralisationRatio = 1e18 - safetyData.slashingPercentage;
// 2. Take slashing percentage
uint256 balance = _balanceOfStakedTokens();
_withdrawStakedTokens(
_recollateraliser(),
(balance * safetyData.slashingPercentage) / 1e18
);
// 3. No functions should work anymore because the colRatio has changed
emit Recollateralised();
}
/**
* @dev Governance can change the slashing percentage here (initially 0). This is the amount of a stakers capital that is at
* risk in the recollateralisation process.
* @param _newRate Rate, where 50% == 5e17
**/
function changeSlashingPercentage(uint256 _newRate)
external
onlyGovernor
onlyBeforeRecollateralisation
{
require(_newRate <= 5e17, "> 50%");
safetyData.slashingPercentage = SafeCast.toUint128(_newRate);
emit SlashRateChanged(_newRate);
}
/**
* @dev Allows governance to whitelist a smart contract to interact with the StakedToken (for example a yield aggregator or simply
* a Gnosis SAFE or other)
* @param _wrapper Address of the smart contract to list
**/
function whitelistWrapper(address _wrapper) external onlyGovernor {
whitelistedWrappers[_wrapper] = true;
emit WrapperWhitelisted(_wrapper);
}
/**
* @dev Allows governance to blacklist a smart contract to end it's interaction with the StakedToken
* @param _wrapper Address of the smart contract to blacklist
**/
function blackListWrapper(address _wrapper) external onlyGovernor {
whitelistedWrappers[_wrapper] = false;
emit WrapperBlacklisted(_wrapper);
}
/***************************************
BACKWARDS COMPATIBILITY
****************************************/
/**
* @dev Allows for backwards compatibility with createLock fn, giving basic args to stake
* @param _value Units to stake
**/
function createLock(
uint256 _value,
uint256 /* _unlockTime */
) external {
_transferAndStake(_value, address(0), false);
}
/**
* @dev Allows for backwards compatibility with increaseLockAmount fn by simply staking more
* @param _value Units to stake
**/
function increaseLockAmount(uint256 _value) external {
require(balanceOf(_msgSender()) != 0, "Nothing to increase");
_transferAndStake(_value, address(0), false);
}
/**
* @dev Backwards compatibility. Previously a lock would run out and a user would call this. Now, it will take 2 calls
* to exit in order to leave. The first will initiate the cooldown period, and the second will execute a full withdrawal.
**/
function exit() external virtual {
// Since there is no immediate exit here, this can be called twice
// If there is no cooldown, or the cooldown has passed the unstake window, enter cooldown
uint128 ts = _balances[_msgSender()].cooldownTimestamp;
if (ts == 0 || block.timestamp > ts + COOLDOWN_SECONDS + UNSTAKE_WINDOW) {
(uint256 raw, uint256 cooldownUnits) = rawBalanceOf(_msgSender());
_startCooldown(raw + cooldownUnits);
}
// Else withdraw all available
else {
_withdraw(_balances[_msgSender()].cooldownUnits, _msgSender(), true, false);
}
}
/***************************************
GETTERS
****************************************/
/**
* @dev fee = sqrt(300/x)-2.5, where x = weeks since user has staked
* @param _weightedTimestamp The users weightedTimestamp
* @return _feeRate where 1% == 1e16
*/
function calcRedemptionFeeRate(uint32 _weightedTimestamp)
public
view
returns (uint256 _feeRate)
{
uint256 weeksStaked = ((block.timestamp - _weightedTimestamp) * 1e18) / ONE_WEEK;
if (weeksStaked > 3e18) {
// e.g. weeks = 1 = sqrt(300e18) = 17320508075
// e.g. weeks = 10 = sqrt(30e18) = 5477225575
// e.g. weeks = 26 = sqrt(11.5) = 3391164991
_feeRate = Root.sqrt(300e36 / weeksStaked) * 1e7;
// e.g. weeks = 1 = 173e15 - 25e15 = 148e15 or 14.8%
// e.g. weeks = 10 = 55e15 - 25e15 = 30e15 or 3%
// e.g. weeks = 26 = 34e15 - 25e15 = 9e15 or 0.9%
_feeRate = _feeRate < 25e15 ? 0 : _feeRate - 25e15;
} else {
_feeRate = 75e15;
}
}
uint256[48] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
struct ExitPoolRequest {
address[] assets;
uint256[] minAmountsOut;
bytes userData;
bool toInternalBalance;
}
interface IBVault {
function exitPool(
bytes32 poolId,
address sender,
address payable recipient,
ExitPoolRequest memory request
) external;
function getPoolTokens(bytes32 poolId)
external
view
returns (
address[] memory tokens,
uint256[] memory balances,
uint256 lastChangeBlock
);
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IBalancerGauge is IERC20 {
/**
* @notice Deposit `_value` LP tokens. eg mBPT.
* @param _value Number of tokens to deposit
*/
function deposit(uint256 _value) external;
/**
* @notice Withdraw `_value` LP tokens. eg mBPT.
* @param _value Number of tokens to withdraw
*/
function withdraw(uint256 _value) external;
/**
* @notice Set the default reward receiver for the caller.
* @dev When set to ZERO_ADDRESS, rewards are sent to the caller
* @param _receiver Receiver address for any rewards claimed via `claim_rewards`
*/
function set_rewards_receiver(address _receiver) external;
/**
* @notice Claim available reward tokens for `_addr`
* @param _addr Address to claim for
*/
function claim_rewards(address _addr) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such 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.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
return uint128(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
return uint96(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
return uint64(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
return uint32(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits.
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128) {
require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
return int128(value);
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64) {
require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
return int64(value);
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32) {
require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
return int32(value);
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16) {
require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
return int16(value);
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits.
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8) {
require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
return int8(value);
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
return int256(value);
}
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "../deps/GamifiedTokenStructs.sol";
interface IStakedToken {
// GETTERS
function COOLDOWN_SECONDS() external view returns (uint256);
function UNSTAKE_WINDOW() external view returns (uint256);
function STAKED_TOKEN() external view returns (IERC20);
function getRewardToken() external view returns (address);
function pendingAdditionalReward() external view returns (uint256);
function whitelistedWrappers(address) external view returns (bool);
function balanceData(address _account) external view returns (Balance memory);
function balanceOf(address _account) external view returns (uint256);
function rawBalanceOf(address _account) external view returns (uint256, uint256);
function calcRedemptionFeeRate(uint32 _weightedTimestamp)
external
view
returns (uint256 _feeRate);
function safetyData()
external
view
returns (uint128 collateralisationRatio, uint128 slashingPercentage);
function delegates(address account) external view returns (address);
function getPastTotalSupply(uint256 blockNumber) external view returns (uint256);
function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);
function getVotes(address account) external view returns (uint256);
// HOOKS/PERMISSIONED
function applyQuestMultiplier(address _account, uint8 _newMultiplier) external;
// ADMIN
function whitelistWrapper(address _wrapper) external;
function blackListWrapper(address _wrapper) external;
function changeSlashingPercentage(uint256 _newRate) external;
function emergencyRecollateralisation() external;
function setGovernanceHook(address _newHook) external;
// USER
function stake(uint256 _amount) external;
function stake(uint256 _amount, address _delegatee) external;
function stake(uint256 _amount, bool _exitCooldown) external;
function withdraw(
uint256 _amount,
address _recipient,
bool _amountIncludesFee,
bool _exitCooldown
) external;
function delegate(address delegatee) external;
function startCooldown(uint256 _units) external;
function endCooldown() external;
function reviewTimestamp(address _account) external;
function claimReward() external;
function claimReward(address _to) external;
// Backwards compatibility
function createLock(uint256 _value, uint256) external;
function exit() external;
function increaseLockAmount(uint256 _value) external;
function increaseLockLength(uint256) external;
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { ECDSAUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
import { GamifiedToken } from "./GamifiedToken.sol";
import { IGovernanceHook } from "./interfaces/IGovernanceHook.sol";
/**
* @title GamifiedVotingToken
* @notice GamifiedToken is a checkpointed Voting Token derived from OpenZeppelin "ERC20VotesUpgradable"
* @author mStable
* @dev Forked from https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/f9cdbd7d82d45a614ee98a5dc8c08fb4347d0fea/contracts/token/ERC20/extensions/ERC20VotesUpgradeable.sol
* Changes:
* - Inherits custom GamifiedToken rather than basic ERC20
* - Removal of `Permit` functionality & `delegatebySig`
* - Override `delegates` fn as described in their docs
* - Prettier formatting
* - Addition of `totalSupply` method to get latest totalSupply
* - Move totalSupply checkpoints to `afterTokenTransfer`
* - Add _governanceHook hook
*/
abstract contract GamifiedVotingToken is GamifiedToken {
struct Checkpoint {
uint32 fromBlock;
uint224 votes;
}
mapping(address => address) private _delegates;
mapping(address => Checkpoint[]) private _checkpoints;
Checkpoint[] private _totalSupplyCheckpoints;
IGovernanceHook private _governanceHook;
event GovernanceHookChanged(address indexed hook);
/**
* @dev Emitted when an account changes their delegate.
*/
event DelegateChanged(
address indexed delegator,
address indexed fromDelegate,
address indexed toDelegate
);
/**
* @dev Emitted when a token transfer or delegate change results in changes to an account's voting power.
*/
event DelegateVotesChanged(
address indexed delegate,
uint256 previousBalance,
uint256 newBalance
);
constructor(
address _nexus,
address _rewardsToken,
address _questManager,
bool _hasPriceCoeff
) GamifiedToken(_nexus, _rewardsToken, _questManager, _hasPriceCoeff) {}
/**
* @dev
*/
function setGovernanceHook(address _newHook) external onlyGovernor {
_governanceHook = IGovernanceHook(_newHook);
emit GovernanceHookChanged(_newHook);
}
/**
* @dev Get the `pos`-th checkpoint for `account`.
*/
function checkpoints(address account, uint32 pos)
public
view
virtual
returns (Checkpoint memory)
{
return _checkpoints[account][pos];
}
/**
* @dev Get number of checkpoints for `account`.
*/
function numCheckpoints(address account) public view virtual returns (uint32) {
return SafeCast.toUint32(_checkpoints[account].length);
}
/**
* @dev Get the address the `delegator` is currently delegating to.
* Return the `delegator` account if not delegating to anyone.
* @param delegator the account that is delegating the votes from
* @return delegatee that is receiving the delegated votes
*/
function delegates(address delegator) public view virtual returns (address) {
// Override as per https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/token/ERC20/extensions/ERC20VotesUpgradeable.sol#L23
// return _delegates[account];
address delegatee = _delegates[delegator];
return delegatee == address(0) ? delegator : delegatee;
}
/**
* @dev Gets the current votes balance for `account`
*/
function getVotes(address account) public view returns (uint256) {
uint256 pos = _checkpoints[account].length;
return pos == 0 ? 0 : _checkpoints[account][pos - 1].votes;
}
/**
* @dev Retrieve the number of votes for `account` at the end of `blockNumber`.
*
* Requirements:
*
* - `blockNumber` must have been already mined
*/
function getPastVotes(address account, uint256 blockNumber) public view returns (uint256) {
require(blockNumber < block.number, "ERC20Votes: block not yet mined");
return _checkpointsLookup(_checkpoints[account], blockNumber);
}
/**
* @dev Retrieve the `totalSupply` at the end of `blockNumber`. Note, this value is the sum of all balances.
* It is but NOT the sum of all the delegated votes!
*
* Requirements:
*
* - `blockNumber` must have been already mined
*/
function getPastTotalSupply(uint256 blockNumber) public view returns (uint256) {
require(blockNumber < block.number, "ERC20Votes: block not yet mined");
return _checkpointsLookup(_totalSupplyCheckpoints, blockNumber);
}
/**
* @dev Total sum of all scaled balances
*/
function totalSupply() public view override returns (uint256) {
uint256 len = _totalSupplyCheckpoints.length;
if (len == 0) return 0;
return _totalSupplyCheckpoints[len - 1].votes;
}
/**
* @dev Lookup a value in a list of (sorted) checkpoints.
*/
function _checkpointsLookup(Checkpoint[] storage ckpts, uint256 blockNumber)
private
view
returns (uint256)
{
// We run a binary search to look for the earliest checkpoint taken after `blockNumber`.
//
// During the loop, the index of the wanted checkpoint remains in the range [low, high).
// With each iteration, either `low` or `high` is moved towards the middle of the range to maintain the invariant.
// - If the middle checkpoint is after `blockNumber`, we look in [low, mid)
// - If the middle checkpoint is before or equal to `blockNumber`, we look in [mid+1, high)
// Once we reach a single value (when low == high), we've found the right checkpoint at the index high-1, if not
// out of bounds (in which case we're looking too far in the past and the result is 0).
// Note that if the latest checkpoint available is exactly for `blockNumber`, we end up with an index that is
// past the end of the array, so we technically don't find a checkpoint after `blockNumber`, but it works out
// the same.
uint256 high = ckpts.length;
uint256 low = 0;
while (low < high) {
uint256 mid = MathUpgradeable.average(low, high);
if (ckpts[mid].fromBlock > blockNumber) {
high = mid;
} else {
low = mid + 1;
}
}
return high == 0 ? 0 : ckpts[high - 1].votes;
}
/**
* @dev Delegate votes from the sender to `delegatee`.
* If `delegatee` is zero, the sender gets the voting power.
* @param delegatee account that gets the voting power.
*/
function delegate(address delegatee) public virtual {
return _delegate(_msgSender(), delegatee);
}
/**
* @dev Move voting power when tokens are transferred.
*
* Emits a {DelegateVotesChanged} event.
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual override {
super._afterTokenTransfer(from, to, amount);
// mint or burn, update total supply
if (from == address(0) || to == address(0)) {
_writeCheckpoint(_totalSupplyCheckpoints, to == address(0) ? _subtract : _add, amount);
}
_moveVotingPower(delegates(from), delegates(to), amount);
}
/**
* @dev Change delegation for `delegator` to `delegatee`.
*
* Emits events {DelegateChanged} and {DelegateVotesChanged}.
*/
function _delegate(address delegator, address delegatee) internal virtual {
address currentDelegatee = delegates(delegator);
uint256 delegatorBalance = balanceOf(delegator);
_delegates[delegator] = delegatee;
delegatee = delegates(delegator);
emit DelegateChanged(delegator, currentDelegatee, delegatee);
_moveVotingPower(currentDelegatee, delegatee, delegatorBalance);
}
function _moveVotingPower(
address src,
address dst,
uint256 amount
) private {
if (src != dst && amount > 0) {
if (src != address(0)) {
(uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(
_checkpoints[src],
_subtract,
amount
);
emit DelegateVotesChanged(src, oldWeight, newWeight);
}
if (dst != address(0)) {
(uint256 oldWeight, uint256 newWeight) = _writeCheckpoint(
_checkpoints[dst],
_add,
amount
);
emit DelegateVotesChanged(dst, oldWeight, newWeight);
}
if (address(_governanceHook) != address(0)) {
_governanceHook.moveVotingPowerHook(src, dst, amount);
}
}
}
function _writeCheckpoint(
Checkpoint[] storage ckpts,
function(uint256, uint256) view returns (uint256) op,
uint256 delta
) private returns (uint256 oldWeight, uint256 newWeight) {
uint256 pos = ckpts.length;
oldWeight = pos == 0 ? 0 : ckpts[pos - 1].votes;
newWeight = op(oldWeight, delta);
if (pos > 0 && ckpts[pos - 1].fromBlock == block.number) {
ckpts[pos - 1].votes = SafeCast.toUint224(newWeight);
} else {
ckpts.push(
Checkpoint({
fromBlock: SafeCast.toUint32(block.number),
votes: SafeCast.toUint224(newWeight)
})
);
}
}
function _add(uint256 a, uint256 b) private pure returns (uint256) {
return a + b;
}
function _subtract(uint256 a, uint256 b) private pure returns (uint256) {
return a - b;
}
uint256[46] private __gap;
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
library Root {
/**
* @dev Returns the square root of a given number
* @param x Input
* @return y Square root of Input
*/
function sqrt(uint256 x) internal pure returns (uint256 y) {
if (x == 0) return 0;
else {
uint256 xx = x;
uint256 r = 1;
if (xx >= 0x100000000000000000000000000000000) {
xx >>= 128;
r <<= 64;
}
if (xx >= 0x10000000000000000) {
xx >>= 64;
r <<= 32;
}
if (xx >= 0x100000000) {
xx >>= 32;
r <<= 16;
}
if (xx >= 0x10000) {
xx >>= 16;
r <<= 8;
}
if (xx >= 0x100) {
xx >>= 8;
r <<= 4;
}
if (xx >= 0x10) {
xx >>= 4;
r <<= 2;
}
if (xx >= 0x8) {
r <<= 1;
}
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1;
r = (r + x / r) >> 1; // Seven iterations should be enough
uint256 r1 = x / r;
return uint256(r < r1 ? r : r1);
}
}
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
/**
* @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].
*
* _Since v2.5.0:_ this module is now much more gas efficient, given net gas
* metering changes introduced in the Istanbul hardfork.
*/
contract InitializableReentrancyGuard {
bool private _notEntered;
function _initializeReentrancyGuard() internal {
// Storing an initial 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 percetange 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.
_notEntered = true;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_notEntered, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_notEntered = false;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_notEntered = true;
}
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
struct Balance {
/// units of staking token that has been deposited and consequently wrapped
uint88 raw;
/// (block.timestamp - weightedTimestamp) represents the seconds a user has had their full raw balance wrapped.
/// If they deposit or withdraw, the weightedTimestamp is dragged towards block.timestamp proportionately
uint32 weightedTimestamp;
/// multiplier awarded for staking for a long time
uint8 timeMultiplier;
/// multiplier duplicated from QuestManager
uint8 questMultiplier;
/// Time at which the relative cooldown began
uint32 cooldownTimestamp;
/// Units up for cooldown
uint88 cooldownUnits;
}
struct QuestBalance {
/// last timestamp at which the user made a write action to this contract
uint32 lastAction;
/// permanent multiplier applied to an account, awarded for PERMANENT QuestTypes
uint8 permMultiplier;
/// multiplier that decays after each "season" (~9 months) by 75%, to avoid multipliers getting out of control
uint8 seasonMultiplier;
}
/// @notice Quests can either give permanent rewards or only for the season
enum QuestType {
PERMANENT,
SEASONAL
}
/// @notice Quests can be turned off by the questMaster. All those who already completed remain
enum QuestStatus {
ACTIVE,
EXPIRED
}
struct Quest {
/// Type of quest rewards
QuestType model;
/// Multiplier, from 1 == 1.01x to 100 == 2.00x
uint8 multiplier;
/// Is the current quest valid?
QuestStatus status;
/// Expiry date in seconds for the quest
uint32 expiry;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library MathUpgradeable {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a / b + (a % b == 0 ? 0 : 1);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSAUpgradeable {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
} else if (error == RecoverError.InvalidSignatureV) {
revert("ECDSA: invalid signature 'v' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
// Check the signature length
// - case 65: r,s,v signature (standard)
// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
return tryRecover(hash, r, vs);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s;
uint8 v;
assembly {
s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
v := add(shr(255, vs), 27)
}
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
if (v != 27 && v != 28) {
return (address(0), RecoverError.InvalidSignatureV);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
import { SafeCastExtended } from "../../shared/SafeCastExtended.sol";
import { ILockedERC20 } from "./interfaces/ILockedERC20.sol";
import { HeadlessStakingRewards } from "../../rewards/staking/HeadlessStakingRewards.sol";
import { IQuestManager } from "./interfaces/IQuestManager.sol";
import "./deps/GamifiedTokenStructs.sol";
import { SlotFiller51 } from "../../shared/SlotFiller51.sol";
/**
* @title GamifiedToken
* @notice GamifiedToken is a non-transferrable ERC20 token that has both a raw balance and a scaled balance.
* Scaled balance is determined by quests a user completes, and the length of time they keep the raw balance wrapped.
* QuestMasters can add new quests for stakers to complete, for which they are rewarded with permanent or seasonal multipliers.
* @author mStable
* @dev Originally forked from openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol
* Changes:
* - Removed the transfer, transferFrom, approve fns to make non-transferrable
* - Removed `_allowances` storage
* - Removed `_beforeTokenTransfer` hook
* - Replaced standard uint256 balance with a single struct containing all data from which the scaledBalance can be derived
* - Quest system implemented that tracks a users quest status and applies multipliers for them
**/
abstract contract GamifiedToken is
ILockedERC20,
SlotFiller51,
HeadlessStakingRewards
{
/// @notice name of this token (ERC20)
bytes32 private _name;
/// @notice symbol of this token (ERC20)
bytes32 private _symbol;
/// @notice number of decimals of this token (ERC20)
uint8 public constant override decimals = 18;
/// @notice User balance structs containing all data needed to scale balance
mapping(address => Balance) internal _balances;
/// @notice Most recent price coefficients per user
mapping(address => uint256) internal _userPriceCoeff;
/// @notice Quest Manager
IQuestManager public immutable questManager;
/// @notice Has variable price
bool public immutable hasPriceCoeff;
/***************************************
INIT
****************************************/
/**
* @param _nexus System nexus
* @param _rewardsToken Token that is being distributed as a reward. eg MTA
* @param _questManager Centralised manager of quests
* @param _hasPriceCoeff true if raw staked amount is multiplied by price coeff to get staked amount. eg BPT Staked Token
*/
constructor(
address _nexus,
address _rewardsToken,
address _questManager,
bool _hasPriceCoeff
) HeadlessStakingRewards(_nexus, _rewardsToken) {
questManager = IQuestManager(_questManager);
hasPriceCoeff = _hasPriceCoeff;
}
/**
* @param _nameArg Token name
* @param _symbolArg Token symbol
* @param _rewardsDistributorArg mStable Rewards Distributor
*/
function __GamifiedToken_init(
bytes32 _nameArg,
bytes32 _symbolArg,
address _rewardsDistributorArg
) internal {
_name = _nameArg;
_symbol = _symbolArg;
HeadlessStakingRewards._initialize(_rewardsDistributorArg);
}
/**
* @dev Checks that _msgSender is the quest Manager
*/
modifier onlyQuestManager() {
require(_msgSender() == address(questManager), "Not verified");
_;
}
/***************************************
VIEWS
****************************************/
function name() public view override returns (string memory) {
return bytes32ToString(_name);
}
function symbol() public view override returns (string memory) {
return bytes32ToString(_symbol);
}
/**
* @dev Total sum of all scaled balances
* In this instance, leave to the child token.
*/
function totalSupply()
public
view
virtual
override(HeadlessStakingRewards, ILockedERC20)
returns (uint256);
/**
* @dev Simply gets scaled balance
* @return scaled balance for user
*/
function balanceOf(address _account)
public
view
virtual
override(HeadlessStakingRewards, ILockedERC20)
returns (uint256)
{
return _getBalance(_account, _balances[_account]);
}
/**
* @dev Simply gets raw balance
* @return raw balance for user
*/
function rawBalanceOf(address _account) public view returns (uint256, uint256) {
return (_balances[_account].raw, _balances[_account].cooldownUnits);
}
/**
* @dev Scales the balance of a given user by applying multipliers
*/
function _getBalance(address _account, Balance memory _balance)
internal
view
returns (uint256 balance)
{
// e.g. raw = 1000, questMultiplier = 40, timeMultiplier = 30. Cooldown of 60%
// e.g. 1000 * (100 + 40) / 100 = 1400
balance = (_balance.raw * (100 + _balance.questMultiplier)) / 100;
// e.g. 1400 * (100 + 30) / 100 = 1820
balance = (balance * (100 + _balance.timeMultiplier)) / 100;
if (hasPriceCoeff) {
// e.g. 1820 * 16000 / 10000 = 2912
balance = (balance * _userPriceCoeff[_account]) / 10000;
}
}
/**
* @notice Raw staked balance without any multipliers
*/
function balanceData(address _account) external view returns (Balance memory) {
return _balances[_account];
}
/**
* @notice Raw staked balance without any multipliers
*/
function userPriceCoeff(address _account) external view returns (uint256) {
return _userPriceCoeff[_account];
}
/***************************************
QUESTS
****************************************/
/**
* @dev Called by anyone to poke the timestamp of a given account. This allows users to
* effectively 'claim' any new timeMultiplier, but will revert if there is no change there.
*/
function reviewTimestamp(address _account) external {
_reviewWeightedTimestamp(_account);
}
/**
* @dev Adds the multiplier awarded from quest completion to a users data, taking the opportunity
* to check time multipliers etc.
* @param _account Address of user that should be updated
* @param _newMultiplier New Quest Multiplier
*/
function applyQuestMultiplier(address _account, uint8 _newMultiplier)
external
onlyQuestManager
{
require(_account != address(0), "Invalid address");
// 1. Get current balance & update questMultiplier, only if user has a balance
Balance memory oldBalance = _balances[_account];
uint256 oldScaledBalance = _getBalance(_account, oldBalance);
if (oldScaledBalance > 0) {
_applyQuestMultiplier(_account, oldBalance, oldScaledBalance, _newMultiplier);
}
}
/**
* @dev Gets the multiplier awarded for a given weightedTimestamp
* @param _ts WeightedTimestamp of a user
* @return timeMultiplier Ranging from 20 (0.2x) to 60 (0.6x)
*/
function _timeMultiplier(uint32 _ts) internal view returns (uint8 timeMultiplier) {
// If the user has no ts yet, they are not in the system
if (_ts == 0) return 0;
uint256 hodlLength = block.timestamp - _ts;
if (hodlLength < 13 weeks) {
// 0-3 months = 1x
return 0;
} else if (hodlLength < 26 weeks) {
// 3 months = 1.2x
return 20;
} else if (hodlLength < 52 weeks) {
// 6 months = 1.3x
return 30;
} else if (hodlLength < 78 weeks) {
// 12 months = 1.4x
return 40;
} else if (hodlLength < 104 weeks) {
// 18 months = 1.5x
return 50;
} else {
// > 24 months = 1.6x
return 60;
}
}
function _getPriceCoeff() internal virtual returns (uint256) {
return 10000;
}
/***************************************
BALANCE CHANGES
****************************************/
/**
* @dev Adds the multiplier awarded from quest completion to a users data, taking the opportunity
* to check time multiplier.
* @param _account Address of user that should be updated
* @param _newMultiplier New Quest Multiplier
*/
function _applyQuestMultiplier(
address _account,
Balance memory _oldBalance,
uint256 _oldScaledBalance,
uint8 _newMultiplier
) private updateReward(_account) {
// 1. Set the questMultiplier
_balances[_account].questMultiplier = _newMultiplier;
// 2. Take the opportunity to set weighted timestamp, if it changes
_balances[_account].timeMultiplier = _timeMultiplier(_oldBalance.weightedTimestamp);
// 3. Update scaled balance
_settleScaledBalance(_account, _oldScaledBalance);
}
/**
* @dev Entering a cooldown period means a user wishes to withdraw. With this in mind, their balance
* should be reduced until they have shown more commitment to the system
* @param _account Address of user that should be cooled
* @param _units Units to cooldown for
*/
function _enterCooldownPeriod(address _account, uint256 _units)
internal
updateReward(_account)
{
require(_account != address(0), "Invalid address");
// 1. Get current balance
(Balance memory oldBalance, uint256 oldScaledBalance) = _prepareOldBalance(_account);
uint88 totalUnits = oldBalance.raw + oldBalance.cooldownUnits;
require(_units > 0 && _units <= totalUnits, "Must choose between 0 and 100%");
// 2. Set weighted timestamp and enter cooldown
_balances[_account].timeMultiplier = _timeMultiplier(oldBalance.weightedTimestamp);
// e.g. 1e18 / 1e16 = 100, 2e16 / 1e16 = 2, 1e15/1e16 = 0
_balances[_account].raw = totalUnits - SafeCastExtended.toUint88(_units);
// 3. Set cooldown data
_balances[_account].cooldownTimestamp = SafeCastExtended.toUint32(block.timestamp);
_balances[_account].cooldownUnits = SafeCastExtended.toUint88(_units);
// 4. Update scaled balance
_settleScaledBalance(_account, oldScaledBalance);
}
/**
* @dev Exiting the cooldown period explicitly resets the users cooldown window and their balance
* @param _account Address of user that should be exited
*/
function _exitCooldownPeriod(address _account) internal updateReward(_account) {
require(_account != address(0), "Invalid address");
// 1. Get current balance
(Balance memory oldBalance, uint256 oldScaledBalance) = _prepareOldBalance(_account);
// 2. Set weighted timestamp and exit cooldown
_balances[_account].timeMultiplier = _timeMultiplier(oldBalance.weightedTimestamp);
_balances[_account].raw += oldBalance.cooldownUnits;
// 3. Set cooldown data
_balances[_account].cooldownTimestamp = 0;
_balances[_account].cooldownUnits = 0;
// 4. Update scaled balance
_settleScaledBalance(_account, oldScaledBalance);
}
/**
* @dev Pokes the weightedTimestamp of a given user and checks if it entitles them
* to a better timeMultiplier. If not, it simply reverts as there is nothing to update.
* @param _account Address of user that should be updated
*/
function _reviewWeightedTimestamp(address _account) internal updateReward(_account) {
require(_account != address(0), "Invalid address");
// 1. Get current balance
(Balance memory oldBalance, uint256 oldScaledBalance) = _prepareOldBalance(_account);
// 2. Set weighted timestamp, if it changes
uint8 newTimeMultiplier = _timeMultiplier(oldBalance.weightedTimestamp);
require(newTimeMultiplier != oldBalance.timeMultiplier, "Nothing worth poking here");
_balances[_account].timeMultiplier = newTimeMultiplier;
// 3. Update scaled balance
_settleScaledBalance(_account, oldScaledBalance);
}
/**
* @dev Called to mint from raw tokens. Adds raw to a users balance, and then propagates the scaledBalance.
* Importantly, when a user stakes more, their weightedTimestamp is reduced proportionate to their stake.
* @param _account Address of user to credit
* @param _rawAmount Raw amount of tokens staked
* @param _exitCooldown Should we end any cooldown?
*/
function _mintRaw(
address _account,
uint256 _rawAmount,
bool _exitCooldown
) internal updateReward(_account) {
require(_account != address(0), "ERC20: mint to the zero address");
// 1. Get and update current balance
(Balance memory oldBalance, uint256 oldScaledBalance) = _prepareOldBalance(_account);
uint88 totalRaw = oldBalance.raw + oldBalance.cooldownUnits;
_balances[_account].raw = oldBalance.raw + SafeCastExtended.toUint88(_rawAmount);
// 2. Exit cooldown if necessary
if (_exitCooldown) {
_balances[_account].raw += oldBalance.cooldownUnits;
_balances[_account].cooldownTimestamp = 0;
_balances[_account].cooldownUnits = 0;
}
// 3. Set weighted timestamp
// i) For new _account, set up weighted timestamp
if (oldBalance.weightedTimestamp == 0) {
_balances[_account].weightedTimestamp = SafeCastExtended.toUint32(block.timestamp);
_mintScaled(_account, _getBalance(_account, _balances[_account]));
return;
}
// ii) For previous minters, recalculate time held
// Calc new weighted timestamp
uint256 oldWeightedSecondsHeld = (block.timestamp - oldBalance.weightedTimestamp) *
totalRaw;
uint256 newSecondsHeld = oldWeightedSecondsHeld / (totalRaw + (_rawAmount / 2));
uint32 newWeightedTs = SafeCastExtended.toUint32(block.timestamp - newSecondsHeld);
_balances[_account].weightedTimestamp = newWeightedTs;
uint8 timeMultiplier = _timeMultiplier(newWeightedTs);
_balances[_account].timeMultiplier = timeMultiplier;
// 3. Update scaled balance
_settleScaledBalance(_account, oldScaledBalance);
}
/**
* @dev Called to burn a given amount of raw tokens.
* @param _account Address of user
* @param _rawAmount Raw amount of tokens to remove
* @param _exitCooldown Exit the cooldown?
* @param _finalise Has recollateralisation happened? If so, everything is cooled down
*/
function _burnRaw(
address _account,
uint256 _rawAmount,
bool _exitCooldown,
bool _finalise
) internal updateReward(_account) {
require(_account != address(0), "ERC20: burn from zero address");
// 1. Get and update current balance
(Balance memory oldBalance, uint256 oldScaledBalance) = _prepareOldBalance(_account);
uint256 totalRaw = oldBalance.raw + oldBalance.cooldownUnits;
// 1.1. If _finalise, move everything to cooldown
if (_finalise) {
_balances[_account].raw = 0;
_balances[_account].cooldownUnits = SafeCastExtended.toUint88(totalRaw);
oldBalance.cooldownUnits = SafeCastExtended.toUint88(totalRaw);
}
// 1.2. Update
require(oldBalance.cooldownUnits >= _rawAmount, "ERC20: burn amount > balance");
unchecked {
_balances[_account].cooldownUnits -= SafeCastExtended.toUint88(_rawAmount);
}
// 2. If we are exiting cooldown, reset the balance
if (_exitCooldown) {
_balances[_account].raw += _balances[_account].cooldownUnits;
_balances[_account].cooldownTimestamp = 0;
_balances[_account].cooldownUnits = 0;
}
// 3. Set back scaled time
// e.g. stake 10 for 100 seconds, withdraw 5.
// secondsHeld = (100 - 0) * (10 - 0.625) = 937.5
uint256 secondsHeld = (block.timestamp - oldBalance.weightedTimestamp) *
(totalRaw - (_rawAmount / 8));
// newWeightedTs = 937.5 / 100 = 93.75
uint256 newSecondsHeld = secondsHeld / totalRaw;
uint32 newWeightedTs = SafeCastExtended.toUint32(block.timestamp - newSecondsHeld);
_balances[_account].weightedTimestamp = newWeightedTs;
uint8 timeMultiplier = _timeMultiplier(newWeightedTs);
_balances[_account].timeMultiplier = timeMultiplier;
// 4. Update scaled balance
_settleScaledBalance(_account, oldScaledBalance);
}
/***************************************
PRIVATE
updateReward should already be called by now
****************************************/
/**
* @dev Fetches the balance of a given user, scales it, and also takes the opportunity
* to check if the season has just finished between now and their last action.
* @param _account Address of user to fetch
* @return oldBalance struct containing all balance information
* @return oldScaledBalance scaled balance after applying multipliers
*/
function _prepareOldBalance(address _account)
private
returns (Balance memory oldBalance, uint256 oldScaledBalance)
{
// Get the old balance
oldBalance = _balances[_account];
oldScaledBalance = _getBalance(_account, oldBalance);
// Take the opportunity to check for season finish
_balances[_account].questMultiplier = questManager.checkForSeasonFinish(_account);
if (hasPriceCoeff) {
_userPriceCoeff[_account] = SafeCastExtended.toUint16(_getPriceCoeff());
}
}
/**
* @dev Settles the scaled balance of a given account. The reason this is done here, is because
* in each of the write functions above, there is the chance that a users balance can go down,
* requiring to burn sacled tokens. This could happen at the end of a season when multipliers are slashed.
* This is called after updating all multipliers etc.
* @param _account Address of user that should be updated
* @param _oldScaledBalance Previous scaled balance of the user
*/
function _settleScaledBalance(address _account, uint256 _oldScaledBalance) private {
uint256 newScaledBalance = _getBalance(_account, _balances[_account]);
if (newScaledBalance > _oldScaledBalance) {
_mintScaled(_account, newScaledBalance - _oldScaledBalance);
}
// This can happen if the user moves back a time class, but is unlikely to result in a negative mint
else {
_burnScaled(_account, _oldScaledBalance - newScaledBalance);
}
}
/**
* @dev Propagates the minting of the tokens downwards.
* @param _account Address of user that has minted
* @param _amount Amount of scaled tokens minted
*/
function _mintScaled(address _account, uint256 _amount) private {
emit Transfer(address(0), _account, _amount);
_afterTokenTransfer(address(0), _account, _amount);
}
/**
* @dev Propagates the burning of the tokens downwards.
* @param _account Address of user that has burned
* @param _amount Amount of scaled tokens burned
*/
function _burnScaled(address _account, uint256 _amount) private {
emit Transfer(_account, address(0), _amount);
_afterTokenTransfer(_account, address(0), _amount);
}
/***************************************
HOOKS
****************************************/
/**
* @dev Triggered after a user claims rewards from the HeadlessStakingRewards. Used
* to check for season finish. If it has not, then do not spend gas updating the other vars.
* @param _account Address of user that has burned
*/
function _claimRewardHook(address _account) internal override {
uint8 newMultiplier = questManager.checkForSeasonFinish(_account);
bool priceCoeffChanged = hasPriceCoeff
? _getPriceCoeff() != _userPriceCoeff[_account]
: false;
if (newMultiplier != _balances[_account].questMultiplier || priceCoeffChanged) {
// 1. Get current balance & trigger season finish
uint256 oldScaledBalance = _getBalance(_account, _balances[_account]);
_balances[_account].questMultiplier = newMultiplier;
if (priceCoeffChanged) {
_userPriceCoeff[_account] = SafeCastExtended.toUint16(_getPriceCoeff());
}
// 3. Update scaled balance
_settleScaledBalance(_account, oldScaledBalance);
}
}
/**
* @dev Unchanged from OpenZeppelin. Used in child contracts to react to any balance changes.
*/
function _afterTokenTransfer(
address _from,
address _to,
uint256 _amount
) internal virtual {}
/***************************************
Utils
****************************************/
function bytes32ToString(bytes32 _bytes32) internal pure returns (string memory) {
uint256 i = 0;
while (i < 32 && _bytes32[i] != 0) {
i++;
}
bytes memory bytesArray = new bytes(i);
for (i = 0; i < 32 && _bytes32[i] != 0; i++) {
bytesArray[i] = _bytes32[i];
}
return string(bytesArray);
}
uint256[46] private __gap;
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
interface IGovernanceHook {
function moveVotingPowerHook(
address from,
address to,
uint256 amount
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such 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.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCastExtended {
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
return uint128(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
return uint88(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
return uint64(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
return uint32(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits.
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128) {
require(
value >= type(int128).min && value <= type(int128).max,
"SafeCast: value doesn't fit in 128 bits"
);
return int128(value);
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64) {
require(
value >= type(int64).min && value <= type(int64).max,
"SafeCast: value doesn't fit in 64 bits"
);
return int64(value);
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32) {
require(
value >= type(int32).min && value <= type(int32).max,
"SafeCast: value doesn't fit in 32 bits"
);
return int32(value);
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16) {
require(
value >= type(int16).min && value <= type(int16).max,
"SafeCast: value doesn't fit in 16 bits"
);
return int16(value);
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits.
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8) {
require(
value >= type(int8).min && value <= type(int8).max,
"SafeCast: value doesn't fit in 8 bits"
);
return int8(value);
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
return int256(value);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface ILockedERC20 {
/**
* @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 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 Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
// Internal
import { InitializableRewardsDistributionRecipient } from "../InitializableRewardsDistributionRecipient.sol";
import { Context } from "../../shared/@openzeppelin-2.5/Context.sol";
import { StableMath } from "../../shared/StableMath.sol";
import { PlatformTokenVendorFactory } from "./PlatformTokenVendorFactory.sol";
// Libs
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
/**
* @title HeadlessStakingRewards
* @author mStable
* @notice Rewards stakers of a given LP token with REWARDS_TOKEN, on a pro-rata basis
* @dev Forked from `StakingRewards.sol`
* Changes:
* - `pendingAdditionalReward` added to support accumulation of any extra staking token
* - Removal of `StakingTokenWrapper`, instead, deposits and withdrawals are made in child contract,
* and balances are read from there through the abstract functions
*/
abstract contract HeadlessStakingRewards is
Context,
InitializableRewardsDistributionRecipient
{
using SafeERC20 for IERC20;
using StableMath for uint256;
/// @notice token the rewards are distributed in. eg MTA
IERC20 public immutable REWARDS_TOKEN;
/// @notice length of each staking period in seconds. 7 days = 604,800; 3 months = 7,862,400
uint256 public constant DURATION = 1 weeks;
/// @notice contract that holds the platform tokens
address public rewardTokenVendor;
struct Data {
/// Timestamp for current period finish
uint32 periodFinish;
/// Last time any user took action
uint32 lastUpdateTime;
/// RewardRate for the rest of the period
uint96 rewardRate;
/// Ever increasing rewardPerToken rate, based on % of total supply
uint96 rewardPerTokenStored;
}
struct UserData {
uint128 rewardPerTokenPaid;
uint128 rewards;
}
Data public globalData;
mapping(address => UserData) public userData;
uint256 public pendingAdditionalReward;
event RewardAdded(uint256 reward);
event RewardPaid(address indexed user, address indexed to, uint256 reward);
/**
* @param _nexus mStable system Nexus address
* @param _rewardsToken first token that is being distributed as a reward. eg MTA
*/
constructor(address _nexus, address _rewardsToken)
InitializableRewardsDistributionRecipient(_nexus)
{
REWARDS_TOKEN = IERC20(_rewardsToken);
}
/**
* @dev Initialization function for upgradable proxy contract.
* This function should be called via Proxy just after contract deployment.
* To avoid variable shadowing appended `Arg` after arguments name.
* @param _rewardsDistributorArg mStable Reward Distributor contract address
*/
function _initialize(address _rewardsDistributorArg) internal virtual override {
InitializableRewardsDistributionRecipient._initialize(_rewardsDistributorArg);
rewardTokenVendor = PlatformTokenVendorFactory.create(REWARDS_TOKEN);
}
/** @dev Updates the reward for a given address, before executing function */
modifier updateReward(address _account) {
_updateReward(_account);
_;
}
function _updateReward(address _account) internal {
// Setting of global vars
(uint256 newRewardPerToken, uint256 lastApplicableTime) = _rewardPerToken();
// If statement protects against loss in initialisation case
if (newRewardPerToken > 0) {
globalData.rewardPerTokenStored = SafeCast.toUint96(newRewardPerToken);
globalData.lastUpdateTime = SafeCast.toUint32(lastApplicableTime);
// Setting of personal vars based on new globals
if (_account != address(0)) {
userData[_account] = UserData({
rewardPerTokenPaid: SafeCast.toUint128(newRewardPerToken),
rewards: SafeCast.toUint128(_earned(_account, newRewardPerToken))
});
}
}
}
/***************************************
ACTIONS
****************************************/
/**
* @dev Claims outstanding rewards for the sender.
* First updates outstanding reward allocation and then transfers.
*/
function claimReward(address _to) public {
_claimReward(_to);
}
/**
* @dev Claims outstanding rewards for the sender.
* First updates outstanding reward allocation and then transfers.
*/
function claimReward() public {
_claimReward(_msgSender());
}
function _claimReward(address _to) internal updateReward(_msgSender()) {
uint128 reward = userData[_msgSender()].rewards;
if (reward > 0) {
userData[_msgSender()].rewards = 0;
REWARDS_TOKEN.safeTransferFrom(rewardTokenVendor, _to, reward);
emit RewardPaid(_msgSender(), _to, reward);
}
_claimRewardHook(_msgSender());
}
/***************************************
GETTERS
****************************************/
/**
* @dev Gets the RewardsToken
*/
function getRewardToken() external view override returns (IERC20) {
return REWARDS_TOKEN;
}
/**
* @dev Gets the last applicable timestamp for this reward period
*/
function lastTimeRewardApplicable() public view returns (uint256) {
return StableMath.min(block.timestamp, globalData.periodFinish);
}
/**
* @dev Calculates the amount of unclaimed rewards per token since last update,
* and sums with stored to give the new cumulative reward per token
* @return 'Reward' per staked token
*/
function rewardPerToken() public view returns (uint256) {
(uint256 rewardPerToken_, ) = _rewardPerToken();
return rewardPerToken_;
}
function _rewardPerToken()
internal
view
returns (uint256 rewardPerToken_, uint256 lastTimeRewardApplicable_)
{
uint256 lastApplicableTime = lastTimeRewardApplicable(); // + 1 SLOAD
Data memory data = globalData;
uint256 timeDelta = lastApplicableTime - data.lastUpdateTime; // + 1 SLOAD
// If this has been called twice in the same block, shortcircuit to reduce gas
if (timeDelta == 0) {
return (data.rewardPerTokenStored, lastApplicableTime);
}
// new reward units to distribute = rewardRate * timeSinceLastUpdate
uint256 rewardUnitsToDistribute = data.rewardRate * timeDelta; // + 1 SLOAD
uint256 supply = totalSupply(); // + 1 SLOAD
// If there is no StakingToken liquidity, avoid div(0)
// If there is nothing to distribute, short circuit
if (supply == 0 || rewardUnitsToDistribute == 0) {
return (data.rewardPerTokenStored, lastApplicableTime);
}
// new reward units per token = (rewardUnitsToDistribute * 1e18) / totalTokens
uint256 unitsToDistributePerToken = rewardUnitsToDistribute.divPrecisely(supply);
// return summed rate
return (data.rewardPerTokenStored + unitsToDistributePerToken, lastApplicableTime); // + 1 SLOAD
}
/**
* @dev Calculates the amount of unclaimed rewards a user has earned
* @param _account User address
* @return Total reward amount earned
*/
function earned(address _account) public view returns (uint256) {
return _earned(_account, rewardPerToken());
}
function _earned(address _account, uint256 _currentRewardPerToken)
internal
view
returns (uint256)
{
// current rate per token - rate user previously received
uint256 userRewardDelta = _currentRewardPerToken - userData[_account].rewardPerTokenPaid; // + 1 SLOAD
// Short circuit if there is nothing new to distribute
if (userRewardDelta == 0) {
return userData[_account].rewards;
}
// new reward = staked tokens * difference in rate
uint256 userNewReward = balanceOf(_account).mulTruncate(userRewardDelta); // + 1 SLOAD
// add to previous rewards
return userData[_account].rewards + userNewReward;
}
/***************************************
ABSTRACT
****************************************/
function balanceOf(address account) public view virtual returns (uint256);
function totalSupply() public view virtual returns (uint256);
function _claimRewardHook(address account) internal virtual;
/***************************************
ADMIN
****************************************/
/**
* @dev Notifies the contract that new rewards have been added.
* Calculates an updated rewardRate based on the rewards in period.
* @param _reward Units of RewardToken that have been added to the pool
*/
function notifyRewardAmount(uint256 _reward)
external
override
onlyRewardsDistributor
updateReward(address(0))
{
require(_reward < 1e24, "Notify more than a million units");
uint256 currentTime = block.timestamp;
// Pay and reset the pendingAdditionalRewards
if (pendingAdditionalReward > 1) {
_reward += (pendingAdditionalReward - 1);
pendingAdditionalReward = 1;
}
if (_reward > 0) {
REWARDS_TOKEN.safeTransfer(rewardTokenVendor, _reward);
}
// If previous period over, reset rewardRate
if (currentTime >= globalData.periodFinish) {
globalData.rewardRate = SafeCast.toUint96(_reward / DURATION);
}
// If additional reward to existing period, calc sum
else {
uint256 remainingSeconds = globalData.periodFinish - currentTime;
uint256 leftover = remainingSeconds * globalData.rewardRate;
globalData.rewardRate = SafeCast.toUint96((_reward + leftover) / DURATION);
}
globalData.lastUpdateTime = SafeCast.toUint32(currentTime);
globalData.periodFinish = SafeCast.toUint32(currentTime + DURATION);
emit RewardAdded(_reward);
}
/**
* @dev Called by the child contract to notify of any additional rewards that have accrued.
* Trusts that this is called honestly.
* @param _additionalReward Units of additional RewardToken to add at the next notification
*/
function _notifyAdditionalReward(uint256 _additionalReward) internal virtual {
require(_additionalReward < 1e24, "Cannot notify with more than a million units");
pendingAdditionalReward += _additionalReward;
}
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
import "../deps/GamifiedTokenStructs.sol";
interface IQuestManager {
event QuestAdded(
address questMaster,
uint256 id,
QuestType model,
uint16 multiplier,
QuestStatus status,
uint32 expiry
);
event QuestCompleteQuests(address indexed user, uint256[] ids);
event QuestCompleteUsers(uint256 indexed questId, address[] accounts);
event QuestExpired(uint16 indexed id);
event QuestMaster(address oldQuestMaster, address newQuestMaster);
event QuestSeasonEnded();
event QuestSigner(address oldQuestSigner, address newQuestSigner);
event StakedTokenAdded(address stakedToken);
// GETTERS
function balanceData(address _account) external view returns (QuestBalance memory);
function getQuest(uint256 _id) external view returns (Quest memory);
function hasCompleted(address _account, uint256 _id) external view returns (bool);
function questMaster() external view returns (address);
function seasonEpoch() external view returns (uint32);
// ADMIN
function addQuest(
QuestType _model,
uint8 _multiplier,
uint32 _expiry
) external;
function addStakedToken(address _stakedToken) external;
function expireQuest(uint16 _id) external;
function setQuestMaster(address _newQuestMaster) external;
function setQuestSigner(address _newQuestSigner) external;
function startNewQuestSeason() external;
// USER
function completeUserQuests(
address _account,
uint256[] memory _ids,
bytes calldata _signature
) external;
function completeQuestUsers(
uint256 _questId,
address[] memory _accounts,
bytes calldata _signature
) external;
function checkForSeasonFinish(address _account) external returns (uint8 newQuestMultiplier);
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
/**
* @title Fills 51 storage slots.
* @notice To be used in inheritance when upgrading contracts to preserve previous storage slots.
* @author mStable
* @dev VERSION: 1.0
* DATE: 2022-04-13
*/
contract SlotFiller51 {
uint256[51] private __gap;
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
import { ImmutableModule } from "../shared/ImmutableModule.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IRewardsDistributionRecipient } from "../interfaces/IRewardsDistributionRecipient.sol";
/**
* @title RewardsDistributionRecipient
* @author Originally: Synthetix (forked from /Synthetixio/synthetix/contracts/RewardsDistributionRecipient.sol)
* Changes by: mStable
* @notice RewardsDistributionRecipient gets notified of additional rewards by the rewardsDistributor
* @dev Changes: Addition of Module and abstract `getRewardToken` func + cosmetic
*/
abstract contract InitializableRewardsDistributionRecipient is
IRewardsDistributionRecipient,
ImmutableModule
{
// This address has the ability to distribute the rewards
address public rewardsDistributor;
constructor(address _nexus) ImmutableModule(_nexus) {}
/** @dev Recipient is a module, governed by mStable governance */
function _initialize(address _rewardsDistributor) internal virtual {
rewardsDistributor = _rewardsDistributor;
}
/**
* @dev Only the rewards distributor can notify about rewards
*/
modifier onlyRewardsDistributor() {
require(msg.sender == rewardsDistributor, "Caller is not reward distributor");
_;
}
/**
* @dev Change the rewardsDistributor - only called by mStable governor
* @param _rewardsDistributor Address of the new distributor
*/
function setRewardsDistribution(address _rewardsDistributor) external onlyGovernor {
rewardsDistributor = _rewardsDistributor;
}
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
// Empty internal constructor, to prevent people from mistakenly deploying
// an instance of this contract, which should be used via inheritance.
// constructor () internal { }
// solhint-disable-previous-line no-empty-blocks
function _msgSender() internal view returns (address payable) {
return payable(msg.sender);
}
function _msgData() internal view returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
/**
* @title StableMath
* @author mStable
* @notice A library providing safe mathematical operations to multiply and
* divide with standardised precision.
* @dev Derives from OpenZeppelin's SafeMath lib and uses generic system
* wide variables for managing precision.
*/
library StableMath {
/**
* @dev Scaling unit for use in specific calculations,
* where 1 * 10**18, or 1e18 represents a unit '1'
*/
uint256 private constant FULL_SCALE = 1e18;
/**
* @dev Token Ratios are used when converting between units of bAsset, mAsset and MTA
* Reasoning: Takes into account token decimals, and difference in base unit (i.e. grams to Troy oz for gold)
* bAsset ratio unit for use in exact calculations,
* where (1 bAsset unit * bAsset.ratio) / ratioScale == x mAsset unit
*/
uint256 private constant RATIO_SCALE = 1e8;
/**
* @dev Provides an interface to the scaling unit
* @return Scaling unit (1e18 or 1 * 10**18)
*/
function getFullScale() internal pure returns (uint256) {
return FULL_SCALE;
}
/**
* @dev Provides an interface to the ratio unit
* @return Ratio scale unit (1e8 or 1 * 10**8)
*/
function getRatioScale() internal pure returns (uint256) {
return RATIO_SCALE;
}
/**
* @dev Scales a given integer to the power of the full scale.
* @param x Simple uint256 to scale
* @return Scaled value a to an exact number
*/
function scaleInteger(uint256 x) internal pure returns (uint256) {
return x * FULL_SCALE;
}
/***************************************
PRECISE ARITHMETIC
****************************************/
/**
* @dev Multiplies two precise units, and then truncates by the full scale
* @param x Left hand input to multiplication
* @param y Right hand input to multiplication
* @return Result after multiplying the two inputs and then dividing by the shared
* scale unit
*/
function mulTruncate(uint256 x, uint256 y) internal pure returns (uint256) {
return mulTruncateScale(x, y, FULL_SCALE);
}
/**
* @dev Multiplies two precise units, and then truncates by the given scale. For example,
* when calculating 90% of 10e18, (10e18 * 9e17) / 1e18 = (9e36) / 1e18 = 9e18
* @param x Left hand input to multiplication
* @param y Right hand input to multiplication
* @param scale Scale unit
* @return Result after multiplying the two inputs and then dividing by the shared
* scale unit
*/
function mulTruncateScale(
uint256 x,
uint256 y,
uint256 scale
) internal pure returns (uint256) {
// e.g. assume scale = fullScale
// z = 10e18 * 9e17 = 9e36
// return 9e36 / 1e18 = 9e18
return (x * y) / scale;
}
/**
* @dev Multiplies two precise units, and then truncates by the full scale, rounding up the result
* @param x Left hand input to multiplication
* @param y Right hand input to multiplication
* @return Result after multiplying the two inputs and then dividing by the shared
* scale unit, rounded up to the closest base unit.
*/
function mulTruncateCeil(uint256 x, uint256 y) internal pure returns (uint256) {
// e.g. 8e17 * 17268172638 = 138145381104e17
uint256 scaled = x * y;
// e.g. 138145381104e17 + 9.99...e17 = 138145381113.99...e17
uint256 ceil = scaled + FULL_SCALE - 1;
// e.g. 13814538111.399...e18 / 1e18 = 13814538111
return ceil / FULL_SCALE;
}
/**
* @dev Precisely divides two units, by first scaling the left hand operand. Useful
* for finding percentage weightings, i.e. 8e18/10e18 = 80% (or 8e17)
* @param x Left hand input to division
* @param y Right hand input to division
* @return Result after multiplying the left operand by the scale, and
* executing the division on the right hand input.
*/
function divPrecisely(uint256 x, uint256 y) internal pure returns (uint256) {
// e.g. 8e18 * 1e18 = 8e36
// e.g. 8e36 / 10e18 = 8e17
return (x * FULL_SCALE) / y;
}
/***************************************
RATIO FUNCS
****************************************/
/**
* @dev Multiplies and truncates a token ratio, essentially flooring the result
* i.e. How much mAsset is this bAsset worth?
* @param x Left hand operand to multiplication (i.e Exact quantity)
* @param ratio bAsset ratio
* @return c Result after multiplying the two inputs and then dividing by the ratio scale
*/
function mulRatioTruncate(uint256 x, uint256 ratio) internal pure returns (uint256 c) {
return mulTruncateScale(x, ratio, RATIO_SCALE);
}
/**
* @dev Multiplies and truncates a token ratio, rounding up the result
* i.e. How much mAsset is this bAsset worth?
* @param x Left hand input to multiplication (i.e Exact quantity)
* @param ratio bAsset ratio
* @return Result after multiplying the two inputs and then dividing by the shared
* ratio scale, rounded up to the closest base unit.
*/
function mulRatioTruncateCeil(uint256 x, uint256 ratio) internal pure returns (uint256) {
// e.g. How much mAsset should I burn for this bAsset (x)?
// 1e18 * 1e8 = 1e26
uint256 scaled = x * ratio;
// 1e26 + 9.99e7 = 100..00.999e8
uint256 ceil = scaled + RATIO_SCALE - 1;
// return 100..00.999e8 / 1e8 = 1e18
return ceil / RATIO_SCALE;
}
/**
* @dev Precisely divides two ratioed units, by first scaling the left hand operand
* i.e. How much bAsset is this mAsset worth?
* @param x Left hand operand in division
* @param ratio bAsset ratio
* @return c Result after multiplying the left operand by the scale, and
* executing the division on the right hand input.
*/
function divRatioPrecisely(uint256 x, uint256 ratio) internal pure returns (uint256 c) {
// e.g. 1e14 * 1e8 = 1e22
// return 1e22 / 1e12 = 1e10
return (x * RATIO_SCALE) / ratio;
}
/***************************************
HELPERS
****************************************/
/**
* @dev Calculates minimum of two numbers
* @param x Left hand input
* @param y Right hand input
* @return Minimum of the two inputs
*/
function min(uint256 x, uint256 y) internal pure returns (uint256) {
return x > y ? y : x;
}
/**
* @dev Calculated maximum of two numbers
* @param x Left hand input
* @param y Right hand input
* @return Maximum of the two inputs
*/
function max(uint256 x, uint256 y) internal pure returns (uint256) {
return x > y ? x : y;
}
/**
* @dev Clamps a value to an upper bound
* @param x Left hand input
* @param upperBound Maximum possible value to return
* @return Input x clamped to a maximum value, upperBound
*/
function clamp(uint256 x, uint256 upperBound) internal pure returns (uint256) {
return x > upperBound ? upperBound : x;
}
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { PlatformTokenVendor } from "./PlatformTokenVendor.sol";
/**
* @title PlatformTokenVendorFactory
* @author mStable
* @notice Library that deploys a PlatformTokenVendor contract which holds rewards tokens
* @dev Used to reduce the byte size of the contracts that need to deploy a PlatformTokenVendor contract
*/
library PlatformTokenVendorFactory {
/// @dev for some reason Typechain will not generate the types if the library only has the create function
function dummy() public pure returns (bool) {
return true;
}
/**
* @notice Deploys a new PlatformTokenVendor contract
* @param _rewardsToken reward or platform rewards token. eg MTA or WMATIC
* @return address of the deployed PlatformTokenVendor contract
*/
function create(IERC20 _rewardsToken) public returns (address) {
PlatformTokenVendor newPlatformTokenVendor = new PlatformTokenVendor(_rewardsToken);
return address(newPlatformTokenVendor);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^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 {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*/
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 || !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
import { ModuleKeys } from "./ModuleKeys.sol";
import { INexus } from "../interfaces/INexus.sol";
/**
* @title ImmutableModule
* @author mStable
* @dev Subscribes to module updates from a given publisher and reads from its registry.
* Contract is used for upgradable proxy contracts.
*/
abstract contract ImmutableModule is ModuleKeys {
INexus public immutable nexus;
/**
* @dev Initialization function for upgradable proxy contracts
* @param _nexus Nexus contract address
*/
constructor(address _nexus) {
require(_nexus != address(0), "Nexus address is zero");
nexus = INexus(_nexus);
}
/**
* @dev Modifier to allow function calls only from the Governor.
*/
modifier onlyGovernor() {
_onlyGovernor();
_;
}
function _onlyGovernor() internal view {
require(msg.sender == _governor(), "Only governor can execute");
}
/**
* @dev Modifier to allow function calls only from the Governor or the Keeper EOA.
*/
modifier onlyKeeperOrGovernor() {
_keeperOrGovernor();
_;
}
function _keeperOrGovernor() internal view {
require(msg.sender == _keeper() || msg.sender == _governor(), "Only keeper or governor");
}
/**
* @dev Modifier to allow function calls only from the Governance.
* Governance is either Governor address or Governance address.
*/
modifier onlyGovernance() {
require(
msg.sender == _governor() || msg.sender == _governance(),
"Only governance can execute"
);
_;
}
/**
* @dev Returns Governor address from the Nexus
* @return Address of Governor Contract
*/
function _governor() internal view returns (address) {
return nexus.governor();
}
/**
* @dev Returns Governance Module address from the Nexus
* @return Address of the Governance (Phase 2)
*/
function _governance() internal view returns (address) {
return nexus.getModule(KEY_GOVERNANCE);
}
/**
* @dev Return Keeper address from the Nexus.
* This account is used for operational transactions that
* don't need multiple signatures.
* @return Address of the Keeper externally owned account.
*/
function _keeper() internal view returns (address) {
return nexus.getModule(KEY_KEEPER);
}
/**
* @dev Return SavingsManager Module address from the Nexus
* @return Address of the SavingsManager Module contract
*/
function _savingsManager() internal view returns (address) {
return nexus.getModule(KEY_SAVINGS_MANAGER);
}
/**
* @dev Return Recollateraliser Module address from the Nexus
* @return Address of the Recollateraliser Module contract (Phase 2)
*/
function _recollateraliser() internal view returns (address) {
return nexus.getModule(KEY_RECOLLATERALISER);
}
/**
* @dev Return Liquidator Module address from the Nexus
* @return Address of the Liquidator Module contract
*/
function _liquidator() internal view returns (address) {
return nexus.getModule(KEY_LIQUIDATOR);
}
/**
* @dev Return ProxyAdmin Module address from the Nexus
* @return Address of the ProxyAdmin Module contract
*/
function _proxyAdmin() internal view returns (address) {
return nexus.getModule(KEY_PROXY_ADMIN);
}
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IRewardsDistributionRecipient {
function notifyRewardAmount(uint256 reward) external;
function getRewardToken() external view returns (IERC20);
}
interface IRewardsRecipientWithPlatformToken {
function notifyRewardAmount(uint256 reward) external;
function getRewardToken() external view returns (IERC20);
function getPlatformToken() external view returns (IERC20);
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
/**
* @title ModuleKeys
* @author mStable
* @notice Provides system wide access to the byte32 represntations of system modules
* This allows each system module to be able to reference and update one another in a
* friendly way
* @dev keccak256() values are hardcoded to avoid re-evaluation of the constants at runtime.
*/
contract ModuleKeys {
// Governance
// ===========
// keccak256("Governance");
bytes32 internal constant KEY_GOVERNANCE =
0x9409903de1e6fd852dfc61c9dacb48196c48535b60e25abf92acc92dd689078d;
//keccak256("Staking");
bytes32 internal constant KEY_STAKING =
0x1df41cd916959d1163dc8f0671a666ea8a3e434c13e40faef527133b5d167034;
//keccak256("ProxyAdmin");
bytes32 internal constant KEY_PROXY_ADMIN =
0x96ed0203eb7e975a4cbcaa23951943fa35c5d8288117d50c12b3d48b0fab48d1;
// mStable
// =======
// keccak256("OracleHub");
bytes32 internal constant KEY_ORACLE_HUB =
0x8ae3a082c61a7379e2280f3356a5131507d9829d222d853bfa7c9fe1200dd040;
// keccak256("Manager");
bytes32 internal constant KEY_MANAGER =
0x6d439300980e333f0256d64be2c9f67e86f4493ce25f82498d6db7f4be3d9e6f;
//keccak256("Recollateraliser");
bytes32 internal constant KEY_RECOLLATERALISER =
0x39e3ed1fc335ce346a8cbe3e64dd525cf22b37f1e2104a755e761c3c1eb4734f;
//keccak256("MetaToken");
bytes32 internal constant KEY_META_TOKEN =
0xea7469b14936af748ee93c53b2fe510b9928edbdccac3963321efca7eb1a57a2;
// keccak256("SavingsManager");
bytes32 internal constant KEY_SAVINGS_MANAGER =
0x12fe936c77a1e196473c4314f3bed8eeac1d757b319abb85bdda70df35511bf1;
// keccak256("Liquidator");
bytes32 internal constant KEY_LIQUIDATOR =
0x1e9cb14d7560734a61fa5ff9273953e971ff3cd9283c03d8346e3264617933d4;
// keccak256("InterestValidator");
bytes32 internal constant KEY_INTEREST_VALIDATOR =
0xc10a28f028c7f7282a03c90608e38a4a646e136e614e4b07d119280c5f7f839f;
// keccak256("Keeper");
bytes32 internal constant KEY_KEEPER =
0x4f78afe9dfc9a0cb0441c27b9405070cd2a48b490636a7bdd09f355e33a5d7de;
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
/**
* @title INexus
* @dev Basic interface for interacting with the Nexus i.e. SystemKernel
*/
interface INexus {
function governor() external view returns (address);
function getModule(bytes32 key) external view returns (address);
function proposeModule(bytes32 _key, address _addr) external;
function cancelProposedModule(bytes32 _key) external;
function acceptProposedModule(bytes32 _key) external;
function acceptProposedModules(bytes32[] calldata _keys) external;
function requestLockModule(bytes32 _key) external;
function cancelLockModule(bytes32 _key) external;
function lockModule(bytes32 _key) external;
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { MassetHelpers } from "../../shared/MassetHelpers.sol";
/**
* @title PlatformTokenVendor
* @author mStable
* @notice Stores platform tokens for distributing to StakingReward participants
* @dev Only deploy this during the constructor of a given StakingReward contract
*/
contract PlatformTokenVendor {
IERC20 public immutable platformToken;
address public immutable parentStakingContract;
/** @dev Simple constructor that stores the parent address */
constructor(IERC20 _platformToken) {
parentStakingContract = msg.sender;
platformToken = _platformToken;
MassetHelpers.safeInfiniteApprove(address(_platformToken), msg.sender);
}
/**
* @dev Re-approves the StakingReward contract to spend the platform token.
* Just incase for some reason approval has been reset.
*/
function reApproveOwner() external {
MassetHelpers.safeInfiniteApprove(address(platformToken), parentStakingContract);
}
}// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.6;
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title MassetHelpers
* @author mStable
* @notice Helper functions to facilitate minting and redemption from off chain
* @dev VERSION: 1.0
* DATE: 2020-03-28
*/
library MassetHelpers {
using SafeERC20 for IERC20;
function transferReturnBalance(
address _sender,
address _recipient,
address _bAsset,
uint256 _qty
) internal returns (uint256 receivedQty, uint256 recipientBalance) {
uint256 balBefore = IERC20(_bAsset).balanceOf(_recipient);
IERC20(_bAsset).safeTransferFrom(_sender, _recipient, _qty);
recipientBalance = IERC20(_bAsset).balanceOf(_recipient);
receivedQty = recipientBalance - balBefore;
}
function safeInfiniteApprove(address _asset, address _spender) internal {
IERC20(_asset).safeApprove(_spender, 0);
IERC20(_asset).safeApprove(_spender, 2**256 - 1);
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_nexus","type":"address"},{"internalType":"address","name":"_rewardsToken","type":"address"},{"internalType":"address","name":"_questManager","type":"address"},{"internalType":"address","name":"_stakedToken","type":"address"},{"internalType":"uint256","name":"_cooldownSeconds","type":"uint256"},{"internalType":"address[2]","name":"_bal","type":"address[2]"},{"internalType":"bytes32","name":"_poolId","type":"bytes32"},{"internalType":"address","name":"_balancerGauge","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newRecipient","type":"address"}],"name":"BalRecipientChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"percentage","type":"uint256"}],"name":"Cooldown","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"CooldownExited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"bpt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"mta","type":"uint256"}],"name":"FeesConverted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"hook","type":"address"}],"name":"GovernanceHookChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newPriceCoeff","type":"uint256"}],"name":"PriceCoefficientUpdated","type":"event"},{"anonymous":false,"inputs":[],"name":"Recollateralised","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newRate","type":"uint256"}],"name":"SlashRateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"delegatee","type":"address"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"}],"name":"WrapperBlacklisted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"wallet","type":"address"}],"name":"WrapperWhitelisted","type":"event"},{"inputs":[],"name":"BAL","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COOLDOWN_SECONDS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARDS_TOKEN","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STAKED_TOKEN","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNSTAKE_WINDOW","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"uint8","name":"_newMultiplier","type":"uint8"}],"name":"applyQuestMultiplier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"balanceData","outputs":[{"components":[{"internalType":"uint88","name":"raw","type":"uint88"},{"internalType":"uint32","name":"weightedTimestamp","type":"uint32"},{"internalType":"uint8","name":"timeMultiplier","type":"uint8"},{"internalType":"uint8","name":"questMultiplier","type":"uint8"},{"internalType":"uint32","name":"cooldownTimestamp","type":"uint32"},{"internalType":"uint88","name":"cooldownUnits","type":"uint88"}],"internalType":"struct Balance","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"balancerGauge","outputs":[{"internalType":"contract IBalancerGauge","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"balancerVault","outputs":[{"internalType":"contract IBVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wrapper","type":"address"}],"name":"blackListWrapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_weightedTimestamp","type":"uint32"}],"name":"calcRedemptionFeeRate","outputs":[{"internalType":"uint256","name":"_feeRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_newRate","type":"uint256"}],"name":"changeSlashingPercentage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint32","name":"pos","type":"uint32"}],"name":"checkpoints","outputs":[{"components":[{"internalType":"uint32","name":"fromBlock","type":"uint32"},{"internalType":"uint224","name":"votes","type":"uint224"}],"internalType":"struct GamifiedVotingToken.Checkpoint","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"claimReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"convertFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"createLock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyRecollateralisation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"endCooldown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fetchPriceCoefficient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProspectivePriceCoefficient","outputs":[{"internalType":"uint256","name":"newPriceCoeff","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalData","outputs":[{"internalType":"uint32","name":"periodFinish","type":"uint32"},{"internalType":"uint32","name":"lastUpdateTime","type":"uint32"},{"internalType":"uint96","name":"rewardRate","type":"uint96"},{"internalType":"uint96","name":"rewardPerTokenStored","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hasPriceCoeff","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"increaseLockAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastPriceUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nexus","outputs":[{"internalType":"contract INexus","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_reward","type":"uint256"}],"name":"notifyRewardAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingAdditionalReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingBPTFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceCoefficient","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"questManager","outputs":[{"internalType":"contract IQuestManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"rawBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"reviewTimestamp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardTokenVendor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsDistributor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"safetyData","outputs":[{"internalType":"uint128","name":"collateralisationRatio","type":"uint128"},{"internalType":"uint128","name":"slashingPercentage","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newRecipient","type":"address"}],"name":"setBalRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newHook","type":"address"}],"name":"setGovernanceHook","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardsDistributor","type":"address"}],"name":"setRewardsDistribution","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_delegatee","type":"address"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bool","name":"_exitCooldown","type":"bool"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_units","type":"uint256"}],"name":"startCooldown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userData","outputs":[{"internalType":"uint128","name":"rewardPerTokenPaid","type":"uint128"},{"internalType":"uint128","name":"rewards","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"userPriceCoeff","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_wrapper","type":"address"}],"name":"whitelistWrapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelistedWrappers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"bool","name":"_amountIncludesFee","type":"bool"},{"internalType":"bool","name":"_exitCooldown","type":"bool"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101c06040523480156200001257600080fd5b5060405162005f5138038062005f51833981016040819052620000359162000161565b878787878760018585858383838383838381806001600160a01b038116620000a35760405162461bcd60e51b815260206004820152601560248201527f4e657875732061646472657373206973207a65726f0000000000000000000000604482015260640160405180910390fd5b6001600160601b0319606091821b811660805292811b831660a05294851b821660c0525091151560f81b60e0525099901b90981661010052505050610120939093525088955060009450620000f89350505050565b602002015160601b6001600160601b0319166101405282600160200201516001600160601b0319606091821b811661016052610180939093521b166101a052506200027e945050505050565b80516001600160a01b03811681146200015c57600080fd5b919050565b600080600080600080600080610120898b0312156200017f57600080fd5b6200018a8962000144565b975060206200019b818b0162000144565b9750620001ab60408b0162000144565b9650620001bb60608b0162000144565b955060808a015194508a60bf8b0112620001d457600080fd5b604080519081016001600160401b0381118282101715620001f957620001f962000268565b6040528060a08c0160e08d018e8111156200021357600080fd5b60005b60028110156200023e576200022b8362000144565b8452928501929185019160010162000216565b505192965091945062000259925050506101008a0162000144565b90509295985092959890939650565b634e487b7160e01b600052604160045260246000fd5b60805160601c60a05160601c60c05160601c60e05160f81c6101005160601c610120516101405160601c6101605160601c610180516101a05160601c615b436200040e6000396000818161057d01528181610d1001528181610fdd0152818161117301528181611bca01528181612a7501528181613805015261389d0152600081816105ca01528181610e3e0152818161108e0152611da40152600081816104c401528181610e720152818161105f0152611dd90152600061091c0152600081816107e50152818161235d015281816130290152818161309a0152613ce501526000818161053201528181611ee501528181612a2f015261390e015260008181610b200152818161335c01528181613f490152614922015260008181610b4701528181611a6301528181613e94015261489e0152600081816107650152818161094b01528181610db501528181610ef6015281816112690152818161151a01528181611e5e01526136270152600081816108ac0152818161376a01528181613a5e0152614ada0152615b436000f3fe608060405234801561001057600080fd5b50600436106103e55760003560e01c806369940d791161020a578063a694fc3a11610125578063c822ddb9116100b8578063d279c19111610087578063d279c19114610b08578063d9a2ee5714610b1b578063dc1627eb14610b42578063e9fad8ee14610b69578063f1127ed814610b7157600080fd5b8063c822ddb91461096d578063c891091314610aa3578063cd3daf9d14610ad7578063d09ad56514610adf57600080fd5b8063ba7a4ec1116100f4578063ba7a4ec11461090f578063beeadf1614610917578063c19fd2171461093e578063c56010721461094657600080fd5b8063a694fc3a146108ce578063abe50f19146108e1578063b52c05fe146108f4578063b88a802f1461090757600080fd5b806380faa57d1161019d578063943182141161016c578063943182141461085957806395d89b411461088c5780639ab24eb014610894578063a3f5c1d2146108a757600080fd5b806380faa57d1461082d57806383069885146108355780638e539e8c1461083e5780639110b7b51461085157600080fd5b806370a08231116101d957806370a08231146107cd57806372b49d63146107e05780637acb7757146108075780637c098a1b1461081a57600080fd5b806369940d79146107635780636c514a99146107895780636e5b0cc5146107925780636fcfff45146107a557600080fd5b80633e0dc34e1161030557806358b235dd11610298578063612556bf11610267578063612556bf146106a8578063613ce410146106bb5780636154913b1461072a57806365c787751461073d578063672e1fd81461075057600080fd5b806358b235dd1461065c5780635c19a95c1461066f5780635fa456dc146106825780635fcddddf1461069557600080fd5b806349b9a7af116102d457806349b9a7af14610625578063519b9a3f1461062e57806357c37b6914610636578063587cde1e1461064957600080fd5b80633e0dc34e146105c55780633f2a5540146105ec578063403f4447146105ff578063478dc62f1461061257600080fd5b80631be052891161037d578063359c4a961161034c578063359c4a961461056e578063367e5f3e146105785780633a46b1a81461059f5780633c6b16ab146105b257600080fd5b80631be052891461051b5780632b5335c314610525578063312f6b831461052d578063313ce5671461055457600080fd5b806312064c34116103b957806312064c341461046c578063158274a5146104bf57806318160ddd146104fe578063197621431461050657600080fd5b80628cc262146103ea5780630552c61c1461041057806306fdde031461044e5780630882af3a14610463575b600080fd5b6103fd6103f8366004615420565b610bae565b6040519081526020015b60405180910390f35b609d5461042e906001600160801b0380821691600160801b90041682565b604080516001600160801b03938416815292909116602083015201610407565b610456610bc7565b604051610407919061585c565b6103fd60d15481565b6104aa61047a366004615420565b6001600160a01b03166000908152603a60205260409020546001600160581b0380821692600160a81b9092041690565b60408051928352602083019190915201610407565b6104e67f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610407565b6103fd610bd9565b610519610514366004615420565b610c2b565b005b6103fd62093a8081565b610519610c55565b6104e67f000000000000000000000000000000000000000000000000000000000000000081565b61055c601281565b60405160ff9091168152602001610407565b6103fd6212750081565b6104e67f000000000000000000000000000000000000000000000000000000000000000081565b6103fd6105ad36600461545a565b61139d565b6105196105c03660046155e6565b611417565b6103fd7f000000000000000000000000000000000000000000000000000000000000000081565b6033546104e6906001600160a01b031681565b61051961060d3660046155e6565b6116bd565b6105196106203660046155e6565b611717565b6103fd60d35481565b6105196117ca565b610519610644366004615420565b611857565b6104e6610657366004615420565b6118a9565b6103fd61066a3660046156d7565b6118d9565b61051961067d366004615420565b61198f565b61051961069036600461563d565b611999565b6105196106a3366004615420565b6119ab565b6105196106b6366004615420565b611a04565b6035546106f39063ffffffff80821691600160201b8104909116906001600160601b03600160401b8204811691600160a01b90041684565b6040805163ffffffff95861681529490931660208501526001600160601b0391821692840192909252166060820152608001610407565b6105196107383660046154bb565b611a60565b61051961074b366004615420565b611b91565b61051961075e3660046155e6565b611b9a565b7f00000000000000000000000000000000000000000000000000000000000000006104e6565b6103fd60d25481565b6105196107a0366004615420565b611ba3565b6107b86107b3366004615420565b611c60565b60405163ffffffff9091168152602001610407565b6103fd6107db366004615420565b611c82565b6103fd7f000000000000000000000000000000000000000000000000000000000000000081565b610519610815366004615618565b611d12565b6034546104e6906001600160a01b031681565b6103fd611d22565b6103fd60375481565b6103fd61084c3660046155e6565b611d39565b6103fd611d95565b61087c610867366004615420565b609e6020526000908152604090205460ff1681565b6040519015158152602001610407565b610456611fc6565b6103fd6108a2366004615420565b611fd3565b6104e67f000000000000000000000000000000000000000000000000000000000000000081565b6105196108dc3660046155e6565b611708565b6105196108ef366004615690565b612059565b6105196109023660046156b5565b612065565b610519612071565b61051961207c565b6104e67f000000000000000000000000000000000000000000000000000000000000000081565b6105196121dd565b6104e67f000000000000000000000000000000000000000000000000000000000000000081565b610a3861097b366004615420565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506001600160a01b03166000908152603a6020908152604091829020825160c08101845290546001600160581b03808216835263ffffffff600160581b830481169484019490945260ff600160781b8304811695840195909552600160801b82049094166060830152600160881b81049092166080820152600160a81b90910490911660a082015290565b6040516104079190600060c0820190506001600160581b03808451168352602084015163ffffffff808216602086015260ff604087015116604086015260ff606087015116606086015280608087015116608086015250508060a08501511660a08401525092915050565b61042e610ab1366004615420565b6036602052600090815260409020546001600160801b0380821691600160801b90041682565b6103fd612314565b6103fd610aed366004615420565b6001600160a01b03166000908152603b602052604090205490565b610519610b16366004615420565b612326565b61087c7f000000000000000000000000000000000000000000000000000000000000000081565b6104e67f000000000000000000000000000000000000000000000000000000000000000081565b61051961232f565b610b84610b7f366004615486565b6123f3565b60408051825163ffffffff1681526020928301516001600160e01b03169281019290925201610407565b6000610bc182610bbc612314565b612476565b92915050565b6060610bd4603854612530565b905090565b606c5460009080610bec57600091505090565b606c610bf9600183615a05565b81548110610c0957610c09615aaf565b600091825260209091200154600160201b90046001600160e01b031692915050565b610c3361264b565b603380546001600160a01b0319166001600160a01b0392909216919091179055565b609c5460ff16610cac5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b609c805460ff1916905560d15460018111610cf35760405162461bcd60e51b81526020600482015260076024820152666e6f206665657360c81b6044820152606401610ca3565b600160d1556040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b158015610d5a57600080fd5b505afa158015610d6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d9291906155ff565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a082319060240160206040518083038186803b158015610df757600080fd5b505afa158015610e0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2f91906155ff565b604051631f29a8cd60e31b81527f000000000000000000000000000000000000000000000000000000000000000060048201529091506000906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063f94d46689060240160006040518083038186803b158015610eb457600080fd5b505afa158015610ec8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ef091908101906154f4565b505090507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681600081518110610f3157610f31615aaf565b60200260200101516001600160a01b031614610f795760405162461bcd60e51b81526020600482015260076024820152666e6f74204d544160c81b6044820152606401610ca3565b604080516002808252606082018352600092602083019080368337019050509050612af860d25486610fab919061598f565b610fb59190615955565b81600081518110610fc857610fc8615aaf565b60209081029190910101526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016632e1a7d4d61100d600188615a05565b6040518263ffffffff1660e01b815260040161102b91815260200190565b600060405180830381600087803b15801561104557600080fd5b505af1158015611059573d6000803e3d6000fd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638bdb39137f000000000000000000000000000000000000000000000000000000000000000030306040518060800160405280888152602001878152602001600060018d6110d59190615a05565b6040805160ff9093166020840152820152600060608201526080016040516020818303038152906040528152602001600015158152506040518563ffffffff1660e01b815260040161112a9493929190615792565b600060405180830381600087803b15801561114457600080fd5b505af1158015611158573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691506370a082319060240160206040518083038186803b1580156111be57600080fd5b505afa1580156111d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f691906155ff565b90506112028686615a05565b61120d9060016158ed565b81146112475760405162461bcd60e51b81526020600482015260096024820152680f081b5a5b8810941560ba1b6044820152606401610ca3565b6040516370a0823160e01b815230600482015260009085906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a082319060240160206040518083038186803b1580156112ab57600080fd5b505afa1580156112bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e391906155ff565b6112ed9190615a05565b90508260008151811061130257611302615aaf565b60200260200101518110156113455760405162461bcd60e51b81526020600482015260096024820152683c206d696e204d544160b81b6044820152606401610ca3565b61134e816126b3565b60408051888152602081018390527f440ff3ae555e6e78856c05bc2eea45928789919497ad6b48a9f5aa065094e286910160405180910390a15050609c805460ff191660011790555050505050565b60004382106113ee5760405162461bcd60e51b815260206004820152601f60248201527f4552433230566f7465733a20626c6f636b206e6f7420796574206d696e6564006044820152606401610ca3565b6001600160a01b0383166000908152606b60205260409020611410908361273b565b9392505050565b6033546001600160a01b031633146114715760405162461bcd60e51b815260206004820181905260248201527f43616c6c6572206973206e6f7420726577617264206469737472696275746f726044820152606401610ca3565b600061147c816127f7565b69d3c21bcecceda100000082106114d55760405162461bcd60e51b815260206004820181905260248201527f4e6f74696679206d6f7265207468616e2061206d696c6c696f6e20756e6974736044820152606401610ca3565b6037544290600110156115035760016037546114f19190615a05565b6114fb90846158ed565b600160375592505b821561154357603454611543906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081169116856128f6565b60355463ffffffff16811061159c5761156761156262093a8085615955565b612959565b603580546001600160601b0392909216600160401b026bffffffffffffffffffffffff60401b19909216919091179055611626565b6035546000906115b390839063ffffffff16615a05565b6035549091506000906115d690600160401b90046001600160601b03168361598f565b90506115f362093a806115e983886158ed565b6115629190615955565b603580546001600160601b0392909216600160401b026bffffffffffffffffffffffff60401b1990921691909117905550505b61162f816129c5565b6035805463ffffffff92909216600160201b0267ffffffff000000001990921691909117905561166a61166562093a80836158ed565b6129c5565b6035805463ffffffff191663ffffffff929092169190911790556040518381527fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d906020015b60405180910390a1505050565b6116c633611c82565b6117085760405162461bcd60e51b81526020600482015260136024820152724e6f7468696e6720746f20696e63726561736560681b6044820152606401610ca3565b61171481600080612a2a565b50565b61171f61264b565b611727612ae4565b6706f05b59d3b200008111156117675760405162461bcd60e51b81526020600482015260056024820152643e2035302560d81b6044820152606401610ca3565b61177081612b46565b609d80546001600160801b03928316600160801b0292169190911790556040517f130d585ea14fd64e9d6a9e64231dba9b8692207d164bd5e38d66c02ae9929a44906117bf9083815260200190565b60405180910390a150565b336000908152603a6020526040902054600160881b900463ffffffff166118215760405162461bcd60e51b815260206004820152600b60248201526a27379031b7b7b63237bbb760a91b6044820152606401610ca3565b61182a33612baf565b60405133907f3e58f193f71ff8415ab4cc6ac25f60be9ba7dfae9eef13769586d089b7887e1a90600090a2565b61185f61264b565b606d80546001600160a01b0319166001600160a01b0383169081179091556040517f70f5968ed58c0d81c7a8bca106614e2ea19375c430722f9f0ad68de8fd74f97890600090a250565b6001600160a01b038082166000908152606a602052604081205490911680156118d25780611410565b5090919050565b60008062093a806118f063ffffffff851642615a05565b61190290670de0b6b3a764000061598f565b61190c9190615955565b90506729a2241af62c000081111561197d5761194061193b826fe1b1e5f90f944d6e1c9e66c000000000615955565b612ca2565b61194d906298968061598f565b91506658d15e1762800082106119735761196e6658d15e1762800083615a05565b611976565b60005b9150611989565b67010a741a4627800091505b50919050565b6117143382612e1c565b6119a584848484612ec4565b50505050565b6119b361264b565b6001600160a01b0381166000818152609e6020908152604091829020805460ff1916905590519182527f6843d4fd51341f35652c364bbf7be3666eacfcfc9ebac9db41bd6e05c432165f91016117bf565b611a0c61264b565b6001600160a01b0381166000818152609e6020908152604091829020805460ff1916600117905590519182527f66a41827fb347195a5a3f3b9c5c921310e2879c6715d6f2dff54add1a2fc562e91016117bf565b337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614611ac75760405162461bcd60e51b815260206004820152600c60248201526b139bdd081d995c9a599a595960a21b6044820152606401610ca3565b6001600160a01b038216611aed5760405162461bcd60e51b8152600401610ca39061586f565b6001600160a01b0382166000908152603a60209081526040808320815160c08101835290546001600160581b03808216835263ffffffff600160581b830481169584019590955260ff600160781b8304811694840194909452600160801b82049093166060830152600160881b81049093166080820152600160a81b9092041660a082015290611b7d84836132f5565b905080156119a5576119a5848383866133b1565b6117148161343e565b61171481613521565b611bab61264b565b604051635efcc08b60e11b81526001600160a01b0382811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063bdf9811690602401600060405180830381600087803b158015611c0e57600080fd5b505af1158015611c22573d6000803e3d6000fd5b50506040516001600160a01b03841681527f485e3ae63165cefd027e0c7be7a882469f11c3ee45cd45815bea8e7a721d1c7f925060200190506117bf565b6001600160a01b0381166000908152606b6020526040812054610bc1906129c5565b6001600160a01b0381166000908152603a60209081526040808320815160c08101835290546001600160581b03808216835263ffffffff600160581b830481169584019590955260ff600160781b8304811694840194909452600160801b82049093166060830152600160881b81049093166080820152600160a81b9092041660a0820152610bc19083906132f5565b611d1e82826000612a2a565b5050565b603554600090610bd490429063ffffffff166135b8565b6000438210611d8a5760405162461bcd60e51b815260206004820152601f60248201527f4552433230566f7465733a20626c6f636b206e6f7420796574206d696e6564006044820152606401610ca3565b610bc1606c8361273b565b604051631f29a8cd60e31b81527f00000000000000000000000000000000000000000000000000000000000000006004820152600090819081906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063f94d46689060240160006040518083038186803b158015611e1b57600080fd5b505afa158015611e2f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e5791908101906154f4565b50915091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031682600081518110611e9957611e99615aaf565b60200260200101516001600160a01b031614611ee15760405162461bcd60e51b81526020600482015260076024820152666e6f74204d544160c81b6044820152606401610ca3565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611f3c57600080fd5b505afa158015611f50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f7491906155ff565b82600081518110611f8757611f87615aaf565b6020026020010151671158e460913d0000611fa2919061598f565b611fac9190615955565b9050611fbe655af3107a400082615955565b935050505090565b6060610bd4603954612530565b6001600160a01b0381166000908152606b60205260408120548015612046576001600160a01b0383166000908152606b60205260409020612015600183615a05565b8154811061202557612025615aaf565b600091825260209091200154600160201b90046001600160e01b0316612049565b60005b6001600160e01b03169392505050565b611d1e82600083612a2a565b611d1e82600080612a2a565b61207a336135c7565b565b6120846136a7565b60d35461209490621275006158ed565b42116120ce5760405162461bcd60e51b81526020600482015260096024820152683c203134206461797360b81b6044820152606401610ca3565b60006120d8611d95565b60d25490915060008183116120f6576120f18383615a05565b612100565b6121008284615a05565b90506101f4826121128361271061598f565b61211c9190615955565b116121555760405162461bcd60e51b81526020600482015260096024820152681e101a92903234b33360b91b6044820152606401610ca3565b613a98831180156121685750620124f883105b6121a45760405162461bcd60e51b815260206004820152600d60248201526c4f7574206f6620626f756e647360981b6044820152606401610ca3565b60d28390554260d3556040518381527f77ad0680939c8f5a6c911101963afa50204eda979fac30e18dfa63af3c68cced906020016116b0565b6121e5613732565b6001600160a01b0316336001600160a01b0316146122455760405162461bcd60e51b815260206004820152601860248201527f4f6e6c79205265636f6c6c61746572616c69736174696f6e00000000000000006044820152606401610ca3565b61224d612ae4565b609d5461227290600160801b90046001600160801b0316670de0b6b3a76400006159dd565b609d80546fffffffffffffffffffffffffffffffff19166001600160801b039290921691909117905560006122a56137ed565b90506122e86122b2613732565b609d54670de0b6b3a7640000906122d990600160801b90046001600160801b03168561598f565b6122e39190615955565b613887565b6040517f8551ab4a9801f4813e5fff4a88eb374487886333f7c4e0aa1ca928a24916d53c90600090a150565b60008061231f613937565b5092915050565b611714816135c7565b336000908152603a6020526040902054600160881b900463ffffffff1680158061239857506212750061238b7f00000000000000000000000000000000000000000000000000000000000000006001600160801b0384166158ed565b61239591906158ed565b42115b156123c5576000806123a93361047a565b90925090506123c06123bb82846158ed565b613521565b505050565b336000818152603a602052604081205461171492600160a81b9091046001600160581b031691600190612ec4565b60408051808201909152600080825260208201526001600160a01b0383166000908152606b60205260409020805463ffffffff841690811061243757612437615aaf565b60009182526020918290206040805180820190915291015463ffffffff81168252600160201b90046001600160e01b0316918101919091529392505050565b6001600160a01b03821660009081526036602052604081205481906124a4906001600160801b031684615a05565b9050806124db5750506001600160a01b038216600090815260366020526040902054600160801b90046001600160801b0316610bc1565b60006124f0826124ea87611c82565b90613a45565b6001600160a01b038616600090815260366020526040902054909150612527908290600160801b90046001600160801b03166158ed565b95945050505050565b606060005b602081108015612563575082816020811061255257612552615aaf565b1a60f81b6001600160f81b03191615155b1561257a578061257281615a68565b915050612535565b60008167ffffffffffffffff81111561259557612595615ac5565b6040519080825280601f01601f1916602001820160405280156125bf576020820181803683370190505b509050600091505b6020821080156125f557508382602081106125e4576125e4615aaf565b1a60f81b6001600160f81b03191615155b156114105783826020811061260c5761260c615aaf565b1a60f81b81838151811061262257612622615aaf565b60200101906001600160f81b031916908160001a9053508161264381615a68565b9250506125c7565b612653613a5a565b6001600160a01b0316336001600160a01b03161461207a5760405162461bcd60e51b815260206004820152601960248201527f4f6e6c7920676f7665726e6f722063616e2065786563757465000000000000006044820152606401610ca3565b69d3c21bcecceda100000081106127215760405162461bcd60e51b815260206004820152602c60248201527f43616e6e6f74206e6f746966792077697468206d6f7265207468616e2061206d60448201526b696c6c696f6e20756e69747360a01b6064820152608401610ca3565b806037600082825461273391906158ed565b909155505050565b8154600090815b8181101561279f5760006127568284613ab5565b90508486828154811061276b5761276b615aaf565b60009182526020909120015463ffffffff16111561278b57809250612799565b6127968160016158ed565b91505b50612742565b81156127e257846127b1600184615a05565b815481106127c1576127c1615aaf565b600091825260209091200154600160201b90046001600160e01b03166127e5565b60005b6001600160e01b031695945050505050565b600080612802613937565b909250905081156123c05761281682612959565b603580546001600160601b0392909216600160a01b026001600160a01b03909216919091179055612846816129c5565b6035805463ffffffff92909216600160201b0267ffffffff00000000199092169190911790556001600160a01b038316156123c057604051806040016040528061288f84612b46565b6001600160801b031681526020016128af6128aa8686612476565b612b46565b6001600160801b039081169091526001600160a01b0385166000908152603660209081526040909120835193909101518216600160801b0292909116919091179055505050565b6040516001600160a01b0383166024820152604481018290526123c090849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613ad0565b60006001600160601b038211156129c15760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201526536206269747360d01b6064820152608401610ca3565b5090565b600063ffffffff8211156129c15760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610ca3565b612a5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316333086613ba2565b60405163b6b55f2560e01b8152600481018490527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063b6b55f2590602401600060405180830381600087803b158015612ac157600080fd5b505af1158015612ad5573d6000803e3d6000fd5b505050506123c0838383613bda565b609d546001600160801b0316670de0b6b3a76400001461207a5760405162461bcd60e51b815260206004820152601960248201527f4f6e6c79207768696c6520636f6c6c61746572616c69736564000000000000006044820152606401610ca3565b60006001600160801b038211156129c15760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b6064820152608401610ca3565b80612bb9816127f7565b6001600160a01b038216612bdf5760405162461bcd60e51b8152600401610ca39061586f565b600080612beb84613daf565b91509150612bfc8260200151613fa5565b6001600160a01b0385166000908152603a60205260408120805460ff93909316600160781b0260ff60781b1984168117825560a086015193919291612c509185916001600160581b0391821691161761592a565b82546001600160581b039182166101009390930a9283029190920219909116179055506001600160a01b0384166000908152603a6020526040902080546001600160881b031690556119a5848261403e565b600081612cb157506000919050565b816001600160801b8210612cca5760809190911c9060401b5b600160401b8210612ce05760409190911c9060201b5b600160201b8210612cf65760209190911c9060101b5b620100008210612d0b5760109190911c9060081b5b6101008210612d1f5760089190911c9060041b5b60108210612d325760049190911c9060021b5b60088210612d3e5760011b5b6001612d4a8286615955565b612d5490836158ed565b901c90506001612d648286615955565b612d6e90836158ed565b901c90506001612d7e8286615955565b612d8890836158ed565b901c90506001612d988286615955565b612da290836158ed565b901c90506001612db28286615955565b612dbc90836158ed565b901c90506001612dcc8286615955565b612dd690836158ed565b901c90506001612de68286615955565b612df090836158ed565b901c90506000612e008286615955565b9050808210612e0f5780612527565b509392505050565b919050565b6000612e27836118a9565b90506000612e3484611c82565b6001600160a01b038581166000908152606a6020526040902080546001600160a01b0319169186169190911790559050612e6d846118a9565b9250826001600160a01b0316826001600160a01b0316856001600160a01b03167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f60405160405180910390a46119a58284836140fe565b612ecc6142ba565b83612f0f5760405162461bcd60e51b81526020600482015260136024820152721253959053125117d6915493d7d05353d55395606a1b6044820152606401610ca3565b609d546001600160801b0316670de0b6b3a764000014612fa257612f37338560006001614312565b609d54612f5d908490670de0b6b3a7640000906122d9906001600160801b03168861598f565b6040518481526001600160a01b0384169033907f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb9060200160405180910390a36119a5565b336000908152603a6020908152604091829020825160c08101845290546001600160581b03808216835263ffffffff600160581b830481169484019490945260ff600160781b8304811695840195909552600160801b82049094166060830152600160881b810490921660808201819052600160a81b90920490921660a083015261304e907f0000000000000000000000000000000000000000000000000000000000000000906158ed565b42116130945760405162461bcd60e51b815260206004820152601560248201527424a729aaa32324a1a4a2a72a2fa1a7a7a62227aba760591b6044820152606401610ca3565b621275007f0000000000000000000000000000000000000000000000000000000000000000826080015163ffffffff166130ce91906158ed565b6130d89042615a05565b11156131265760405162461bcd60e51b815260206004820152601760248201527f554e5354414b455f57494e444f575f46494e49534845440000000000000000006044820152606401610ca3565b336000908152603a60209081526040808320815160c08101835290546001600160581b03808216835263ffffffff600160581b8304811695840186905260ff600160781b8404811695850195909552600160801b83049094166060840152600160881b82049093166080830152600160a81b900490911660a082015291906131ad906118d9565b90506000856131e257670de0b6b3a76400006131c983826158ed565b6131d3908a61598f565b6131dd9190615955565b6131e4565b875b905060006131fa83670de0b6b3a76400006158ed565b61320c83670de0b6b3a764000061598f565b6132169190615955565b60a08601519091506001600160581b0316808311156132705760405162461bcd60e51b8152602060048201526016602482015275115e18d959591cc81b585e081dda5d1a191c985dd85b60521b6044820152606401610ca3565b6000878061327d57508184145b905061328c3385836000614312565b61329e6132998486615a05565b614641565b6132a88a84613887565b6040518b81526001600160a01b038b169033907f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb9060200160405180910390a35050505050505050505050565b600060648260600151606461330a9190615905565b83516133199160ff16906159ae565b6133239190615969565b6001600160581b031690506064826040015160646133419190615905565b61334e9060ff168361598f565b6133589190615955565b90507f000000000000000000000000000000000000000000000000000000000000000015610bc1576001600160a01b0383166000908152603b6020526040902054612710906133a7908361598f565b6114109190615955565b836133bb816127f7565b6001600160a01b0385166000908152603a60209081526040909120805460ff60801b1916600160801b60ff8616021790558401516133f890613fa5565b6001600160a01b0386166000908152603a60205260409020805460ff92909216600160781b0260ff60781b19909216919091179055613437858461403e565b5050505050565b80613448816127f7565b6001600160a01b03821661346e5760405162461bcd60e51b8152600401610ca39061586f565b60008061347a84613daf565b91509150600061348d8360200151613fa5565b9050826040015160ff168160ff1614156134e95760405162461bcd60e51b815260206004820152601960248201527f4e6f7468696e6720776f72746820706f6b696e672068657265000000000000006044820152606401610ca3565b6001600160a01b0385166000908152603a60205260409020805460ff60781b1916600160781b60ff841602179055613437858361403e565b61352a33611c82565b6135765760405162461bcd60e51b815260206004820152601b60248201527f494e56414c49445f42414c414e43455f4f4e5f434f4f4c444f574e00000000006044820152606401610ca3565b6135803382614694565b60405181815233907f8a05f911d8ab7fc50fec37ef4ba7f9bfcb1a3c191c81dcd824ad0946c4e20d659060200160405180910390a250565b60008183116119895782611410565b336135d1816127f7565b33600090815260366020526040902054600160801b90046001600160801b0316801561369e5733600090815260366020526040902080546001600160801b03908116909155603454613654916001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811692169086908516613ba2565b604080516001600160801b038316815290516001600160a01b0385169133917f540798df468d7b23d11f156fdb954cb19ad414d150722a7b6d55ba369dea792e9181900360200190a35b6123c03361487c565b6136af614aa2565b6001600160a01b0316336001600160a01b031614806136e657506136d1613a5a565b6001600160a01b0316336001600160a01b0316145b61207a5760405162461bcd60e51b815260206004820152601760248201527f4f6e6c79206b6565706572206f7220676f7665726e6f720000000000000000006044820152606401610ca3565b6040516385acd64160e01b81527f39e3ed1fc335ce346a8cbe3e64dd525cf22b37f1e2104a755e761c3c1eb4734f60048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906385acd641906024015b60206040518083038186803b1580156137b557600080fd5b505afa1580156137c9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bd4919061543d565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a082319060240160206040518083038186803b15801561384f57600080fd5b505afa158015613863573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bd491906155ff565b604051632e1a7d4d60e01b8152600481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690632e1a7d4d90602401600060405180830381600087803b1580156138e957600080fd5b505af11580156138fd573d6000803e3d6000fd5b50611d1e9250506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016905083836128f6565b6000806000613944611d22565b6040805160808101825260355463ffffffff8082168352600160201b820416602083018190526001600160601b03600160401b8304811694840194909452600160a01b90910490921660608201529192506000906139a29084615a05565b9050806139bf5750606001516001600160601b0316939092509050565b60008183604001516001600160601b03166139da919061598f565b905060006139e6610bd9565b90508015806139f3575081155b15613a1057505050606001516001600160601b0316939092509050565b6000613a1c8383614b11565b90508085606001516001600160601b0316613a3791906158ed565b989597509495505050505050565b60006114108383670de0b6b3a7640000614b26565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316630c340a246040518163ffffffff1660e01b815260040160206040518083038186803b1580156137b557600080fd5b6000613ac46002848418615955565b611410908484166158ed565b6000613b25826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614b459092919063ffffffff16565b8051909150156123c05780806020019051810190613b4391906155c9565b6123c05760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610ca3565b6040516001600160a01b03808516602483015283166044820152606481018290526119a59085906323b872dd60e01b90608401612922565b613be2612ae4565b613bea6142ba565b82613c2d5760405162461bcd60e51b81526020600482015260136024820152721253959053125117d6915493d7d05353d55395606a1b6044820152606401610ca3565b6001600160a01b03821615613c4657613c463383612e1c565b336000908152603a60209081526040808320815160c08101835290546001600160581b03808216835263ffffffff600160581b830481169584019590955260ff600160781b8304811694840194909452600160801b82049093166060830152600160881b81049093166080820152600160a81b9092041660a0820152908280613d2657506000826080015163ffffffff16118015613d265750621275007f0000000000000000000000000000000000000000000000000000000000000000836080015163ffffffff16613d1991906158ed565b613d2391906158ed565b42115b90508015613d5a5760405133907f3e58f193f71ff8415ab4cc6ac25f60be9ba7dfae9eef13769586d089b7887e1a90600090a25b613d65338683614b54565b604080518681526001600160a01b038616602082015233917f9f9e4044c5742cca66ca090b21552bac14645e68bad7a92364a9d9ff18111a1c910160405180910390a25050505050565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506001600160a01b0381166000908152603a60209081526040808320815160c08101835290546001600160581b03808216835263ffffffff600160581b830481169584019590955260ff600160781b8304811694840194909452600160801b82049093166060830152600160881b81049093166080820152600160a81b9092041660a082015290613e7283836132f5565b604051639295ac5960e01b81526001600160a01b0385811660048301529192507f000000000000000000000000000000000000000000000000000000000000000090911690639295ac5990602401602060405180830381600087803b158015613eda57600080fd5b505af1158015613eee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f1291906156f2565b6001600160a01b0384166000908152603a60205260409020805460ff92909216600160801b0260ff60801b199092169190911790557f000000000000000000000000000000000000000000000000000000000000000015613fa057613f7e613f7960d25490565b614e76565b6001600160a01b0384166000908152603b6020526040902061ffff9190911690555b915091565b600063ffffffff8216613fba57506000919050565b6000613fcc63ffffffff841642615a05565b90506277f880811015613fe25750600092915050565b62eff100811015613ff65750601492915050565b6301dfe20081101561400b5750601e92915050565b6302cfd3008110156140205750602892915050565b6303bfc4008110156140355750603292915050565b50603c92915050565b6001600160a01b0382166000908152603a60209081526040808320815160c08101835290546001600160581b03808216835263ffffffff600160581b830481169584019590955260ff600160781b8304811694840194909452600160801b82049093166060830152600160881b81049093166080820152600160a81b9092041660a08201526140ce9084906132f5565b9050818111156140eb576123c0836140e68484615a05565b614ed9565b6123c0836140f98385615a05565b614f26565b816001600160a01b0316836001600160a01b0316141580156141205750600081115b156123c0576001600160a01b038316156141ae576001600160a01b0383166000908152606b60205260408120819061415b90614f7385614f7f565b91509150846001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a72483836040516141a3929190918252602082015260400190565b60405180910390a250505b6001600160a01b03821615614237576001600160a01b0382166000908152606b6020526040812081906141e4906150f685614f7f565b91509150836001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a724838360405161422c929190918252602082015260400190565b60405180910390a250505b606d546001600160a01b0316156123c057606d546040516216c4a960ea1b81526001600160a01b03858116600483015284811660248301526044820184905290911690635b12a40090606401600060405180830381600087803b15801561429d57600080fd5b505af11580156142b1573d6000803e3d6000fd5b50505050505050565b33321461207a57336000908152609e602052604090205460ff1661207a5760405162461bcd60e51b815260206004820152600f60248201526e139bdd081dda1a5d195b1a5cdd1959608a1b6044820152606401610ca3565b8361431c816127f7565b6001600160a01b0385166143725760405162461bcd60e51b815260206004820152601d60248201527f45524332303a206275726e2066726f6d207a65726f20616464726573730000006044820152606401610ca3565b60008061437e87613daf565b9150915060008260a001518360000151614398919061592a565b6001600160581b031690508415614431576001600160a01b0388166000908152603a6020526040902080546affffffffffffffffffffff191690556143dc81615102565b6001600160a01b0389166000908152603a6020526040902080546001600160581b0392909216600160a81b026001600160a81b0390921691909117905561442281615102565b6001600160581b031660a08401525b868360a001516001600160581b0316101561448e5760405162461bcd60e51b815260206004820152601c60248201527f45524332303a206275726e20616d6f756e74203e2062616c616e6365000000006044820152606401610ca3565b61449787615102565b6001600160a01b0389166000908152603a6020526040902080546001600160581b03600160a81b808304821694909403169092026001600160a81b039092169190911790558515614564576001600160a01b0388166000908152603a6020526040812080546001600160581b03600160a81b82048116939161451b9185911661592a565b82546001600160581b039182166101009390930a9283029190920219909116179055506001600160a01b0388166000908152603a6020526040902080546001600160881b031690555b6000614571600889615955565b61457b9083615a05565b60208501516145909063ffffffff1642615a05565b61459a919061598f565b905060006145a88383615955565b905060006145b96116658342615a05565b6001600160a01b038c166000908152603a60205260408120805463ffffffff60581b1916600160581b63ffffffff8516021790559091506145f982613fa5565b6001600160a01b038d166000908152603a60205260409020805460ff60781b1916600160781b60ff84160217905590506146338c8761403e565b505050505050505050505050565b69d3c21bcecceda100000081106146825760405162461bcd60e51b81526020600482015260056024820152640f881b5a5b60da1b6044820152606401610ca3565b8060d1600082825461273391906158ed565b8161469e816127f7565b6001600160a01b0383166146c45760405162461bcd60e51b8152600401610ca39061586f565b6000806146d085613daf565b9150915060008260a0015183600001516146ea919061592a565b90506000851180156147055750806001600160581b03168511155b6147515760405162461bcd60e51b815260206004820152601e60248201527f4d7573742063686f6f7365206265747765656e203020616e64203130302500006044820152606401610ca3565b61475e8360200151613fa5565b6001600160a01b0387166000908152603a60205260409020805460ff92909216600160781b0260ff60781b1990921691909117905561479c85615102565b6147a69082615a1c565b6001600160a01b0387166000908152603a6020526040902080546affffffffffffffffffffff19166001600160581b03929092169190911790556147e9426129c5565b6001600160a01b0387166000908152603a60205260409020805463ffffffff92909216600160881b0263ffffffff60881b1990921691909117905561482d85615102565b6001600160a01b0387166000908152603a6020526040902080546001600160581b0392909216600160a81b026001600160a81b03909216919091179055614874868361403e565b505050505050565b604051639295ac5960e01b81526001600160a01b0382811660048301526000917f000000000000000000000000000000000000000000000000000000000000000090911690639295ac5990602401602060405180830381600087803b1580156148e457600080fd5b505af11580156148f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061491c91906156f2565b905060007f000000000000000000000000000000000000000000000000000000000000000061494c57600061496b565b6001600160a01b0383166000908152603b602052604090205460d25414155b6001600160a01b0384166000908152603a602052604090205490915060ff838116600160801b9092041614158061499f5750805b156123c0576001600160a01b0383166000908152603a60209081526040808320815160c08101835290546001600160581b03808216835263ffffffff600160581b830481169584019590955260ff600160781b8304811694840194909452600160801b82049093166060830152600160881b81049093166080820152600160a81b9092041660a0820152614a349085906132f5565b6001600160a01b0385166000908152603a60205260409020805460ff60801b1916600160801b60ff87160217905590508115614a9857614a76613f7960d25490565b6001600160a01b0385166000908152603b6020526040902061ffff9190911690555b6119a5848261403e565b6040516385acd64160e01b81527f4f78afe9dfc9a0cb0441c27b9405070cd2a48b490636a7bdd09f355e33a5d7de60048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906385acd6419060240161379d565b6000816133a7670de0b6b3a76400008561598f565b600081614b33848661598f565b614b3d9190615955565b949350505050565b6060614b3d848460008561516a565b82614b5e816127f7565b6001600160a01b038416614bb45760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610ca3565b600080614bc086613daf565b9150915060008260a001518360000151614bda919061592a565b9050614be586615102565b8351614bf1919061592a565b6001600160a01b0388166000908152603a6020526040902080546affffffffffffffffffffff19166001600160581b03929092169190911790558415614cb05760a08301516001600160a01b0388166000908152603a602052604081208054909190614c679084906001600160581b031661592a565b82546001600160581b039182166101009390930a9283029190920219909116179055506001600160a01b0387166000908152603a6020526040902080546001600160881b031690555b602083015163ffffffff16614d8657614cc8426129c5565b6001600160a01b0388166000908152603a6020908152604091829020805463ffffffff60581b198116600160581b63ffffffff968716810291821793849055855160c0810187526001600160581b039384169284169290921782528304861693810193909352600160781b820460ff90811694840194909452600160801b82049093166060830152600160881b81049093166080820152600160a81b9092041660a0820152614d7e9088906140e69082906132f5565b5050506119a5565b6000816001600160581b0316846020015163ffffffff1642614da89190615a05565b614db2919061598f565b90506000614dc1600289615955565b614dd4906001600160581b0385166158ed565b614dde9083615955565b90506000614def6116658342615a05565b6001600160a01b038b166000908152603a60205260408120805463ffffffff60581b1916600160581b63ffffffff851602179055909150614e2f82613fa5565b6001600160a01b038c166000908152603a60205260409020805460ff60781b1916600160781b60ff8416021790559050614e698b8761403e565b5050505050505050505050565b600061ffff8211156129c15760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201526536206269747360d01b6064820152608401610ca3565b6040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3611d1e60008383615292565b6040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3611d1e82600083615292565b60006114108284615a05565b825460009081908015614fc95785614f98600183615a05565b81548110614fa857614fa8615aaf565b600091825260209091200154600160201b90046001600160e01b0316614fcc565b60005b6001600160e01b03169250614fe583858763ffffffff16565b915060008111801561502357504386614fff600184615a05565b8154811061500f5761500f615aaf565b60009182526020909120015463ffffffff16145b1561508357615031826152f8565b8661503d600184615a05565b8154811061504d5761504d615aaf565b9060005260206000200160000160046101000a8154816001600160e01b0302191690836001600160e01b031602179055506150ed565b856040518060400160405280615098436129c5565b63ffffffff1681526020016150ac856152f8565b6001600160e01b039081169091528254600181018455600093845260209384902083519490930151909116600160201b0263ffffffff909316929092179101555b50935093915050565b600061141082846158ed565b60006001600160581b038211156129c15760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201526538206269747360d01b6064820152608401610ca3565b6060824710156151cb5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610ca3565b843b6152195760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610ca3565b600080866001600160a01b031685876040516152359190615776565b60006040518083038185875af1925050503d8060008114615272576040519150601f19603f3d011682016040523d82523d6000602084013e615277565b606091505b5091509150615287828286615361565b979650505050505050565b6001600160a01b03831615806152af57506001600160a01b038216155b156152dd576152da606c6001600160a01b038416156152d0576150f66152d4565b614f735b83614f7f565b50505b6123c06152e9846118a9565b6152f2846118a9565b836140fe565b60006001600160e01b038211156129c15760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20326044820152663234206269747360c81b6064820152608401610ca3565b60608315615370575081611410565b8251156153805782518084602001fd5b8160405162461bcd60e51b8152600401610ca3919061585c565b600082601f8301126153ab57600080fd5b815160206153c06153bb836158c9565b615898565b80838252828201915082860187848660051b89010111156153e057600080fd5b60005b858110156153ff578151845292840192908401906001016153e3565b5090979650505050505050565b803563ffffffff81168114612e1757600080fd5b60006020828403121561543257600080fd5b813561141081615adb565b60006020828403121561544f57600080fd5b815161141081615adb565b6000806040838503121561546d57600080fd5b823561547881615adb565b946020939093013593505050565b6000806040838503121561549957600080fd5b82356154a481615adb565b91506154b26020840161540c565b90509250929050565b600080604083850312156154ce57600080fd5b82356154d981615adb565b915060208301356154e981615afe565b809150509250929050565b60008060006060848603121561550957600080fd5b835167ffffffffffffffff8082111561552157600080fd5b818601915086601f83011261553557600080fd5b815160206155456153bb836158c9565b8083825282820191508286018b848660051b890101111561556557600080fd5b600096505b8487101561559157805161557d81615adb565b83526001969096019591830191830161556a565b50918901519197509093505050808211156155ab57600080fd5b506155b88682870161539a565b925050604084015190509250925092565b6000602082840312156155db57600080fd5b815161141081615af0565b6000602082840312156155f857600080fd5b5035919050565b60006020828403121561561157600080fd5b5051919050565b6000806040838503121561562b57600080fd5b8235915060208301356154e981615adb565b6000806000806080858703121561565357600080fd5b84359350602085013561566581615adb565b9250604085013561567581615af0565b9150606085013561568581615af0565b939692955090935050565b600080604083850312156156a357600080fd5b8235915060208301356154e981615af0565b600080604083850312156156c857600080fd5b50508035926020909101359150565b6000602082840312156156e957600080fd5b6114108261540c565b60006020828403121561570457600080fd5b815161141081615afe565b600081518084526020808501945080840160005b8381101561573f57815187529582019590820190600101615723565b509495945050505050565b60008151808452615762816020860160208601615a3c565b601f01601f19169290920160200192915050565b60008251615788818460208701615a3c565b9190910192915050565b8481526000602060018060a01b038087168285015280861660408501526080606085015261010084018551608080870152818151808452610120880191508583019350600092505b808310156157fc578351851682529285019260019290920191908501906157da565b50848801519450607f199350838782030160a088015261581c818661570f565b94505050506040850151818584030160c086015261583a838261574a565b92505050606084015161585160e085018215159052565b509695505050505050565b602081526000611410602083018461574a565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b604051601f8201601f1916810167ffffffffffffffff811182821017156158c1576158c1615ac5565b604052919050565b600067ffffffffffffffff8211156158e3576158e3615ac5565b5060051b60200190565b6000821982111561590057615900615a83565b500190565b600060ff821660ff84168060ff0382111561592257615922615a83565b019392505050565b60006001600160581b0380831681851680830382111561594c5761594c615a83565b01949350505050565b60008261596457615964615a99565b500490565b60006001600160581b038084168061598357615983615a99565b92169190910492915050565b60008160001904831182151516156159a9576159a9615a83565b500290565b60006001600160581b03808316818516818304811182151516156159d4576159d4615a83565b02949350505050565b60006001600160801b03838116908316818110156159fd576159fd615a83565b039392505050565b600082821015615a1757615a17615a83565b500390565b60006001600160581b03838116908316818110156159fd576159fd615a83565b60005b83811015615a57578181015183820152602001615a3f565b838111156119a55750506000910152565b6000600019821415615a7c57615a7c615a83565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461171457600080fd5b801515811461171457600080fd5b60ff8116811461171457600080fdfea264697066735822122015019797db0519bb75c9b618af32f315d8f52c8e6376687dc86e5737896fc41564736f6c63430008060033000000000000000000000000afce80b19a8ce13dec0739a1aab7a028d6845eb3000000000000000000000000a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd2000000000000000000000000861f12764780896fd783ea615dd55df0ff865752000000000000000000000000e2469f47ab58cf9cf59f9822e3c5de4950a41c4900000000000000000000000000000000000000000000000000000000001baf80000000000000000000000000ba100000625a3754423978a60c9317c58a424e3d000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8e2469f47ab58cf9cf59f9822e3c5de4950a41c49000200000000000000000089000000000000000000000000bec2d02008dc64a6ad519471048cf3d3af5ca0c5
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103e55760003560e01c806369940d791161020a578063a694fc3a11610125578063c822ddb9116100b8578063d279c19111610087578063d279c19114610b08578063d9a2ee5714610b1b578063dc1627eb14610b42578063e9fad8ee14610b69578063f1127ed814610b7157600080fd5b8063c822ddb91461096d578063c891091314610aa3578063cd3daf9d14610ad7578063d09ad56514610adf57600080fd5b8063ba7a4ec1116100f4578063ba7a4ec11461090f578063beeadf1614610917578063c19fd2171461093e578063c56010721461094657600080fd5b8063a694fc3a146108ce578063abe50f19146108e1578063b52c05fe146108f4578063b88a802f1461090757600080fd5b806380faa57d1161019d578063943182141161016c578063943182141461085957806395d89b411461088c5780639ab24eb014610894578063a3f5c1d2146108a757600080fd5b806380faa57d1461082d57806383069885146108355780638e539e8c1461083e5780639110b7b51461085157600080fd5b806370a08231116101d957806370a08231146107cd57806372b49d63146107e05780637acb7757146108075780637c098a1b1461081a57600080fd5b806369940d79146107635780636c514a99146107895780636e5b0cc5146107925780636fcfff45146107a557600080fd5b80633e0dc34e1161030557806358b235dd11610298578063612556bf11610267578063612556bf146106a8578063613ce410146106bb5780636154913b1461072a57806365c787751461073d578063672e1fd81461075057600080fd5b806358b235dd1461065c5780635c19a95c1461066f5780635fa456dc146106825780635fcddddf1461069557600080fd5b806349b9a7af116102d457806349b9a7af14610625578063519b9a3f1461062e57806357c37b6914610636578063587cde1e1461064957600080fd5b80633e0dc34e146105c55780633f2a5540146105ec578063403f4447146105ff578063478dc62f1461061257600080fd5b80631be052891161037d578063359c4a961161034c578063359c4a961461056e578063367e5f3e146105785780633a46b1a81461059f5780633c6b16ab146105b257600080fd5b80631be052891461051b5780632b5335c314610525578063312f6b831461052d578063313ce5671461055457600080fd5b806312064c34116103b957806312064c341461046c578063158274a5146104bf57806318160ddd146104fe578063197621431461050657600080fd5b80628cc262146103ea5780630552c61c1461041057806306fdde031461044e5780630882af3a14610463575b600080fd5b6103fd6103f8366004615420565b610bae565b6040519081526020015b60405180910390f35b609d5461042e906001600160801b0380821691600160801b90041682565b604080516001600160801b03938416815292909116602083015201610407565b610456610bc7565b604051610407919061585c565b6103fd60d15481565b6104aa61047a366004615420565b6001600160a01b03166000908152603a60205260409020546001600160581b0380821692600160a81b9092041690565b60408051928352602083019190915201610407565b6104e67f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c881565b6040516001600160a01b039091168152602001610407565b6103fd610bd9565b610519610514366004615420565b610c2b565b005b6103fd62093a8081565b610519610c55565b6104e67f000000000000000000000000e2469f47ab58cf9cf59f9822e3c5de4950a41c4981565b61055c601281565b60405160ff9091168152602001610407565b6103fd6212750081565b6104e67f000000000000000000000000bec2d02008dc64a6ad519471048cf3d3af5ca0c581565b6103fd6105ad36600461545a565b61139d565b6105196105c03660046155e6565b611417565b6103fd7fe2469f47ab58cf9cf59f9822e3c5de4950a41c4900020000000000000000008981565b6033546104e6906001600160a01b031681565b61051961060d3660046155e6565b6116bd565b6105196106203660046155e6565b611717565b6103fd60d35481565b6105196117ca565b610519610644366004615420565b611857565b6104e6610657366004615420565b6118a9565b6103fd61066a3660046156d7565b6118d9565b61051961067d366004615420565b61198f565b61051961069036600461563d565b611999565b6105196106a3366004615420565b6119ab565b6105196106b6366004615420565b611a04565b6035546106f39063ffffffff80821691600160201b8104909116906001600160601b03600160401b8204811691600160a01b90041684565b6040805163ffffffff95861681529490931660208501526001600160601b0391821692840192909252166060820152608001610407565b6105196107383660046154bb565b611a60565b61051961074b366004615420565b611b91565b61051961075e3660046155e6565b611b9a565b7f000000000000000000000000a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd26104e6565b6103fd60d25481565b6105196107a0366004615420565b611ba3565b6107b86107b3366004615420565b611c60565b60405163ffffffff9091168152602001610407565b6103fd6107db366004615420565b611c82565b6103fd7f00000000000000000000000000000000000000000000000000000000001baf8081565b610519610815366004615618565b611d12565b6034546104e6906001600160a01b031681565b6103fd611d22565b6103fd60375481565b6103fd61084c3660046155e6565b611d39565b6103fd611d95565b61087c610867366004615420565b609e6020526000908152604090205460ff1681565b6040519015158152602001610407565b610456611fc6565b6103fd6108a2366004615420565b611fd3565b6104e67f000000000000000000000000afce80b19a8ce13dec0739a1aab7a028d6845eb381565b6105196108dc3660046155e6565b611708565b6105196108ef366004615690565b612059565b6105196109023660046156b5565b612065565b610519612071565b61051961207c565b6104e67f000000000000000000000000ba100000625a3754423978a60c9317c58a424e3d81565b6105196121dd565b6104e67f000000000000000000000000a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd281565b610a3861097b366004615420565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506001600160a01b03166000908152603a6020908152604091829020825160c08101845290546001600160581b03808216835263ffffffff600160581b830481169484019490945260ff600160781b8304811695840195909552600160801b82049094166060830152600160881b81049092166080820152600160a81b90910490911660a082015290565b6040516104079190600060c0820190506001600160581b03808451168352602084015163ffffffff808216602086015260ff604087015116604086015260ff606087015116606086015280608087015116608086015250508060a08501511660a08401525092915050565b61042e610ab1366004615420565b6036602052600090815260409020546001600160801b0380821691600160801b90041682565b6103fd612314565b6103fd610aed366004615420565b6001600160a01b03166000908152603b602052604090205490565b610519610b16366004615420565b612326565b61087c7f000000000000000000000000000000000000000000000000000000000000000181565b6104e67f000000000000000000000000861f12764780896fd783ea615dd55df0ff86575281565b61051961232f565b610b84610b7f366004615486565b6123f3565b60408051825163ffffffff1681526020928301516001600160e01b03169281019290925201610407565b6000610bc182610bbc612314565b612476565b92915050565b6060610bd4603854612530565b905090565b606c5460009080610bec57600091505090565b606c610bf9600183615a05565b81548110610c0957610c09615aaf565b600091825260209091200154600160201b90046001600160e01b031692915050565b610c3361264b565b603380546001600160a01b0319166001600160a01b0392909216919091179055565b609c5460ff16610cac5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b609c805460ff1916905560d15460018111610cf35760405162461bcd60e51b81526020600482015260076024820152666e6f206665657360c81b6044820152606401610ca3565b600160d1556040516370a0823160e01b81523060048201526000907f000000000000000000000000bec2d02008dc64a6ad519471048cf3d3af5ca0c56001600160a01b0316906370a082319060240160206040518083038186803b158015610d5a57600080fd5b505afa158015610d6e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d9291906155ff565b6040516370a0823160e01b81523060048201529091506000906001600160a01b037f000000000000000000000000a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd216906370a082319060240160206040518083038186803b158015610df757600080fd5b505afa158015610e0b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2f91906155ff565b604051631f29a8cd60e31b81527fe2469f47ab58cf9cf59f9822e3c5de4950a41c4900020000000000000000008960048201529091506000906001600160a01b037f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8169063f94d46689060240160006040518083038186803b158015610eb457600080fd5b505afa158015610ec8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ef091908101906154f4565b505090507f000000000000000000000000a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd26001600160a01b031681600081518110610f3157610f31615aaf565b60200260200101516001600160a01b031614610f795760405162461bcd60e51b81526020600482015260076024820152666e6f74204d544160c81b6044820152606401610ca3565b604080516002808252606082018352600092602083019080368337019050509050612af860d25486610fab919061598f565b610fb59190615955565b81600081518110610fc857610fc8615aaf565b60209081029190910101526001600160a01b037f000000000000000000000000bec2d02008dc64a6ad519471048cf3d3af5ca0c516632e1a7d4d61100d600188615a05565b6040518263ffffffff1660e01b815260040161102b91815260200190565b600060405180830381600087803b15801561104557600080fd5b505af1158015611059573d6000803e3d6000fd5b505050507f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c86001600160a01b0316638bdb39137fe2469f47ab58cf9cf59f9822e3c5de4950a41c4900020000000000000000008930306040518060800160405280888152602001878152602001600060018d6110d59190615a05565b6040805160ff9093166020840152820152600060608201526080016040516020818303038152906040528152602001600015158152506040518563ffffffff1660e01b815260040161112a9493929190615792565b600060405180830381600087803b15801561114457600080fd5b505af1158015611158573d6000803e3d6000fd5b50506040516370a0823160e01b8152306004820152600092507f000000000000000000000000bec2d02008dc64a6ad519471048cf3d3af5ca0c56001600160a01b031691506370a082319060240160206040518083038186803b1580156111be57600080fd5b505afa1580156111d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f691906155ff565b90506112028686615a05565b61120d9060016158ed565b81146112475760405162461bcd60e51b81526020600482015260096024820152680f081b5a5b8810941560ba1b6044820152606401610ca3565b6040516370a0823160e01b815230600482015260009085906001600160a01b037f000000000000000000000000a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd216906370a082319060240160206040518083038186803b1580156112ab57600080fd5b505afa1580156112bf573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e391906155ff565b6112ed9190615a05565b90508260008151811061130257611302615aaf565b60200260200101518110156113455760405162461bcd60e51b81526020600482015260096024820152683c206d696e204d544160b81b6044820152606401610ca3565b61134e816126b3565b60408051888152602081018390527f440ff3ae555e6e78856c05bc2eea45928789919497ad6b48a9f5aa065094e286910160405180910390a15050609c805460ff191660011790555050505050565b60004382106113ee5760405162461bcd60e51b815260206004820152601f60248201527f4552433230566f7465733a20626c6f636b206e6f7420796574206d696e6564006044820152606401610ca3565b6001600160a01b0383166000908152606b60205260409020611410908361273b565b9392505050565b6033546001600160a01b031633146114715760405162461bcd60e51b815260206004820181905260248201527f43616c6c6572206973206e6f7420726577617264206469737472696275746f726044820152606401610ca3565b600061147c816127f7565b69d3c21bcecceda100000082106114d55760405162461bcd60e51b815260206004820181905260248201527f4e6f74696679206d6f7265207468616e2061206d696c6c696f6e20756e6974736044820152606401610ca3565b6037544290600110156115035760016037546114f19190615a05565b6114fb90846158ed565b600160375592505b821561154357603454611543906001600160a01b037f000000000000000000000000a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd281169116856128f6565b60355463ffffffff16811061159c5761156761156262093a8085615955565b612959565b603580546001600160601b0392909216600160401b026bffffffffffffffffffffffff60401b19909216919091179055611626565b6035546000906115b390839063ffffffff16615a05565b6035549091506000906115d690600160401b90046001600160601b03168361598f565b90506115f362093a806115e983886158ed565b6115629190615955565b603580546001600160601b0392909216600160401b026bffffffffffffffffffffffff60401b1990921691909117905550505b61162f816129c5565b6035805463ffffffff92909216600160201b0267ffffffff000000001990921691909117905561166a61166562093a80836158ed565b6129c5565b6035805463ffffffff191663ffffffff929092169190911790556040518381527fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d906020015b60405180910390a1505050565b6116c633611c82565b6117085760405162461bcd60e51b81526020600482015260136024820152724e6f7468696e6720746f20696e63726561736560681b6044820152606401610ca3565b61171481600080612a2a565b50565b61171f61264b565b611727612ae4565b6706f05b59d3b200008111156117675760405162461bcd60e51b81526020600482015260056024820152643e2035302560d81b6044820152606401610ca3565b61177081612b46565b609d80546001600160801b03928316600160801b0292169190911790556040517f130d585ea14fd64e9d6a9e64231dba9b8692207d164bd5e38d66c02ae9929a44906117bf9083815260200190565b60405180910390a150565b336000908152603a6020526040902054600160881b900463ffffffff166118215760405162461bcd60e51b815260206004820152600b60248201526a27379031b7b7b63237bbb760a91b6044820152606401610ca3565b61182a33612baf565b60405133907f3e58f193f71ff8415ab4cc6ac25f60be9ba7dfae9eef13769586d089b7887e1a90600090a2565b61185f61264b565b606d80546001600160a01b0319166001600160a01b0383169081179091556040517f70f5968ed58c0d81c7a8bca106614e2ea19375c430722f9f0ad68de8fd74f97890600090a250565b6001600160a01b038082166000908152606a602052604081205490911680156118d25780611410565b5090919050565b60008062093a806118f063ffffffff851642615a05565b61190290670de0b6b3a764000061598f565b61190c9190615955565b90506729a2241af62c000081111561197d5761194061193b826fe1b1e5f90f944d6e1c9e66c000000000615955565b612ca2565b61194d906298968061598f565b91506658d15e1762800082106119735761196e6658d15e1762800083615a05565b611976565b60005b9150611989565b67010a741a4627800091505b50919050565b6117143382612e1c565b6119a584848484612ec4565b50505050565b6119b361264b565b6001600160a01b0381166000818152609e6020908152604091829020805460ff1916905590519182527f6843d4fd51341f35652c364bbf7be3666eacfcfc9ebac9db41bd6e05c432165f91016117bf565b611a0c61264b565b6001600160a01b0381166000818152609e6020908152604091829020805460ff1916600117905590519182527f66a41827fb347195a5a3f3b9c5c921310e2879c6715d6f2dff54add1a2fc562e91016117bf565b337f000000000000000000000000861f12764780896fd783ea615dd55df0ff8657526001600160a01b031614611ac75760405162461bcd60e51b815260206004820152600c60248201526b139bdd081d995c9a599a595960a21b6044820152606401610ca3565b6001600160a01b038216611aed5760405162461bcd60e51b8152600401610ca39061586f565b6001600160a01b0382166000908152603a60209081526040808320815160c08101835290546001600160581b03808216835263ffffffff600160581b830481169584019590955260ff600160781b8304811694840194909452600160801b82049093166060830152600160881b81049093166080820152600160a81b9092041660a082015290611b7d84836132f5565b905080156119a5576119a5848383866133b1565b6117148161343e565b61171481613521565b611bab61264b565b604051635efcc08b60e11b81526001600160a01b0382811660048301527f000000000000000000000000bec2d02008dc64a6ad519471048cf3d3af5ca0c5169063bdf9811690602401600060405180830381600087803b158015611c0e57600080fd5b505af1158015611c22573d6000803e3d6000fd5b50506040516001600160a01b03841681527f485e3ae63165cefd027e0c7be7a882469f11c3ee45cd45815bea8e7a721d1c7f925060200190506117bf565b6001600160a01b0381166000908152606b6020526040812054610bc1906129c5565b6001600160a01b0381166000908152603a60209081526040808320815160c08101835290546001600160581b03808216835263ffffffff600160581b830481169584019590955260ff600160781b8304811694840194909452600160801b82049093166060830152600160881b81049093166080820152600160a81b9092041660a0820152610bc19083906132f5565b611d1e82826000612a2a565b5050565b603554600090610bd490429063ffffffff166135b8565b6000438210611d8a5760405162461bcd60e51b815260206004820152601f60248201527f4552433230566f7465733a20626c6f636b206e6f7420796574206d696e6564006044820152606401610ca3565b610bc1606c8361273b565b604051631f29a8cd60e31b81527fe2469f47ab58cf9cf59f9822e3c5de4950a41c490002000000000000000000896004820152600090819081906001600160a01b037f000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8169063f94d46689060240160006040518083038186803b158015611e1b57600080fd5b505afa158015611e2f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e5791908101906154f4565b50915091507f000000000000000000000000a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd26001600160a01b031682600081518110611e9957611e99615aaf565b60200260200101516001600160a01b031614611ee15760405162461bcd60e51b81526020600482015260076024820152666e6f74204d544160c81b6044820152606401610ca3565b60007f000000000000000000000000e2469f47ab58cf9cf59f9822e3c5de4950a41c496001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b158015611f3c57600080fd5b505afa158015611f50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f7491906155ff565b82600081518110611f8757611f87615aaf565b6020026020010151671158e460913d0000611fa2919061598f565b611fac9190615955565b9050611fbe655af3107a400082615955565b935050505090565b6060610bd4603954612530565b6001600160a01b0381166000908152606b60205260408120548015612046576001600160a01b0383166000908152606b60205260409020612015600183615a05565b8154811061202557612025615aaf565b600091825260209091200154600160201b90046001600160e01b0316612049565b60005b6001600160e01b03169392505050565b611d1e82600083612a2a565b611d1e82600080612a2a565b61207a336135c7565b565b6120846136a7565b60d35461209490621275006158ed565b42116120ce5760405162461bcd60e51b81526020600482015260096024820152683c203134206461797360b81b6044820152606401610ca3565b60006120d8611d95565b60d25490915060008183116120f6576120f18383615a05565b612100565b6121008284615a05565b90506101f4826121128361271061598f565b61211c9190615955565b116121555760405162461bcd60e51b81526020600482015260096024820152681e101a92903234b33360b91b6044820152606401610ca3565b613a98831180156121685750620124f883105b6121a45760405162461bcd60e51b815260206004820152600d60248201526c4f7574206f6620626f756e647360981b6044820152606401610ca3565b60d28390554260d3556040518381527f77ad0680939c8f5a6c911101963afa50204eda979fac30e18dfa63af3c68cced906020016116b0565b6121e5613732565b6001600160a01b0316336001600160a01b0316146122455760405162461bcd60e51b815260206004820152601860248201527f4f6e6c79205265636f6c6c61746572616c69736174696f6e00000000000000006044820152606401610ca3565b61224d612ae4565b609d5461227290600160801b90046001600160801b0316670de0b6b3a76400006159dd565b609d80546fffffffffffffffffffffffffffffffff19166001600160801b039290921691909117905560006122a56137ed565b90506122e86122b2613732565b609d54670de0b6b3a7640000906122d990600160801b90046001600160801b03168561598f565b6122e39190615955565b613887565b6040517f8551ab4a9801f4813e5fff4a88eb374487886333f7c4e0aa1ca928a24916d53c90600090a150565b60008061231f613937565b5092915050565b611714816135c7565b336000908152603a6020526040902054600160881b900463ffffffff1680158061239857506212750061238b7f00000000000000000000000000000000000000000000000000000000001baf806001600160801b0384166158ed565b61239591906158ed565b42115b156123c5576000806123a93361047a565b90925090506123c06123bb82846158ed565b613521565b505050565b336000818152603a602052604081205461171492600160a81b9091046001600160581b031691600190612ec4565b60408051808201909152600080825260208201526001600160a01b0383166000908152606b60205260409020805463ffffffff841690811061243757612437615aaf565b60009182526020918290206040805180820190915291015463ffffffff81168252600160201b90046001600160e01b0316918101919091529392505050565b6001600160a01b03821660009081526036602052604081205481906124a4906001600160801b031684615a05565b9050806124db5750506001600160a01b038216600090815260366020526040902054600160801b90046001600160801b0316610bc1565b60006124f0826124ea87611c82565b90613a45565b6001600160a01b038616600090815260366020526040902054909150612527908290600160801b90046001600160801b03166158ed565b95945050505050565b606060005b602081108015612563575082816020811061255257612552615aaf565b1a60f81b6001600160f81b03191615155b1561257a578061257281615a68565b915050612535565b60008167ffffffffffffffff81111561259557612595615ac5565b6040519080825280601f01601f1916602001820160405280156125bf576020820181803683370190505b509050600091505b6020821080156125f557508382602081106125e4576125e4615aaf565b1a60f81b6001600160f81b03191615155b156114105783826020811061260c5761260c615aaf565b1a60f81b81838151811061262257612622615aaf565b60200101906001600160f81b031916908160001a9053508161264381615a68565b9250506125c7565b612653613a5a565b6001600160a01b0316336001600160a01b03161461207a5760405162461bcd60e51b815260206004820152601960248201527f4f6e6c7920676f7665726e6f722063616e2065786563757465000000000000006044820152606401610ca3565b69d3c21bcecceda100000081106127215760405162461bcd60e51b815260206004820152602c60248201527f43616e6e6f74206e6f746966792077697468206d6f7265207468616e2061206d60448201526b696c6c696f6e20756e69747360a01b6064820152608401610ca3565b806037600082825461273391906158ed565b909155505050565b8154600090815b8181101561279f5760006127568284613ab5565b90508486828154811061276b5761276b615aaf565b60009182526020909120015463ffffffff16111561278b57809250612799565b6127968160016158ed565b91505b50612742565b81156127e257846127b1600184615a05565b815481106127c1576127c1615aaf565b600091825260209091200154600160201b90046001600160e01b03166127e5565b60005b6001600160e01b031695945050505050565b600080612802613937565b909250905081156123c05761281682612959565b603580546001600160601b0392909216600160a01b026001600160a01b03909216919091179055612846816129c5565b6035805463ffffffff92909216600160201b0267ffffffff00000000199092169190911790556001600160a01b038316156123c057604051806040016040528061288f84612b46565b6001600160801b031681526020016128af6128aa8686612476565b612b46565b6001600160801b039081169091526001600160a01b0385166000908152603660209081526040909120835193909101518216600160801b0292909116919091179055505050565b6040516001600160a01b0383166024820152604481018290526123c090849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613ad0565b60006001600160601b038211156129c15760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201526536206269747360d01b6064820152608401610ca3565b5090565b600063ffffffff8211156129c15760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203360448201526532206269747360d01b6064820152608401610ca3565b612a5f7f000000000000000000000000e2469f47ab58cf9cf59f9822e3c5de4950a41c496001600160a01b0316333086613ba2565b60405163b6b55f2560e01b8152600481018490527f000000000000000000000000bec2d02008dc64a6ad519471048cf3d3af5ca0c56001600160a01b03169063b6b55f2590602401600060405180830381600087803b158015612ac157600080fd5b505af1158015612ad5573d6000803e3d6000fd5b505050506123c0838383613bda565b609d546001600160801b0316670de0b6b3a76400001461207a5760405162461bcd60e51b815260206004820152601960248201527f4f6e6c79207768696c6520636f6c6c61746572616c69736564000000000000006044820152606401610ca3565b60006001600160801b038211156129c15760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20316044820152663238206269747360c81b6064820152608401610ca3565b80612bb9816127f7565b6001600160a01b038216612bdf5760405162461bcd60e51b8152600401610ca39061586f565b600080612beb84613daf565b91509150612bfc8260200151613fa5565b6001600160a01b0385166000908152603a60205260408120805460ff93909316600160781b0260ff60781b1984168117825560a086015193919291612c509185916001600160581b0391821691161761592a565b82546001600160581b039182166101009390930a9283029190920219909116179055506001600160a01b0384166000908152603a6020526040902080546001600160881b031690556119a5848261403e565b600081612cb157506000919050565b816001600160801b8210612cca5760809190911c9060401b5b600160401b8210612ce05760409190911c9060201b5b600160201b8210612cf65760209190911c9060101b5b620100008210612d0b5760109190911c9060081b5b6101008210612d1f5760089190911c9060041b5b60108210612d325760049190911c9060021b5b60088210612d3e5760011b5b6001612d4a8286615955565b612d5490836158ed565b901c90506001612d648286615955565b612d6e90836158ed565b901c90506001612d7e8286615955565b612d8890836158ed565b901c90506001612d988286615955565b612da290836158ed565b901c90506001612db28286615955565b612dbc90836158ed565b901c90506001612dcc8286615955565b612dd690836158ed565b901c90506001612de68286615955565b612df090836158ed565b901c90506000612e008286615955565b9050808210612e0f5780612527565b509392505050565b919050565b6000612e27836118a9565b90506000612e3484611c82565b6001600160a01b038581166000908152606a6020526040902080546001600160a01b0319169186169190911790559050612e6d846118a9565b9250826001600160a01b0316826001600160a01b0316856001600160a01b03167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f60405160405180910390a46119a58284836140fe565b612ecc6142ba565b83612f0f5760405162461bcd60e51b81526020600482015260136024820152721253959053125117d6915493d7d05353d55395606a1b6044820152606401610ca3565b609d546001600160801b0316670de0b6b3a764000014612fa257612f37338560006001614312565b609d54612f5d908490670de0b6b3a7640000906122d9906001600160801b03168861598f565b6040518481526001600160a01b0384169033907f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb9060200160405180910390a36119a5565b336000908152603a6020908152604091829020825160c08101845290546001600160581b03808216835263ffffffff600160581b830481169484019490945260ff600160781b8304811695840195909552600160801b82049094166060830152600160881b810490921660808201819052600160a81b90920490921660a083015261304e907f00000000000000000000000000000000000000000000000000000000001baf80906158ed565b42116130945760405162461bcd60e51b815260206004820152601560248201527424a729aaa32324a1a4a2a72a2fa1a7a7a62227aba760591b6044820152606401610ca3565b621275007f00000000000000000000000000000000000000000000000000000000001baf80826080015163ffffffff166130ce91906158ed565b6130d89042615a05565b11156131265760405162461bcd60e51b815260206004820152601760248201527f554e5354414b455f57494e444f575f46494e49534845440000000000000000006044820152606401610ca3565b336000908152603a60209081526040808320815160c08101835290546001600160581b03808216835263ffffffff600160581b8304811695840186905260ff600160781b8404811695850195909552600160801b83049094166060840152600160881b82049093166080830152600160a81b900490911660a082015291906131ad906118d9565b90506000856131e257670de0b6b3a76400006131c983826158ed565b6131d3908a61598f565b6131dd9190615955565b6131e4565b875b905060006131fa83670de0b6b3a76400006158ed565b61320c83670de0b6b3a764000061598f565b6132169190615955565b60a08601519091506001600160581b0316808311156132705760405162461bcd60e51b8152602060048201526016602482015275115e18d959591cc81b585e081dda5d1a191c985dd85b60521b6044820152606401610ca3565b6000878061327d57508184145b905061328c3385836000614312565b61329e6132998486615a05565b614641565b6132a88a84613887565b6040518b81526001600160a01b038b169033907f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb9060200160405180910390a35050505050505050505050565b600060648260600151606461330a9190615905565b83516133199160ff16906159ae565b6133239190615969565b6001600160581b031690506064826040015160646133419190615905565b61334e9060ff168361598f565b6133589190615955565b90507f000000000000000000000000000000000000000000000000000000000000000115610bc1576001600160a01b0383166000908152603b6020526040902054612710906133a7908361598f565b6114109190615955565b836133bb816127f7565b6001600160a01b0385166000908152603a60209081526040909120805460ff60801b1916600160801b60ff8616021790558401516133f890613fa5565b6001600160a01b0386166000908152603a60205260409020805460ff92909216600160781b0260ff60781b19909216919091179055613437858461403e565b5050505050565b80613448816127f7565b6001600160a01b03821661346e5760405162461bcd60e51b8152600401610ca39061586f565b60008061347a84613daf565b91509150600061348d8360200151613fa5565b9050826040015160ff168160ff1614156134e95760405162461bcd60e51b815260206004820152601960248201527f4e6f7468696e6720776f72746820706f6b696e672068657265000000000000006044820152606401610ca3565b6001600160a01b0385166000908152603a60205260409020805460ff60781b1916600160781b60ff841602179055613437858361403e565b61352a33611c82565b6135765760405162461bcd60e51b815260206004820152601b60248201527f494e56414c49445f42414c414e43455f4f4e5f434f4f4c444f574e00000000006044820152606401610ca3565b6135803382614694565b60405181815233907f8a05f911d8ab7fc50fec37ef4ba7f9bfcb1a3c191c81dcd824ad0946c4e20d659060200160405180910390a250565b60008183116119895782611410565b336135d1816127f7565b33600090815260366020526040902054600160801b90046001600160801b0316801561369e5733600090815260366020526040902080546001600160801b03908116909155603454613654916001600160a01b037f000000000000000000000000a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd2811692169086908516613ba2565b604080516001600160801b038316815290516001600160a01b0385169133917f540798df468d7b23d11f156fdb954cb19ad414d150722a7b6d55ba369dea792e9181900360200190a35b6123c03361487c565b6136af614aa2565b6001600160a01b0316336001600160a01b031614806136e657506136d1613a5a565b6001600160a01b0316336001600160a01b0316145b61207a5760405162461bcd60e51b815260206004820152601760248201527f4f6e6c79206b6565706572206f7220676f7665726e6f720000000000000000006044820152606401610ca3565b6040516385acd64160e01b81527f39e3ed1fc335ce346a8cbe3e64dd525cf22b37f1e2104a755e761c3c1eb4734f60048201526000907f000000000000000000000000afce80b19a8ce13dec0739a1aab7a028d6845eb36001600160a01b0316906385acd641906024015b60206040518083038186803b1580156137b557600080fd5b505afa1580156137c9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bd4919061543d565b6040516370a0823160e01b81523060048201526000907f000000000000000000000000bec2d02008dc64a6ad519471048cf3d3af5ca0c56001600160a01b0316906370a082319060240160206040518083038186803b15801561384f57600080fd5b505afa158015613863573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bd491906155ff565b604051632e1a7d4d60e01b8152600481018290527f000000000000000000000000bec2d02008dc64a6ad519471048cf3d3af5ca0c56001600160a01b031690632e1a7d4d90602401600060405180830381600087803b1580156138e957600080fd5b505af11580156138fd573d6000803e3d6000fd5b50611d1e9250506001600160a01b037f000000000000000000000000e2469f47ab58cf9cf59f9822e3c5de4950a41c4916905083836128f6565b6000806000613944611d22565b6040805160808101825260355463ffffffff8082168352600160201b820416602083018190526001600160601b03600160401b8304811694840194909452600160a01b90910490921660608201529192506000906139a29084615a05565b9050806139bf5750606001516001600160601b0316939092509050565b60008183604001516001600160601b03166139da919061598f565b905060006139e6610bd9565b90508015806139f3575081155b15613a1057505050606001516001600160601b0316939092509050565b6000613a1c8383614b11565b90508085606001516001600160601b0316613a3791906158ed565b989597509495505050505050565b60006114108383670de0b6b3a7640000614b26565b60007f000000000000000000000000afce80b19a8ce13dec0739a1aab7a028d6845eb36001600160a01b0316630c340a246040518163ffffffff1660e01b815260040160206040518083038186803b1580156137b557600080fd5b6000613ac46002848418615955565b611410908484166158ed565b6000613b25826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614b459092919063ffffffff16565b8051909150156123c05780806020019051810190613b4391906155c9565b6123c05760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610ca3565b6040516001600160a01b03808516602483015283166044820152606481018290526119a59085906323b872dd60e01b90608401612922565b613be2612ae4565b613bea6142ba565b82613c2d5760405162461bcd60e51b81526020600482015260136024820152721253959053125117d6915493d7d05353d55395606a1b6044820152606401610ca3565b6001600160a01b03821615613c4657613c463383612e1c565b336000908152603a60209081526040808320815160c08101835290546001600160581b03808216835263ffffffff600160581b830481169584019590955260ff600160781b8304811694840194909452600160801b82049093166060830152600160881b81049093166080820152600160a81b9092041660a0820152908280613d2657506000826080015163ffffffff16118015613d265750621275007f00000000000000000000000000000000000000000000000000000000001baf80836080015163ffffffff16613d1991906158ed565b613d2391906158ed565b42115b90508015613d5a5760405133907f3e58f193f71ff8415ab4cc6ac25f60be9ba7dfae9eef13769586d089b7887e1a90600090a25b613d65338683614b54565b604080518681526001600160a01b038616602082015233917f9f9e4044c5742cca66ca090b21552bac14645e68bad7a92364a9d9ff18111a1c910160405180910390a25050505050565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a0810191909152506001600160a01b0381166000908152603a60209081526040808320815160c08101835290546001600160581b03808216835263ffffffff600160581b830481169584019590955260ff600160781b8304811694840194909452600160801b82049093166060830152600160881b81049093166080820152600160a81b9092041660a082015290613e7283836132f5565b604051639295ac5960e01b81526001600160a01b0385811660048301529192507f000000000000000000000000861f12764780896fd783ea615dd55df0ff86575290911690639295ac5990602401602060405180830381600087803b158015613eda57600080fd5b505af1158015613eee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f1291906156f2565b6001600160a01b0384166000908152603a60205260409020805460ff92909216600160801b0260ff60801b199092169190911790557f000000000000000000000000000000000000000000000000000000000000000115613fa057613f7e613f7960d25490565b614e76565b6001600160a01b0384166000908152603b6020526040902061ffff9190911690555b915091565b600063ffffffff8216613fba57506000919050565b6000613fcc63ffffffff841642615a05565b90506277f880811015613fe25750600092915050565b62eff100811015613ff65750601492915050565b6301dfe20081101561400b5750601e92915050565b6302cfd3008110156140205750602892915050565b6303bfc4008110156140355750603292915050565b50603c92915050565b6001600160a01b0382166000908152603a60209081526040808320815160c08101835290546001600160581b03808216835263ffffffff600160581b830481169584019590955260ff600160781b8304811694840194909452600160801b82049093166060830152600160881b81049093166080820152600160a81b9092041660a08201526140ce9084906132f5565b9050818111156140eb576123c0836140e68484615a05565b614ed9565b6123c0836140f98385615a05565b614f26565b816001600160a01b0316836001600160a01b0316141580156141205750600081115b156123c0576001600160a01b038316156141ae576001600160a01b0383166000908152606b60205260408120819061415b90614f7385614f7f565b91509150846001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a72483836040516141a3929190918252602082015260400190565b60405180910390a250505b6001600160a01b03821615614237576001600160a01b0382166000908152606b6020526040812081906141e4906150f685614f7f565b91509150836001600160a01b03167fdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a724838360405161422c929190918252602082015260400190565b60405180910390a250505b606d546001600160a01b0316156123c057606d546040516216c4a960ea1b81526001600160a01b03858116600483015284811660248301526044820184905290911690635b12a40090606401600060405180830381600087803b15801561429d57600080fd5b505af11580156142b1573d6000803e3d6000fd5b50505050505050565b33321461207a57336000908152609e602052604090205460ff1661207a5760405162461bcd60e51b815260206004820152600f60248201526e139bdd081dda1a5d195b1a5cdd1959608a1b6044820152606401610ca3565b8361431c816127f7565b6001600160a01b0385166143725760405162461bcd60e51b815260206004820152601d60248201527f45524332303a206275726e2066726f6d207a65726f20616464726573730000006044820152606401610ca3565b60008061437e87613daf565b9150915060008260a001518360000151614398919061592a565b6001600160581b031690508415614431576001600160a01b0388166000908152603a6020526040902080546affffffffffffffffffffff191690556143dc81615102565b6001600160a01b0389166000908152603a6020526040902080546001600160581b0392909216600160a81b026001600160a81b0390921691909117905561442281615102565b6001600160581b031660a08401525b868360a001516001600160581b0316101561448e5760405162461bcd60e51b815260206004820152601c60248201527f45524332303a206275726e20616d6f756e74203e2062616c616e6365000000006044820152606401610ca3565b61449787615102565b6001600160a01b0389166000908152603a6020526040902080546001600160581b03600160a81b808304821694909403169092026001600160a81b039092169190911790558515614564576001600160a01b0388166000908152603a6020526040812080546001600160581b03600160a81b82048116939161451b9185911661592a565b82546001600160581b039182166101009390930a9283029190920219909116179055506001600160a01b0388166000908152603a6020526040902080546001600160881b031690555b6000614571600889615955565b61457b9083615a05565b60208501516145909063ffffffff1642615a05565b61459a919061598f565b905060006145a88383615955565b905060006145b96116658342615a05565b6001600160a01b038c166000908152603a60205260408120805463ffffffff60581b1916600160581b63ffffffff8516021790559091506145f982613fa5565b6001600160a01b038d166000908152603a60205260409020805460ff60781b1916600160781b60ff84160217905590506146338c8761403e565b505050505050505050505050565b69d3c21bcecceda100000081106146825760405162461bcd60e51b81526020600482015260056024820152640f881b5a5b60da1b6044820152606401610ca3565b8060d1600082825461273391906158ed565b8161469e816127f7565b6001600160a01b0383166146c45760405162461bcd60e51b8152600401610ca39061586f565b6000806146d085613daf565b9150915060008260a0015183600001516146ea919061592a565b90506000851180156147055750806001600160581b03168511155b6147515760405162461bcd60e51b815260206004820152601e60248201527f4d7573742063686f6f7365206265747765656e203020616e64203130302500006044820152606401610ca3565b61475e8360200151613fa5565b6001600160a01b0387166000908152603a60205260409020805460ff92909216600160781b0260ff60781b1990921691909117905561479c85615102565b6147a69082615a1c565b6001600160a01b0387166000908152603a6020526040902080546affffffffffffffffffffff19166001600160581b03929092169190911790556147e9426129c5565b6001600160a01b0387166000908152603a60205260409020805463ffffffff92909216600160881b0263ffffffff60881b1990921691909117905561482d85615102565b6001600160a01b0387166000908152603a6020526040902080546001600160581b0392909216600160a81b026001600160a81b03909216919091179055614874868361403e565b505050505050565b604051639295ac5960e01b81526001600160a01b0382811660048301526000917f000000000000000000000000861f12764780896fd783ea615dd55df0ff86575290911690639295ac5990602401602060405180830381600087803b1580156148e457600080fd5b505af11580156148f8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061491c91906156f2565b905060007f000000000000000000000000000000000000000000000000000000000000000161494c57600061496b565b6001600160a01b0383166000908152603b602052604090205460d25414155b6001600160a01b0384166000908152603a602052604090205490915060ff838116600160801b9092041614158061499f5750805b156123c0576001600160a01b0383166000908152603a60209081526040808320815160c08101835290546001600160581b03808216835263ffffffff600160581b830481169584019590955260ff600160781b8304811694840194909452600160801b82049093166060830152600160881b81049093166080820152600160a81b9092041660a0820152614a349085906132f5565b6001600160a01b0385166000908152603a60205260409020805460ff60801b1916600160801b60ff87160217905590508115614a9857614a76613f7960d25490565b6001600160a01b0385166000908152603b6020526040902061ffff9190911690555b6119a5848261403e565b6040516385acd64160e01b81527f4f78afe9dfc9a0cb0441c27b9405070cd2a48b490636a7bdd09f355e33a5d7de60048201526000907f000000000000000000000000afce80b19a8ce13dec0739a1aab7a028d6845eb36001600160a01b0316906385acd6419060240161379d565b6000816133a7670de0b6b3a76400008561598f565b600081614b33848661598f565b614b3d9190615955565b949350505050565b6060614b3d848460008561516a565b82614b5e816127f7565b6001600160a01b038416614bb45760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610ca3565b600080614bc086613daf565b9150915060008260a001518360000151614bda919061592a565b9050614be586615102565b8351614bf1919061592a565b6001600160a01b0388166000908152603a6020526040902080546affffffffffffffffffffff19166001600160581b03929092169190911790558415614cb05760a08301516001600160a01b0388166000908152603a602052604081208054909190614c679084906001600160581b031661592a565b82546001600160581b039182166101009390930a9283029190920219909116179055506001600160a01b0387166000908152603a6020526040902080546001600160881b031690555b602083015163ffffffff16614d8657614cc8426129c5565b6001600160a01b0388166000908152603a6020908152604091829020805463ffffffff60581b198116600160581b63ffffffff968716810291821793849055855160c0810187526001600160581b039384169284169290921782528304861693810193909352600160781b820460ff90811694840194909452600160801b82049093166060830152600160881b81049093166080820152600160a81b9092041660a0820152614d7e9088906140e69082906132f5565b5050506119a5565b6000816001600160581b0316846020015163ffffffff1642614da89190615a05565b614db2919061598f565b90506000614dc1600289615955565b614dd4906001600160581b0385166158ed565b614dde9083615955565b90506000614def6116658342615a05565b6001600160a01b038b166000908152603a60205260408120805463ffffffff60581b1916600160581b63ffffffff851602179055909150614e2f82613fa5565b6001600160a01b038c166000908152603a60205260409020805460ff60781b1916600160781b60ff8416021790559050614e698b8761403e565b5050505050505050505050565b600061ffff8211156129c15760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203160448201526536206269747360d01b6064820152608401610ca3565b6040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3611d1e60008383615292565b6040518181526000906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a3611d1e82600083615292565b60006114108284615a05565b825460009081908015614fc95785614f98600183615a05565b81548110614fa857614fa8615aaf565b600091825260209091200154600160201b90046001600160e01b0316614fcc565b60005b6001600160e01b03169250614fe583858763ffffffff16565b915060008111801561502357504386614fff600184615a05565b8154811061500f5761500f615aaf565b60009182526020909120015463ffffffff16145b1561508357615031826152f8565b8661503d600184615a05565b8154811061504d5761504d615aaf565b9060005260206000200160000160046101000a8154816001600160e01b0302191690836001600160e01b031602179055506150ed565b856040518060400160405280615098436129c5565b63ffffffff1681526020016150ac856152f8565b6001600160e01b039081169091528254600181018455600093845260209384902083519490930151909116600160201b0263ffffffff909316929092179101555b50935093915050565b600061141082846158ed565b60006001600160581b038211156129c15760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203860448201526538206269747360d01b6064820152608401610ca3565b6060824710156151cb5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610ca3565b843b6152195760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610ca3565b600080866001600160a01b031685876040516152359190615776565b60006040518083038185875af1925050503d8060008114615272576040519150601f19603f3d011682016040523d82523d6000602084013e615277565b606091505b5091509150615287828286615361565b979650505050505050565b6001600160a01b03831615806152af57506001600160a01b038216155b156152dd576152da606c6001600160a01b038416156152d0576150f66152d4565b614f735b83614f7f565b50505b6123c06152e9846118a9565b6152f2846118a9565b836140fe565b60006001600160e01b038211156129c15760405162461bcd60e51b815260206004820152602760248201527f53616665436173743a2076616c756520646f65736e27742066697420696e20326044820152663234206269747360c81b6064820152608401610ca3565b60608315615370575081611410565b8251156153805782518084602001fd5b8160405162461bcd60e51b8152600401610ca3919061585c565b600082601f8301126153ab57600080fd5b815160206153c06153bb836158c9565b615898565b80838252828201915082860187848660051b89010111156153e057600080fd5b60005b858110156153ff578151845292840192908401906001016153e3565b5090979650505050505050565b803563ffffffff81168114612e1757600080fd5b60006020828403121561543257600080fd5b813561141081615adb565b60006020828403121561544f57600080fd5b815161141081615adb565b6000806040838503121561546d57600080fd5b823561547881615adb565b946020939093013593505050565b6000806040838503121561549957600080fd5b82356154a481615adb565b91506154b26020840161540c565b90509250929050565b600080604083850312156154ce57600080fd5b82356154d981615adb565b915060208301356154e981615afe565b809150509250929050565b60008060006060848603121561550957600080fd5b835167ffffffffffffffff8082111561552157600080fd5b818601915086601f83011261553557600080fd5b815160206155456153bb836158c9565b8083825282820191508286018b848660051b890101111561556557600080fd5b600096505b8487101561559157805161557d81615adb565b83526001969096019591830191830161556a565b50918901519197509093505050808211156155ab57600080fd5b506155b88682870161539a565b925050604084015190509250925092565b6000602082840312156155db57600080fd5b815161141081615af0565b6000602082840312156155f857600080fd5b5035919050565b60006020828403121561561157600080fd5b5051919050565b6000806040838503121561562b57600080fd5b8235915060208301356154e981615adb565b6000806000806080858703121561565357600080fd5b84359350602085013561566581615adb565b9250604085013561567581615af0565b9150606085013561568581615af0565b939692955090935050565b600080604083850312156156a357600080fd5b8235915060208301356154e981615af0565b600080604083850312156156c857600080fd5b50508035926020909101359150565b6000602082840312156156e957600080fd5b6114108261540c565b60006020828403121561570457600080fd5b815161141081615afe565b600081518084526020808501945080840160005b8381101561573f57815187529582019590820190600101615723565b509495945050505050565b60008151808452615762816020860160208601615a3c565b601f01601f19169290920160200192915050565b60008251615788818460208701615a3c565b9190910192915050565b8481526000602060018060a01b038087168285015280861660408501526080606085015261010084018551608080870152818151808452610120880191508583019350600092505b808310156157fc578351851682529285019260019290920191908501906157da565b50848801519450607f199350838782030160a088015261581c818661570f565b94505050506040850151818584030160c086015261583a838261574a565b92505050606084015161585160e085018215159052565b509695505050505050565b602081526000611410602083018461574a565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b604051601f8201601f1916810167ffffffffffffffff811182821017156158c1576158c1615ac5565b604052919050565b600067ffffffffffffffff8211156158e3576158e3615ac5565b5060051b60200190565b6000821982111561590057615900615a83565b500190565b600060ff821660ff84168060ff0382111561592257615922615a83565b019392505050565b60006001600160581b0380831681851680830382111561594c5761594c615a83565b01949350505050565b60008261596457615964615a99565b500490565b60006001600160581b038084168061598357615983615a99565b92169190910492915050565b60008160001904831182151516156159a9576159a9615a83565b500290565b60006001600160581b03808316818516818304811182151516156159d4576159d4615a83565b02949350505050565b60006001600160801b03838116908316818110156159fd576159fd615a83565b039392505050565b600082821015615a1757615a17615a83565b500390565b60006001600160581b03838116908316818110156159fd576159fd615a83565b60005b83811015615a57578181015183820152602001615a3f565b838111156119a55750506000910152565b6000600019821415615a7c57615a7c615a83565b5060010190565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fd5b6001600160a01b038116811461171457600080fd5b801515811461171457600080fd5b60ff8116811461171457600080fdfea264697066735822122015019797db0519bb75c9b618af32f315d8f52c8e6376687dc86e5737896fc41564736f6c63430008060033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000afce80b19a8ce13dec0739a1aab7a028d6845eb3000000000000000000000000a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd2000000000000000000000000861f12764780896fd783ea615dd55df0ff865752000000000000000000000000e2469f47ab58cf9cf59f9822e3c5de4950a41c4900000000000000000000000000000000000000000000000000000000001baf80000000000000000000000000ba100000625a3754423978a60c9317c58a424e3d000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8e2469f47ab58cf9cf59f9822e3c5de4950a41c49000200000000000000000089000000000000000000000000bec2d02008dc64a6ad519471048cf3d3af5ca0c5
-----Decoded View---------------
Arg [0] : _nexus (address): 0xAFcE80b19A8cE13DEc0739a1aaB7A028d6845Eb3
Arg [1] : _rewardsToken (address): 0xa3BeD4E1c75D00fa6f4E5E6922DB7261B5E9AcD2
Arg [2] : _questManager (address): 0x861f12764780896FD783eA615Dd55Df0FF865752
Arg [3] : _stakedToken (address): 0xe2469f47aB58cf9CF59F9822e3C5De4950a41C49
Arg [4] : _cooldownSeconds (uint256): 1814400
Arg [5] : _bal (address[2]): 0xba100000625a3754423978a60c9317c58a424e3D,0xBA12222222228d8Ba445958a75a0704d566BF2C8
Arg [6] : _poolId (bytes32): 0xe2469f47ab58cf9cf59f9822e3c5de4950a41c49000200000000000000000089
Arg [7] : _balancerGauge (address): 0xbeC2d02008Dc64A6AD519471048CF3D3aF5ca0C5
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 000000000000000000000000afce80b19a8ce13dec0739a1aab7a028d6845eb3
Arg [1] : 000000000000000000000000a3bed4e1c75d00fa6f4e5e6922db7261b5e9acd2
Arg [2] : 000000000000000000000000861f12764780896fd783ea615dd55df0ff865752
Arg [3] : 000000000000000000000000e2469f47ab58cf9cf59f9822e3c5de4950a41c49
Arg [4] : 00000000000000000000000000000000000000000000000000000000001baf80
Arg [5] : 000000000000000000000000ba100000625a3754423978a60c9317c58a424e3d
Arg [6] : 000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8
Arg [7] : e2469f47ab58cf9cf59f9822e3c5de4950a41c49000200000000000000000089
Arg [8] : 000000000000000000000000bec2d02008dc64a6ad519471048cf3d3af5ca0c5
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.