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:
DelegateManagerV2
Compiler Version
v0.5.17+commit.d19bba13
Contract Source Code (Solidity)
/**
*Submitted for verification at Etherscan.io on 2022-07-26
*/
pragma solidity ^0.5.5;
/**
* @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) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly { codehash := extcodehash(account) }
return (codehash != accountHash && codehash != 0x0);
}
/**
* @dev Converts an `address` into `address payable`. Note that this is
* simply a type cast: the actual underlying value is not changed.
*
* _Available since v2.4.0._
*/
function toPayable(address account) internal pure returns (address payable) {
return address(uint160(account));
}
/**
* @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].
*
* _Available since v2.4.0._
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-call-value
(bool success, ) = recipient.call.value(amount)("");
require(success, "Address: unable to send value, recipient may have reverted");
}
}
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see {ERC20Detailed}.
*/
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);
}
/**
* @title Initializable
*
* @dev Helper contract to support initializer functions. To use it, replace
* the constructor with a function that has the `initializer` modifier.
* WARNING: Unlike constructors, initializer functions must be manually
* invoked. This applies both to deploying an Initializable contract, as well
* as extending an Initializable contract via inheritance.
* WARNING: When used with inheritance, manual care must be taken to not invoke
* a parent initializer twice, or ensure that all initializers are idempotent,
* because this is not dealt with automatically as with constructors.
*/
contract Initializable {
address private proxyAdmin;
uint256 private filler1;
uint256 private filler2;
/**
* @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 use in the initializer function of a contract.
*/
modifier initializer() {
require(msg.sender == proxyAdmin, "Only proxy admin can initialize");
require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
bool isTopLevelCall = !initializing;
if (isTopLevelCall) {
initializing = true;
initialized = true;
}
_;
if (isTopLevelCall) {
initializing = false;
}
}
/// @dev Returns true if and only if the function is running in the constructor
function isConstructor() private view returns (bool) {
// extcodesize checks the size of the code stored in an address, and
// address returns the current address. Since the code is still not
// deployed when running a constructor, any checks on its code size will
// yield zero, making it an effective way to detect if a contract is
// under construction or not.
address self = address(this);
uint256 cs;
assembly { cs := extcodesize(self) }
return cs == 0;
}
// Reserved storage space to allow for layout changes in the future.
uint256[47] private ______gap;
}
/**
* Wrapper around OpenZeppelin's Initializable contract.
* Exposes initialized state management to ensure logic contract functions cannot be called before initialization.
* This is needed because OZ's Initializable contract no longer exposes initialized state variable.
* https://github.com/OpenZeppelin/openzeppelin-sdk/blob/v2.8.0/packages/lib/contracts/Initializable.sol
*/
contract InitializableV2 is Initializable {
bool private isInitialized;
string private constant ERROR_NOT_INITIALIZED = "InitializableV2: Not initialized";
/**
* @notice wrapper function around parent contract Initializable's `initializable` modifier
* initializable modifier ensures this function can only be called once by each deployed child contract
* sets isInitialized flag to true to which is used by _requireIsInitialized()
*/
function initialize() public initializer {
isInitialized = true;
}
/**
* @notice Reverts transaction if isInitialized is false. Used by child contracts to ensure
* contract is initialized before functions can be called.
*/
function _requireIsInitialized() internal view {
require(isInitialized == true, ERROR_NOT_INITIALIZED);
}
/**
* @notice Exposes isInitialized bool var to child contracts with read-only access
*/
function _isInitialized() internal view returns (bool) {
return isInitialized;
}
}
/*
* @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.
*/
contract Context is Initializable {
// 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 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;
}
}
/// @notice ERC20 imported via Staking.sol
/// @notice SafeERC20 imported via Staking.sol
/// @notice Governance imported via Staking.sol
/// @notice SafeMath imported via ServiceProviderFactory.sol
/**
* Designed to automate claim funding, minting tokens as necessary
* @notice - will call InitializableV2 constructor
*/
contract ClaimsManager is InitializableV2 {
using SafeMath for uint256;
using SafeERC20 for ERC20;
string private constant ERROR_ONLY_GOVERNANCE = (
"ClaimsManager: Only callable by Governance contract"
);
address private governanceAddress;
address private stakingAddress;
address private serviceProviderFactoryAddress;
address private delegateManagerAddress;
/**
* @notice - Minimum number of blocks between funding rounds
* 604800 seconds / week
* Avg block time - 13s
* 604800 / 13 = 46523.0769231 blocks
*/
uint256 private fundingRoundBlockDiff;
/**
* @notice - Configures the current funding amount per round
* Weekly rounds, 7% PA inflation = 70,000,000 new tokens in first year
* = 70,000,000/365*7 (year is slightly more than a week)
* = 1342465.75342 new AUDS per week
* = 1342465753420000000000000 new wei units per week
* @dev - Past a certain block height, this schedule will be updated
* - Logic determining schedule will be sourced from an external contract
*/
uint256 private fundingAmount;
// Denotes current round
uint256 private roundNumber;
// Staking contract ref
ERC20Mintable public audiusToken;
/// @dev - Address to which recurringCommunityFundingAmount is transferred at funding round start
address private communityPoolAddress;
/// @dev - Reward amount transferred to communityPoolAddress at funding round start
uint256 private recurringCommunityFundingAmount;
// Struct representing round state
// 1) Block at which round was funded
// 2) Total funded for this round
// 3) Total claimed in round
struct Round {
uint256 fundedBlock;
uint256 fundedAmount;
uint256 totalClaimedInRound;
}
// Current round information
Round private currentRound;
event RoundInitiated(
uint256 indexed _blockNumber,
uint256 indexed _roundNumber,
uint256 indexed _fundAmount
);
event ClaimProcessed(
address indexed _claimer,
uint256 indexed _rewards,
uint256 _oldTotal,
uint256 indexed _newTotal
);
event CommunityRewardsTransferred(
address indexed _transferAddress,
uint256 indexed _amount
);
event FundingAmountUpdated(uint256 indexed _amount);
event FundingRoundBlockDiffUpdated(uint256 indexed _blockDifference);
event GovernanceAddressUpdated(address indexed _newGovernanceAddress);
event StakingAddressUpdated(address indexed _newStakingAddress);
event ServiceProviderFactoryAddressUpdated(address indexed _newServiceProviderFactoryAddress);
event DelegateManagerAddressUpdated(address indexed _newDelegateManagerAddress);
event RecurringCommunityFundingAmountUpdated(uint256 indexed _amount);
event CommunityPoolAddressUpdated(address indexed _newCommunityPoolAddress);
/**
* @notice Function to initialize the contract
* @dev stakingAddress must be initialized separately after Staking contract is deployed
* @dev serviceProviderFactoryAddress must be initialized separately after ServiceProviderFactory contract is deployed
* @dev delegateManagerAddress must be initialized separately after DelegateManager contract is deployed
* @param _tokenAddress - address of ERC20 token that will be claimed
* @param _governanceAddress - address for Governance proxy contract
*/
function initialize(
address _tokenAddress,
address _governanceAddress
) public initializer
{
_updateGovernanceAddress(_governanceAddress);
// audiusToken = ERC20Mintable(_tokenAddress);
// fundingRoundBlockDiff = 46523;
// fundingAmount = 1342465753420000000000000; // 1342465.75342 AUDS
// roundNumber = 0;
// currentRound = Round({
// fundedBlock: 0,
// fundedAmount: 0,
// totalClaimedInRound: 0
// });
// Community pool funding amount and address initialized to zero
// recurringCommunityFundingAmount = 0;
// communityPoolAddress = address(0x0);
InitializableV2.initialize();
}
/// @notice Get the duration of a funding round in blocks
function getFundingRoundBlockDiff() external view returns (uint256)
{
_requireIsInitialized();
return fundingRoundBlockDiff;
}
/// @notice Get the last block where a funding round was initiated
function getLastFundedBlock() external view returns (uint256)
{
_requireIsInitialized();
return currentRound.fundedBlock;
}
/// @notice Get the amount funded per round in wei
function getFundsPerRound() external view returns (uint256)
{
_requireIsInitialized();
return fundingAmount;
}
/// @notice Get the total amount claimed in the current round
function getTotalClaimedInRound() external view returns (uint256)
{
_requireIsInitialized();
return currentRound.totalClaimedInRound;
}
/// @notice Get the Governance address
function getGovernanceAddress() external view returns (address) {
_requireIsInitialized();
return governanceAddress;
}
/// @notice Get the ServiceProviderFactory address
function getServiceProviderFactoryAddress() external view returns (address) {
_requireIsInitialized();
return serviceProviderFactoryAddress;
}
/// @notice Get the DelegateManager address
function getDelegateManagerAddress() external view returns (address) {
_requireIsInitialized();
return delegateManagerAddress;
}
/**
* @notice Get the Staking address
*/
function getStakingAddress() external view returns (address)
{
_requireIsInitialized();
return stakingAddress;
}
/**
* @notice Get the community pool address
*/
function getCommunityPoolAddress() external view returns (address)
{
_requireIsInitialized();
return communityPoolAddress;
}
/**
* @notice Get the community funding amount
*/
function getRecurringCommunityFundingAmount() external view returns (uint256)
{
_requireIsInitialized();
return recurringCommunityFundingAmount;
}
/**
* @notice Set the Governance address
* @dev Only callable by Governance address
* @param _governanceAddress - address for new Governance contract
*/
function setGovernanceAddress(address _governanceAddress) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
_updateGovernanceAddress(_governanceAddress);
emit GovernanceAddressUpdated(_governanceAddress);
}
/**
* @notice Set the Staking address
* @dev Only callable by Governance address
* @param _stakingAddress - address for new Staking contract
*/
function setStakingAddress(address _stakingAddress) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
stakingAddress = _stakingAddress;
emit StakingAddressUpdated(_stakingAddress);
}
/**
* @notice Set the ServiceProviderFactory address
* @dev Only callable by Governance address
* @param _serviceProviderFactoryAddress - address for new ServiceProviderFactory contract
*/
function setServiceProviderFactoryAddress(address _serviceProviderFactoryAddress) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
serviceProviderFactoryAddress = _serviceProviderFactoryAddress;
emit ServiceProviderFactoryAddressUpdated(_serviceProviderFactoryAddress);
}
/**
* @notice Set the DelegateManager address
* @dev Only callable by Governance address
* @param _delegateManagerAddress - address for new DelegateManager contract
*/
function setDelegateManagerAddress(address _delegateManagerAddress) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
delegateManagerAddress = _delegateManagerAddress;
emit DelegateManagerAddressUpdated(_delegateManagerAddress);
}
/**
* @notice Start a new funding round
* @dev Permissioned to be callable by stakers or governance contract
*/
function initiateRound() external {
_requireIsInitialized();
_requireStakingAddressIsSet();
require(
block.number.sub(currentRound.fundedBlock) > fundingRoundBlockDiff,
"ClaimsManager: Required block difference not met"
);
currentRound = Round({
fundedBlock: block.number,
fundedAmount: fundingAmount,
totalClaimedInRound: 0
});
roundNumber = roundNumber.add(1);
/*
* Transfer community funding amount to community pool address, if set
*/
if (recurringCommunityFundingAmount > 0 && communityPoolAddress != address(0x0)) {
// ERC20Mintable always returns true
audiusToken.mint(address(this), recurringCommunityFundingAmount);
// Approve transfer to community pool address
audiusToken.approve(communityPoolAddress, recurringCommunityFundingAmount);
// Transfer to community pool address
ERC20(address(audiusToken)).safeTransfer(communityPoolAddress, recurringCommunityFundingAmount);
emit CommunityRewardsTransferred(communityPoolAddress, recurringCommunityFundingAmount);
}
emit RoundInitiated(
currentRound.fundedBlock,
roundNumber,
currentRound.fundedAmount
);
}
/**
* @notice Mints and stakes tokens on behalf of ServiceProvider + delegators
* @dev Callable through DelegateManager by Service Provider
* @param _claimer - service provider address
* @param _totalLockedForSP - amount of tokens locked up across DelegateManager + ServiceProvider
* @return minted rewards for this claimer
*/
function processClaim(
address _claimer,
uint256 _totalLockedForSP
) external returns (uint256)
{
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireDelegateManagerAddressIsSet();
_requireServiceProviderFactoryAddressIsSet();
require(
msg.sender == delegateManagerAddress,
"ClaimsManager: ProcessClaim only accessible to DelegateManager"
);
Staking stakingContract = Staking(stakingAddress);
// Prevent duplicate claim
uint256 lastUserClaimBlock = stakingContract.lastClaimedFor(_claimer);
require(
lastUserClaimBlock <= currentRound.fundedBlock,
"ClaimsManager: Claim already processed for user"
);
uint256 totalStakedAtFundBlockForClaimer = stakingContract.totalStakedForAt(
_claimer,
currentRound.fundedBlock);
(,,bool withinBounds,,,) = (
ServiceProviderFactory(serviceProviderFactoryAddress).getServiceProviderDetails(_claimer)
);
// Once they claim the zero reward amount, stake can be modified once again
// Subtract total locked amount for SP from stake at fund block
uint256 totalActiveClaimerStake = totalStakedAtFundBlockForClaimer.sub(_totalLockedForSP);
uint256 totalStakedAtFundBlock = stakingContract.totalStakedAt(currentRound.fundedBlock);
// Calculate claimer rewards
uint256 rewardsForClaimer = (
totalActiveClaimerStake.mul(fundingAmount)
).div(totalStakedAtFundBlock);
// For a claimer violating bounds, no new tokens are minted
// Claim history is marked to zero and function is short-circuited
// Total rewards can be zero if all stake is currently locked up
if (!withinBounds || rewardsForClaimer == 0) {
stakingContract.updateClaimHistory(0, _claimer);
emit ClaimProcessed(
_claimer,
0,
totalStakedAtFundBlockForClaimer,
totalActiveClaimerStake
);
return 0;
}
// ERC20Mintable always returns true
audiusToken.mint(address(this), rewardsForClaimer);
// Approve transfer to staking address for claimer rewards
// ERC20 always returns true
audiusToken.approve(stakingAddress, rewardsForClaimer);
// Transfer rewards
stakingContract.stakeRewards(rewardsForClaimer, _claimer);
// Update round claim value
currentRound.totalClaimedInRound = currentRound.totalClaimedInRound.add(rewardsForClaimer);
// Update round claim value
uint256 newTotal = stakingContract.totalStakedFor(_claimer);
emit ClaimProcessed(
_claimer,
rewardsForClaimer,
totalStakedAtFundBlockForClaimer,
newTotal
);
return rewardsForClaimer;
}
/**
* @notice Modify funding amount per round
* @param _newAmount - new amount to fund per round in wei
*/
function updateFundingAmount(uint256 _newAmount) external
{
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
fundingAmount = _newAmount;
emit FundingAmountUpdated(_newAmount);
}
/**
* @notice Returns boolean indicating whether a claim is considered pending
* @dev Note that an address with no endpoints can never have a pending claim
* @param _sp - address of the service provider to check
* @return true if eligible for claim, false if not
*/
function claimPending(address _sp) external view returns (bool) {
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceProviderFactoryAddressIsSet();
uint256 lastClaimedForSP = Staking(stakingAddress).lastClaimedFor(_sp);
(,,,uint256 numEndpoints,,) = (
ServiceProviderFactory(serviceProviderFactoryAddress).getServiceProviderDetails(_sp)
);
return (lastClaimedForSP < currentRound.fundedBlock && numEndpoints > 0);
}
/**
* @notice Modify minimum block difference between funding rounds
* @param _newFundingRoundBlockDiff - new min block difference to set
*/
function updateFundingRoundBlockDiff(uint256 _newFundingRoundBlockDiff) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
emit FundingRoundBlockDiffUpdated(_newFundingRoundBlockDiff);
fundingRoundBlockDiff = _newFundingRoundBlockDiff;
}
/**
* @notice Modify community funding amound for each round
* @param _newRecurringCommunityFundingAmount - new reward amount transferred to
* communityPoolAddress at funding round start
*/
function updateRecurringCommunityFundingAmount(
uint256 _newRecurringCommunityFundingAmount
) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
recurringCommunityFundingAmount = _newRecurringCommunityFundingAmount;
emit RecurringCommunityFundingAmountUpdated(_newRecurringCommunityFundingAmount);
}
/**
* @notice Modify community pool address
* @param _newCommunityPoolAddress - new address to which recurringCommunityFundingAmount
* is transferred at funding round start
*/
function updateCommunityPoolAddress(address _newCommunityPoolAddress) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
communityPoolAddress = _newCommunityPoolAddress;
emit CommunityPoolAddressUpdated(_newCommunityPoolAddress);
}
// ========================================= Private Functions =========================================
/**
* @notice Set the governance address after confirming contract identity
* @param _governanceAddress - Incoming governance address
*/
function _updateGovernanceAddress(address _governanceAddress) private {
require(
Governance(_governanceAddress).isGovernanceAddress() == true,
"ClaimsManager: _governanceAddress is not a valid governance contract"
);
governanceAddress = _governanceAddress;
}
function _requireStakingAddressIsSet() private view {
require(stakingAddress != address(0x00), "ClaimsManager: stakingAddress is not set");
}
function _requireDelegateManagerAddressIsSet() private view {
require(
delegateManagerAddress != address(0x00),
"ClaimsManager: delegateManagerAddress is not set"
);
}
function _requireServiceProviderFactoryAddressIsSet() private view {
require(
serviceProviderFactoryAddress != address(0x00),
"ClaimsManager: serviceProviderFactoryAddress is not set"
);
}
}
// Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/Uint256Helpers.sol
// Adapted to use pragma ^0.5.8 and satisfy our linter rules
library Uint256Helpers {
uint256 private constant MAX_UINT8 = uint8(-1);
uint256 private constant MAX_UINT64 = uint64(-1);
string private constant ERROR_UINT8_NUMBER_TOO_BIG = "UINT8_NUMBER_TOO_BIG";
string private constant ERROR_UINT64_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG";
function toUint8(uint256 a) internal pure returns (uint8) {
require(a <= MAX_UINT8, ERROR_UINT8_NUMBER_TOO_BIG);
return uint8(a);
}
function toUint64(uint256 a) internal pure returns (uint64) {
require(a <= MAX_UINT64, ERROR_UINT64_NUMBER_TOO_BIG);
return uint64(a);
}
}
// SPDX-License-Identifier: MIT
/**
* @title Roles
* @dev Library for managing addresses assigned to a Role.
*/
library Roles {
struct Role {
mapping (address => bool) bearer;
}
/**
* @dev Give an account access to this role.
*/
function add(Role storage role, address account) internal {
require(!has(role, account), "Roles: account already has role");
role.bearer[account] = true;
}
/**
* @dev Remove an account's access to this role.
*/
function remove(Role storage role, address account) internal {
require(has(role, account), "Roles: account does not have role");
role.bearer[account] = false;
}
/**
* @dev Check if an account has this role.
* @return bool
*/
function has(Role storage role, address account) internal view returns (bool) {
require(account != address(0), "Roles: account is the zero address");
return role.bearer[account];
}
}
contract MinterRole is Initializable, Context {
using Roles for Roles.Role;
event MinterAdded(address indexed account);
event MinterRemoved(address indexed account);
Roles.Role private _minters;
function initialize(address sender) public initializer {
if (!isMinter(sender)) {
_addMinter(sender);
}
}
modifier onlyMinter() {
require(isMinter(_msgSender()), "MinterRole: caller does not have the Minter role");
_;
}
function isMinter(address account) public view returns (bool) {
return _minters.has(account);
}
function addMinter(address account) public onlyMinter {
_addMinter(account);
}
function renounceMinter() public {
_removeMinter(_msgSender());
}
function _addMinter(address account) internal {
_minters.add(account);
emit MinterAdded(account);
}
function _removeMinter(address account) internal {
_minters.remove(account);
emit MinterRemoved(account);
}
uint256[50] private ______gap;
}
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20Mintable}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Initializable, Context, IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowances;
uint256 private _totalSupply;
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for `sender`'s tokens of at least
* `amount`.
*/
function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero"));
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal {
require(account != address(0), "ERC20: burn from the zero address");
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Destroys `amount` tokens from `account`.`amount` is then deducted
* from the caller's allowance.
*
* See {_burn} and {_approve}.
*/
function _burnFrom(address account, uint256 amount) internal {
_burn(account, amount);
_approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
}
uint256[50] private ______gap;
}
/// @notice SafeMath imported via ServiceProviderFactory.sol
/// @notice Governance imported via Staking.sol
/**
* @dev Extension of {ERC20} that adds a set of accounts with the {MinterRole},
* which have permission to mint (create) new tokens as they see fit.
*
* At construction, the deployer of the contract is the only minter.
*/
contract ERC20Mintable is Initializable, ERC20, MinterRole {
function initialize(address sender) public initializer {
MinterRole.initialize(sender);
}
/**
* @dev See {ERC20-_mint}.
*
* Requirements:
*
* - the caller must have the {MinterRole}.
*/
function mint(address account, uint256 amount) public onlyMinter returns (bool) {
_mint(account, amount);
return true;
}
uint256[50] private ______gap;
}
/**
* @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 ERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
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));
}
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'
// solhint-disable-next-line max-line-length
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).add(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
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.
// A Solidity high level call has three parts:
// 1. The target address is checked to verify it contains contract code
// 2. The call itself is made, and success asserted
// 3. The return value is decoded, which in turn checks the size of the returned data.
// solhint-disable-next-line max-line-length
require(address(token).isContract(), "SafeERC20: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = address(token).call(data);
require(success, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
/**
* @dev Extension of {ERC20} that allows token holders to destroy both their own
* tokens and those that they have an allowance for, in a way that can be
* recognized off-chain (via event analysis).
*/
contract ERC20Burnable is Initializable, Context, ERC20 {
/**
* @dev Destroys `amount` tokens from the caller.
*
* See {ERC20-_burn}.
*/
function burn(uint256 amount) public {
_burn(_msgSender(), amount);
}
/**
* @dev See {ERC20-_burnFrom}.
*/
function burnFrom(address account, uint256 amount) public {
_burnFrom(account, amount);
}
uint256[50] private ______gap;
}
/**
* @title Checkpointing - Library to handle a historic set of numeric values
*/
library Checkpointing {
uint256 private constant MAX_UINT192 = uint256(uint192(-1));
string private constant ERROR_VALUE_TOO_BIG = "CHECKPOINT_VALUE_TOO_BIG";
string private constant ERROR_CANNOT_ADD_PAST_VALUE = "CHECKPOINT_CANNOT_ADD_PAST_VALUE";
/**
* @dev To specify a value at a given point in time, we need to store two values:
* - `time`: unit-time value to denote the first time when a value was registered
* - `value`: a positive numeric value to registered at a given point in time
*
* Note that `time` does not need to refer necessarily to a timestamp value, any time unit could be used
* for it like block numbers, terms, etc.
*/
struct Checkpoint {
uint64 time;
uint192 value;
}
/**
* @dev A history simply denotes a list of checkpoints
*/
struct History {
Checkpoint[] history;
}
/**
* @dev Add a new value to a history for a given point in time. This function does not allow to add values previous
* to the latest registered value, if the value willing to add corresponds to the latest registered value, it
* will be updated.
* @param self Checkpoints history to be altered
* @param _time Point in time to register the given value
* @param _value Numeric value to be registered at the given point in time
*/
function add(History storage self, uint64 _time, uint256 _value) internal {
require(_value <= MAX_UINT192, ERROR_VALUE_TOO_BIG);
_add192(self, _time, uint192(_value));
}
/**
* @dev Fetch the latest registered value of history, it will return zero if there was no value registered
* @param self Checkpoints history to be queried
*/
function getLast(History storage self) internal view returns (uint256) {
uint256 length = self.history.length;
if (length > 0) {
return uint256(self.history[length - 1].value);
}
return 0;
}
/**
* @dev Fetch the most recent registered past value of a history based on a given point in time that is not known
* how recent it is beforehand. It will return zero if there is no registered value or if given time is
* previous to the first registered value.
* It uses a binary search.
* @param self Checkpoints history to be queried
* @param _time Point in time to query the most recent registered past value of
*/
function get(History storage self, uint64 _time) internal view returns (uint256) {
return _binarySearch(self, _time);
}
/**
* @dev Fetch the most recent registered past value of a history based on a given point in time. It will return zero
* if there is no registered value or if given time is previous to the first registered value.
* It uses a linear search starting from the end.
* @param self Checkpoints history to be queried
* @param _time Point in time to query the most recent registered past value of
*/
function getRecent(History storage self, uint64 _time) internal view returns (uint256) {
return _backwardsLinearSearch(self, _time);
}
/**
* @dev Private function to add a new value to a history for a given point in time. This function does not allow to
* add values previous to the latest registered value, if the value willing to add corresponds to the latest
* registered value, it will be updated.
* @param self Checkpoints history to be altered
* @param _time Point in time to register the given value
* @param _value Numeric value to be registered at the given point in time
*/
function _add192(History storage self, uint64 _time, uint192 _value) private {
uint256 length = self.history.length;
if (length == 0 || self.history[self.history.length - 1].time < _time) {
// If there was no value registered or the given point in time is after the latest registered value,
// we can insert it to the history directly.
self.history.push(Checkpoint(_time, _value));
} else {
// If the point in time given for the new value is not after the latest registered value, we must ensure
// we are only trying to update the latest value, otherwise we would be changing past data.
Checkpoint storage currentCheckpoint = self.history[length - 1];
require(_time == currentCheckpoint.time, ERROR_CANNOT_ADD_PAST_VALUE);
currentCheckpoint.value = _value;
}
}
/**
* @dev Private function to execute a backwards linear search to find the most recent registered past value of a
* history based on a given point in time. It will return zero if there is no registered value or if given time
* is previous to the first registered value. Note that this function will be more suitable when we already know
* that the time used to index the search is recent in the given history.
* @param self Checkpoints history to be queried
* @param _time Point in time to query the most recent registered past value of
*/
function _backwardsLinearSearch(History storage self, uint64 _time) private view returns (uint256) {
// If there was no value registered for the given history return simply zero
uint256 length = self.history.length;
if (length == 0) {
return 0;
}
uint256 index = length - 1;
Checkpoint storage checkpoint = self.history[index];
while (index > 0 && checkpoint.time > _time) {
index--;
checkpoint = self.history[index];
}
return checkpoint.time > _time ? 0 : uint256(checkpoint.value);
}
/**
* @dev Private function execute a binary search to find the most recent registered past value of a history based on
* a given point in time. It will return zero if there is no registered value or if given time is previous to
* the first registered value. Note that this function will be more suitable when don't know how recent the
* time used to index may be.
* @param self Checkpoints history to be queried
* @param _time Point in time to query the most recent registered past value of
*/
function _binarySearch(History storage self, uint64 _time) private view returns (uint256) {
// If there was no value registered for the given history return simply zero
uint256 length = self.history.length;
if (length == 0) {
return 0;
}
// If the requested time is equal to or after the time of the latest registered value, return latest value
uint256 lastIndex = length - 1;
if (_time >= self.history[lastIndex].time) {
return uint256(self.history[lastIndex].value);
}
// If the requested time is previous to the first registered value, return zero to denote missing checkpoint
if (_time < self.history[0].time) {
return 0;
}
// Execute a binary search between the checkpointed times of the history
uint256 low = 0;
uint256 high = lastIndex;
while (high > low) {
// No need for SafeMath: for this to overflow array size should be ~2^255
uint256 mid = (high + low + 1) / 2;
Checkpoint storage checkpoint = self.history[mid];
uint64 midTime = checkpoint.time;
if (_time > midTime) {
low = mid;
} else if (_time < midTime) {
// No need for SafeMath: high > low >= 0 => high >= 1 => mid >= 1
high = mid - 1;
} else {
return uint256(checkpoint.value);
}
}
return uint256(self.history[low].value);
}
}
/// @notice SafeMath imported via ServiceProviderFactory.sol
/// @notice Governance imported via Staking.sol
/**
* Designed to manage delegation to staking contract
*/
contract DelegateManager is InitializableV2 {
using SafeMath for uint256;
string private constant ERROR_ONLY_GOVERNANCE = (
"DelegateManager: Only callable by Governance contract"
);
string private constant ERROR_MINIMUM_DELEGATION = (
"DelegateManager: Minimum delegation amount required"
);
string private constant ERROR_ONLY_SP_GOVERNANCE = (
"DelegateManager: Only callable by target SP or governance"
);
string private constant ERROR_DELEGATOR_STAKE = (
"DelegateManager: Delegator must be staked for SP"
);
address private governanceAddress;
address private stakingAddress;
address private serviceProviderFactoryAddress;
address private claimsManagerAddress;
/**
* Period in blocks an undelegate operation is delayed.
* The undelegate operation speed bump is to prevent a delegator from
* attempting to remove their delegation in anticipation of a slash.
* @notice Must be greater than governance votingPeriod + executionDelay
*/
uint256 private undelegateLockupDuration;
/// @notice Maximum number of delegators a single account can handle
uint256 private maxDelegators;
/// @notice Minimum amount of delegation allowed
uint256 private minDelegationAmount;
/**
* Lockup duration for a remove delegator request.
* The remove delegator speed bump is to prevent a service provider from maliciously
* removing a delegator prior to the evaluation of a proposal.
* @notice Must be greater than governance votingPeriod + executionDelay
*/
uint256 private removeDelegatorLockupDuration;
/**
* Evaluation period for a remove delegator request
* @notice added to expiry block calculated for removeDelegatorLockupDuration
*/
uint256 private removeDelegatorEvalDuration;
// Staking contract ref
ERC20Mintable private audiusToken;
// Struct representing total delegated to SP and list of delegators
struct ServiceProviderDelegateInfo {
uint256 totalDelegatedStake;
uint256 totalLockedUpStake;
address[] delegators;
}
// Data structures for lockup during withdrawal
struct UndelegateStakeRequest {
address serviceProvider;
uint256 amount;
uint256 lockupExpiryBlock;
}
// Service provider address -> ServiceProviderDelegateInfo
mapping (address => ServiceProviderDelegateInfo) private spDelegateInfo;
// Delegator stake by address delegated to
// delegator -> (service provider -> delegatedStake)
mapping (address => mapping(address => uint256)) private delegateInfo;
// Delegator stake total by address
// delegator -> (totalDelegated)
// Note - delegator properties are maintained in a mapping instead of struct
// in order to facilitate extensibility in the future.
mapping (address => uint256) private delegatorTotalStake;
// Requester to pending undelegate request
mapping (address => UndelegateStakeRequest) private undelegateRequests;
// Pending remove delegator requests
// service provider -> (delegator -> lockupExpiryBlock)
mapping (address => mapping (address => uint256)) private removeDelegatorRequests;
event IncreaseDelegatedStake(
address indexed _delegator,
address indexed _serviceProvider,
uint256 indexed _increaseAmount
);
event UndelegateStakeRequested(
address indexed _delegator,
address indexed _serviceProvider,
uint256 indexed _amount,
uint256 _lockupExpiryBlock
);
event UndelegateStakeRequestCancelled(
address indexed _delegator,
address indexed _serviceProvider,
uint256 indexed _amount
);
event UndelegateStakeRequestEvaluated(
address indexed _delegator,
address indexed _serviceProvider,
uint256 indexed _amount
);
event Claim(
address indexed _claimer,
uint256 indexed _rewards,
uint256 indexed _newTotal
);
event Slash(
address indexed _target,
uint256 indexed _amount,
uint256 indexed _newTotal
);
event RemoveDelegatorRequested(
address indexed _serviceProvider,
address indexed _delegator,
uint256 indexed _lockupExpiryBlock
);
event RemoveDelegatorRequestCancelled(
address indexed _serviceProvider,
address indexed _delegator
);
event RemoveDelegatorRequestEvaluated(
address indexed _serviceProvider,
address indexed _delegator,
uint256 indexed _unstakedAmount
);
event MaxDelegatorsUpdated(uint256 indexed _maxDelegators);
event MinDelegationUpdated(uint256 indexed _minDelegationAmount);
event UndelegateLockupDurationUpdated(uint256 indexed _undelegateLockupDuration);
event GovernanceAddressUpdated(address indexed _newGovernanceAddress);
event StakingAddressUpdated(address indexed _newStakingAddress);
event ServiceProviderFactoryAddressUpdated(address indexed _newServiceProviderFactoryAddress);
event ClaimsManagerAddressUpdated(address indexed _newClaimsManagerAddress);
event RemoveDelegatorLockupDurationUpdated(uint256 indexed _removeDelegatorLockupDuration);
event RemoveDelegatorEvalDurationUpdated(uint256 indexed _removeDelegatorEvalDuration);
/**
* @notice Function to initialize the contract
* @dev stakingAddress must be initialized separately after Staking contract is deployed
* @dev serviceProviderFactoryAddress must be initialized separately after ServiceProviderFactory contract is deployed
* @dev claimsManagerAddress must be initialized separately after ClaimsManager contract is deployed
* @param _tokenAddress - address of ERC20 token that will be claimed
* @param _governanceAddress - Governance proxy address
*/
function initialize (
address _tokenAddress,
address _governanceAddress,
uint256 _undelegateLockupDuration
) public initializer
{
_updateGovernanceAddress(_governanceAddress);
audiusToken = ERC20Mintable(_tokenAddress);
maxDelegators = 175;
// Default minimum delegation amount set to 100AUD
minDelegationAmount = 100 * 10**uint256(18);
InitializableV2.initialize();
_updateUndelegateLockupDuration(_undelegateLockupDuration);
// 1 week = 168hrs * 60 min/hr * 60 sec/min / ~13 sec/block = 46523 blocks
_updateRemoveDelegatorLockupDuration(46523);
// 24hr * 60min/hr * 60sec/min / ~13 sec/block = 6646 blocks
removeDelegatorEvalDuration = 6646;
}
/**
* @notice Allow a delegator to delegate stake to a service provider
* @param _targetSP - address of service provider to delegate to
* @param _amount - amount in wei to delegate
* @return Updated total amount delegated to the service provider by delegator
*/
function delegateStake(
address _targetSP,
uint256 _amount
) external returns (uint256)
{
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceProviderFactoryAddressIsSet();
_requireClaimsManagerAddressIsSet();
require(
!_claimPending(_targetSP),
"DelegateManager: Delegation not permitted for SP pending claim"
);
address delegator = msg.sender;
Staking stakingContract = Staking(stakingAddress);
// Stake on behalf of target service provider
stakingContract.delegateStakeFor(
_targetSP,
delegator,
_amount
);
// Update list of delegators to SP if necessary
if (!_delegatorExistsForSP(delegator, _targetSP)) {
// If not found, update list of delegates
spDelegateInfo[_targetSP].delegators.push(delegator);
require(
spDelegateInfo[_targetSP].delegators.length <= maxDelegators,
"DelegateManager: Maximum delegators exceeded"
);
}
// Update following values in storage through helper
// totalServiceProviderDelegatedStake = current sp total + new amount,
// totalStakedForSpFromDelegator = current delegator total for sp + new amount,
// totalDelegatorStake = current delegator total + new amount
_updateDelegatorStake(
delegator,
_targetSP,
spDelegateInfo[_targetSP].totalDelegatedStake.add(_amount),
delegateInfo[delegator][_targetSP].add(_amount),
delegatorTotalStake[delegator].add(_amount)
);
require(
delegateInfo[delegator][_targetSP] >= minDelegationAmount,
ERROR_MINIMUM_DELEGATION
);
// Validate balance
ServiceProviderFactory(
serviceProviderFactoryAddress
).validateAccountStakeBalance(_targetSP);
emit IncreaseDelegatedStake(
delegator,
_targetSP,
_amount
);
// Return new total
return delegateInfo[delegator][_targetSP];
}
/**
* @notice Submit request for undelegation
* @param _target - address of service provider to undelegate stake from
* @param _amount - amount in wei to undelegate
* @return Updated total amount delegated to the service provider by delegator
*/
function requestUndelegateStake(
address _target,
uint256 _amount
) external returns (uint256)
{
_requireIsInitialized();
_requireClaimsManagerAddressIsSet();
require(
_amount > 0,
"DelegateManager: Requested undelegate stake amount must be greater than zero"
);
require(
!_claimPending(_target),
"DelegateManager: Undelegate request not permitted for SP pending claim"
);
address delegator = msg.sender;
require(
_delegatorExistsForSP(delegator, _target),
ERROR_DELEGATOR_STAKE
);
// Confirm no pending delegation request
require(
!_undelegateRequestIsPending(delegator),
"DelegateManager: No pending lockup expected"
);
// Ensure valid bounds
uint256 currentlyDelegatedToSP = delegateInfo[delegator][_target];
require(
_amount <= currentlyDelegatedToSP,
"DelegateManager: Cannot decrease greater than currently staked for this ServiceProvider"
);
// Submit updated request for sender, with target sp, undelegate amount, target expiry block
uint256 lockupExpiryBlock = block.number.add(undelegateLockupDuration);
_updateUndelegateStakeRequest(
delegator,
_target,
_amount,
lockupExpiryBlock
);
// Update total locked for this service provider, increasing by unstake amount
_updateServiceProviderLockupAmount(
_target,
spDelegateInfo[_target].totalLockedUpStake.add(_amount)
);
emit UndelegateStakeRequested(delegator, _target, _amount, lockupExpiryBlock);
return delegateInfo[delegator][_target].sub(_amount);
}
/**
* @notice Cancel undelegation request
*/
function cancelUndelegateStakeRequest() external {
_requireIsInitialized();
address delegator = msg.sender;
// Confirm pending delegation request
require(
_undelegateRequestIsPending(delegator),
"DelegateManager: Pending lockup expected"
);
uint256 unstakeAmount = undelegateRequests[delegator].amount;
address unlockFundsSP = undelegateRequests[delegator].serviceProvider;
// Update total locked for this service provider, decreasing by unstake amount
_updateServiceProviderLockupAmount(
unlockFundsSP,
spDelegateInfo[unlockFundsSP].totalLockedUpStake.sub(unstakeAmount)
);
// Remove pending request
_resetUndelegateStakeRequest(delegator);
emit UndelegateStakeRequestCancelled(delegator, unlockFundsSP, unstakeAmount);
}
/**
* @notice Finalize undelegation request and withdraw stake
* @return New total amount currently staked after stake has been undelegated
*/
function undelegateStake() external returns (uint256) {
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceProviderFactoryAddressIsSet();
_requireClaimsManagerAddressIsSet();
address delegator = msg.sender;
// Confirm pending delegation request
require(
_undelegateRequestIsPending(delegator),
"DelegateManager: Pending lockup expected"
);
// Confirm lockup expiry has expired
require(
undelegateRequests[delegator].lockupExpiryBlock <= block.number,
"DelegateManager: Lockup must be expired"
);
// Confirm no pending claim for this service provider
require(
!_claimPending(undelegateRequests[delegator].serviceProvider),
"DelegateManager: Undelegate not permitted for SP pending claim"
);
address serviceProvider = undelegateRequests[delegator].serviceProvider;
uint256 unstakeAmount = undelegateRequests[delegator].amount;
// Unstake on behalf of target service provider
Staking(stakingAddress).undelegateStakeFor(
serviceProvider,
delegator,
unstakeAmount
);
// Update total delegated for SP
// totalServiceProviderDelegatedStake - total amount delegated to service provider
// totalStakedForSpFromDelegator - amount staked from this delegator to targeted service provider
_updateDelegatorStake(
delegator,
serviceProvider,
spDelegateInfo[serviceProvider].totalDelegatedStake.sub(unstakeAmount),
delegateInfo[delegator][serviceProvider].sub(unstakeAmount),
delegatorTotalStake[delegator].sub(unstakeAmount)
);
require(
(delegateInfo[delegator][serviceProvider] >= minDelegationAmount ||
delegateInfo[delegator][serviceProvider] == 0),
ERROR_MINIMUM_DELEGATION
);
// Remove from delegators list if no delegated stake remaining
if (delegateInfo[delegator][serviceProvider] == 0) {
_removeFromDelegatorsList(serviceProvider, delegator);
}
// Update total locked for this service provider, decreasing by unstake amount
_updateServiceProviderLockupAmount(
serviceProvider,
spDelegateInfo[serviceProvider].totalLockedUpStake.sub(unstakeAmount)
);
// Reset undelegate request
_resetUndelegateStakeRequest(delegator);
emit UndelegateStakeRequestEvaluated(
delegator,
serviceProvider,
unstakeAmount
);
// Return new total
return delegateInfo[delegator][serviceProvider];
}
/**
* @notice Claim and distribute rewards to delegators and service provider as necessary
* @param _serviceProvider - Provider for which rewards are being distributed
* @dev Factors in service provider rewards from delegator and transfers deployer cut
*/
function claimRewards(address _serviceProvider) external {
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceProviderFactoryAddressIsSet();
_requireClaimsManagerAddressIsSet();
ServiceProviderFactory spFactory = ServiceProviderFactory(serviceProviderFactoryAddress);
// Total rewards = (balance in staking) - ((balance in sp factory) + (balance in delegate manager))
(
uint256 totalBalanceInStaking,
uint256 totalBalanceInSPFactory,
uint256 totalActiveFunds,
uint256 totalRewards,
uint256 deployerCut
) = _validateClaimRewards(spFactory, _serviceProvider);
// No-op if balance is already equivalent
// This case can occur if no rewards due to bound violation or all stake is locked
if (totalRewards == 0) {
return;
}
uint256 totalDelegatedStakeIncrease = _distributeDelegateRewards(
_serviceProvider,
totalActiveFunds,
totalRewards,
deployerCut,
spFactory.getServiceProviderDeployerCutBase()
);
// Update total delegated to this SP
spDelegateInfo[_serviceProvider].totalDelegatedStake = (
spDelegateInfo[_serviceProvider].totalDelegatedStake.add(totalDelegatedStakeIncrease)
);
// spRewardShare represents rewards directly allocated to service provider for their stake
// Value is computed as the remainder of total minted rewards after distribution to
// delegators, eliminating any potential for precision loss.
uint256 spRewardShare = totalRewards.sub(totalDelegatedStakeIncrease);
// Adding the newly calculated reward share to current balance
uint256 newSPFactoryBalance = totalBalanceInSPFactory.add(spRewardShare);
require(
totalBalanceInStaking == newSPFactoryBalance.add(spDelegateInfo[_serviceProvider].totalDelegatedStake),
"DelegateManager: claimRewards amount mismatch"
);
spFactory.updateServiceProviderStake(
_serviceProvider,
newSPFactoryBalance
);
}
/**
* @notice Reduce current stake amount
* @dev Only callable by governance. Slashes service provider and delegators equally
* @param _amount - amount in wei to slash
* @param _slashAddress - address of service provider to slash
*/
function slash(uint256 _amount, address _slashAddress)
external
{
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceProviderFactoryAddressIsSet();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
Staking stakingContract = Staking(stakingAddress);
ServiceProviderFactory spFactory = ServiceProviderFactory(serviceProviderFactoryAddress);
// Amount stored in staking contract for owner
uint256 totalBalanceInStakingPreSlash = stakingContract.totalStakedFor(_slashAddress);
require(
(totalBalanceInStakingPreSlash >= _amount),
"DelegateManager: Cannot slash more than total currently staked"
);
// Cancel any withdrawal request for this service provider
(uint256 spLockedStake,) = spFactory.getPendingDecreaseStakeRequest(_slashAddress);
if (spLockedStake > 0) {
spFactory.cancelDecreaseStakeRequest(_slashAddress);
}
// Amount in sp factory for slash target
(uint256 totalBalanceInSPFactory,,,,,) = (
spFactory.getServiceProviderDetails(_slashAddress)
);
require(
totalBalanceInSPFactory > 0,
"DelegateManager: Service Provider stake required"
);
// Decrease value in Staking contract
// A value of zero slash will fail in staking, reverting this transaction
stakingContract.slash(_amount, _slashAddress);
uint256 totalBalanceInStakingAfterSlash = stakingContract.totalStakedFor(_slashAddress);
// Emit slash event
emit Slash(_slashAddress, _amount, totalBalanceInStakingAfterSlash);
uint256 totalDelegatedStakeDecrease = 0;
// For each delegator and deployer, recalculate new value
// newStakeAmount = newStakeAmount * (oldStakeAmount / totalBalancePreSlash)
for (uint256 i = 0; i < spDelegateInfo[_slashAddress].delegators.length; i++) {
address delegator = spDelegateInfo[_slashAddress].delegators[i];
uint256 preSlashDelegateStake = delegateInfo[delegator][_slashAddress];
uint256 newDelegateStake = (
totalBalanceInStakingAfterSlash.mul(preSlashDelegateStake)
).div(totalBalanceInStakingPreSlash);
// slashAmountForDelegator = preSlashDelegateStake - newDelegateStake;
delegateInfo[delegator][_slashAddress] = (
delegateInfo[delegator][_slashAddress].sub(preSlashDelegateStake.sub(newDelegateStake))
);
// Update total stake for delegator
_updateDelegatorTotalStake(
delegator,
delegatorTotalStake[delegator].sub(preSlashDelegateStake.sub(newDelegateStake))
);
// Update total decrease amount
totalDelegatedStakeDecrease = (
totalDelegatedStakeDecrease.add(preSlashDelegateStake.sub(newDelegateStake))
);
// Check for any locked up funds for this slashed delegator
// Slash overrides any pending withdrawal requests
if (undelegateRequests[delegator].amount != 0) {
address unstakeSP = undelegateRequests[delegator].serviceProvider;
uint256 unstakeAmount = undelegateRequests[delegator].amount;
// Remove pending request
_updateServiceProviderLockupAmount(
unstakeSP,
spDelegateInfo[unstakeSP].totalLockedUpStake.sub(unstakeAmount)
);
_resetUndelegateStakeRequest(delegator);
}
}
// Update total delegated to this SP
spDelegateInfo[_slashAddress].totalDelegatedStake = (
spDelegateInfo[_slashAddress].totalDelegatedStake.sub(totalDelegatedStakeDecrease)
);
// Remaining decrease applied to service provider
uint256 totalStakeDecrease = (
totalBalanceInStakingPreSlash.sub(totalBalanceInStakingAfterSlash)
);
uint256 totalSPFactoryBalanceDecrease = (
totalStakeDecrease.sub(totalDelegatedStakeDecrease)
);
spFactory.updateServiceProviderStake(
_slashAddress,
totalBalanceInSPFactory.sub(totalSPFactoryBalanceDecrease)
);
}
/**
* @notice Initiate forcible removal of a delegator
* @param _serviceProvider - address of service provider
* @param _delegator - address of delegator
*/
function requestRemoveDelegator(address _serviceProvider, address _delegator) external {
_requireIsInitialized();
require(
msg.sender == _serviceProvider || msg.sender == governanceAddress,
ERROR_ONLY_SP_GOVERNANCE
);
require(
removeDelegatorRequests[_serviceProvider][_delegator] == 0,
"DelegateManager: Pending remove delegator request"
);
require(
_delegatorExistsForSP(_delegator, _serviceProvider),
ERROR_DELEGATOR_STAKE
);
// Update lockup
removeDelegatorRequests[_serviceProvider][_delegator] = (
block.number + removeDelegatorLockupDuration
);
emit RemoveDelegatorRequested(
_serviceProvider,
_delegator,
removeDelegatorRequests[_serviceProvider][_delegator]
);
}
/**
* @notice Cancel pending removeDelegator request
* @param _serviceProvider - address of service provider
* @param _delegator - address of delegator
*/
function cancelRemoveDelegatorRequest(address _serviceProvider, address _delegator) external {
require(
msg.sender == _serviceProvider || msg.sender == governanceAddress,
ERROR_ONLY_SP_GOVERNANCE
);
require(
removeDelegatorRequests[_serviceProvider][_delegator] != 0,
"DelegateManager: No pending request"
);
// Reset lockup expiry
removeDelegatorRequests[_serviceProvider][_delegator] = 0;
emit RemoveDelegatorRequestCancelled(_serviceProvider, _delegator);
}
/**
* @notice Evaluate removeDelegator request
* @param _serviceProvider - address of service provider
* @param _delegator - address of delegator
* @return Updated total amount delegated to the service provider by delegator
*/
function removeDelegator(address _serviceProvider, address _delegator) external {
_requireIsInitialized();
_requireStakingAddressIsSet();
require(
msg.sender == _serviceProvider || msg.sender == governanceAddress,
ERROR_ONLY_SP_GOVERNANCE
);
require(
removeDelegatorRequests[_serviceProvider][_delegator] != 0,
"DelegateManager: No pending request"
);
// Enforce lockup expiry block
require(
block.number >= removeDelegatorRequests[_serviceProvider][_delegator],
"DelegateManager: Lockup must be expired"
);
// Enforce evaluation window for request
require(
block.number < removeDelegatorRequests[_serviceProvider][_delegator] + removeDelegatorEvalDuration,
"DelegateManager: RemoveDelegator evaluation window expired"
);
uint256 unstakeAmount = delegateInfo[_delegator][_serviceProvider];
// Unstake on behalf of target service provider
Staking(stakingAddress).undelegateStakeFor(
_serviceProvider,
_delegator,
unstakeAmount
);
// Update total delegated for SP
// totalServiceProviderDelegatedStake - total amount delegated to service provider
// totalStakedForSpFromDelegator - amount staked from this delegator to targeted service provider
_updateDelegatorStake(
_delegator,
_serviceProvider,
spDelegateInfo[_serviceProvider].totalDelegatedStake.sub(unstakeAmount),
delegateInfo[_delegator][_serviceProvider].sub(unstakeAmount),
delegatorTotalStake[_delegator].sub(unstakeAmount)
);
if (
_undelegateRequestIsPending(_delegator) &&
undelegateRequests[_delegator].serviceProvider == _serviceProvider
) {
// Remove pending request information
_updateServiceProviderLockupAmount(
_serviceProvider,
spDelegateInfo[_serviceProvider].totalLockedUpStake.sub(undelegateRequests[_delegator].amount)
);
_resetUndelegateStakeRequest(_delegator);
}
// Remove from list of delegators
_removeFromDelegatorsList(_serviceProvider, _delegator);
// Reset lockup expiry
removeDelegatorRequests[_serviceProvider][_delegator] = 0;
emit RemoveDelegatorRequestEvaluated(_serviceProvider, _delegator, unstakeAmount);
}
/**
* @notice Update duration for undelegate request lockup
* @param _duration - new lockup duration
*/
function updateUndelegateLockupDuration(uint256 _duration) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
_updateUndelegateLockupDuration(_duration);
emit UndelegateLockupDurationUpdated(_duration);
}
/**
* @notice Update maximum delegators allowed
* @param _maxDelegators - new max delegators
*/
function updateMaxDelegators(uint256 _maxDelegators) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
maxDelegators = _maxDelegators;
emit MaxDelegatorsUpdated(_maxDelegators);
}
/**
* @notice Update minimum delegation amount
* @param _minDelegationAmount - min new min delegation amount
*/
function updateMinDelegationAmount(uint256 _minDelegationAmount) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
minDelegationAmount = _minDelegationAmount;
emit MinDelegationUpdated(_minDelegationAmount);
}
/**
* @notice Update remove delegator lockup duration
* @param _duration - new lockup duration
*/
function updateRemoveDelegatorLockupDuration(uint256 _duration) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
_updateRemoveDelegatorLockupDuration(_duration);
emit RemoveDelegatorLockupDurationUpdated(_duration);
}
/**
* @notice Update remove delegator evaluation window duration
* @param _duration - new window duration
*/
function updateRemoveDelegatorEvalDuration(uint256 _duration) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
removeDelegatorEvalDuration = _duration;
emit RemoveDelegatorEvalDurationUpdated(_duration);
}
/**
* @notice Set the Governance address
* @dev Only callable by Governance address
* @param _governanceAddress - address for new Governance contract
*/
function setGovernanceAddress(address _governanceAddress) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
_updateGovernanceAddress(_governanceAddress);
governanceAddress = _governanceAddress;
emit GovernanceAddressUpdated(_governanceAddress);
}
/**
* @notice Set the Staking address
* @dev Only callable by Governance address
* @param _stakingAddress - address for new Staking contract
*/
function setStakingAddress(address _stakingAddress) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
stakingAddress = _stakingAddress;
emit StakingAddressUpdated(_stakingAddress);
}
/**
* @notice Set the ServiceProviderFactory address
* @dev Only callable by Governance address
* @param _spFactory - address for new ServiceProviderFactory contract
*/
function setServiceProviderFactoryAddress(address _spFactory) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
serviceProviderFactoryAddress = _spFactory;
emit ServiceProviderFactoryAddressUpdated(_spFactory);
}
/**
* @notice Set the ClaimsManager address
* @dev Only callable by Governance address
* @param _claimsManagerAddress - address for new ClaimsManager contract
*/
function setClaimsManagerAddress(address _claimsManagerAddress) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
claimsManagerAddress = _claimsManagerAddress;
emit ClaimsManagerAddressUpdated(_claimsManagerAddress);
}
// ========================================= View Functions =========================================
/**
* @notice Get list of delegators for a given service provider
* @param _sp - service provider address
*/
function getDelegatorsList(address _sp)
external view returns (address[] memory)
{
_requireIsInitialized();
return spDelegateInfo[_sp].delegators;
}
/**
* @notice Get total delegation from a given address
* @param _delegator - delegator address
*/
function getTotalDelegatorStake(address _delegator)
external view returns (uint256)
{
_requireIsInitialized();
return delegatorTotalStake[_delegator];
}
/// @notice Get total amount delegated to a service provider
function getTotalDelegatedToServiceProvider(address _sp)
external view returns (uint256)
{
_requireIsInitialized();
return spDelegateInfo[_sp].totalDelegatedStake;
}
/// @notice Get total delegated stake locked up for a service provider
function getTotalLockedDelegationForServiceProvider(address _sp)
external view returns (uint256)
{
_requireIsInitialized();
return spDelegateInfo[_sp].totalLockedUpStake;
}
/// @notice Get total currently staked for a delegator, for a given service provider
function getDelegatorStakeForServiceProvider(address _delegator, address _serviceProvider)
external view returns (uint256)
{
_requireIsInitialized();
return delegateInfo[_delegator][_serviceProvider];
}
/**
* @notice Get status of pending undelegate request for a given address
* @param _delegator - address of the delegator
*/
function getPendingUndelegateRequest(address _delegator)
external view returns (address target, uint256 amount, uint256 lockupExpiryBlock)
{
_requireIsInitialized();
UndelegateStakeRequest memory req = undelegateRequests[_delegator];
return (req.serviceProvider, req.amount, req.lockupExpiryBlock);
}
/**
* @notice Get status of pending remove delegator request for a given address
* @param _serviceProvider - address of the service provider
* @param _delegator - address of the delegator
* @return - current lockup expiry block for remove delegator request
*/
function getPendingRemoveDelegatorRequest(
address _serviceProvider,
address _delegator
) external view returns (uint256)
{
_requireIsInitialized();
return removeDelegatorRequests[_serviceProvider][_delegator];
}
/// @notice Get current undelegate lockup duration
function getUndelegateLockupDuration()
external view returns (uint256)
{
_requireIsInitialized();
return undelegateLockupDuration;
}
/// @notice Current maximum delegators
function getMaxDelegators()
external view returns (uint256)
{
_requireIsInitialized();
return maxDelegators;
}
/// @notice Get minimum delegation amount
function getMinDelegationAmount()
external view returns (uint256)
{
_requireIsInitialized();
return minDelegationAmount;
}
/// @notice Get the duration for remove delegator request lockup
function getRemoveDelegatorLockupDuration()
external view returns (uint256)
{
_requireIsInitialized();
return removeDelegatorLockupDuration;
}
/// @notice Get the duration for evaluation of remove delegator operations
function getRemoveDelegatorEvalDuration()
external view returns (uint256)
{
_requireIsInitialized();
return removeDelegatorEvalDuration;
}
/// @notice Get the Governance address
function getGovernanceAddress() external view returns (address) {
_requireIsInitialized();
return governanceAddress;
}
/// @notice Get the ServiceProviderFactory address
function getServiceProviderFactoryAddress() external view returns (address) {
_requireIsInitialized();
return serviceProviderFactoryAddress;
}
/// @notice Get the ClaimsManager address
function getClaimsManagerAddress() external view returns (address) {
_requireIsInitialized();
return claimsManagerAddress;
}
/// @notice Get the Staking address
function getStakingAddress() external view returns (address)
{
_requireIsInitialized();
return stakingAddress;
}
// ========================================= Internal functions =========================================
/**
* @notice Helper function for claimRewards to get balances from Staking contract
and do validation
* @param spFactory - reference to ServiceProviderFactory contract
* @param _serviceProvider - address for which rewards are being claimed
* @return (totalBalanceInStaking, totalBalanceInSPFactory, totalActiveFunds, spLockedStake, totalRewards, deployerCut)
*/
function _validateClaimRewards(ServiceProviderFactory spFactory, address _serviceProvider)
internal returns (
uint256 totalBalanceInStaking,
uint256 totalBalanceInSPFactory,
uint256 totalActiveFunds,
uint256 totalRewards,
uint256 deployerCut
)
{
// Account for any pending locked up stake for the service provider
(uint256 spLockedStake,) = spFactory.getPendingDecreaseStakeRequest(_serviceProvider);
uint256 totalLockedUpStake = (
spDelegateInfo[_serviceProvider].totalLockedUpStake.add(spLockedStake)
);
// Process claim for msg.sender
// Total locked parameter is equal to delegate locked up stake + service provider locked up stake
uint256 mintedRewards = ClaimsManager(claimsManagerAddress).processClaim(
_serviceProvider,
totalLockedUpStake
);
// Amount stored in staking contract for owner
totalBalanceInStaking = Staking(stakingAddress).totalStakedFor(_serviceProvider);
// Amount in sp factory for claimer
(
totalBalanceInSPFactory,
deployerCut,
,,,
) = spFactory.getServiceProviderDetails(_serviceProvider);
// Require active stake to claim any rewards
// Amount in delegate manager staked to service provider
uint256 totalBalanceOutsideStaking = (
totalBalanceInSPFactory.add(spDelegateInfo[_serviceProvider].totalDelegatedStake)
);
totalActiveFunds = totalBalanceOutsideStaking.sub(totalLockedUpStake);
require(
mintedRewards == totalBalanceInStaking.sub(totalBalanceOutsideStaking),
"DelegateManager: Reward amount mismatch"
);
// Emit claim event
emit Claim(_serviceProvider, totalRewards, totalBalanceInStaking);
return (
totalBalanceInStaking,
totalBalanceInSPFactory,
totalActiveFunds,
mintedRewards,
deployerCut
);
}
/**
* @notice Perform state updates when a delegate stake has changed
* @param _delegator - address of delegator
* @param _serviceProvider - address of service provider
* @param _totalServiceProviderDelegatedStake - total delegated to this service provider
* @param _totalStakedForSpFromDelegator - total delegated to this service provider by delegator
* @param _totalDelegatorStake - total delegated from this delegator address
*/
function _updateDelegatorStake(
address _delegator,
address _serviceProvider,
uint256 _totalServiceProviderDelegatedStake,
uint256 _totalStakedForSpFromDelegator,
uint256 _totalDelegatorStake
) internal
{
// Update total delegated for SP
spDelegateInfo[_serviceProvider].totalDelegatedStake = _totalServiceProviderDelegatedStake;
// Update amount staked from this delegator to targeted service provider
delegateInfo[_delegator][_serviceProvider] = _totalStakedForSpFromDelegator;
// Update total delegated from this delegator
_updateDelegatorTotalStake(_delegator, _totalDelegatorStake);
}
/**
* @notice Reset pending undelegate stake request
* @param _delegator - address of delegator
*/
function _resetUndelegateStakeRequest(address _delegator) internal
{
_updateUndelegateStakeRequest(_delegator, address(0), 0, 0);
}
/**
* @notice Perform updates when undelegate request state has changed
* @param _delegator - address of delegator
* @param _serviceProvider - address of service provider
* @param _amount - amount being undelegated
* @param _lockupExpiryBlock - block at which stake can be undelegated
*/
function _updateUndelegateStakeRequest(
address _delegator,
address _serviceProvider,
uint256 _amount,
uint256 _lockupExpiryBlock
) internal
{
// Update lockup information
undelegateRequests[_delegator] = UndelegateStakeRequest({
lockupExpiryBlock: _lockupExpiryBlock,
amount: _amount,
serviceProvider: _serviceProvider
});
}
/**
* @notice Update total amount delegated from an address
* @param _delegator - address of service provider
* @param _amount - updated delegator total
*/
function _updateDelegatorTotalStake(address _delegator, uint256 _amount) internal
{
delegatorTotalStake[_delegator] = _amount;
}
/**
* @notice Update amount currently locked up for this service provider
* @param _serviceProvider - address of service provider
* @param _updatedLockupAmount - updated lock up amount
*/
function _updateServiceProviderLockupAmount(
address _serviceProvider,
uint256 _updatedLockupAmount
) internal
{
spDelegateInfo[_serviceProvider].totalLockedUpStake = _updatedLockupAmount;
}
function _removeFromDelegatorsList(address _serviceProvider, address _delegator) internal
{
for (uint256 i = 0; i < spDelegateInfo[_serviceProvider].delegators.length; i++) {
if (spDelegateInfo[_serviceProvider].delegators[i] == _delegator) {
// Overwrite and shrink delegators list
spDelegateInfo[_serviceProvider].delegators[i] = spDelegateInfo[_serviceProvider].delegators[spDelegateInfo[_serviceProvider].delegators.length - 1];
spDelegateInfo[_serviceProvider].delegators.length--;
break;
}
}
}
/**
* @notice Helper function to distribute rewards to any delegators
* @param _sp - service provider account tracked in staking
* @param _totalActiveFunds - total funds minus any locked stake
* @param _totalRewards - total rewaards generated in this round
* @param _deployerCut - service provider cut of delegate rewards, defined as deployerCut / deployerCutBase
* @param _deployerCutBase - denominator value for calculating service provider cut as a %
* @return (totalBalanceInStaking, totalBalanceInSPFactory, totalBalanceOutsideStaking)
*/
function _distributeDelegateRewards(
address _sp,
uint256 _totalActiveFunds,
uint256 _totalRewards,
uint256 _deployerCut,
uint256 _deployerCutBase
)
internal returns (uint256 totalDelegatedStakeIncrease)
{
// Traverse all delegates and calculate their rewards
// As each delegate reward is calculated, increment SP cut reward accordingly
for (uint256 i = 0; i < spDelegateInfo[_sp].delegators.length; i++) {
address delegator = spDelegateInfo[_sp].delegators[i];
uint256 delegateStakeToSP = delegateInfo[delegator][_sp];
// Subtract any locked up stake
if (undelegateRequests[delegator].serviceProvider == _sp) {
delegateStakeToSP = delegateStakeToSP.sub(undelegateRequests[delegator].amount);
}
// Calculate rewards by ((delegateStakeToSP / totalActiveFunds) * totalRewards)
uint256 rewardsPriorToSPCut = (
delegateStakeToSP.mul(_totalRewards)
).div(_totalActiveFunds);
// Multiply by deployer cut fraction to calculate reward for SP
// Operation constructed to perform all multiplication prior to division
// uint256 spDeployerCut = (rewardsPriorToSPCut * deployerCut ) / (deployerCutBase);
// = ((delegateStakeToSP * totalRewards) / totalActiveFunds) * deployerCut ) / (deployerCutBase);
// = ((delegateStakeToSP * totalRewards * deployerCut) / totalActiveFunds ) / (deployerCutBase);
// = (delegateStakeToSP * totalRewards * deployerCut) / (deployerCutBase * totalActiveFunds);
uint256 spDeployerCut = (
(delegateStakeToSP.mul(_totalRewards)).mul(_deployerCut)
).div(
_totalActiveFunds.mul(_deployerCutBase)
);
// Increase total delegate reward in DelegateManager
// Subtract SP reward from rewards to calculate delegate reward
// delegateReward = rewardsPriorToSPCut - spDeployerCut;
delegateInfo[delegator][_sp] = (
delegateInfo[delegator][_sp].add(rewardsPriorToSPCut.sub(spDeployerCut))
);
// Update total for this delegator
_updateDelegatorTotalStake(
delegator,
delegatorTotalStake[delegator].add(rewardsPriorToSPCut.sub(spDeployerCut))
);
totalDelegatedStakeIncrease = (
totalDelegatedStakeIncrease.add(rewardsPriorToSPCut.sub(spDeployerCut))
);
}
return (totalDelegatedStakeIncrease);
}
/**
* @notice Set the governance address after confirming contract identity
* @param _governanceAddress - Incoming governance address
*/
function _updateGovernanceAddress(address _governanceAddress) internal {
require(
Governance(_governanceAddress).isGovernanceAddress() == true,
"DelegateManager: _governanceAddress is not a valid governance contract"
);
governanceAddress = _governanceAddress;
}
/**
* @notice Set the remove delegator lockup duration after validating against governance
* @param _duration - Incoming remove delegator duration value
*/
function _updateRemoveDelegatorLockupDuration(uint256 _duration) internal {
Governance governance = Governance(governanceAddress);
require(
_duration > governance.getVotingPeriod() + governance.getExecutionDelay(),
"DelegateManager: removeDelegatorLockupDuration duration must be greater than governance votingPeriod + executionDelay"
);
removeDelegatorLockupDuration = _duration;
}
/**
* @notice Set the undelegate lockup duration after validating against governance
* @param _duration - Incoming undelegate lockup duration value
*/
function _updateUndelegateLockupDuration(uint256 _duration) internal {
Governance governance = Governance(governanceAddress);
require(
_duration > governance.getVotingPeriod() + governance.getExecutionDelay(),
"DelegateManager: undelegateLockupDuration duration must be greater than governance votingPeriod + executionDelay"
);
undelegateLockupDuration = _duration;
}
/**
* @notice Returns if delegator has delegated to a service provider
* @param _delegator - address of delegator
* @param _serviceProvider - address of service provider
* @return boolean indicating whether delegator exists for service provider
*/
function _delegatorExistsForSP(
address _delegator,
address _serviceProvider
) internal view returns (bool)
{
for (uint256 i = 0; i < spDelegateInfo[_serviceProvider].delegators.length; i++) {
if (spDelegateInfo[_serviceProvider].delegators[i] == _delegator) {
return true;
}
}
// Not found
return false;
}
/**
* @notice Determine if a claim is pending for this service provider
* @param _sp - address of service provider
* @return boolean indicating whether a claim is pending
*/
function _claimPending(address _sp) internal view returns (bool) {
ClaimsManager claimsManager = ClaimsManager(claimsManagerAddress);
return claimsManager.claimPending(_sp);
}
/**
* @notice Determine if a decrease request has been initiated
* @param _delegator - address of delegator
* @return boolean indicating whether a decrease request is pending
*/
function _undelegateRequestIsPending(address _delegator) internal view returns (bool)
{
return (
(undelegateRequests[_delegator].lockupExpiryBlock != 0) &&
(undelegateRequests[_delegator].amount != 0) &&
(undelegateRequests[_delegator].serviceProvider != address(0))
);
}
// ========================================= Private Functions =========================================
function _requireStakingAddressIsSet() private view {
require(
stakingAddress != address(0x00),
"DelegateManager: stakingAddress is not set"
);
}
function _requireServiceProviderFactoryAddressIsSet() private view {
require(
serviceProviderFactoryAddress != address(0x00),
"DelegateManager: serviceProviderFactoryAddress is not set"
);
}
function _requireClaimsManagerAddressIsSet() private view {
require(
claimsManagerAddress != address(0x00),
"DelegateManager: claimsManagerAddress is not set"
);
}
}
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be aplied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Initializable, Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function initialize(address sender) public initializer {
_owner = sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return _msgSender() == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* > Note: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
uint256[50] private ______gap;
}
/**
* @title Central hub for Audius protocol. It stores all contract addresses to facilitate
* external access and enable version management.
*/
contract Registry is InitializableV2, Ownable {
using SafeMath for uint256;
/**
* @dev addressStorage mapping allows efficient lookup of current contract version
* addressStorageHistory maintains record of all contract versions
*/
mapping(bytes32 => address) private addressStorage;
mapping(bytes32 => address[]) private addressStorageHistory;
event ContractAdded(
bytes32 indexed _name,
address indexed _address
);
event ContractRemoved(
bytes32 indexed _name,
address indexed _address
);
event ContractUpgraded(
bytes32 indexed _name,
address indexed _oldAddress,
address indexed _newAddress
);
function initialize() public initializer {
/// @notice Ownable.initialize(address _sender) sets contract owner to _sender.
Ownable.initialize(msg.sender);
InitializableV2.initialize();
}
// ========================================= Setters =========================================
/**
* @notice addContract registers contract name to address mapping under given registry key
* @param _name - registry key that will be used for lookups
* @param _address - address of contract
*/
function addContract(bytes32 _name, address _address) external onlyOwner {
_requireIsInitialized();
require(
addressStorage[_name] == address(0x00),
"Registry: Contract already registered with given name."
);
require(
_address != address(0x00),
"Registry: Cannot register zero address."
);
setAddress(_name, _address);
emit ContractAdded(_name, _address);
}
/**
* @notice removes contract address registered under given registry key
* @param _name - registry key for lookup
*/
function removeContract(bytes32 _name) external onlyOwner {
_requireIsInitialized();
address contractAddress = addressStorage[_name];
require(
contractAddress != address(0x00),
"Registry: Cannot remove - no contract registered with given _name."
);
setAddress(_name, address(0x00));
emit ContractRemoved(_name, contractAddress);
}
/**
* @notice replaces contract address registered under given key with provided address
* @param _name - registry key for lookup
* @param _newAddress - new contract address to register under given key
*/
function upgradeContract(bytes32 _name, address _newAddress) external onlyOwner {
_requireIsInitialized();
address oldAddress = addressStorage[_name];
require(
oldAddress != address(0x00),
"Registry: Cannot upgrade - no contract registered with given _name."
);
require(
_newAddress != address(0x00),
"Registry: Cannot upgrade - cannot register zero address."
);
setAddress(_name, _newAddress);
emit ContractUpgraded(_name, oldAddress, _newAddress);
}
// ========================================= Getters =========================================
/**
* @notice returns contract address registered under given registry key
* @param _name - registry key for lookup
* @return contractAddr - address of contract registered under given registry key
*/
function getContract(bytes32 _name) external view returns (address contractAddr) {
_requireIsInitialized();
return addressStorage[_name];
}
/// @notice overloaded getContract to return explicit version of contract
function getContract(bytes32 _name, uint256 _version) external view
returns (address contractAddr)
{
_requireIsInitialized();
// array length for key implies version number
require(
_version <= addressStorageHistory[_name].length,
"Registry: Index out of range _version."
);
return addressStorageHistory[_name][_version.sub(1)];
}
/**
* @notice Returns the number of versions for a contract key
* @param _name - registry key for lookup
* @return number of contract versions
*/
function getContractVersionCount(bytes32 _name) external view returns (uint256) {
_requireIsInitialized();
return addressStorageHistory[_name].length;
}
// ========================================= Private functions =========================================
/**
* @param _key the key for the contract address
* @param _value the contract address
*/
function setAddress(bytes32 _key, address _value) private {
// main map for cheap lookup
addressStorage[_key] = _value;
// keep track of contract address history
addressStorageHistory[_key].push(_value);
}
}
contract Governance is InitializableV2 {
using SafeMath for uint256;
string private constant ERROR_ONLY_GOVERNANCE = (
"Governance: Only callable by self"
);
string private constant ERROR_INVALID_VOTING_PERIOD = (
"Governance: Requires non-zero _votingPeriod"
);
string private constant ERROR_INVALID_REGISTRY = (
"Governance: Requires non-zero _registryAddress"
);
string private constant ERROR_INVALID_VOTING_QUORUM = (
"Governance: Requires _votingQuorumPercent between 1 & 100"
);
/**
* @notice Address and contract instance of Audius Registry. Used to ensure this contract
* can only govern contracts that are registered in the Audius Registry.
*/
Registry private registry;
/// @notice Address of Audius staking contract, used to permission Governance method calls
address private stakingAddress;
/// @notice Address of Audius ServiceProvider contract, used to permission Governance method calls
address private serviceProviderFactoryAddress;
/// @notice Address of Audius DelegateManager contract, used to permission Governance method calls
address private delegateManagerAddress;
/// @notice Period in blocks for which a governance proposal is open for voting
uint256 private votingPeriod;
/// @notice Number of blocks that must pass after votingPeriod has expired before proposal can be evaluated/executed
uint256 private executionDelay;
/// @notice Required minimum percentage of total stake to have voted to consider a proposal valid
/// Percentaged stored as a uint256 between 0 & 100
/// Calculated as: 100 * sum of voter stakes / total staked in Staking (at proposal submission block)
uint256 private votingQuorumPercent;
/// @notice Max number of InProgress proposals possible at once
/// @dev uint16 gives max possible value of 65,535
uint16 private maxInProgressProposals;
/**
* @notice Address of account that has special Governance permissions. Can veto proposals
* and execute transactions directly on contracts.
*/
address private guardianAddress;
/***** Enums *****/
/**
* @notice All Proposal Outcome states.
* InProgress - Proposal is active and can be voted on.
* Rejected - Proposal votingPeriod has closed and vote failed to pass. Proposal will not be executed.
* ApprovedExecuted - Proposal votingPeriod has closed and vote passed. Proposal was successfully executed.
* QuorumNotMet - Proposal votingPeriod has closed and votingQuorumPercent was not met. Proposal will not be executed.
* ApprovedExecutionFailed - Proposal vote passed, but transaction execution failed.
* Evaluating - Proposal vote passed, and evaluateProposalOutcome function is currently running.
* This status is transiently used inside that function to prevent re-entrancy.
* Vetoed - Proposal was vetoed by Guardian.
* TargetContractAddressChanged - Proposal considered invalid since target contract address changed
* TargetContractCodeHashChanged - Proposal considered invalid since code has at target contract address has changed
*/
enum Outcome {
InProgress,
Rejected,
ApprovedExecuted,
QuorumNotMet,
ApprovedExecutionFailed,
Evaluating,
Vetoed,
TargetContractAddressChanged,
TargetContractCodeHashChanged
}
/**
* @notice All Proposal Vote states for a voter.
* None - The default state, for any account that has not previously voted on this Proposal.
* No - The account voted No on this Proposal.
* Yes - The account voted Yes on this Proposal.
* @dev Enum values map to uints, so first value in Enum always is 0.
*/
enum Vote {None, No, Yes}
struct Proposal {
uint256 proposalId;
address proposer;
uint256 submissionBlockNumber;
bytes32 targetContractRegistryKey;
address targetContractAddress;
uint256 callValue;
string functionSignature;
bytes callData;
Outcome outcome;
uint256 voteMagnitudeYes;
uint256 voteMagnitudeNo;
uint256 numVotes;
mapping(address => Vote) votes;
mapping(address => uint256) voteMagnitudes;
bytes32 contractHash;
}
/***** Proposal storage *****/
/// @notice ID of most recently created proposal. Ids are monotonically increasing and 1-indexed.
uint256 lastProposalId = 0;
/// @notice mapping of proposalId to Proposal struct with all proposal state
mapping(uint256 => Proposal) proposals;
/// @notice array of proposals with InProgress state
uint256[] inProgressProposals;
/***** Events *****/
event ProposalSubmitted(
uint256 indexed _proposalId,
address indexed _proposer,
string _name,
string _description
);
event ProposalVoteSubmitted(
uint256 indexed _proposalId,
address indexed _voter,
Vote indexed _vote,
uint256 _voterStake
);
event ProposalVoteUpdated(
uint256 indexed _proposalId,
address indexed _voter,
Vote indexed _vote,
uint256 _voterStake,
Vote _previousVote
);
event ProposalOutcomeEvaluated(
uint256 indexed _proposalId,
Outcome indexed _outcome,
uint256 _voteMagnitudeYes,
uint256 _voteMagnitudeNo,
uint256 _numVotes
);
event ProposalTransactionExecuted(
uint256 indexed _proposalId,
bool indexed _success,
bytes _returnData
);
event GuardianTransactionExecuted(
address indexed _targetContractAddress,
uint256 _callValue,
string indexed _functionSignature,
bytes indexed _callData,
bytes _returnData
);
event ProposalVetoed(uint256 indexed _proposalId);
event RegistryAddressUpdated(address indexed _newRegistryAddress);
event GuardianshipTransferred(address indexed _newGuardianAddress);
event VotingPeriodUpdated(uint256 indexed _newVotingPeriod);
event ExecutionDelayUpdated(uint256 indexed _newExecutionDelay);
event VotingQuorumPercentUpdated(uint256 indexed _newVotingQuorumPercent);
event MaxInProgressProposalsUpdated(uint256 indexed _newMaxInProgressProposals);
/**
* @notice Initialize the Governance contract
* @dev _votingPeriod <= DelegateManager.undelegateLockupDuration
* @dev stakingAddress must be initialized separately after Staking contract is deployed
* @param _registryAddress - address of the registry proxy contract
* @param _votingPeriod - period in blocks for which a governance proposal is open for voting
* @param _executionDelay - number of blocks that must pass after votingPeriod has expired before proposal can be evaluated/executed
* @param _votingQuorumPercent - required minimum percentage of total stake to have voted to consider a proposal valid
* @param _maxInProgressProposals - max number of InProgress proposals possible at once
* @param _guardianAddress - address of account that has special Governance permissions
*/
function initialize(
address _registryAddress,
uint256 _votingPeriod,
uint256 _executionDelay,
uint256 _votingQuorumPercent,
uint16 _maxInProgressProposals,
address _guardianAddress
) public initializer {
require(_registryAddress != address(0x00), ERROR_INVALID_REGISTRY);
registry = Registry(_registryAddress);
require(_votingPeriod > 0, ERROR_INVALID_VOTING_PERIOD);
votingPeriod = _votingPeriod;
// executionDelay does not have to be non-zero
executionDelay = _executionDelay;
require(
_maxInProgressProposals > 0,
"Governance: Requires non-zero _maxInProgressProposals"
);
maxInProgressProposals = _maxInProgressProposals;
require(
_votingQuorumPercent > 0 && _votingQuorumPercent <= 100,
ERROR_INVALID_VOTING_QUORUM
);
votingQuorumPercent = _votingQuorumPercent;
require(
_guardianAddress != address(0x00),
"Governance: Requires non-zero _guardianAddress"
);
guardianAddress = _guardianAddress; //Guardian address becomes the only party
InitializableV2.initialize();
}
// ========================================= Governance Actions =========================================
/**
* @notice Submit a proposal for vote. Only callable by addresses with non-zero total active stake.
* total active stake = total active deployer stake + total active delegator stake
*
* @dev _name and _description length is not enforced since they aren't stored on-chain and only event emitted
*
* @param _targetContractRegistryKey - Registry key for the contract concerning this proposal
* @param _callValue - amount of wei to pass with function call if a token transfer is involved
* @param _functionSignature - function signature of the function to be executed if proposal is successful
* @param _callData - encoded value(s) to call function with if proposal is successful
* @param _name - Text name of proposal to be emitted in event
* @param _description - Text description of proposal to be emitted in event
*
* @return - ID of new proposal
*/
function submitProposal(
bytes32 _targetContractRegistryKey,
uint256 _callValue,
string calldata _functionSignature,
bytes calldata _callData,
string calldata _name,
string calldata _description
) external returns (uint256)
{
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceProviderFactoryAddressIsSet();
_requireDelegateManagerAddressIsSet();
address proposer = msg.sender;
// Require all InProgress proposals that can be evaluated have been evaluated before new proposal submission
require(
this.inProgressProposalsAreUpToDate(),
"Governance: Cannot submit new proposal until all evaluatable InProgress proposals are evaluated."
);
// Require new proposal submission would not push number of InProgress proposals over max number
require(
inProgressProposals.length < maxInProgressProposals,
"Governance: Number of InProgress proposals already at max. Please evaluate if possible, or wait for current proposals' votingPeriods to expire."
);
// Require proposer has non-zero total active stake or is guardian address
require(
_calculateAddressActiveStake(proposer) > 0 || proposer == guardianAddress,
"Governance: Proposer must be address with non-zero total active stake or be guardianAddress."
);
// Require _targetContractRegistryKey points to a valid registered contract
address targetContractAddress = registry.getContract(_targetContractRegistryKey);
require(
targetContractAddress != address(0x00),
"Governance: _targetContractRegistryKey must point to valid registered contract"
);
// Signature cannot be empty
require(
bytes(_functionSignature).length != 0,
"Governance: _functionSignature cannot be empty."
);
// Require non-zero description length
require(bytes(_description).length > 0, "Governance: _description length must be > 0");
// Require non-zero name length
require(bytes(_name).length > 0, "Governance: _name length must be > 0");
// set proposalId
uint256 newProposalId = lastProposalId.add(1);
// Store new Proposal obj in proposals mapping
proposals[newProposalId] = Proposal({
proposalId: newProposalId,
proposer: proposer,
submissionBlockNumber: block.number,
targetContractRegistryKey: _targetContractRegistryKey,
targetContractAddress: targetContractAddress,
callValue: _callValue,
functionSignature: _functionSignature,
callData: _callData,
outcome: Outcome.InProgress,
voteMagnitudeYes: 0,
voteMagnitudeNo: 0,
numVotes: 0,
contractHash: _getCodeHash(targetContractAddress)
/* votes: mappings are auto-initialized to default state */
/* voteMagnitudes: mappings are auto-initialized to default state */
});
// Append new proposalId to inProgressProposals array
inProgressProposals.push(newProposalId);
emit ProposalSubmitted(
newProposalId,
proposer,
_name,
_description
);
lastProposalId = newProposalId;
return newProposalId;
}
/**
* @notice Vote on an active Proposal. Only callable by addresses with non-zero active stake.
* @param _proposalId - id of the proposal this vote is for
* @param _vote - can be either {Yes, No} from Vote enum. No other values allowed
*/
function submitVote(uint256 _proposalId, Vote _vote) external {
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceProviderFactoryAddressIsSet();
_requireDelegateManagerAddressIsSet();
_requireValidProposalId(_proposalId);
address voter = msg.sender;
// Require proposal votingPeriod is still active
uint256 submissionBlockNumber = proposals[_proposalId].submissionBlockNumber;
uint256 endBlockNumber = submissionBlockNumber.add(votingPeriod);
require(
block.number > submissionBlockNumber && block.number <= endBlockNumber,
"Governance: Proposal votingPeriod has ended"
);
// Require voter has non-zero total active stake
uint256 voterActiveStake = _calculateAddressActiveStake(voter);
require(
voterActiveStake > 0,
"Governance: Voter must be address with non-zero total active stake."
);
// Require previous vote is None
require(
proposals[_proposalId].votes[voter] == Vote.None,
"Governance: To update previous vote, call updateVote()"
);
// Require vote is either Yes or No
require(
_vote == Vote.Yes || _vote == Vote.No,
"Governance: Can only submit a Yes or No vote"
);
// Record vote
proposals[_proposalId].votes[voter] = _vote;
// Record voteMagnitude for voter
proposals[_proposalId].voteMagnitudes[voter] = voterActiveStake;
// Update proposal cumulative vote magnitudes
if (_vote == Vote.Yes) {
_increaseVoteMagnitudeYes(_proposalId, voterActiveStake);
} else {
_increaseVoteMagnitudeNo(_proposalId, voterActiveStake);
}
// Increment proposal numVotes
proposals[_proposalId].numVotes = proposals[_proposalId].numVotes.add(1);
emit ProposalVoteSubmitted(
_proposalId,
voter,
_vote,
voterActiveStake
);
}
/**
* @notice Update previous vote on an active Proposal. Only callable by addresses with non-zero active stake.
* @param _proposalId - id of the proposal this vote is for
* @param _vote - can be either {Yes, No} from Vote enum. No other values allowed
*/
function updateVote(uint256 _proposalId, Vote _vote) external {
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceProviderFactoryAddressIsSet();
_requireDelegateManagerAddressIsSet();
_requireValidProposalId(_proposalId);
address voter = msg.sender;
// Require proposal votingPeriod is still active
uint256 submissionBlockNumber = proposals[_proposalId].submissionBlockNumber;
uint256 endBlockNumber = submissionBlockNumber.add(votingPeriod);
require(
block.number > submissionBlockNumber && block.number <= endBlockNumber,
"Governance: Proposal votingPeriod has ended"
);
// Retrieve previous vote
Vote previousVote = proposals[_proposalId].votes[voter];
// Require previous vote is not None
require(
previousVote != Vote.None,
"Governance: To submit new vote, call submitVote()"
);
// Require vote is either Yes or No
require(
_vote == Vote.Yes || _vote == Vote.No,
"Governance: Can only submit a Yes or No vote"
);
// Record updated vote
proposals[_proposalId].votes[voter] = _vote;
// Update vote magnitudes, using vote magnitude from when previous vote was submitted
uint256 voteMagnitude = proposals[_proposalId].voteMagnitudes[voter];
if (previousVote == Vote.Yes && _vote == Vote.No) {
_decreaseVoteMagnitudeYes(_proposalId, voteMagnitude);
_increaseVoteMagnitudeNo(_proposalId, voteMagnitude);
} else if (previousVote == Vote.No && _vote == Vote.Yes) {
_decreaseVoteMagnitudeNo(_proposalId, voteMagnitude);
_increaseVoteMagnitudeYes(_proposalId, voteMagnitude);
}
// If _vote == previousVote, no changes needed to vote magnitudes.
// Do not update numVotes
emit ProposalVoteUpdated(
_proposalId,
voter,
_vote,
voteMagnitude,
previousVote
);
}
/**
* @notice Once the voting period + executionDelay for a proposal has ended, evaluate the outcome and
* execute the proposal if voting quorum met & vote passes.
* To pass, stake-weighted vote must be > 50% Yes.
* @dev Requires that caller is an active staker at the time the proposal is created
* @param _proposalId - id of the proposal
* @return Outcome of proposal evaluation
*/
function evaluateProposalOutcome(uint256 _proposalId)
external returns (Outcome)
{
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceProviderFactoryAddressIsSet();
_requireDelegateManagerAddressIsSet();
_requireValidProposalId(_proposalId);
// Require proposal has not already been evaluated.
require(
proposals[_proposalId].outcome == Outcome.InProgress,
"Governance: Can only evaluate InProgress proposal."
);
// Re-entrancy should not be possible here since this switches the status of the
// proposal to 'Evaluating' so it should fail the status is 'InProgress' check
proposals[_proposalId].outcome = Outcome.Evaluating;
// Require proposal votingPeriod + executionDelay have ended.
uint256 submissionBlockNumber = proposals[_proposalId].submissionBlockNumber;
uint256 endBlockNumber = submissionBlockNumber.add(votingPeriod).add(executionDelay);
require(
block.number > endBlockNumber,
"Governance: Proposal votingPeriod & executionDelay must end before evaluation."
);
address targetContractAddress = registry.getContract(
proposals[_proposalId].targetContractRegistryKey
);
Outcome outcome;
// target contract address changed -> close proposal without execution.
if (targetContractAddress != proposals[_proposalId].targetContractAddress) {
outcome = Outcome.TargetContractAddressChanged;
}
// target contract code hash changed -> close proposal without execution.
else if (_getCodeHash(targetContractAddress) != proposals[_proposalId].contractHash) {
outcome = Outcome.TargetContractCodeHashChanged;
}
// voting quorum not met -> close proposal without execution.
else if (_quorumMet(proposals[_proposalId], Staking(stakingAddress)) == false) {
outcome = Outcome.QuorumNotMet;
}
// votingQuorumPercent met & vote passed -> execute proposed transaction & close proposal.
else if (
proposals[_proposalId].voteMagnitudeYes > proposals[_proposalId].voteMagnitudeNo
) {
(bool success, bytes memory returnData) = _executeTransaction(
targetContractAddress,
proposals[_proposalId].callValue,
proposals[_proposalId].functionSignature,
proposals[_proposalId].callData
);
emit ProposalTransactionExecuted(
_proposalId,
success,
returnData
);
// Proposal outcome depends on success of transaction execution.
if (success) {
outcome = Outcome.ApprovedExecuted;
} else {
outcome = Outcome.ApprovedExecutionFailed;
}
}
// votingQuorumPercent met & vote did not pass -> close proposal without transaction execution.
else {
outcome = Outcome.Rejected;
}
// This records the final outcome in the proposals mapping
proposals[_proposalId].outcome = outcome;
// Remove from inProgressProposals array
_removeFromInProgressProposals(_proposalId);
emit ProposalOutcomeEvaluated(
_proposalId,
outcome,
proposals[_proposalId].voteMagnitudeYes,
proposals[_proposalId].voteMagnitudeNo,
proposals[_proposalId].numVotes
);
return outcome;
}
/**
* @notice Action limited to the guardian address that can veto a proposal
* @param _proposalId - id of the proposal
*/
function vetoProposal(uint256 _proposalId) external {
_requireIsInitialized();
_requireValidProposalId(_proposalId);
require(
msg.sender == guardianAddress,
"Governance: Only guardian can veto proposals."
);
require(
proposals[_proposalId].outcome == Outcome.InProgress,
"Governance: Cannot veto inactive proposal."
);
proposals[_proposalId].outcome = Outcome.Vetoed;
// Remove from inProgressProposals array
_removeFromInProgressProposals(_proposalId);
emit ProposalVetoed(_proposalId);
}
// ========================================= Config Setters =========================================
/**
* @notice Set the Staking address
* @dev Only callable by self via _executeTransaction
* @param _stakingAddress - address for new Staking contract
*/
function setStakingAddress(address _stakingAddress) external {
_requireIsInitialized();
require(msg.sender == address(this), ERROR_ONLY_GOVERNANCE);
require(_stakingAddress != address(0x00), "Governance: Requires non-zero _stakingAddress");
stakingAddress = _stakingAddress;
}
/**
* @notice Set the ServiceProviderFactory address
* @dev Only callable by self via _executeTransaction
* @param _serviceProviderFactoryAddress - address for new ServiceProviderFactory contract
*/
function setServiceProviderFactoryAddress(address _serviceProviderFactoryAddress) external {
_requireIsInitialized();
require(msg.sender == address(this), ERROR_ONLY_GOVERNANCE);
require(
_serviceProviderFactoryAddress != address(0x00),
"Governance: Requires non-zero _serviceProviderFactoryAddress"
);
serviceProviderFactoryAddress = _serviceProviderFactoryAddress;
}
/**
* @notice Set the DelegateManager address
* @dev Only callable by self via _executeTransaction
* @param _delegateManagerAddress - address for new DelegateManager contract
*/
function setDelegateManagerAddress(address _delegateManagerAddress) external {
_requireIsInitialized();
require(msg.sender == address(this), ERROR_ONLY_GOVERNANCE);
require(
_delegateManagerAddress != address(0x00),
"Governance: Requires non-zero _delegateManagerAddress"
);
delegateManagerAddress = _delegateManagerAddress;
}
/**
* @notice Set the voting period for a Governance proposal
* @dev Only callable by self via _executeTransaction
* @param _votingPeriod - new voting period
*/
function setVotingPeriod(uint256 _votingPeriod) external {
_requireIsInitialized();
require(msg.sender == address(this), ERROR_ONLY_GOVERNANCE);
require(_votingPeriod > 0, ERROR_INVALID_VOTING_PERIOD);
votingPeriod = _votingPeriod;
emit VotingPeriodUpdated(_votingPeriod);
}
/**
* @notice Set the voting quorum percentage for a Governance proposal
* @dev Only callable by self via _executeTransaction
* @param _votingQuorumPercent - new voting period
*/
function setVotingQuorumPercent(uint256 _votingQuorumPercent) external {
_requireIsInitialized();
require(msg.sender == address(this), ERROR_ONLY_GOVERNANCE);
require(
_votingQuorumPercent > 0 && _votingQuorumPercent <= 100,
ERROR_INVALID_VOTING_QUORUM
);
votingQuorumPercent = _votingQuorumPercent;
emit VotingQuorumPercentUpdated(_votingQuorumPercent);
}
/**
* @notice Set the Registry address
* @dev Only callable by self via _executeTransaction
* @param _registryAddress - address for new Registry contract
*/
function setRegistryAddress(address _registryAddress) external {
_requireIsInitialized();
require(msg.sender == address(this), ERROR_ONLY_GOVERNANCE);
require(_registryAddress != address(0x00), ERROR_INVALID_REGISTRY);
registry = Registry(_registryAddress);
emit RegistryAddressUpdated(_registryAddress);
}
/**
* @notice Set the max number of concurrent InProgress proposals
* @dev Only callable by self via _executeTransaction
* @param _newMaxInProgressProposals - new value for maxInProgressProposals
*/
function setMaxInProgressProposals(uint16 _newMaxInProgressProposals) external {
_requireIsInitialized();
require(msg.sender == address(this), ERROR_ONLY_GOVERNANCE);
require(
_newMaxInProgressProposals > 0,
"Governance: Requires non-zero _newMaxInProgressProposals"
);
maxInProgressProposals = _newMaxInProgressProposals;
emit MaxInProgressProposalsUpdated(_newMaxInProgressProposals);
}
/**
* @notice Set the execution delay for a proposal
* @dev Only callable by self via _executeTransaction
* @param _newExecutionDelay - new value for executionDelay
*/
function setExecutionDelay(uint256 _newExecutionDelay) external {
_requireIsInitialized();
require(msg.sender == address(this), ERROR_ONLY_GOVERNANCE);
// executionDelay does not have to be non-zero
executionDelay = _newExecutionDelay;
emit ExecutionDelayUpdated(_newExecutionDelay);
}
// ========================================= Guardian Actions =========================================
/**
* @notice Allows the guardianAddress to execute protocol actions
* @param _targetContractRegistryKey - key in registry of target contract
* @param _callValue - amount of wei if a token transfer is involved
* @param _functionSignature - function signature of the function to be executed if proposal is successful
* @param _callData - encoded value(s) to call function with if proposal is successful
*/
function guardianExecuteTransaction(
bytes32 _targetContractRegistryKey,
uint256 _callValue,
string calldata _functionSignature,
bytes calldata _callData
) external
{
_requireIsInitialized();
require(
msg.sender == guardianAddress,
"Governance: Only guardian."
);
// _targetContractRegistryKey must point to a valid registered contract
address targetContractAddress = registry.getContract(_targetContractRegistryKey);
require(
targetContractAddress != address(0x00),
"Governance: _targetContractRegistryKey must point to valid registered contract"
);
// Signature cannot be empty
require(
bytes(_functionSignature).length != 0,
"Governance: _functionSignature cannot be empty."
);
(bool success, bytes memory returnData) = _executeTransaction(
targetContractAddress,
_callValue,
_functionSignature,
_callData
);
require(success, "Governance: Transaction failed.");
emit GuardianTransactionExecuted(
targetContractAddress,
_callValue,
_functionSignature,
_callData,
returnData
);
}
/**
* @notice Change the guardian address
* @dev Only callable by current guardian
* @param _newGuardianAddress - new guardian address
*/
function transferGuardianship(address _newGuardianAddress) external {
_requireIsInitialized();
require(
msg.sender == guardianAddress,
"Governance: Only guardian."
);
guardianAddress = _newGuardianAddress;
emit GuardianshipTransferred(_newGuardianAddress);
}
// ========================================= Getter Functions =========================================
/**
* @notice Get proposal information by proposal Id
* @param _proposalId - id of proposal
*/
function getProposalById(uint256 _proposalId)
external view returns (
uint256 proposalId,
address proposer,
uint256 submissionBlockNumber,
bytes32 targetContractRegistryKey,
address targetContractAddress,
uint256 callValue,
string memory functionSignature,
bytes memory callData,
Outcome outcome,
uint256 voteMagnitudeYes,
uint256 voteMagnitudeNo,
uint256 numVotes
)
{
_requireIsInitialized();
_requireValidProposalId(_proposalId);
Proposal memory proposal = proposals[_proposalId];
return (
proposal.proposalId,
proposal.proposer,
proposal.submissionBlockNumber,
proposal.targetContractRegistryKey,
proposal.targetContractAddress,
proposal.callValue,
proposal.functionSignature,
proposal.callData,
proposal.outcome,
proposal.voteMagnitudeYes,
proposal.voteMagnitudeNo,
proposal.numVotes
/** @notice - votes mapping cannot be returned by external function */
/** @notice - voteMagnitudes mapping cannot be returned by external function */
/** @notice - returning contractHash leads to stack too deep compiler error, see getProposalTargetContractHash() */
);
}
/**
* @notice Get proposal target contract hash by proposalId
* @dev This is a separate function because the getProposalById returns too many
variables already and by adding more, you get the error
`InternalCompilerError: Stack too deep, try using fewer variables`
* @param _proposalId - id of proposal
*/
function getProposalTargetContractHash(uint256 _proposalId)
external view returns (bytes32)
{
_requireIsInitialized();
_requireValidProposalId(_proposalId);
return (proposals[_proposalId].contractHash);
}
/**
* @notice Get vote direction and vote magnitude for a given proposal and voter
* @param _proposalId - id of the proposal
* @param _voter - address of the voter we want to check
* @return returns vote direction and magnitude if valid vote, else default values
*/
function getVoteInfoByProposalAndVoter(uint256 _proposalId, address _voter)
external view returns (Vote vote, uint256 voteMagnitude)
{
_requireIsInitialized();
_requireValidProposalId(_proposalId);
return (
proposals[_proposalId].votes[_voter],
proposals[_proposalId].voteMagnitudes[_voter]
);
}
/// @notice Get the contract Guardian address
function getGuardianAddress() external view returns (address) {
_requireIsInitialized();
return guardianAddress;
}
/// @notice Get the Staking address
function getStakingAddress() external view returns (address) {
_requireIsInitialized();
return stakingAddress;
}
/// @notice Get the ServiceProviderFactory address
function getServiceProviderFactoryAddress() external view returns (address) {
_requireIsInitialized();
return serviceProviderFactoryAddress;
}
/// @notice Get the DelegateManager address
function getDelegateManagerAddress() external view returns (address) {
_requireIsInitialized();
return delegateManagerAddress;
}
/// @notice Get the contract voting period
function getVotingPeriod() external view returns (uint256) {
_requireIsInitialized();
return votingPeriod;
}
/// @notice Get the contract voting quorum percent
function getVotingQuorumPercent() external view returns (uint256) {
_requireIsInitialized();
return votingQuorumPercent;
}
/// @notice Get the registry address
function getRegistryAddress() external view returns (address) {
_requireIsInitialized();
return address(registry);
}
/// @notice Used to check if is governance contract before setting governance address in other contracts
function isGovernanceAddress() external pure returns (bool) {
return true;
}
/// @notice Get the max number of concurrent InProgress proposals
function getMaxInProgressProposals() external view returns (uint16) {
_requireIsInitialized();
return maxInProgressProposals;
}
/// @notice Get the proposal execution delay
function getExecutionDelay() external view returns (uint256) {
_requireIsInitialized();
return executionDelay;
}
/// @notice Get the array of all InProgress proposal Ids
function getInProgressProposals() external view returns (uint256[] memory) {
_requireIsInitialized();
return inProgressProposals;
}
/**
* @notice Returns false if any proposals in inProgressProposals array are evaluatable
* Evaluatable = proposals with closed votingPeriod
* @dev Is public since its called internally in `submitProposal()` as well as externally in UI
*/
function inProgressProposalsAreUpToDate() external view returns (bool) {
_requireIsInitialized();
// compare current block number against endBlockNumber of each proposal
for (uint256 i = 0; i < inProgressProposals.length; i++) {
if (
block.number >
(proposals[inProgressProposals[i]].submissionBlockNumber).add(votingPeriod).add(executionDelay)
) {
return false;
}
}
return true;
}
// ========================================= Internal Functions =========================================
/**
* @notice Execute a transaction attached to a governance proposal
* @dev We are aware of both potential re-entrancy issues and the risks associated with low-level solidity
* function calls here, but have chosen to keep this code with those issues in mind. All governance
* proposals go through a voting process, and all will be reviewed carefully to ensure that they
* adhere to the expected behaviors of this call - but adding restrictions here would limit the ability
* of the governance system to do required work in a generic way.
* @param _targetContractAddress - address of registry proxy contract to execute transaction on
* @param _callValue - amount of wei if a token transfer is involved
* @param _functionSignature - function signature of the function to be executed if proposal is successful
* @param _callData - encoded value(s) to call function with if proposal is successful
*/
function _executeTransaction(
address _targetContractAddress,
uint256 _callValue,
string memory _functionSignature,
bytes memory _callData
) internal returns (bool success, bytes memory returnData)
{
bytes memory encodedCallData = abi.encodePacked(
bytes4(keccak256(bytes(_functionSignature))),
_callData
);
(success, returnData) = (
// solium-disable-next-line security/no-call-value
_targetContractAddress.call.value(_callValue)(encodedCallData)
);
return (success, returnData);
}
function _increaseVoteMagnitudeYes(uint256 _proposalId, uint256 _voterStake) internal {
proposals[_proposalId].voteMagnitudeYes = (
proposals[_proposalId].voteMagnitudeYes.add(_voterStake)
);
}
function _increaseVoteMagnitudeNo(uint256 _proposalId, uint256 _voterStake) internal {
proposals[_proposalId].voteMagnitudeNo = (
proposals[_proposalId].voteMagnitudeNo.add(_voterStake)
);
}
function _decreaseVoteMagnitudeYes(uint256 _proposalId, uint256 _voterStake) internal {
proposals[_proposalId].voteMagnitudeYes = (
proposals[_proposalId].voteMagnitudeYes.sub(_voterStake)
);
}
function _decreaseVoteMagnitudeNo(uint256 _proposalId, uint256 _voterStake) internal {
proposals[_proposalId].voteMagnitudeNo = (
proposals[_proposalId].voteMagnitudeNo.sub(_voterStake)
);
}
/**
* @dev Can make O(1) by storing index pointer in proposals mapping.
* Requires inProgressProposals to be 1-indexed, since all proposals that are not present
* will have pointer set to 0.
*/
function _removeFromInProgressProposals(uint256 _proposalId) internal {
uint256 index = 0;
for (uint256 i = 0; i < inProgressProposals.length; i++) {
if (inProgressProposals[i] == _proposalId) {
index = i;
break;
}
}
// Swap proposalId to end of array + pop (deletes last elem + decrements array length)
inProgressProposals[index] = inProgressProposals[inProgressProposals.length - 1];
inProgressProposals.pop();
}
/**
* @notice Returns true if voting quorum percentage met for proposal, else false.
* @dev Quorum is met if total voteMagnitude * 100 / total active stake in Staking
* @dev Eventual multiplication overflow:
* (proposal.voteMagnitudeYes + proposal.voteMagnitudeNo), with 100% staking participation,
* can sum to at most the entire token supply of 10^27
* With 7% annual token supply inflation, multiplication can overflow ~1635 years at the earliest:
* log(2^256/(10^27*100))/log(1.07) ~= 1635
*
* @dev Note that quorum is evaluated based on total staked at proposal submission
* not total staked at proposal evaluation, this is expected behavior
*/
function _quorumMet(Proposal memory proposal, Staking stakingContract)
internal view returns (bool)
{
uint256 participation = (
(proposal.voteMagnitudeYes + proposal.voteMagnitudeNo)
.mul(100)
.div(stakingContract.totalStakedAt(proposal.submissionBlockNumber))
);
return participation >= votingQuorumPercent;
}
// ========================================= Private Functions =========================================
function _requireStakingAddressIsSet() private view {
require(
stakingAddress != address(0x00),
"Governance: stakingAddress is not set"
);
}
function _requireServiceProviderFactoryAddressIsSet() private view {
require(
serviceProviderFactoryAddress != address(0x00),
"Governance: serviceProviderFactoryAddress is not set"
);
}
function _requireDelegateManagerAddressIsSet() private view {
require(
delegateManagerAddress != address(0x00),
"Governance: delegateManagerAddress is not set"
);
}
function _requireValidProposalId(uint256 _proposalId) private view {
require(
_proposalId <= lastProposalId && _proposalId > 0,
"Governance: Must provide valid non-zero _proposalId"
);
}
/**
* Calculates and returns active stake for address
*
* Active stake = (active deployer stake + active delegator stake)
* active deployer stake = (direct deployer stake - locked deployer stake)
* locked deployer stake = amount of pending decreaseStakeRequest for address
* active delegator stake = (total delegator stake - locked delegator stake)
* locked delegator stake = amount of pending undelegateRequest for address
*/
function _calculateAddressActiveStake(address _address) private view returns (uint256) {
ServiceProviderFactory spFactory = ServiceProviderFactory(serviceProviderFactoryAddress);
DelegateManager delegateManager = DelegateManager(delegateManagerAddress);
// Amount directly staked by address, if any, in ServiceProviderFactory
(uint256 directDeployerStake,,,,,) = spFactory.getServiceProviderDetails(_address);
// Amount of pending decreasedStakeRequest for address, if any, in ServiceProviderFactory
(uint256 lockedDeployerStake,) = spFactory.getPendingDecreaseStakeRequest(_address);
// active deployer stake = (direct deployer stake - locked deployer stake)
uint256 activeDeployerStake = directDeployerStake.sub(lockedDeployerStake);
// Total amount delegated by address, if any, in DelegateManager
uint256 totalDelegatorStake = delegateManager.getTotalDelegatorStake(_address);
// Amount of pending undelegateRequest for address, if any, in DelegateManager
(,uint256 lockedDelegatorStake, ) = delegateManager.getPendingUndelegateRequest(_address);
// active delegator stake = (total delegator stake - locked delegator stake)
uint256 activeDelegatorStake = totalDelegatorStake.sub(lockedDelegatorStake);
// activeStake = (activeDeployerStake + activeDelegatorStake)
uint256 activeStake = activeDeployerStake.add(activeDelegatorStake);
return activeStake;
}
// solium-disable security/no-inline-assembly
/**
* @notice Helper function to generate the code hash for a contract address
* @return contract code hash
*/
function _getCodeHash(address _contract) private view returns (bytes32) {
bytes32 contractHash;
assembly {
contractHash := extcodehash(_contract)
}
return contractHash;
}
}
contract Staking is InitializableV2 {
using SafeMath for uint256;
using Uint256Helpers for uint256;
using Checkpointing for Checkpointing.History;
using SafeERC20 for ERC20;
string private constant ERROR_TOKEN_NOT_CONTRACT = "Staking: Staking token is not a contract";
string private constant ERROR_AMOUNT_ZERO = "Staking: Zero amount not allowed";
string private constant ERROR_ONLY_GOVERNANCE = "Staking: Only governance";
string private constant ERROR_ONLY_DELEGATE_MANAGER = (
"Staking: Only callable from DelegateManager"
);
string private constant ERROR_ONLY_SERVICE_PROVIDER_FACTORY = (
"Staking: Only callable from ServiceProviderFactory"
);
address private governanceAddress;
address private claimsManagerAddress;
address private delegateManagerAddress;
address private serviceProviderFactoryAddress;
/// @dev stores the history of staking and claims for a given address
struct Account {
Checkpointing.History stakedHistory;
Checkpointing.History claimHistory;
}
/// @dev ERC-20 token that will be used to stake with
ERC20 internal stakingToken;
/// @dev maps addresses to staking and claims history
mapping (address => Account) internal accounts;
/// @dev total staked tokens at a given block
Checkpointing.History internal totalStakedHistory;
event Staked(address indexed user, uint256 amount, uint256 total);
event Unstaked(address indexed user, uint256 amount, uint256 total);
event Slashed(address indexed user, uint256 amount, uint256 total);
/**
* @notice Function to initialize the contract
* @dev claimsManagerAddress must be initialized separately after ClaimsManager contract is deployed
* @dev delegateManagerAddress must be initialized separately after DelegateManager contract is deployed
* @dev serviceProviderFactoryAddress must be initialized separately after ServiceProviderFactory contract is deployed
* @param _tokenAddress - address of ERC20 token that will be staked
* @param _governanceAddress - address for Governance proxy contract
*/
function initialize(
address _tokenAddress,
address _governanceAddress
) public initializer
{
require(Address.isContract(_tokenAddress), ERROR_TOKEN_NOT_CONTRACT);
stakingToken = ERC20(_tokenAddress);
_updateGovernanceAddress(_governanceAddress);
InitializableV2.initialize();
}
/**
* @notice Set the Governance address
* @dev Only callable by Governance address
* @param _governanceAddress - address for new Governance contract
*/
function setGovernanceAddress(address _governanceAddress) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
_updateGovernanceAddress(_governanceAddress);
}
/**
* @notice Set the ClaimsManaager address
* @dev Only callable by Governance address
* @param _claimsManager - address for new ClaimsManaager contract
*/
function setClaimsManagerAddress(address _claimsManager) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
claimsManagerAddress = _claimsManager;
}
/**
* @notice Set the ServiceProviderFactory address
* @dev Only callable by Governance address
* @param _spFactory - address for new ServiceProviderFactory contract
*/
function setServiceProviderFactoryAddress(address _spFactory) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
serviceProviderFactoryAddress = _spFactory;
}
/**
* @notice Set the DelegateManager address
* @dev Only callable by Governance address
* @param _delegateManager - address for new DelegateManager contract
*/
function setDelegateManagerAddress(address _delegateManager) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
delegateManagerAddress = _delegateManager;
}
/* External functions */
/**
* @notice Funds `_amount` of tokens from ClaimsManager to target account
* @param _amount - amount of rewards to add to stake
* @param _stakerAccount - address of staker
*/
function stakeRewards(uint256 _amount, address _stakerAccount) external {
_requireIsInitialized();
_requireClaimsManagerAddressIsSet();
require(
msg.sender == claimsManagerAddress,
"Staking: Only callable from ClaimsManager"
);
_stakeFor(_stakerAccount, msg.sender, _amount);
this.updateClaimHistory(_amount, _stakerAccount);
}
/**
* @notice Update claim history by adding an event to the claim history
* @param _amount - amount to add to claim history
* @param _stakerAccount - address of staker
*/
function updateClaimHistory(uint256 _amount, address _stakerAccount) external {
_requireIsInitialized();
_requireClaimsManagerAddressIsSet();
require(
msg.sender == claimsManagerAddress || msg.sender == address(this),
"Staking: Only callable from ClaimsManager or Staking.sol"
);
// Update claim history even if no value claimed
accounts[_stakerAccount].claimHistory.add(block.number.toUint64(), _amount);
}
/**
* @notice Slashes `_amount` tokens from _slashAddress
* @dev Callable from DelegateManager
* @param _amount - Number of tokens slashed
* @param _slashAddress - Address being slashed
*/
function slash(
uint256 _amount,
address _slashAddress
) external
{
_requireIsInitialized();
_requireDelegateManagerAddressIsSet();
require(
msg.sender == delegateManagerAddress,
ERROR_ONLY_DELEGATE_MANAGER
);
// Burn slashed tokens from account
_burnFor(_slashAddress, _amount);
emit Slashed(
_slashAddress,
_amount,
totalStakedFor(_slashAddress)
);
}
/**
* @notice Stakes `_amount` tokens, transferring them from _accountAddress, and assigns them to `_accountAddress`
* @param _accountAddress - The final staker of the tokens
* @param _amount - Number of tokens staked
*/
function stakeFor(
address _accountAddress,
uint256 _amount
) external
{
_requireIsInitialized();
_requireServiceProviderFactoryAddressIsSet();
require(
msg.sender == serviceProviderFactoryAddress,
ERROR_ONLY_SERVICE_PROVIDER_FACTORY
);
_stakeFor(
_accountAddress,
_accountAddress,
_amount
);
}
/**
* @notice Unstakes `_amount` tokens, returning them to the desired account.
* @param _accountAddress - Account unstaked for, and token recipient
* @param _amount - Number of tokens staked
*/
function unstakeFor(
address _accountAddress,
uint256 _amount
) external
{
_requireIsInitialized();
_requireServiceProviderFactoryAddressIsSet();
require(
msg.sender == serviceProviderFactoryAddress,
ERROR_ONLY_SERVICE_PROVIDER_FACTORY
);
_unstakeFor(
_accountAddress,
_accountAddress,
_amount
);
}
/**
* @notice Stakes `_amount` tokens, transferring them from `_delegatorAddress` to `_accountAddress`,
only callable by DelegateManager
* @param _accountAddress - The final staker of the tokens
* @param _delegatorAddress - Address from which to transfer tokens
* @param _amount - Number of tokens staked
*/
function delegateStakeFor(
address _accountAddress,
address _delegatorAddress,
uint256 _amount
) external {
_requireIsInitialized();
_requireDelegateManagerAddressIsSet();
require(
msg.sender == delegateManagerAddress,
ERROR_ONLY_DELEGATE_MANAGER
);
_stakeFor(
_accountAddress,
_delegatorAddress,
_amount);
}
/**
* @notice Unstakes '_amount` tokens, transferring them from `_accountAddress` to `_delegatorAddress`,
only callable by DelegateManager
* @param _accountAddress - The staker of the tokens
* @param _delegatorAddress - Address from which to transfer tokens
* @param _amount - Number of tokens unstaked
*/
function undelegateStakeFor(
address _accountAddress,
address _delegatorAddress,
uint256 _amount
) external {
_requireIsInitialized();
_requireDelegateManagerAddressIsSet();
require(
msg.sender == delegateManagerAddress,
ERROR_ONLY_DELEGATE_MANAGER
);
_unstakeFor(
_accountAddress,
_delegatorAddress,
_amount);
}
/**
* @notice Get the token used by the contract for staking and locking
* @return The token used by the contract for staking and locking
*/
function token() external view returns (address) {
_requireIsInitialized();
return address(stakingToken);
}
/**
* @notice Check whether it supports history of stakes
* @return Always true
*/
function supportsHistory() external view returns (bool) {
_requireIsInitialized();
return true;
}
/**
* @notice Get last time `_accountAddress` modified its staked balance
* @param _accountAddress - Account requesting for
* @return Last block number when account's balance was modified
*/
function lastStakedFor(address _accountAddress) external view returns (uint256) {
_requireIsInitialized();
uint256 length = accounts[_accountAddress].stakedHistory.history.length;
if (length > 0) {
return uint256(accounts[_accountAddress].stakedHistory.history[length - 1].time);
}
return 0;
}
/**
* @notice Get last time `_accountAddress` claimed a staking reward
* @param _accountAddress - Account requesting for
* @return Last block number when claim requested
*/
function lastClaimedFor(address _accountAddress) external view returns (uint256) {
_requireIsInitialized();
uint256 length = accounts[_accountAddress].claimHistory.history.length;
if (length > 0) {
return uint256(accounts[_accountAddress].claimHistory.history[length - 1].time);
}
return 0;
}
/**
* @notice Get the total amount of tokens staked by `_accountAddress` at block number `_blockNumber`
* @param _accountAddress - Account requesting for
* @param _blockNumber - Block number at which we are requesting
* @return The amount of tokens staked by the account at the given block number
*/
function totalStakedForAt(
address _accountAddress,
uint256 _blockNumber
) external view returns (uint256) {
_requireIsInitialized();
return accounts[_accountAddress].stakedHistory.get(_blockNumber.toUint64());
}
/**
* @notice Get the total amount of tokens staked by all users at block number `_blockNumber`
* @param _blockNumber - Block number at which we are requesting
* @return The amount of tokens staked at the given block number
*/
function totalStakedAt(uint256 _blockNumber) external view returns (uint256) {
_requireIsInitialized();
return totalStakedHistory.get(_blockNumber.toUint64());
}
/// @notice Get the Governance address
function getGovernanceAddress() external view returns (address) {
_requireIsInitialized();
return governanceAddress;
}
/// @notice Get the ClaimsManager address
function getClaimsManagerAddress() external view returns (address) {
_requireIsInitialized();
return claimsManagerAddress;
}
/// @notice Get the ServiceProviderFactory address
function getServiceProviderFactoryAddress() external view returns (address) {
_requireIsInitialized();
return serviceProviderFactoryAddress;
}
/// @notice Get the DelegateManager address
function getDelegateManagerAddress() external view returns (address) {
_requireIsInitialized();
return delegateManagerAddress;
}
/**
* @notice Helper function wrapped around totalStakedFor. Checks whether _accountAddress
is currently a valid staker with a non-zero stake
* @param _accountAddress - Account requesting for
* @return Boolean indicating whether account is a staker
*/
function isStaker(address _accountAddress) external view returns (bool) {
_requireIsInitialized();
return totalStakedFor(_accountAddress) > 0;
}
/* Public functions */
/**
* @notice Get the amount of tokens staked by `_accountAddress`
* @param _accountAddress - The owner of the tokens
* @return The amount of tokens staked by the given account
*/
function totalStakedFor(address _accountAddress) public view returns (uint256) {
_requireIsInitialized();
// we assume it's not possible to stake in the future
return accounts[_accountAddress].stakedHistory.getLast();
}
/**
* @notice Get the total amount of tokens staked by all users
* @return The total amount of tokens staked by all users
*/
function totalStaked() public view returns (uint256) {
_requireIsInitialized();
// we assume it's not possible to stake in the future
return totalStakedHistory.getLast();
}
// ========================================= Internal Functions =========================================
/**
* @notice Adds stake from a transfer account to the stake account
* @param _stakeAccount - Account that funds will be staked for
* @param _transferAccount - Account that funds will be transferred from
* @param _amount - amount to stake
*/
function _stakeFor(
address _stakeAccount,
address _transferAccount,
uint256 _amount
) internal
{
// staking 0 tokens is invalid
require(_amount > 0, ERROR_AMOUNT_ZERO);
// Checkpoint updated staking balance
_modifyStakeBalance(_stakeAccount, _amount, true);
// checkpoint total supply
_modifyTotalStaked(_amount, true);
// pull tokens into Staking contract
stakingToken.safeTransferFrom(_transferAccount, address(this), _amount);
emit Staked(
_stakeAccount,
_amount,
totalStakedFor(_stakeAccount));
}
/**
* @notice Unstakes tokens from a stake account to a transfer account
* @param _stakeAccount - Account that staked funds will be transferred from
* @param _transferAccount - Account that funds will be transferred to
* @param _amount - amount to unstake
*/
function _unstakeFor(
address _stakeAccount,
address _transferAccount,
uint256 _amount
) internal
{
require(_amount > 0, ERROR_AMOUNT_ZERO);
// checkpoint updated staking balance
_modifyStakeBalance(_stakeAccount, _amount, false);
// checkpoint total supply
_modifyTotalStaked(_amount, false);
// transfer tokens
stakingToken.safeTransfer(_transferAccount, _amount);
emit Unstaked(
_stakeAccount,
_amount,
totalStakedFor(_stakeAccount)
);
}
/**
* @notice Burn tokens for a given staker
* @dev Called when slash occurs
* @param _stakeAccount - Account for which funds will be burned
* @param _amount - amount to burn
*/
function _burnFor(address _stakeAccount, uint256 _amount) internal {
// burning zero tokens is not allowed
require(_amount > 0, ERROR_AMOUNT_ZERO);
// checkpoint updated staking balance
_modifyStakeBalance(_stakeAccount, _amount, false);
// checkpoint total supply
_modifyTotalStaked(_amount, false);
// burn
ERC20Burnable(address(stakingToken)).burn(_amount);
/** No event emitted since token.burn() call already emits a Transfer event */
}
/**
* @notice Increase or decrease the staked balance for an account
* @param _accountAddress - Account to modify
* @param _by - amount to modify
* @param _increase - true if increase in stake, false if decrease
*/
function _modifyStakeBalance(address _accountAddress, uint256 _by, bool _increase) internal {
uint256 currentInternalStake = accounts[_accountAddress].stakedHistory.getLast();
uint256 newStake;
if (_increase) {
newStake = currentInternalStake.add(_by);
} else {
require(
currentInternalStake >= _by,
"Staking: Cannot decrease greater than current balance");
newStake = currentInternalStake.sub(_by);
}
// add new value to account history
accounts[_accountAddress].stakedHistory.add(block.number.toUint64(), newStake);
}
/**
* @notice Increase or decrease the staked balance across all accounts
* @param _by - amount to modify
* @param _increase - true if increase in stake, false if decrease
*/
function _modifyTotalStaked(uint256 _by, bool _increase) internal {
uint256 currentStake = totalStaked();
uint256 newStake;
if (_increase) {
newStake = currentStake.add(_by);
} else {
newStake = currentStake.sub(_by);
}
// add new value to total history
totalStakedHistory.add(block.number.toUint64(), newStake);
}
/**
* @notice Set the governance address after confirming contract identity
* @param _governanceAddress - Incoming governance address
*/
function _updateGovernanceAddress(address _governanceAddress) internal {
require(
Governance(_governanceAddress).isGovernanceAddress() == true,
"Staking: _governanceAddress is not a valid governance contract"
);
governanceAddress = _governanceAddress;
}
// ========================================= Private Functions =========================================
function _requireClaimsManagerAddressIsSet() private view {
require(claimsManagerAddress != address(0x00), "Staking: claimsManagerAddress is not set");
}
function _requireDelegateManagerAddressIsSet() private view {
require(
delegateManagerAddress != address(0x00),
"Staking: delegateManagerAddress is not set"
);
}
function _requireServiceProviderFactoryAddressIsSet() private view {
require(
serviceProviderFactoryAddress != address(0x00),
"Staking: serviceProviderFactoryAddress is not set"
);
}
}
contract ServiceTypeManager is InitializableV2 {
address governanceAddress;
string private constant ERROR_ONLY_GOVERNANCE = (
"ServiceTypeManager: Only callable by Governance contract"
);
/**
* @dev - mapping of serviceType - serviceTypeVersion
* Example - "discovery-provider" - ["0.0.1", "0.0.2", ..., "currentVersion"]
*/
mapping(bytes32 => bytes32[]) private serviceTypeVersions;
/**
* @dev - mapping of serviceType - < serviceTypeVersion, isValid >
* Example - "discovery-provider" - <"0.0.1", true>
*/
mapping(bytes32 => mapping(bytes32 => bool)) private serviceTypeVersionInfo;
/// @dev List of valid service types
bytes32[] private validServiceTypes;
/// @dev Struct representing service type info
struct ServiceTypeInfo {
bool isValid;
uint256 minStake;
uint256 maxStake;
}
/// @dev mapping of service type info
mapping(bytes32 => ServiceTypeInfo) private serviceTypeInfo;
event SetServiceVersion(
bytes32 indexed _serviceType,
bytes32 indexed _serviceVersion
);
event ServiceTypeAdded(
bytes32 indexed _serviceType,
uint256 indexed _serviceTypeMin,
uint256 indexed _serviceTypeMax
);
event ServiceTypeRemoved(bytes32 indexed _serviceType);
/**
* @notice Function to initialize the contract
* @param _governanceAddress - Governance proxy address
*/
function initialize(address _governanceAddress) public initializer
{
_updateGovernanceAddress(_governanceAddress);
InitializableV2.initialize();
}
/// @notice Get the Governance address
function getGovernanceAddress() external view returns (address) {
_requireIsInitialized();
return governanceAddress;
}
/**
* @notice Set the Governance address
* @dev Only callable by Governance address
* @param _governanceAddress - address for new Governance contract
*/
function setGovernanceAddress(address _governanceAddress) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
_updateGovernanceAddress(_governanceAddress);
}
// ========================================= Service Type Logic =========================================
/**
* @notice Add a new service type
* @param _serviceType - type of service to add
* @param _serviceTypeMin - minimum stake for service type
* @param _serviceTypeMax - maximum stake for service type
*/
function addServiceType(
bytes32 _serviceType,
uint256 _serviceTypeMin,
uint256 _serviceTypeMax
) external
{
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
require(
!this.serviceTypeIsValid(_serviceType),
"ServiceTypeManager: Already known service type"
);
require(
_serviceTypeMax > _serviceTypeMin,
"ServiceTypeManager: Max stake must be non-zero and greater than min stake"
);
// Ensure serviceType cannot be re-added if it previously existed and was removed
// stored maxStake > 0 means it was previously added and removed
require(
serviceTypeInfo[_serviceType].maxStake == 0,
"ServiceTypeManager: Cannot re-add serviceType after it was removed."
);
validServiceTypes.push(_serviceType);
serviceTypeInfo[_serviceType] = ServiceTypeInfo({
isValid: true,
minStake: _serviceTypeMin,
maxStake: _serviceTypeMax
});
emit ServiceTypeAdded(_serviceType, _serviceTypeMin, _serviceTypeMax);
}
/**
* @notice Remove an existing service type
* @param _serviceType - name of service type to remove
*/
function removeServiceType(bytes32 _serviceType) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
uint256 serviceIndex = 0;
bool foundService = false;
for (uint256 i = 0; i < validServiceTypes.length; i ++) {
if (validServiceTypes[i] == _serviceType) {
serviceIndex = i;
foundService = true;
break;
}
}
require(foundService == true, "ServiceTypeManager: Invalid service type, not found");
// Overwrite service index
uint256 lastIndex = validServiceTypes.length - 1;
validServiceTypes[serviceIndex] = validServiceTypes[lastIndex];
validServiceTypes.length--;
// Mark as invalid
serviceTypeInfo[_serviceType].isValid = false;
// Note - stake bounds are not reset so they can be checked to prevent serviceType from being re-added
emit ServiceTypeRemoved(_serviceType);
}
/**
* @notice Get isValid, min and max stake for a given service type
* @param _serviceType - type of service
* @return isValid, min and max stake for type
*/
function getServiceTypeInfo(bytes32 _serviceType)
external view returns (bool isValid, uint256 minStake, uint256 maxStake)
{
_requireIsInitialized();
return (
serviceTypeInfo[_serviceType].isValid,
serviceTypeInfo[_serviceType].minStake,
serviceTypeInfo[_serviceType].maxStake
);
}
/**
* @notice Get list of valid service types
*/
function getValidServiceTypes()
external view returns (bytes32[] memory)
{
_requireIsInitialized();
return validServiceTypes;
}
/**
* @notice Return indicating whether this is a valid service type
*/
function serviceTypeIsValid(bytes32 _serviceType)
external view returns (bool)
{
_requireIsInitialized();
return serviceTypeInfo[_serviceType].isValid;
}
// ========================================= Service Version Logic =========================================
/**
* @notice Add new version for a serviceType
* @param _serviceType - type of service
* @param _serviceVersion - new version of service to add
*/
function setServiceVersion(
bytes32 _serviceType,
bytes32 _serviceVersion
) external
{
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
require(this.serviceTypeIsValid(_serviceType), "ServiceTypeManager: Invalid service type");
require(
serviceTypeVersionInfo[_serviceType][_serviceVersion] == false,
"ServiceTypeManager: Already registered"
);
// Update array of known versions for type
serviceTypeVersions[_serviceType].push(_serviceVersion);
// Update status for this specific service version
serviceTypeVersionInfo[_serviceType][_serviceVersion] = true;
emit SetServiceVersion(_serviceType, _serviceVersion);
}
/**
* @notice Get a version for a service type given it's index
* @param _serviceType - type of service
* @param _versionIndex - index in list of service versions
* @return bytes32 value for serviceVersion
*/
function getVersion(bytes32 _serviceType, uint256 _versionIndex)
external view returns (bytes32)
{
_requireIsInitialized();
require(
serviceTypeVersions[_serviceType].length > _versionIndex,
"ServiceTypeManager: No registered version of serviceType"
);
return (serviceTypeVersions[_serviceType][_versionIndex]);
}
/**
* @notice Get curent version for a service type
* @param _serviceType - type of service
* @return Returns current version of service
*/
function getCurrentVersion(bytes32 _serviceType)
external view returns (bytes32)
{
_requireIsInitialized();
require(
serviceTypeVersions[_serviceType].length >= 1,
"ServiceTypeManager: No registered version of serviceType"
);
uint256 latestVersionIndex = serviceTypeVersions[_serviceType].length - 1;
return (serviceTypeVersions[_serviceType][latestVersionIndex]);
}
/**
* @notice Get total number of versions for a service type
* @param _serviceType - type of service
*/
function getNumberOfVersions(bytes32 _serviceType)
external view returns (uint256)
{
_requireIsInitialized();
return serviceTypeVersions[_serviceType].length;
}
/**
* @notice Return boolean indicating whether given version is valid for given type
* @param _serviceType - type of service
* @param _serviceVersion - version of service to check
*/
function serviceVersionIsValid(bytes32 _serviceType, bytes32 _serviceVersion)
external view returns (bool)
{
_requireIsInitialized();
return serviceTypeVersionInfo[_serviceType][_serviceVersion];
}
/**
* @notice Set the governance address after confirming contract identity
* @param _governanceAddress - Incoming governance address
*/
function _updateGovernanceAddress(address _governanceAddress) internal {
require(
Governance(_governanceAddress).isGovernanceAddress() == true,
"ServiceTypeManager: _governanceAddress is not a valid governance contract"
);
governanceAddress = _governanceAddress;
}
}
/// @notice Governance imported via Staking.sol
contract ServiceProviderFactory is InitializableV2 {
using SafeMath for uint256;
/// @dev - denominator for deployer cut calculations
/// @dev - user values are intended to be x/DEPLOYER_CUT_BASE
uint256 private constant DEPLOYER_CUT_BASE = 100;
string private constant ERROR_ONLY_GOVERNANCE = (
"ServiceProviderFactory: Only callable by Governance contract"
);
string private constant ERROR_ONLY_SP_GOVERNANCE = (
"ServiceProviderFactory: Only callable by Service Provider or Governance"
);
address private stakingAddress;
address private delegateManagerAddress;
address private governanceAddress;
address private serviceTypeManagerAddress;
address private claimsManagerAddress;
/// @notice Period in blocks that a decrease stake operation is delayed.
/// Must be greater than governance votingPeriod + executionDelay in order to
/// prevent pre-emptive withdrawal in anticipation of a slash proposal
uint256 private decreaseStakeLockupDuration;
/// @notice Period in blocks that an update deployer cut operation is delayed.
/// Must be greater than funding round block diff in order
/// to prevent manipulation around a funding round
uint256 private deployerCutLockupDuration;
/// @dev - Stores following entities
/// 1) Directly staked amount by SP, not including delegators
/// 2) % Cut of delegator tokens taken during reward
/// 3) Bool indicating whether this SP has met min/max requirements
/// 4) Number of endpoints registered by SP
/// 5) Minimum deployer stake for this service provider
/// 6) Maximum total stake for this account
struct ServiceProviderDetails {
uint256 deployerStake;
uint256 deployerCut;
bool validBounds;
uint256 numberOfEndpoints;
uint256 minAccountStake;
uint256 maxAccountStake;
}
/// @dev - Data structure for time delay during withdrawal
struct DecreaseStakeRequest {
uint256 decreaseAmount;
uint256 lockupExpiryBlock;
}
/// @dev - Data structure for time delay during deployer cut update
struct UpdateDeployerCutRequest {
uint256 newDeployerCut;
uint256 lockupExpiryBlock;
}
/// @dev - Struct maintaining information about sp
/// @dev - blocknumber is block.number when endpoint registered
struct ServiceEndpoint {
address owner;
string endpoint;
uint256 blocknumber;
address delegateOwnerWallet;
}
/// @dev - Mapping of service provider address to details
mapping(address => ServiceProviderDetails) private spDetails;
/// @dev - Uniquely assigned serviceProvider ID, incremented for each service type
/// @notice - Keeps track of the total number of services registered regardless of
/// whether some have been deregistered since
mapping(bytes32 => uint256) private serviceProviderTypeIDs;
/// @dev - mapping of (serviceType -> (serviceInstanceId <-> serviceProviderInfo))
/// @notice - stores the actual service provider data like endpoint and owner wallet
/// with the ability lookup by service type and service id */
mapping(bytes32 => mapping(uint256 => ServiceEndpoint)) private serviceProviderInfo;
/// @dev - mapping of keccak256(endpoint) to uint256 ID
/// @notice - used to check if a endpoint has already been registered and also lookup
/// the id of an endpoint
mapping(bytes32 => uint256) private serviceProviderEndpointToId;
/// @dev - mapping of address -> sp id array */
/// @notice - stores all the services registered by a provider. for each address,
/// provides the ability to lookup by service type and see all registered services
mapping(address => mapping(bytes32 => uint256[])) private serviceProviderAddressToId;
/// @dev - Mapping of service provider -> decrease stake request
mapping(address => DecreaseStakeRequest) private decreaseStakeRequests;
/// @dev - Mapping of service provider -> update deployer cut requests
mapping(address => UpdateDeployerCutRequest) private updateDeployerCutRequests;
event RegisteredServiceProvider(
uint256 indexed _spID,
bytes32 indexed _serviceType,
address indexed _owner,
string _endpoint,
uint256 _stakeAmount
);
event DeregisteredServiceProvider(
uint256 indexed _spID,
bytes32 indexed _serviceType,
address indexed _owner,
string _endpoint,
uint256 _unstakeAmount
);
event IncreasedStake(
address indexed _owner,
uint256 indexed _increaseAmount,
uint256 indexed _newStakeAmount
);
event DecreaseStakeRequested(
address indexed _owner,
uint256 indexed _decreaseAmount,
uint256 indexed _lockupExpiryBlock
);
event DecreaseStakeRequestCancelled(
address indexed _owner,
uint256 indexed _decreaseAmount,
uint256 indexed _lockupExpiryBlock
);
event DecreaseStakeRequestEvaluated(
address indexed _owner,
uint256 indexed _decreaseAmount,
uint256 indexed _newStakeAmount
);
event EndpointUpdated(
bytes32 indexed _serviceType,
address indexed _owner,
string _oldEndpoint,
string _newEndpoint,
uint256 indexed _spID
);
event DelegateOwnerWalletUpdated(
address indexed _owner,
bytes32 indexed _serviceType,
uint256 indexed _spID,
address _updatedWallet
);
event DeployerCutUpdateRequested(
address indexed _owner,
uint256 indexed _updatedCut,
uint256 indexed _lockupExpiryBlock
);
event DeployerCutUpdateRequestCancelled(
address indexed _owner,
uint256 indexed _requestedCut,
uint256 indexed _finalCut
);
event DeployerCutUpdateRequestEvaluated(
address indexed _owner,
uint256 indexed _updatedCut
);
event DecreaseStakeLockupDurationUpdated(uint256 indexed _lockupDuration);
event UpdateDeployerCutLockupDurationUpdated(uint256 indexed _lockupDuration);
event GovernanceAddressUpdated(address indexed _newGovernanceAddress);
event StakingAddressUpdated(address indexed _newStakingAddress);
event ClaimsManagerAddressUpdated(address indexed _newClaimsManagerAddress);
event DelegateManagerAddressUpdated(address indexed _newDelegateManagerAddress);
event ServiceTypeManagerAddressUpdated(address indexed _newServiceTypeManagerAddress);
/**
* @notice Function to initialize the contract
* @dev stakingAddress must be initialized separately after Staking contract is deployed
* @dev delegateManagerAddress must be initialized separately after DelegateManager contract is deployed
* @dev serviceTypeManagerAddress must be initialized separately after ServiceTypeManager contract is deployed
* @dev claimsManagerAddress must be initialized separately after ClaimsManager contract is deployed
* @param _governanceAddress - Governance proxy address
*/
function initialize (
address _governanceAddress,
address _claimsManagerAddress,
uint256 _decreaseStakeLockupDuration,
uint256 _deployerCutLockupDuration
) public initializer
{
_updateGovernanceAddress(_governanceAddress);
claimsManagerAddress = _claimsManagerAddress;
_updateDecreaseStakeLockupDuration(_decreaseStakeLockupDuration);
_updateDeployerCutLockupDuration(_deployerCutLockupDuration);
InitializableV2.initialize();
}
/**
* @notice Register a new endpoint to the account of msg.sender
* @dev Transfers stake from service provider into staking pool
* @param _serviceType - type of service to register, must be valid in ServiceTypeManager
* @param _endpoint - url of the service to register - url of the service to register
* @param _stakeAmount - amount to stake, must be within bounds in ServiceTypeManager
* @param _delegateOwnerWallet - wallet to delegate some permissions for some basic management properties
* @return New service provider ID for this endpoint
*/
function register(
bytes32 _serviceType,
string calldata _endpoint,
uint256 _stakeAmount,
address _delegateOwnerWallet
) external returns (uint256)
{
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceTypeManagerAddressIsSet();
_requireClaimsManagerAddressIsSet();
require(
ServiceTypeManager(serviceTypeManagerAddress).serviceTypeIsValid(_serviceType),
"ServiceProviderFactory: Valid service type required");
// Stake token amount from msg.sender
if (_stakeAmount > 0) {
require(
!_claimPending(msg.sender),
"ServiceProviderFactory: No pending claim expected"
);
Staking(stakingAddress).stakeFor(msg.sender, _stakeAmount);
}
require (
serviceProviderEndpointToId[keccak256(bytes(_endpoint))] == 0,
"ServiceProviderFactory: Endpoint already registered");
uint256 newServiceProviderID = serviceProviderTypeIDs[_serviceType].add(1);
serviceProviderTypeIDs[_serviceType] = newServiceProviderID;
// Index spInfo
serviceProviderInfo[_serviceType][newServiceProviderID] = ServiceEndpoint({
owner: msg.sender,
endpoint: _endpoint,
blocknumber: block.number,
delegateOwnerWallet: _delegateOwnerWallet
});
// Update endpoint mapping
serviceProviderEndpointToId[keccak256(bytes(_endpoint))] = newServiceProviderID;
// Update (address -> type -> ids[])
serviceProviderAddressToId[msg.sender][_serviceType].push(newServiceProviderID);
// Increment number of endpoints for this address
spDetails[msg.sender].numberOfEndpoints = spDetails[msg.sender].numberOfEndpoints.add(1);
// Update deployer total
spDetails[msg.sender].deployerStake = (
spDetails[msg.sender].deployerStake.add(_stakeAmount)
);
// Update min and max totals for this service provider
(, uint256 typeMin, uint256 typeMax) = ServiceTypeManager(
serviceTypeManagerAddress
).getServiceTypeInfo(_serviceType);
spDetails[msg.sender].minAccountStake = spDetails[msg.sender].minAccountStake.add(typeMin);
spDetails[msg.sender].maxAccountStake = spDetails[msg.sender].maxAccountStake.add(typeMax);
// Confirm both aggregate account balance and directly staked amount are valid
this.validateAccountStakeBalance(msg.sender);
uint256 currentlyStakedForOwner = Staking(stakingAddress).totalStakedFor(msg.sender);
// Indicate this service provider is within bounds
spDetails[msg.sender].validBounds = true;
emit RegisteredServiceProvider(
newServiceProviderID,
_serviceType,
msg.sender,
_endpoint,
currentlyStakedForOwner
);
return newServiceProviderID;
}
/**
* @notice Deregister an endpoint from the account of msg.sender
* @dev Unstakes all tokens for service provider if this is the last endpoint
* @param _serviceType - type of service to deregister
* @param _endpoint - endpoint to deregister
* @return spId of the service that was deregistered
*/
function deregister(
bytes32 _serviceType,
string calldata _endpoint
) external returns (uint256)
{
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceTypeManagerAddressIsSet();
// Unstake on deregistration if and only if this is the last service endpoint
uint256 unstakeAmount = 0;
bool unstaked = false;
// owned by the service provider
if (spDetails[msg.sender].numberOfEndpoints == 1) {
unstakeAmount = spDetails[msg.sender].deployerStake;
// Submit request to decrease stake, overriding any pending request
decreaseStakeRequests[msg.sender] = DecreaseStakeRequest({
decreaseAmount: unstakeAmount,
lockupExpiryBlock: block.number.add(decreaseStakeLockupDuration)
});
unstaked = true;
}
require (
serviceProviderEndpointToId[keccak256(bytes(_endpoint))] != 0,
"ServiceProviderFactory: Endpoint not registered");
// Cache invalided service provider ID
uint256 deregisteredID = serviceProviderEndpointToId[keccak256(bytes(_endpoint))];
// Update endpoint mapping
serviceProviderEndpointToId[keccak256(bytes(_endpoint))] = 0;
require(
keccak256(bytes(serviceProviderInfo[_serviceType][deregisteredID].endpoint)) == keccak256(bytes(_endpoint)),
"ServiceProviderFactory: Invalid endpoint for service type");
require (
serviceProviderInfo[_serviceType][deregisteredID].owner == msg.sender,
"ServiceProviderFactory: Only callable by endpoint owner");
// Update info mapping
delete serviceProviderInfo[_serviceType][deregisteredID];
// Reset id, update array
uint256 spTypeLength = serviceProviderAddressToId[msg.sender][_serviceType].length;
for (uint256 i = 0; i < spTypeLength; i ++) {
if (serviceProviderAddressToId[msg.sender][_serviceType][i] == deregisteredID) {
// Overwrite element to be deleted with last element in array
serviceProviderAddressToId[msg.sender][_serviceType][i] = serviceProviderAddressToId[msg.sender][_serviceType][spTypeLength - 1];
// Reduce array size, exit loop
serviceProviderAddressToId[msg.sender][_serviceType].length--;
// Confirm this ID has been found for the service provider
break;
}
}
// Decrement number of endpoints for this address
spDetails[msg.sender].numberOfEndpoints -= 1;
// Update min and max totals for this service provider
(, uint256 typeMin, uint256 typeMax) = ServiceTypeManager(
serviceTypeManagerAddress
).getServiceTypeInfo(_serviceType);
spDetails[msg.sender].minAccountStake = spDetails[msg.sender].minAccountStake.sub(typeMin);
spDetails[msg.sender].maxAccountStake = spDetails[msg.sender].maxAccountStake.sub(typeMax);
emit DeregisteredServiceProvider(
deregisteredID,
_serviceType,
msg.sender,
_endpoint,
unstakeAmount);
// Confirm both aggregate account balance and directly staked amount are valid
// Only if unstake operation has not occurred
if (!unstaked) {
this.validateAccountStakeBalance(msg.sender);
// Indicate this service provider is within bounds
spDetails[msg.sender].validBounds = true;
}
return deregisteredID;
}
/**
* @notice Increase stake for service provider
* @param _increaseStakeAmount - amount to increase staked amount by
* @return New total stake for service provider
*/
function increaseStake(
uint256 _increaseStakeAmount
) external returns (uint256)
{
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireClaimsManagerAddressIsSet();
// Confirm owner has an endpoint
require(
spDetails[msg.sender].numberOfEndpoints > 0,
"ServiceProviderFactory: Registered endpoint required to increase stake"
);
require(
!_claimPending(msg.sender),
"ServiceProviderFactory: No claim expected to be pending prior to stake transfer"
);
Staking stakingContract = Staking(
stakingAddress
);
// Stake increased token amount for msg.sender
stakingContract.stakeFor(msg.sender, _increaseStakeAmount);
uint256 newStakeAmount = stakingContract.totalStakedFor(msg.sender);
// Update deployer total
spDetails[msg.sender].deployerStake = (
spDetails[msg.sender].deployerStake.add(_increaseStakeAmount)
);
// Confirm both aggregate account balance and directly staked amount are valid
this.validateAccountStakeBalance(msg.sender);
// Indicate this service provider is within bounds
spDetails[msg.sender].validBounds = true;
emit IncreasedStake(
msg.sender,
_increaseStakeAmount,
newStakeAmount
);
return newStakeAmount;
}
/**
* @notice Request to decrease stake. This sets a lockup for decreaseStakeLockupDuration after
which the actual decreaseStake can be called
* @dev Decreasing stake is only processed if a service provider is within valid bounds
* @param _decreaseStakeAmount - amount to decrease stake by in wei
* @return New total stake amount after the lockup
*/
function requestDecreaseStake(uint256 _decreaseStakeAmount)
external returns (uint256)
{
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireClaimsManagerAddressIsSet();
require(
_decreaseStakeAmount > 0,
"ServiceProviderFactory: Requested stake decrease amount must be greater than zero"
);
require(
!_claimPending(msg.sender),
"ServiceProviderFactory: No claim expected to be pending prior to stake transfer"
);
Staking stakingContract = Staking(
stakingAddress
);
uint256 currentStakeAmount = stakingContract.totalStakedFor(msg.sender);
// Prohibit decreasing stake to invalid bounds
_validateBalanceInternal(msg.sender, (currentStakeAmount.sub(_decreaseStakeAmount)));
uint256 expiryBlock = block.number.add(decreaseStakeLockupDuration);
decreaseStakeRequests[msg.sender] = DecreaseStakeRequest({
decreaseAmount: _decreaseStakeAmount,
lockupExpiryBlock: expiryBlock
});
emit DecreaseStakeRequested(msg.sender, _decreaseStakeAmount, expiryBlock);
return currentStakeAmount.sub(_decreaseStakeAmount);
}
/**
* @notice Cancel a decrease stake request during the lockup
* @dev Either called by the service provider via DelegateManager or governance
during a slash action
* @param _account - address of service provider
*/
function cancelDecreaseStakeRequest(address _account) external
{
_requireIsInitialized();
_requireDelegateManagerAddressIsSet();
require(
msg.sender == _account || msg.sender == delegateManagerAddress,
"ServiceProviderFactory: Only owner or DelegateManager"
);
require(
_decreaseRequestIsPending(_account),
"ServiceProviderFactory: Decrease stake request must be pending"
);
DecreaseStakeRequest memory cancelledRequest = decreaseStakeRequests[_account];
// Clear decrease stake request
decreaseStakeRequests[_account] = DecreaseStakeRequest({
decreaseAmount: 0,
lockupExpiryBlock: 0
});
emit DecreaseStakeRequestCancelled(
_account,
cancelledRequest.decreaseAmount,
cancelledRequest.lockupExpiryBlock
);
}
/**
* @notice Called by user to decrease a stake after waiting the appropriate lockup period.
* @return New total stake after decrease
*/
function decreaseStake() external returns (uint256)
{
_requireIsInitialized();
_requireStakingAddressIsSet();
require(
_decreaseRequestIsPending(msg.sender),
"ServiceProviderFactory: Decrease stake request must be pending"
);
require(
decreaseStakeRequests[msg.sender].lockupExpiryBlock <= block.number,
"ServiceProviderFactory: Lockup must be expired"
);
Staking stakingContract = Staking(
stakingAddress
);
uint256 decreaseAmount = decreaseStakeRequests[msg.sender].decreaseAmount;
// Decrease staked token amount for msg.sender
stakingContract.unstakeFor(msg.sender, decreaseAmount);
// Query current stake
uint256 newStakeAmount = stakingContract.totalStakedFor(msg.sender);
// Update deployer total
spDetails[msg.sender].deployerStake = (
spDetails[msg.sender].deployerStake.sub(decreaseAmount)
);
// Confirm both aggregate account balance and directly staked amount are valid
// During registration this validation is bypassed since no endpoints remain
if (spDetails[msg.sender].numberOfEndpoints > 0) {
this.validateAccountStakeBalance(msg.sender);
}
// Indicate this service provider is within bounds
spDetails[msg.sender].validBounds = true;
// Clear decrease stake request
delete decreaseStakeRequests[msg.sender];
emit DecreaseStakeRequestEvaluated(msg.sender, decreaseAmount, newStakeAmount);
return newStakeAmount;
}
/**
* @notice Update delegate owner wallet for a given endpoint
* @param _serviceType - type of service to register, must be valid in ServiceTypeManager
* @param _endpoint - url of the service to register - url of the service to register
* @param _updatedDelegateOwnerWallet - address of new delegate wallet
*/
function updateDelegateOwnerWallet(
bytes32 _serviceType,
string calldata _endpoint,
address _updatedDelegateOwnerWallet
) external
{
_requireIsInitialized();
uint256 spID = this.getServiceProviderIdFromEndpoint(_endpoint);
require(
serviceProviderInfo[_serviceType][spID].owner == msg.sender,
"ServiceProviderFactory: Invalid update operation, wrong owner"
);
serviceProviderInfo[_serviceType][spID].delegateOwnerWallet = _updatedDelegateOwnerWallet;
emit DelegateOwnerWalletUpdated(
msg.sender,
_serviceType,
spID,
_updatedDelegateOwnerWallet
);
}
/**
* @notice Update the endpoint for a given service
* @param _serviceType - type of service to register, must be valid in ServiceTypeManager
* @param _oldEndpoint - old endpoint currently registered
* @param _newEndpoint - new endpoint to replace old endpoint
* @return ID of updated service provider
*/
function updateEndpoint(
bytes32 _serviceType,
string calldata _oldEndpoint,
string calldata _newEndpoint
) external returns (uint256)
{
_requireIsInitialized();
uint256 spId = this.getServiceProviderIdFromEndpoint(_oldEndpoint);
require (
spId != 0,
"ServiceProviderFactory: Could not find service provider with that endpoint"
);
ServiceEndpoint memory serviceEndpoint = serviceProviderInfo[_serviceType][spId];
require(
serviceEndpoint.owner == msg.sender,
"ServiceProviderFactory: Invalid update endpoint operation, wrong owner"
);
require(
keccak256(bytes(serviceEndpoint.endpoint)) == keccak256(bytes(_oldEndpoint)),
"ServiceProviderFactory: Old endpoint doesn't match what's registered for the service provider"
);
// invalidate old endpoint
serviceProviderEndpointToId[keccak256(bytes(serviceEndpoint.endpoint))] = 0;
// update to new endpoint
serviceEndpoint.endpoint = _newEndpoint;
serviceProviderInfo[_serviceType][spId] = serviceEndpoint;
serviceProviderEndpointToId[keccak256(bytes(_newEndpoint))] = spId;
emit EndpointUpdated(_serviceType, msg.sender, _oldEndpoint, _newEndpoint, spId);
return spId;
}
/**
* @notice Update the deployer cut for a given service provider
* @param _serviceProvider - address of service provider
* @param _cut - new value for deployer cut
*/
function requestUpdateDeployerCut(address _serviceProvider, uint256 _cut) external
{
_requireIsInitialized();
require(
msg.sender == _serviceProvider || msg.sender == governanceAddress,
ERROR_ONLY_SP_GOVERNANCE
);
require(
(updateDeployerCutRequests[_serviceProvider].lockupExpiryBlock == 0) &&
(updateDeployerCutRequests[_serviceProvider].newDeployerCut == 0),
"ServiceProviderFactory: Update deployer cut operation pending"
);
require(
_cut <= DEPLOYER_CUT_BASE,
"ServiceProviderFactory: Service Provider cut cannot exceed base value"
);
uint256 expiryBlock = block.number + deployerCutLockupDuration;
updateDeployerCutRequests[_serviceProvider] = UpdateDeployerCutRequest({
lockupExpiryBlock: expiryBlock,
newDeployerCut: _cut
});
emit DeployerCutUpdateRequested(_serviceProvider, _cut, expiryBlock);
}
/**
* @notice Cancel a pending request to update deployer cut
* @param _serviceProvider - address of service provider
*/
function cancelUpdateDeployerCut(address _serviceProvider) external
{
_requireIsInitialized();
_requirePendingDeployerCutOperation(_serviceProvider);
require(
msg.sender == _serviceProvider || msg.sender == governanceAddress,
ERROR_ONLY_SP_GOVERNANCE
);
UpdateDeployerCutRequest memory cancelledRequest = (
updateDeployerCutRequests[_serviceProvider]
);
// Zero out request information
delete updateDeployerCutRequests[_serviceProvider];
emit DeployerCutUpdateRequestCancelled(
_serviceProvider,
cancelledRequest.newDeployerCut,
spDetails[_serviceProvider].deployerCut
);
}
/**
* @notice Evalue request to update service provider cut of claims
* @notice Update service provider cut as % of delegate claim, divided by the deployerCutBase.
* @dev SPs will interact with this value as a percent, value translation done client side
@dev A value of 5 dictates a 5% cut, with ( 5 / 100 ) * delegateReward going to an SP from each delegator each round.
*/
function updateDeployerCut(address _serviceProvider) external
{
_requireIsInitialized();
_requirePendingDeployerCutOperation(_serviceProvider);
require(
msg.sender == _serviceProvider || msg.sender == governanceAddress,
ERROR_ONLY_SP_GOVERNANCE
);
require(
updateDeployerCutRequests[_serviceProvider].lockupExpiryBlock <= block.number,
"ServiceProviderFactory: Lockup must be expired"
);
spDetails[_serviceProvider].deployerCut = (
updateDeployerCutRequests[_serviceProvider].newDeployerCut
);
// Zero out request information
delete updateDeployerCutRequests[_serviceProvider];
emit DeployerCutUpdateRequestEvaluated(
_serviceProvider,
spDetails[_serviceProvider].deployerCut
);
}
/**
* @notice Update service provider balance
* @dev Called by DelegateManager by functions modifying entire stake like claim and slash
* @param _serviceProvider - address of service provider
* @param _amount - new amount of direct state for service provider
*/
function updateServiceProviderStake(
address _serviceProvider,
uint256 _amount
) external
{
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireDelegateManagerAddressIsSet();
require(
msg.sender == delegateManagerAddress,
"ServiceProviderFactory: only callable by DelegateManager"
);
// Update SP tracked total
spDetails[_serviceProvider].deployerStake = _amount;
_updateServiceProviderBoundStatus(_serviceProvider);
}
/// @notice Update service provider lockup duration
function updateDecreaseStakeLockupDuration(uint256 _duration) external {
_requireIsInitialized();
require(
msg.sender == governanceAddress,
ERROR_ONLY_GOVERNANCE
);
_updateDecreaseStakeLockupDuration(_duration);
emit DecreaseStakeLockupDurationUpdated(_duration);
}
/// @notice Update service provider lockup duration
function updateDeployerCutLockupDuration(uint256 _duration) external {
_requireIsInitialized();
require(
msg.sender == governanceAddress,
ERROR_ONLY_GOVERNANCE
);
_updateDeployerCutLockupDuration(_duration);
emit UpdateDeployerCutLockupDurationUpdated(_duration);
}
/// @notice Get denominator for deployer cut calculations
function getServiceProviderDeployerCutBase()
external view returns (uint256)
{
_requireIsInitialized();
return DEPLOYER_CUT_BASE;
}
/// @notice Get current deployer cut update lockup duration
function getDeployerCutLockupDuration()
external view returns (uint256)
{
_requireIsInitialized();
return deployerCutLockupDuration;
}
/// @notice Get total number of service providers for a given serviceType
function getTotalServiceTypeProviders(bytes32 _serviceType)
external view returns (uint256)
{
_requireIsInitialized();
return serviceProviderTypeIDs[_serviceType];
}
/// @notice Get service provider id for an endpoint
function getServiceProviderIdFromEndpoint(string calldata _endpoint)
external view returns (uint256)
{
_requireIsInitialized();
return serviceProviderEndpointToId[keccak256(bytes(_endpoint))];
}
/**
* @notice Get service provider ids for a given service provider and service type
* @return List of service ids of that type for a service provider
*/
function getServiceProviderIdsFromAddress(address _ownerAddress, bytes32 _serviceType)
external view returns (uint256[] memory)
{
_requireIsInitialized();
return serviceProviderAddressToId[_ownerAddress][_serviceType];
}
/**
* @notice Get information about a service endpoint given its service id
* @param _serviceType - type of service, must be a valid service from ServiceTypeManager
* @param _serviceId - id of service
*/
function getServiceEndpointInfo(bytes32 _serviceType, uint256 _serviceId)
external view returns (address owner, string memory endpoint, uint256 blockNumber, address delegateOwnerWallet)
{
_requireIsInitialized();
ServiceEndpoint memory serviceEndpoint = serviceProviderInfo[_serviceType][_serviceId];
return (
serviceEndpoint.owner,
serviceEndpoint.endpoint,
serviceEndpoint.blocknumber,
serviceEndpoint.delegateOwnerWallet
);
}
/**
* @notice Get information about a service provider given their address
* @param _serviceProvider - address of service provider
*/
function getServiceProviderDetails(address _serviceProvider)
external view returns (
uint256 deployerStake,
uint256 deployerCut,
bool validBounds,
uint256 numberOfEndpoints,
uint256 minAccountStake,
uint256 maxAccountStake)
{
_requireIsInitialized();
return (
spDetails[_serviceProvider].deployerStake,
spDetails[_serviceProvider].deployerCut,
spDetails[_serviceProvider].validBounds,
spDetails[_serviceProvider].numberOfEndpoints,
spDetails[_serviceProvider].minAccountStake,
spDetails[_serviceProvider].maxAccountStake
);
}
/**
* @notice Get information about pending decrease stake requests for service provider
* @param _serviceProvider - address of service provider
*/
function getPendingDecreaseStakeRequest(address _serviceProvider)
external view returns (uint256 amount, uint256 lockupExpiryBlock)
{
_requireIsInitialized();
return (
decreaseStakeRequests[_serviceProvider].decreaseAmount,
decreaseStakeRequests[_serviceProvider].lockupExpiryBlock
);
}
/**
* @notice Get information about pending decrease stake requests for service provider
* @param _serviceProvider - address of service provider
*/
function getPendingUpdateDeployerCutRequest(address _serviceProvider)
external view returns (uint256 newDeployerCut, uint256 lockupExpiryBlock)
{
_requireIsInitialized();
return (
updateDeployerCutRequests[_serviceProvider].newDeployerCut,
updateDeployerCutRequests[_serviceProvider].lockupExpiryBlock
);
}
/// @notice Get current unstake lockup duration
function getDecreaseStakeLockupDuration()
external view returns (uint256)
{
_requireIsInitialized();
return decreaseStakeLockupDuration;
}
/**
* @notice Validate that the total service provider balance is between the min and max stakes
for all their registered services and validate direct stake for sp is above minimum
* @param _serviceProvider - address of service provider
*/
function validateAccountStakeBalance(address _serviceProvider)
external view
{
_requireIsInitialized();
_requireStakingAddressIsSet();
_validateBalanceInternal(
_serviceProvider,
Staking(stakingAddress).totalStakedFor(_serviceProvider)
);
}
/// @notice Get the Governance address
function getGovernanceAddress() external view returns (address) {
_requireIsInitialized();
return governanceAddress;
}
/// @notice Get the Staking address
function getStakingAddress() external view returns (address) {
_requireIsInitialized();
return stakingAddress;
}
/// @notice Get the DelegateManager address
function getDelegateManagerAddress() external view returns (address) {
_requireIsInitialized();
return delegateManagerAddress;
}
/// @notice Get the ServiceTypeManager address
function getServiceTypeManagerAddress() external view returns (address) {
_requireIsInitialized();
return serviceTypeManagerAddress;
}
/// @notice Get the ClaimsManager address
function getClaimsManagerAddress() external view returns (address) {
_requireIsInitialized();
return claimsManagerAddress;
}
/**
* @notice Set the Governance address
* @dev Only callable by Governance address
* @param _governanceAddress - address for new Governance contract
*/
function setGovernanceAddress(address _governanceAddress) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
_updateGovernanceAddress(_governanceAddress);
emit GovernanceAddressUpdated(_governanceAddress);
}
/**
* @notice Set the Staking address
* @dev Only callable by Governance address
* @param _address - address for new Staking contract
*/
function setStakingAddress(address _address) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
stakingAddress = _address;
emit StakingAddressUpdated(_address);
}
/**
* @notice Set the DelegateManager address
* @dev Only callable by Governance address
* @param _address - address for new DelegateManager contract
*/
function setDelegateManagerAddress(address _address) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
delegateManagerAddress = _address;
emit DelegateManagerAddressUpdated(_address);
}
/**
* @notice Set the ServiceTypeManager address
* @dev Only callable by Governance address
* @param _address - address for new ServiceTypeManager contract
*/
function setServiceTypeManagerAddress(address _address) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
serviceTypeManagerAddress = _address;
emit ServiceTypeManagerAddressUpdated(_address);
}
/**
* @notice Set the ClaimsManager address
* @dev Only callable by Governance address
* @param _address - address for new ClaimsManager contract
*/
function setClaimsManagerAddress(address _address) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
claimsManagerAddress = _address;
emit ClaimsManagerAddressUpdated(_address);
}
// ========================================= Internal Functions =========================================
/**
* @notice Update status in spDetails if the bounds for a service provider is valid
*/
function _updateServiceProviderBoundStatus(address _serviceProvider) internal {
// Validate bounds for total stake
uint256 totalSPStake = Staking(stakingAddress).totalStakedFor(_serviceProvider);
if (totalSPStake < spDetails[_serviceProvider].minAccountStake ||
totalSPStake > spDetails[_serviceProvider].maxAccountStake) {
// Indicate this service provider is out of bounds
spDetails[_serviceProvider].validBounds = false;
} else {
// Indicate this service provider is within bounds
spDetails[_serviceProvider].validBounds = true;
}
}
/**
* @notice Set the governance address after confirming contract identity
* @param _governanceAddress - Incoming governance address
*/
function _updateGovernanceAddress(address _governanceAddress) internal {
require(
Governance(_governanceAddress).isGovernanceAddress() == true,
"ServiceProviderFactory: _governanceAddress is not a valid governance contract"
);
governanceAddress = _governanceAddress;
}
/**
* @notice Set the deployer cut lockup duration
* @param _duration - incoming duration
*/
function _updateDeployerCutLockupDuration(uint256 _duration) internal
{
require(
ClaimsManager(claimsManagerAddress).getFundingRoundBlockDiff() < _duration,
"ServiceProviderFactory: Incoming duration must be greater than funding round block diff"
);
deployerCutLockupDuration = _duration;
}
/**
* @notice Set the decrease stake lockup duration
* @param _duration - incoming duration
*/
function _updateDecreaseStakeLockupDuration(uint256 _duration) internal
{
Governance governance = Governance(governanceAddress);
require(
_duration > governance.getVotingPeriod() + governance.getExecutionDelay(),
"ServiceProviderFactory: decreaseStakeLockupDuration duration must be greater than governance votingPeriod + executionDelay"
);
decreaseStakeLockupDuration = _duration;
}
/**
* @notice Compare a given amount input against valid min and max bounds for service provider
* @param _serviceProvider - address of service provider
* @param _amount - amount in wei to compare
*/
function _validateBalanceInternal(address _serviceProvider, uint256 _amount) internal view
{
require(
_amount <= spDetails[_serviceProvider].maxAccountStake,
"ServiceProviderFactory: Maximum stake amount exceeded"
);
require(
spDetails[_serviceProvider].deployerStake >= spDetails[_serviceProvider].minAccountStake,
"ServiceProviderFactory: Minimum stake requirement not met"
);
}
/**
* @notice Get whether a decrease request has been initiated for service provider
* @param _serviceProvider - address of service provider
* return Boolean of whether decrease request has been initiated
*/
function _decreaseRequestIsPending(address _serviceProvider)
internal view returns (bool)
{
return (
(decreaseStakeRequests[_serviceProvider].lockupExpiryBlock > 0) &&
(decreaseStakeRequests[_serviceProvider].decreaseAmount > 0)
);
}
/**
* @notice Boolean indicating whether a claim is pending for this service provider
*/
/**
* @notice Get whether a claim is pending for this service provider
* @param _serviceProvider - address of service provider
* return Boolean of whether claim is pending
*/
function _claimPending(address _serviceProvider) internal view returns (bool) {
return ClaimsManager(claimsManagerAddress).claimPending(_serviceProvider);
}
// ========================================= Private Functions =========================================
function _requirePendingDeployerCutOperation (address _serviceProvider) private view {
require(
(updateDeployerCutRequests[_serviceProvider].lockupExpiryBlock != 0),
"ServiceProviderFactory: No update deployer cut operation pending"
);
}
function _requireStakingAddressIsSet() private view {
require(
stakingAddress != address(0x00),
"ServiceProviderFactory: stakingAddress is not set"
);
}
function _requireDelegateManagerAddressIsSet() private view {
require(
delegateManagerAddress != address(0x00),
"ServiceProviderFactory: delegateManagerAddress is not set"
);
}
function _requireServiceTypeManagerAddressIsSet() private view {
require(
serviceTypeManagerAddress != address(0x00),
"ServiceProviderFactory: serviceTypeManagerAddress is not set"
);
}
function _requireClaimsManagerAddressIsSet() private view {
require(
claimsManagerAddress != address(0x00),
"ServiceProviderFactory: claimsManagerAddress is not set"
);
}
}
/**
* Designed to manage delegation to staking contract
*/
contract DelegateManagerV2 is InitializableV2 {
using SafeMath for uint256;
string private constant ERROR_ONLY_GOVERNANCE = (
"DelegateManager: Only callable by Governance contract"
);
string private constant ERROR_MINIMUM_DELEGATION = (
"DelegateManager: Minimum delegation amount required"
);
string private constant ERROR_ONLY_SP_GOVERNANCE = (
"DelegateManager: Only callable by target SP or governance"
);
string private constant ERROR_DELEGATOR_STAKE = (
"DelegateManager: Delegator must be staked for SP"
);
address private governanceAddress;
address private stakingAddress;
address private serviceProviderFactoryAddress;
address private claimsManagerAddress;
/**
* Period in blocks an undelegate operation is delayed.
* The undelegate operation speed bump is to prevent a delegator from
* attempting to remove their delegation in anticipation of a slash.
* @notice Must be greater than governance votingPeriod + executionDelay
*/
uint256 private undelegateLockupDuration;
/// @notice Maximum number of delegators a single account can handle
uint256 private maxDelegators;
/// @notice Minimum amount of delegation allowed
uint256 private minDelegationAmount;
/**
* Lockup duration for a remove delegator request.
* The remove delegator speed bump is to prevent a service provider from maliciously
* removing a delegator prior to the evaluation of a proposal.
* @notice Must be greater than governance votingPeriod + executionDelay
*/
uint256 private removeDelegatorLockupDuration;
/**
* Evaluation period for a remove delegator request
* @notice added to expiry block calculated for removeDelegatorLockupDuration
*/
uint256 private removeDelegatorEvalDuration;
// Staking contract ref
ERC20Mintable private audiusToken;
// Struct representing total delegated to SP and list of delegators
struct ServiceProviderDelegateInfo {
uint256 totalDelegatedStake;
uint256 totalLockedUpStake;
address[] delegators;
}
// Data structures for lockup during withdrawal
struct UndelegateStakeRequest {
address serviceProvider;
uint256 amount;
uint256 lockupExpiryBlock;
}
// Service provider address -> ServiceProviderDelegateInfo
mapping (address => ServiceProviderDelegateInfo) private spDelegateInfo;
// Delegator stake by address delegated to
// delegator -> (service provider -> delegatedStake)
mapping (address => mapping(address => uint256)) private delegateInfo;
// Delegator stake total by address
// delegator -> (totalDelegated)
// Note - delegator properties are maintained in a mapping instead of struct
// in order to facilitate extensibility in the future.
mapping (address => uint256) private delegatorTotalStake;
// Requester to pending undelegate request
mapping (address => UndelegateStakeRequest) private undelegateRequests;
// Pending remove delegator requests
// service provider -> (delegator -> lockupExpiryBlock)
mapping (address => mapping (address => uint256)) private removeDelegatorRequests;
event IncreaseDelegatedStake(
address indexed _delegator,
address indexed _serviceProvider,
uint256 indexed _increaseAmount
);
event UndelegateStakeRequested(
address indexed _delegator,
address indexed _serviceProvider,
uint256 indexed _amount,
uint256 _lockupExpiryBlock
);
event UndelegateStakeRequestCancelled(
address indexed _delegator,
address indexed _serviceProvider,
uint256 indexed _amount
);
event UndelegateStakeRequestEvaluated(
address indexed _delegator,
address indexed _serviceProvider,
uint256 indexed _amount
);
event Claim(
address indexed _claimer,
uint256 indexed _rewards,
uint256 indexed _newTotal
);
event Slash(
address indexed _target,
uint256 indexed _amount,
uint256 indexed _newTotal
);
event RemoveDelegatorRequested(
address indexed _serviceProvider,
address indexed _delegator,
uint256 indexed _lockupExpiryBlock
);
event RemoveDelegatorRequestCancelled(
address indexed _serviceProvider,
address indexed _delegator
);
event RemoveDelegatorRequestEvaluated(
address indexed _serviceProvider,
address indexed _delegator,
uint256 indexed _unstakedAmount
);
event MaxDelegatorsUpdated(uint256 indexed _maxDelegators);
event MinDelegationUpdated(uint256 indexed _minDelegationAmount);
event UndelegateLockupDurationUpdated(uint256 indexed _undelegateLockupDuration);
event GovernanceAddressUpdated(address indexed _newGovernanceAddress);
event StakingAddressUpdated(address indexed _newStakingAddress);
event ServiceProviderFactoryAddressUpdated(address indexed _newServiceProviderFactoryAddress);
event ClaimsManagerAddressUpdated(address indexed _newClaimsManagerAddress);
event RemoveDelegatorLockupDurationUpdated(uint256 indexed _removeDelegatorLockupDuration);
event RemoveDelegatorEvalDurationUpdated(uint256 indexed _removeDelegatorEvalDuration);
// ========================================= New State Variables =========================================
string private constant ERROR_ONLY_SERVICE_PROVIDER = (
"DelegateManager: Only callable by valid Service Provider"
);
// minDelegationAmount per service provider
mapping (address => uint256) private spMinDelegationAmounts;
event SPMinDelegationAmountUpdated(
address indexed _serviceProvider,
uint256 indexed _spMinDelegationAmount
);
// ========================================= Modifier Functions =========================================
/**
* @notice Function to initialize the contract
* @dev stakingAddress must be initialized separately after Staking contract is deployed
* @dev serviceProviderFactoryAddress must be initialized separately after ServiceProviderFactory contract is deployed
* @dev claimsManagerAddress must be initialized separately after ClaimsManager contract is deployed
* @param _tokenAddress - address of ERC20 token that will be claimed
* @param _governanceAddress - Governance proxy address
*/
function initialize (
address _tokenAddress,
address _governanceAddress,
uint256 _undelegateLockupDuration
) public initializer
{
_updateGovernanceAddress(_governanceAddress);
audiusToken = ERC20Mintable(_tokenAddress);
maxDelegators = 175;
// Default minimum delegation amount set to 100AUD
minDelegationAmount = 100 * 10**uint256(18);
InitializableV2.initialize();
_updateUndelegateLockupDuration(_undelegateLockupDuration);
// 1 week = 168hrs * 60 min/hr * 60 sec/min / ~13 sec/block = 46523 blocks
_updateRemoveDelegatorLockupDuration(46523);
// 24hr * 60min/hr * 60sec/min / ~13 sec/block = 6646 blocks
removeDelegatorEvalDuration = 6646;
}
/**
* @notice Allow a delegator to delegate stake to a service provider
* @param _targetSP - address of service provider to delegate to
* @param _amount - amount in wei to delegate
* @return Updated total amount delegated to the service provider by delegator
*/
function delegateStake(
address _targetSP,
uint256 _amount
) external returns (uint256)
{
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceProviderFactoryAddressIsSet();
_requireClaimsManagerAddressIsSet();
require(
!_claimPending(_targetSP),
"DelegateManager: Delegation not permitted for SP pending claim"
);
address delegator = msg.sender;
Staking stakingContract = Staking(stakingAddress);
// Stake on behalf of target service provider
stakingContract.delegateStakeFor(
_targetSP,
delegator,
_amount
);
// Update list of delegators to SP if necessary
if (!_delegatorExistsForSP(delegator, _targetSP)) {
// If not found, update list of delegates
spDelegateInfo[_targetSP].delegators.push(delegator);
require(
spDelegateInfo[_targetSP].delegators.length <= maxDelegators,
"DelegateManager: Maximum delegators exceeded"
);
}
// Update following values in storage through helper
// totalServiceProviderDelegatedStake = current sp total + new amount,
// totalStakedForSpFromDelegator = current delegator total for sp + new amount,
// totalDelegatorStake = current delegator total + new amount
_updateDelegatorStake(
delegator,
_targetSP,
spDelegateInfo[_targetSP].totalDelegatedStake.add(_amount),
delegateInfo[delegator][_targetSP].add(_amount),
delegatorTotalStake[delegator].add(_amount)
);
// Need to ensure delegationAmount is >= both minDelegationAmount and spMinDelegationAmount
// since spMinDelegationAmount by default is 0
require(
(delegateInfo[delegator][_targetSP] >= minDelegationAmount &&
delegateInfo[delegator][_targetSP] >= spMinDelegationAmounts[_targetSP]
),
ERROR_MINIMUM_DELEGATION
);
// Validate balance
ServiceProviderFactory(
serviceProviderFactoryAddress
).validateAccountStakeBalance(_targetSP);
emit IncreaseDelegatedStake(
delegator,
_targetSP,
_amount
);
// Return new total
return delegateInfo[delegator][_targetSP];
}
/**
* @notice Submit request for undelegation
* @param _target - address of service provider to undelegate stake from
* @param _amount - amount in wei to undelegate
* @return Updated total amount delegated to the service provider by delegator
*/
function requestUndelegateStake(
address _target,
uint256 _amount
) external returns (uint256)
{
_requireIsInitialized();
_requireClaimsManagerAddressIsSet();
require(
_amount > 0,
"DelegateManager: Requested undelegate stake amount must be greater than zero"
);
require(
!_claimPending(_target),
"DelegateManager: Undelegate request not permitted for SP pending claim"
);
address delegator = msg.sender;
require(
_delegatorExistsForSP(delegator, _target),
ERROR_DELEGATOR_STAKE
);
// Confirm no pending delegation request
require(
!_undelegateRequestIsPending(delegator),
"DelegateManager: No pending lockup expected"
);
// Ensure valid bounds
uint256 currentlyDelegatedToSP = delegateInfo[delegator][_target];
require(
_amount <= currentlyDelegatedToSP,
"DelegateManager: Cannot decrease greater than currently staked for this ServiceProvider"
);
// Submit updated request for sender, with target sp, undelegate amount, target expiry block
uint256 lockupExpiryBlock = block.number.add(undelegateLockupDuration);
_updateUndelegateStakeRequest(
delegator,
_target,
_amount,
lockupExpiryBlock
);
// Update total locked for this service provider, increasing by unstake amount
_updateServiceProviderLockupAmount(
_target,
spDelegateInfo[_target].totalLockedUpStake.add(_amount)
);
emit UndelegateStakeRequested(delegator, _target, _amount, lockupExpiryBlock);
return delegateInfo[delegator][_target].sub(_amount);
}
/**
* @notice Cancel undelegation request
*/
function cancelUndelegateStakeRequest() external {
_requireIsInitialized();
address delegator = msg.sender;
// Confirm pending delegation request
require(
_undelegateRequestIsPending(delegator),
"DelegateManager: Pending lockup expected"
);
uint256 unstakeAmount = undelegateRequests[delegator].amount;
address unlockFundsSP = undelegateRequests[delegator].serviceProvider;
// Update total locked for this service provider, decreasing by unstake amount
_updateServiceProviderLockupAmount(
unlockFundsSP,
spDelegateInfo[unlockFundsSP].totalLockedUpStake.sub(unstakeAmount)
);
// Remove pending request
_resetUndelegateStakeRequest(delegator);
emit UndelegateStakeRequestCancelled(delegator, unlockFundsSP, unstakeAmount);
}
/**
* @notice Finalize undelegation request and withdraw stake
* @return New total amount currently staked after stake has been undelegated
*/
function undelegateStake() external returns (uint256) {
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceProviderFactoryAddressIsSet();
_requireClaimsManagerAddressIsSet();
address delegator = msg.sender;
// Confirm pending delegation request
require(
_undelegateRequestIsPending(delegator),
"DelegateManager: Pending lockup expected"
);
// Confirm lockup expiry has expired
require(
undelegateRequests[delegator].lockupExpiryBlock <= block.number,
"DelegateManager: Lockup must be expired"
);
// Confirm no pending claim for this service provider
require(
!_claimPending(undelegateRequests[delegator].serviceProvider),
"DelegateManager: Undelegate not permitted for SP pending claim"
);
address serviceProvider = undelegateRequests[delegator].serviceProvider;
uint256 unstakeAmount = undelegateRequests[delegator].amount;
// Unstake on behalf of target service provider
Staking(stakingAddress).undelegateStakeFor(
serviceProvider,
delegator,
unstakeAmount
);
// Update total delegated for SP
// totalServiceProviderDelegatedStake - total amount delegated to service provider
// totalStakedForSpFromDelegator - amount staked from this delegator to targeted service provider
_updateDelegatorStake(
delegator,
serviceProvider,
spDelegateInfo[serviceProvider].totalDelegatedStake.sub(unstakeAmount),
delegateInfo[delegator][serviceProvider].sub(unstakeAmount),
delegatorTotalStake[delegator].sub(unstakeAmount)
);
// Need to ensure delegationAmount is >= both minDelegationAmount and spMinDelegationAmount
// since spMinDelegationAmount by default is 0
// Only exception is when delegating entire stake down to 0
require(
(
delegateInfo[delegator][serviceProvider] >= minDelegationAmount &&
delegateInfo[delegator][serviceProvider] >= spMinDelegationAmounts[serviceProvider]
) || delegateInfo[delegator][serviceProvider] == 0,
ERROR_MINIMUM_DELEGATION
);
// Remove from delegators list if no delegated stake remaining
if (delegateInfo[delegator][serviceProvider] == 0) {
_removeFromDelegatorsList(serviceProvider, delegator);
}
// Update total locked for this service provider, decreasing by unstake amount
_updateServiceProviderLockupAmount(
serviceProvider,
spDelegateInfo[serviceProvider].totalLockedUpStake.sub(unstakeAmount)
);
// Reset undelegate request
_resetUndelegateStakeRequest(delegator);
emit UndelegateStakeRequestEvaluated(
delegator,
serviceProvider,
unstakeAmount
);
// Need to update service provider's `validBounds` flag
// Only way to do this is through `SPFactory.updateServiceProviderStake()`
// So we call it with the existing `spDeployerStake`
(uint256 spDeployerStake,,,,,) = (
ServiceProviderFactory(serviceProviderFactoryAddress).getServiceProviderDetails(serviceProvider)
);
ServiceProviderFactory(serviceProviderFactoryAddress).updateServiceProviderStake(
serviceProvider, spDeployerStake
);
// Return new total
return delegateInfo[delegator][serviceProvider];
}
/**
* @notice Claim and distribute rewards to delegators and service provider as necessary
* @param _serviceProvider - Provider for which rewards are being distributed
* @dev Factors in service provider rewards from delegator and transfers deployer cut
*/
function claimRewards(address _serviceProvider) external {
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceProviderFactoryAddressIsSet();
_requireClaimsManagerAddressIsSet();
ServiceProviderFactory spFactory = ServiceProviderFactory(serviceProviderFactoryAddress);
// Total rewards = (balance in staking) - ((balance in sp factory) + (balance in delegate manager))
(
uint256 totalBalanceInStaking,
uint256 totalBalanceInSPFactory,
uint256 totalActiveFunds,
uint256 totalRewards,
uint256 deployerCut
) = _validateClaimRewards(spFactory, _serviceProvider);
// No-op if balance is already equivalent
// This case can occur if no rewards due to bound violation or all stake is locked
if (totalRewards == 0) {
return;
}
uint256 totalDelegatedStakeIncrease = _distributeDelegateRewards(
_serviceProvider,
totalActiveFunds,
totalRewards,
deployerCut,
spFactory.getServiceProviderDeployerCutBase()
);
// Update total delegated to this SP
spDelegateInfo[_serviceProvider].totalDelegatedStake = (
spDelegateInfo[_serviceProvider].totalDelegatedStake.add(totalDelegatedStakeIncrease)
);
// spRewardShare represents rewards directly allocated to service provider for their stake
// Value is computed as the remainder of total minted rewards after distribution to
// delegators, eliminating any potential for precision loss.
uint256 spRewardShare = totalRewards.sub(totalDelegatedStakeIncrease);
// Adding the newly calculated reward share to current balance
uint256 newSPFactoryBalance = totalBalanceInSPFactory.add(spRewardShare);
require(
totalBalanceInStaking == newSPFactoryBalance.add(spDelegateInfo[_serviceProvider].totalDelegatedStake),
"DelegateManager: claimRewards amount mismatch"
);
spFactory.updateServiceProviderStake(
_serviceProvider,
newSPFactoryBalance
);
}
/**
* @notice Reduce current stake amount
* @dev Only callable by governance. Slashes service provider and delegators equally
* @param _amount - amount in wei to slash
* @param _slashAddress - address of service provider to slash
*/
function slash(uint256 _amount, address _slashAddress)
external
{
_requireIsInitialized();
_requireStakingAddressIsSet();
_requireServiceProviderFactoryAddressIsSet();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
Staking stakingContract = Staking(stakingAddress);
ServiceProviderFactory spFactory = ServiceProviderFactory(serviceProviderFactoryAddress);
// Amount stored in staking contract for owner
uint256 totalBalanceInStakingPreSlash = stakingContract.totalStakedFor(_slashAddress);
require(
(totalBalanceInStakingPreSlash >= _amount),
"DelegateManager: Cannot slash more than total currently staked"
);
// Cancel any withdrawal request for this service provider
(uint256 spLockedStake,) = spFactory.getPendingDecreaseStakeRequest(_slashAddress);
if (spLockedStake > 0) {
spFactory.cancelDecreaseStakeRequest(_slashAddress);
}
// Amount in sp factory for slash target
(uint256 totalBalanceInSPFactory,,,,,) = (
spFactory.getServiceProviderDetails(_slashAddress)
);
require(
totalBalanceInSPFactory > 0,
"DelegateManager: Service Provider stake required"
);
// Decrease value in Staking contract
// A value of zero slash will fail in staking, reverting this transaction
stakingContract.slash(_amount, _slashAddress);
uint256 totalBalanceInStakingAfterSlash = stakingContract.totalStakedFor(_slashAddress);
// Emit slash event
emit Slash(_slashAddress, _amount, totalBalanceInStakingAfterSlash);
uint256 totalDelegatedStakeDecrease = 0;
// For each delegator and deployer, recalculate new value
// newStakeAmount = newStakeAmount * (oldStakeAmount / totalBalancePreSlash)
for (uint256 i = 0; i < spDelegateInfo[_slashAddress].delegators.length; i++) {
address delegator = spDelegateInfo[_slashAddress].delegators[i];
uint256 preSlashDelegateStake = delegateInfo[delegator][_slashAddress];
uint256 newDelegateStake = (
totalBalanceInStakingAfterSlash.mul(preSlashDelegateStake)
).div(totalBalanceInStakingPreSlash);
// slashAmountForDelegator = preSlashDelegateStake - newDelegateStake;
delegateInfo[delegator][_slashAddress] = (
delegateInfo[delegator][_slashAddress].sub(preSlashDelegateStake.sub(newDelegateStake))
);
// Update total stake for delegator
_updateDelegatorTotalStake(
delegator,
delegatorTotalStake[delegator].sub(preSlashDelegateStake.sub(newDelegateStake))
);
// Update total decrease amount
totalDelegatedStakeDecrease = (
totalDelegatedStakeDecrease.add(preSlashDelegateStake.sub(newDelegateStake))
);
// Check for any locked up funds for this slashed delegator
// Slash overrides any pending withdrawal requests
if (undelegateRequests[delegator].amount != 0) {
address unstakeSP = undelegateRequests[delegator].serviceProvider;
uint256 unstakeAmount = undelegateRequests[delegator].amount;
// Remove pending request
_updateServiceProviderLockupAmount(
unstakeSP,
spDelegateInfo[unstakeSP].totalLockedUpStake.sub(unstakeAmount)
);
_resetUndelegateStakeRequest(delegator);
}
}
// Update total delegated to this SP
spDelegateInfo[_slashAddress].totalDelegatedStake = (
spDelegateInfo[_slashAddress].totalDelegatedStake.sub(totalDelegatedStakeDecrease)
);
// Remaining decrease applied to service provider
uint256 totalStakeDecrease = (
totalBalanceInStakingPreSlash.sub(totalBalanceInStakingAfterSlash)
);
uint256 totalSPFactoryBalanceDecrease = (
totalStakeDecrease.sub(totalDelegatedStakeDecrease)
);
spFactory.updateServiceProviderStake(
_slashAddress,
totalBalanceInSPFactory.sub(totalSPFactoryBalanceDecrease)
);
}
/**
* @notice Initiate forcible removal of a delegator
* @param _serviceProvider - address of service provider
* @param _delegator - address of delegator
*/
function requestRemoveDelegator(address _serviceProvider, address _delegator) external {
_requireIsInitialized();
require(
msg.sender == _serviceProvider || msg.sender == governanceAddress,
ERROR_ONLY_SP_GOVERNANCE
);
require(
removeDelegatorRequests[_serviceProvider][_delegator] == 0,
"DelegateManager: Pending remove delegator request"
);
require(
_delegatorExistsForSP(_delegator, _serviceProvider),
ERROR_DELEGATOR_STAKE
);
// Update lockup
removeDelegatorRequests[_serviceProvider][_delegator] = (
block.number + removeDelegatorLockupDuration
);
emit RemoveDelegatorRequested(
_serviceProvider,
_delegator,
removeDelegatorRequests[_serviceProvider][_delegator]
);
}
/**
* @notice Cancel pending removeDelegator request
* @param _serviceProvider - address of service provider
* @param _delegator - address of delegator
*/
function cancelRemoveDelegatorRequest(address _serviceProvider, address _delegator) external {
require(
msg.sender == _serviceProvider || msg.sender == governanceAddress,
ERROR_ONLY_SP_GOVERNANCE
);
require(
removeDelegatorRequests[_serviceProvider][_delegator] != 0,
"DelegateManager: No pending request"
);
// Reset lockup expiry
removeDelegatorRequests[_serviceProvider][_delegator] = 0;
emit RemoveDelegatorRequestCancelled(_serviceProvider, _delegator);
}
/**
* @notice Evaluate removeDelegator request
* @param _serviceProvider - address of service provider
* @param _delegator - address of delegator
* @return Updated total amount delegated to the service provider by delegator
*/
function removeDelegator(address _serviceProvider, address _delegator) external {
_requireIsInitialized();
_requireStakingAddressIsSet();
require(
msg.sender == _serviceProvider || msg.sender == governanceAddress,
ERROR_ONLY_SP_GOVERNANCE
);
require(
removeDelegatorRequests[_serviceProvider][_delegator] != 0,
"DelegateManager: No pending request"
);
// Enforce lockup expiry block
require(
block.number >= removeDelegatorRequests[_serviceProvider][_delegator],
"DelegateManager: Lockup must be expired"
);
// Enforce evaluation window for request
require(
block.number < removeDelegatorRequests[_serviceProvider][_delegator] + removeDelegatorEvalDuration,
"DelegateManager: RemoveDelegator evaluation window expired"
);
uint256 unstakeAmount = delegateInfo[_delegator][_serviceProvider];
// Unstake on behalf of target service provider
Staking(stakingAddress).undelegateStakeFor(
_serviceProvider,
_delegator,
unstakeAmount
);
// Update total delegated for SP
// totalServiceProviderDelegatedStake - total amount delegated to service provider
// totalStakedForSpFromDelegator - amount staked from this delegator to targeted service provider
_updateDelegatorStake(
_delegator,
_serviceProvider,
spDelegateInfo[_serviceProvider].totalDelegatedStake.sub(unstakeAmount),
delegateInfo[_delegator][_serviceProvider].sub(unstakeAmount),
delegatorTotalStake[_delegator].sub(unstakeAmount)
);
if (
_undelegateRequestIsPending(_delegator) &&
undelegateRequests[_delegator].serviceProvider == _serviceProvider
) {
// Remove pending request information
_updateServiceProviderLockupAmount(
_serviceProvider,
spDelegateInfo[_serviceProvider].totalLockedUpStake.sub(undelegateRequests[_delegator].amount)
);
_resetUndelegateStakeRequest(_delegator);
}
// Remove from list of delegators
_removeFromDelegatorsList(_serviceProvider, _delegator);
// Reset lockup expiry
removeDelegatorRequests[_serviceProvider][_delegator] = 0;
emit RemoveDelegatorRequestEvaluated(_serviceProvider, _delegator, unstakeAmount);
}
/**
* @notice SP can update their minDelegationAmount
* @param _serviceProvider - address of service provider
* @param _spMinDelegationAmount - new minDelegationAmount for SP
* @notice does not enforce _spMinDelegationAmount >= minDelegationAmount since not necessary
* delegateStake() and undelegateStake() always take the max of both already
*/
function updateSPMinDelegationAmount(
address _serviceProvider,
uint256 _spMinDelegationAmount
) external {
_requireIsInitialized();
require(msg.sender == _serviceProvider, ERROR_ONLY_SERVICE_PROVIDER);
/**
* Ensure _serviceProvider is a valid SP
* No objective source of truth, closest heuristic is numEndpoints > 0
*/
(,,, uint256 numEndpoints,,) = (
ServiceProviderFactory(serviceProviderFactoryAddress)
.getServiceProviderDetails(_serviceProvider)
);
require(numEndpoints > 0, ERROR_ONLY_SERVICE_PROVIDER);
spMinDelegationAmounts[_serviceProvider] = _spMinDelegationAmount;
emit SPMinDelegationAmountUpdated(_serviceProvider, _spMinDelegationAmount);
}
/**
* @notice Update duration for undelegate request lockup
* @param _duration - new lockup duration
*/
function updateUndelegateLockupDuration(uint256 _duration) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
_updateUndelegateLockupDuration(_duration);
emit UndelegateLockupDurationUpdated(_duration);
}
/**
* @notice Update maximum delegators allowed
* @param _maxDelegators - new max delegators
*/
function updateMaxDelegators(uint256 _maxDelegators) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
maxDelegators = _maxDelegators;
emit MaxDelegatorsUpdated(_maxDelegators);
}
/**
* @notice Update minimum delegation amount
* @param _minDelegationAmount - min new min delegation amount
*/
function updateMinDelegationAmount(uint256 _minDelegationAmount) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
minDelegationAmount = _minDelegationAmount;
emit MinDelegationUpdated(_minDelegationAmount);
}
/**
* @notice Update remove delegator lockup duration
* @param _duration - new lockup duration
*/
function updateRemoveDelegatorLockupDuration(uint256 _duration) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
_updateRemoveDelegatorLockupDuration(_duration);
emit RemoveDelegatorLockupDurationUpdated(_duration);
}
/**
* @notice Update remove delegator evaluation window duration
* @param _duration - new window duration
*/
function updateRemoveDelegatorEvalDuration(uint256 _duration) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
removeDelegatorEvalDuration = _duration;
emit RemoveDelegatorEvalDurationUpdated(_duration);
}
/**
* @notice Set the Governance address
* @dev Only callable by Governance address
* @param _governanceAddress - address for new Governance contract
*/
function setGovernanceAddress(address _governanceAddress) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
_updateGovernanceAddress(_governanceAddress);
governanceAddress = _governanceAddress;
emit GovernanceAddressUpdated(_governanceAddress);
}
/**
* @notice Set the Staking address
* @dev Only callable by Governance address
* @param _stakingAddress - address for new Staking contract
*/
function setStakingAddress(address _stakingAddress) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
stakingAddress = _stakingAddress;
emit StakingAddressUpdated(_stakingAddress);
}
/**
* @notice Set the ServiceProviderFactory address
* @dev Only callable by Governance address
* @param _spFactory - address for new ServiceProviderFactory contract
*/
function setServiceProviderFactoryAddress(address _spFactory) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
serviceProviderFactoryAddress = _spFactory;
emit ServiceProviderFactoryAddressUpdated(_spFactory);
}
/**
* @notice Set the ClaimsManager address
* @dev Only callable by Governance address
* @param _claimsManagerAddress - address for new ClaimsManager contract
*/
function setClaimsManagerAddress(address _claimsManagerAddress) external {
_requireIsInitialized();
require(msg.sender == governanceAddress, ERROR_ONLY_GOVERNANCE);
claimsManagerAddress = _claimsManagerAddress;
emit ClaimsManagerAddressUpdated(_claimsManagerAddress);
}
// ========================================= View Functions =========================================
/**
* @notice Get list of delegators for a given service provider
* @param _sp - service provider address
*/
function getDelegatorsList(address _sp)
external view returns (address[] memory)
{
_requireIsInitialized();
return spDelegateInfo[_sp].delegators;
}
/**
* @notice Get total delegation from a given address
* @param _delegator - delegator address
*/
function getTotalDelegatorStake(address _delegator)
external view returns (uint256)
{
_requireIsInitialized();
return delegatorTotalStake[_delegator];
}
/// @notice Get total amount delegated to a service provider
function getTotalDelegatedToServiceProvider(address _sp)
external view returns (uint256)
{
_requireIsInitialized();
return spDelegateInfo[_sp].totalDelegatedStake;
}
/// @notice Get total delegated stake locked up for a service provider
function getTotalLockedDelegationForServiceProvider(address _sp)
external view returns (uint256)
{
_requireIsInitialized();
return spDelegateInfo[_sp].totalLockedUpStake;
}
/// @notice Get total currently staked for a delegator, for a given service provider
function getDelegatorStakeForServiceProvider(address _delegator, address _serviceProvider)
external view returns (uint256)
{
_requireIsInitialized();
return delegateInfo[_delegator][_serviceProvider];
}
/**
* @notice Get status of pending undelegate request for a given address
* @param _delegator - address of the delegator
*/
function getPendingUndelegateRequest(address _delegator)
external view returns (address target, uint256 amount, uint256 lockupExpiryBlock)
{
_requireIsInitialized();
UndelegateStakeRequest memory req = undelegateRequests[_delegator];
return (req.serviceProvider, req.amount, req.lockupExpiryBlock);
}
/**
* @notice Get status of pending remove delegator request for a given address
* @param _serviceProvider - address of the service provider
* @param _delegator - address of the delegator
* @return - current lockup expiry block for remove delegator request
*/
function getPendingRemoveDelegatorRequest(
address _serviceProvider,
address _delegator
) external view returns (uint256)
{
_requireIsInitialized();
return removeDelegatorRequests[_serviceProvider][_delegator];
}
/**
* @notice Get minDelegationAmount for given SP
* @param _serviceProvider - address of the service provider
* @return - minDelegationAmount for given SP
*/
function getSPMinDelegationAmount(address _serviceProvider) external view returns (uint256) {
return spMinDelegationAmounts[_serviceProvider];
}
/// @notice Get current undelegate lockup duration
function getUndelegateLockupDuration()
external view returns (uint256)
{
_requireIsInitialized();
return undelegateLockupDuration;
}
/// @notice Current maximum delegators
function getMaxDelegators()
external view returns (uint256)
{
_requireIsInitialized();
return maxDelegators;
}
/// @notice Get minimum delegation amount
function getMinDelegationAmount()
external view returns (uint256)
{
_requireIsInitialized();
return minDelegationAmount;
}
/// @notice Get the duration for remove delegator request lockup
function getRemoveDelegatorLockupDuration()
external view returns (uint256)
{
_requireIsInitialized();
return removeDelegatorLockupDuration;
}
/// @notice Get the duration for evaluation of remove delegator operations
function getRemoveDelegatorEvalDuration()
external view returns (uint256)
{
_requireIsInitialized();
return removeDelegatorEvalDuration;
}
/// @notice Get the Governance address
function getGovernanceAddress() external view returns (address) {
_requireIsInitialized();
return governanceAddress;
}
/// @notice Get the ServiceProviderFactory address
function getServiceProviderFactoryAddress() external view returns (address) {
_requireIsInitialized();
return serviceProviderFactoryAddress;
}
/// @notice Get the ClaimsManager address
function getClaimsManagerAddress() external view returns (address) {
_requireIsInitialized();
return claimsManagerAddress;
}
/// @notice Get the Staking address
function getStakingAddress() external view returns (address)
{
_requireIsInitialized();
return stakingAddress;
}
// ========================================= Internal functions =========================================
/**
* @notice Helper function for claimRewards to get balances from Staking contract
and do validation
* @param spFactory - reference to ServiceProviderFactory contract
* @param _serviceProvider - address for which rewards are being claimed
* @return (totalBalanceInStaking, totalBalanceInSPFactory, totalActiveFunds, spLockedStake, totalRewards, deployerCut)
*/
function _validateClaimRewards(ServiceProviderFactory spFactory, address _serviceProvider)
internal returns (
uint256 totalBalanceInStaking,
uint256 totalBalanceInSPFactory,
uint256 totalActiveFunds,
uint256 totalRewards,
uint256 deployerCut
)
{
// Account for any pending locked up stake for the service provider
(uint256 spLockedStake,) = spFactory.getPendingDecreaseStakeRequest(_serviceProvider);
uint256 totalLockedUpStake = (
spDelegateInfo[_serviceProvider].totalLockedUpStake.add(spLockedStake)
);
// Process claim for msg.sender
// Total locked parameter is equal to delegate locked up stake + service provider locked up stake
uint256 mintedRewards = ClaimsManager(claimsManagerAddress).processClaim(
_serviceProvider,
totalLockedUpStake
);
// Amount stored in staking contract for owner
totalBalanceInStaking = Staking(stakingAddress).totalStakedFor(_serviceProvider);
// Amount in sp factory for claimer
(
totalBalanceInSPFactory,
deployerCut,
,,,
) = spFactory.getServiceProviderDetails(_serviceProvider);
// Require active stake to claim any rewards
// Amount in delegate manager staked to service provider
uint256 totalBalanceOutsideStaking = (
totalBalanceInSPFactory.add(spDelegateInfo[_serviceProvider].totalDelegatedStake)
);
totalActiveFunds = totalBalanceOutsideStaking.sub(totalLockedUpStake);
require(
mintedRewards == totalBalanceInStaking.sub(totalBalanceOutsideStaking),
"DelegateManager: Reward amount mismatch"
);
// Emit claim event
emit Claim(_serviceProvider, totalRewards, totalBalanceInStaking);
return (
totalBalanceInStaking,
totalBalanceInSPFactory,
totalActiveFunds,
mintedRewards,
deployerCut
);
}
/**
* @notice Perform state updates when a delegate stake has changed
* @param _delegator - address of delegator
* @param _serviceProvider - address of service provider
* @param _totalServiceProviderDelegatedStake - total delegated to this service provider
* @param _totalStakedForSpFromDelegator - total delegated to this service provider by delegator
* @param _totalDelegatorStake - total delegated from this delegator address
*/
function _updateDelegatorStake(
address _delegator,
address _serviceProvider,
uint256 _totalServiceProviderDelegatedStake,
uint256 _totalStakedForSpFromDelegator,
uint256 _totalDelegatorStake
) internal
{
// Update total delegated for SP
spDelegateInfo[_serviceProvider].totalDelegatedStake = _totalServiceProviderDelegatedStake;
// Update amount staked from this delegator to targeted service provider
delegateInfo[_delegator][_serviceProvider] = _totalStakedForSpFromDelegator;
// Update total delegated from this delegator
_updateDelegatorTotalStake(_delegator, _totalDelegatorStake);
}
/**
* @notice Reset pending undelegate stake request
* @param _delegator - address of delegator
*/
function _resetUndelegateStakeRequest(address _delegator) internal
{
_updateUndelegateStakeRequest(_delegator, address(0), 0, 0);
}
/**
* @notice Perform updates when undelegate request state has changed
* @param _delegator - address of delegator
* @param _serviceProvider - address of service provider
* @param _amount - amount being undelegated
* @param _lockupExpiryBlock - block at which stake can be undelegated
*/
function _updateUndelegateStakeRequest(
address _delegator,
address _serviceProvider,
uint256 _amount,
uint256 _lockupExpiryBlock
) internal
{
// Update lockup information
undelegateRequests[_delegator] = UndelegateStakeRequest({
lockupExpiryBlock: _lockupExpiryBlock,
amount: _amount,
serviceProvider: _serviceProvider
});
}
/**
* @notice Update total amount delegated from an address
* @param _delegator - address of service provider
* @param _amount - updated delegator total
*/
function _updateDelegatorTotalStake(address _delegator, uint256 _amount) internal
{
delegatorTotalStake[_delegator] = _amount;
}
/**
* @notice Update amount currently locked up for this service provider
* @param _serviceProvider - address of service provider
* @param _updatedLockupAmount - updated lock up amount
*/
function _updateServiceProviderLockupAmount(
address _serviceProvider,
uint256 _updatedLockupAmount
) internal
{
spDelegateInfo[_serviceProvider].totalLockedUpStake = _updatedLockupAmount;
}
function _removeFromDelegatorsList(address _serviceProvider, address _delegator) internal
{
for (uint256 i = 0; i < spDelegateInfo[_serviceProvider].delegators.length; i++) {
if (spDelegateInfo[_serviceProvider].delegators[i] == _delegator) {
// Overwrite and shrink delegators list
spDelegateInfo[_serviceProvider].delegators[i] = spDelegateInfo[_serviceProvider].delegators[spDelegateInfo[_serviceProvider].delegators.length - 1];
spDelegateInfo[_serviceProvider].delegators.length--;
break;
}
}
}
/**
* @notice Helper function to distribute rewards to any delegators
* @param _sp - service provider account tracked in staking
* @param _totalActiveFunds - total funds minus any locked stake
* @param _totalRewards - total rewaards generated in this round
* @param _deployerCut - service provider cut of delegate rewards, defined as deployerCut / deployerCutBase
* @param _deployerCutBase - denominator value for calculating service provider cut as a %
* @return (totalBalanceInStaking, totalBalanceInSPFactory, totalBalanceOutsideStaking)
*/
function _distributeDelegateRewards(
address _sp,
uint256 _totalActiveFunds,
uint256 _totalRewards,
uint256 _deployerCut,
uint256 _deployerCutBase
)
internal returns (uint256 totalDelegatedStakeIncrease)
{
// Traverse all delegates and calculate their rewards
// As each delegate reward is calculated, increment SP cut reward accordingly
for (uint256 i = 0; i < spDelegateInfo[_sp].delegators.length; i++) {
address delegator = spDelegateInfo[_sp].delegators[i];
uint256 delegateStakeToSP = delegateInfo[delegator][_sp];
// Subtract any locked up stake
if (undelegateRequests[delegator].serviceProvider == _sp) {
delegateStakeToSP = delegateStakeToSP.sub(undelegateRequests[delegator].amount);
}
// Calculate rewards by ((delegateStakeToSP / totalActiveFunds) * totalRewards)
uint256 rewardsPriorToSPCut = (
delegateStakeToSP.mul(_totalRewards)
).div(_totalActiveFunds);
// Multiply by deployer cut fraction to calculate reward for SP
// Operation constructed to perform all multiplication prior to division
// uint256 spDeployerCut = (rewardsPriorToSPCut * deployerCut ) / (deployerCutBase);
// = ((delegateStakeToSP * totalRewards) / totalActiveFunds) * deployerCut ) / (deployerCutBase);
// = ((delegateStakeToSP * totalRewards * deployerCut) / totalActiveFunds ) / (deployerCutBase);
// = (delegateStakeToSP * totalRewards * deployerCut) / (deployerCutBase * totalActiveFunds);
uint256 spDeployerCut = (
(delegateStakeToSP.mul(_totalRewards)).mul(_deployerCut)
).div(
_totalActiveFunds.mul(_deployerCutBase)
);
// Increase total delegate reward in DelegateManager
// Subtract SP reward from rewards to calculate delegate reward
// delegateReward = rewardsPriorToSPCut - spDeployerCut;
delegateInfo[delegator][_sp] = (
delegateInfo[delegator][_sp].add(rewardsPriorToSPCut.sub(spDeployerCut))
);
// Update total for this delegator
_updateDelegatorTotalStake(
delegator,
delegatorTotalStake[delegator].add(rewardsPriorToSPCut.sub(spDeployerCut))
);
totalDelegatedStakeIncrease = (
totalDelegatedStakeIncrease.add(rewardsPriorToSPCut.sub(spDeployerCut))
);
}
return (totalDelegatedStakeIncrease);
}
/**
* @notice Set the governance address after confirming contract identity
* @param _governanceAddress - Incoming governance address
*/
function _updateGovernanceAddress(address _governanceAddress) internal {
require(
Governance(_governanceAddress).isGovernanceAddress() == true,
"DelegateManager: _governanceAddress is not a valid governance contract"
);
governanceAddress = _governanceAddress;
}
/**
* @notice Set the remove delegator lockup duration after validating against governance
* @param _duration - Incoming remove delegator duration value
*/
function _updateRemoveDelegatorLockupDuration(uint256 _duration) internal {
Governance governance = Governance(governanceAddress);
require(
_duration > governance.getVotingPeriod() + governance.getExecutionDelay(),
"DelegateManager: removeDelegatorLockupDuration duration must be greater than governance votingPeriod + executionDelay"
);
removeDelegatorLockupDuration = _duration;
}
/**
* @notice Set the undelegate lockup duration after validating against governance
* @param _duration - Incoming undelegate lockup duration value
*/
function _updateUndelegateLockupDuration(uint256 _duration) internal {
Governance governance = Governance(governanceAddress);
require(
_duration > governance.getVotingPeriod() + governance.getExecutionDelay(),
"DelegateManager: undelegateLockupDuration duration must be greater than governance votingPeriod + executionDelay"
);
undelegateLockupDuration = _duration;
}
/**
* @notice Returns if delegator has delegated to a service provider
* @param _delegator - address of delegator
* @param _serviceProvider - address of service provider
* @return boolean indicating whether delegator exists for service provider
*/
function _delegatorExistsForSP(
address _delegator,
address _serviceProvider
) internal view returns (bool)
{
for (uint256 i = 0; i < spDelegateInfo[_serviceProvider].delegators.length; i++) {
if (spDelegateInfo[_serviceProvider].delegators[i] == _delegator) {
return true;
}
}
// Not found
return false;
}
/**
* @notice Determine if a claim is pending for this service provider
* @param _sp - address of service provider
* @return boolean indicating whether a claim is pending
*/
function _claimPending(address _sp) internal view returns (bool) {
ClaimsManager claimsManager = ClaimsManager(claimsManagerAddress);
return claimsManager.claimPending(_sp);
}
/**
* @notice Determine if a decrease request has been initiated
* @param _delegator - address of delegator
* @return boolean indicating whether a decrease request is pending
*/
function _undelegateRequestIsPending(address _delegator) internal view returns (bool)
{
return (
(undelegateRequests[_delegator].lockupExpiryBlock != 0) &&
(undelegateRequests[_delegator].amount != 0) &&
(undelegateRequests[_delegator].serviceProvider != address(0))
);
}
// ========================================= Private Functions =========================================
function _requireStakingAddressIsSet() private view {
require(
stakingAddress != address(0x00),
"DelegateManager: stakingAddress is not set"
);
}
function _requireServiceProviderFactoryAddressIsSet() private view {
require(
serviceProviderFactoryAddress != address(0x00),
"DelegateManager: serviceProviderFactoryAddress is not set"
);
}
function _requireClaimsManagerAddressIsSet() private view {
require(
claimsManagerAddress != address(0x00),
"DelegateManager: claimsManagerAddress is not set"
);
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_claimer","type":"address"},{"indexed":true,"internalType":"uint256","name":"_rewards","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_newTotal","type":"uint256"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_newClaimsManagerAddress","type":"address"}],"name":"ClaimsManagerAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_newGovernanceAddress","type":"address"}],"name":"GovernanceAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_delegator","type":"address"},{"indexed":true,"internalType":"address","name":"_serviceProvider","type":"address"},{"indexed":true,"internalType":"uint256","name":"_increaseAmount","type":"uint256"}],"name":"IncreaseDelegatedStake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_maxDelegators","type":"uint256"}],"name":"MaxDelegatorsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_minDelegationAmount","type":"uint256"}],"name":"MinDelegationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_removeDelegatorEvalDuration","type":"uint256"}],"name":"RemoveDelegatorEvalDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_removeDelegatorLockupDuration","type":"uint256"}],"name":"RemoveDelegatorLockupDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_serviceProvider","type":"address"},{"indexed":true,"internalType":"address","name":"_delegator","type":"address"}],"name":"RemoveDelegatorRequestCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_serviceProvider","type":"address"},{"indexed":true,"internalType":"address","name":"_delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"_unstakedAmount","type":"uint256"}],"name":"RemoveDelegatorRequestEvaluated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_serviceProvider","type":"address"},{"indexed":true,"internalType":"address","name":"_delegator","type":"address"},{"indexed":true,"internalType":"uint256","name":"_lockupExpiryBlock","type":"uint256"}],"name":"RemoveDelegatorRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_serviceProvider","type":"address"},{"indexed":true,"internalType":"uint256","name":"_spMinDelegationAmount","type":"uint256"}],"name":"SPMinDelegationAmountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_newServiceProviderFactoryAddress","type":"address"}],"name":"ServiceProviderFactoryAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_target","type":"address"},{"indexed":true,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_newTotal","type":"uint256"}],"name":"Slash","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_newStakingAddress","type":"address"}],"name":"StakingAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_undelegateLockupDuration","type":"uint256"}],"name":"UndelegateLockupDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_delegator","type":"address"},{"indexed":true,"internalType":"address","name":"_serviceProvider","type":"address"},{"indexed":true,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"UndelegateStakeRequestCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_delegator","type":"address"},{"indexed":true,"internalType":"address","name":"_serviceProvider","type":"address"},{"indexed":true,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"UndelegateStakeRequestEvaluated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_delegator","type":"address"},{"indexed":true,"internalType":"address","name":"_serviceProvider","type":"address"},{"indexed":true,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_lockupExpiryBlock","type":"uint256"}],"name":"UndelegateStakeRequested","type":"event"},{"constant":false,"inputs":[{"internalType":"address","name":"_serviceProvider","type":"address"},{"internalType":"address","name":"_delegator","type":"address"}],"name":"cancelRemoveDelegatorRequest","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"cancelUndelegateStakeRequest","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_serviceProvider","type":"address"}],"name":"claimRewards","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_targetSP","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"delegateStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getClaimsManagerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_delegator","type":"address"},{"internalType":"address","name":"_serviceProvider","type":"address"}],"name":"getDelegatorStakeForServiceProvider","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_sp","type":"address"}],"name":"getDelegatorsList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getGovernanceAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getMaxDelegators","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getMinDelegationAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_serviceProvider","type":"address"},{"internalType":"address","name":"_delegator","type":"address"}],"name":"getPendingRemoveDelegatorRequest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_delegator","type":"address"}],"name":"getPendingUndelegateRequest","outputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"lockupExpiryBlock","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getRemoveDelegatorEvalDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getRemoveDelegatorLockupDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_serviceProvider","type":"address"}],"name":"getSPMinDelegationAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getServiceProviderFactoryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getStakingAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_sp","type":"address"}],"name":"getTotalDelegatedToServiceProvider","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_delegator","type":"address"}],"name":"getTotalDelegatorStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"_sp","type":"address"}],"name":"getTotalLockedDelegationForServiceProvider","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getUndelegateLockupDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"address","name":"_governanceAddress","type":"address"},{"internalType":"uint256","name":"_undelegateLockupDuration","type":"uint256"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_serviceProvider","type":"address"},{"internalType":"address","name":"_delegator","type":"address"}],"name":"removeDelegator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_serviceProvider","type":"address"},{"internalType":"address","name":"_delegator","type":"address"}],"name":"requestRemoveDelegator","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_target","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"requestUndelegateStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_claimsManagerAddress","type":"address"}],"name":"setClaimsManagerAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_governanceAddress","type":"address"}],"name":"setGovernanceAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_spFactory","type":"address"}],"name":"setServiceProviderFactoryAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_stakingAddress","type":"address"}],"name":"setStakingAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_slashAddress","type":"address"}],"name":"slash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"undelegateStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_maxDelegators","type":"uint256"}],"name":"updateMaxDelegators","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_minDelegationAmount","type":"uint256"}],"name":"updateMinDelegationAmount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_duration","type":"uint256"}],"name":"updateRemoveDelegatorEvalDuration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_duration","type":"uint256"}],"name":"updateRemoveDelegatorLockupDuration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_serviceProvider","type":"address"},{"internalType":"uint256","name":"_spMinDelegationAmount","type":"uint256"}],"name":"updateSPMinDelegationAmount","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_duration","type":"uint256"}],"name":"updateUndelegateLockupDuration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b50614926806100206000396000f3fe608060405234801561001057600080fd5b50600436106102315760003560e01c8063862c95b911610130578063b9ca6067116100b8578063ef5cfb8c1161007c578063ef5cfb8c14610656578063f4e0d9ac1461067c578063f5c081ad146106a2578063feaf8048146106bf578063fed3d1fd146106c757610231565b8063b9ca606714610591578063ca31b4b5146105bf578063cfc16254146105e5578063e0d229ff1461060b578063e37e191c1461063957610231565b8063a7bac487116100ff578063a7bac487146104f4578063aa70d23614610520578063b0303b7514610546578063b11caba51461056c578063b26df5641461057457610231565b8063862c95b9146104795780639336086f14610496578063948e5426146104e45780639d974fb5146104ec57610231565b80634a551fe7116101be578063732524941161018257806373252494146104155780637dc1eeba1461041d5780638129fc1c1461044357806382d51e2c1461044b5780638504f1881461045357610231565b80634a551fe7146103685780635ad15ada1461039657806368579837146103b35780636a53f10f146103df578063721e4221146103e757610231565b80631794bb3c116102055780631794bb3c146102845780631d0f283a146102bc578063201ae9db146102ea5780633c323a1b146103105780633d82e3c11461033c57610231565b80622ae74a1461023657806309a945a01461025a5780630e9ed68b1461027457806315fe40701461027c575b600080fd5b61023e61073d565b604080516001600160a01b039092168252519081900360200190f35b610262610758565b60408051918252519081900360200190f35b61023e610769565b610262610783565b6102ba6004803603606081101561029a57600080fd5b506001600160a01b03813581169160208101359091169060400135610794565b005b6102ba600480360360408110156102d257600080fd5b506001600160a01b03813581169160200135166108f0565b6102ba6004803603602081101561030057600080fd5b50356001600160a01b0316610a6f565b6102626004803603604081101561032657600080fd5b506001600160a01b038135169060200135610b51565b6102ba6004803603604081101561035257600080fd5b50803590602001356001600160a01b0316610efd565b6102626004803603604081101561037e57600080fd5b506001600160a01b038135811691602001351661169f565b6102ba600480360360208110156103ac57600080fd5b50356116d5565b6102ba600480360360408110156103c957600080fd5b506001600160a01b0381351690602001356117a0565b6102ba611958565b6102ba600480360360408110156103fd57600080fd5b506001600160a01b0381358116916020013516611a3f565b61023e611c02565b6102626004803603602081101561043357600080fd5b50356001600160a01b0316611c21565b6102ba611c4a565b610262611d58565b6102626004803603602081101561046957600080fd5b50356001600160a01b0316611d69565b6102ba6004803603602081101561048f57600080fd5b5035611d8f565b6104bc600480360360208110156104ac57600080fd5b50356001600160a01b0316611e5a565b604080516001600160a01b039094168452602084019290925282820152519081900360600190f35b61023e611ebc565b610262611ed6565b6102626004803603604081101561050a57600080fd5b506001600160a01b038135169060200135611ee7565b6102ba6004803603602081101561053657600080fd5b50356001600160a01b031661217b565b6102626004803603602081101561055c57600080fd5b50356001600160a01b031661225d565b610262612283565b6102ba6004803603602081101561058a57600080fd5b5035612294565b610262600480360360408110156105a757600080fd5b506001600160a01b038135811691602001351661235f565b610262600480360360208110156105d557600080fd5b50356001600160a01b0316612395565b6102ba600480360360208110156105fb57600080fd5b50356001600160a01b03166123b0565b6102ba6004803603604081101561062157600080fd5b506001600160a01b03813581169160200135166124a3565b6102ba6004803603602081101561064f57600080fd5b5035612873565b6102ba6004803603602081101561066c57600080fd5b50356001600160a01b0316612942565b6102ba6004803603602081101561069257600080fd5b50356001600160a01b0316612b69565b6102ba600480360360208110156106b857600080fd5b5035612c4b565b610262612d1a565b6106ed600480360360208110156106dd57600080fd5b50356001600160a01b031661321c565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610729578181015183820152602001610711565b505050509050019250505060405180910390f35b600061074761329d565b506035546001600160a01b03165b90565b600061076261329d565b5060375490565b600061077361329d565b506034546001600160a01b031690565b600061078d61329d565b5060385490565b6000546001600160a01b031633146107f3576040805162461bcd60e51b815260206004820152601f60248201527f4f6e6c792070726f78792061646d696e2063616e20696e697469616c697a6500604482015290519081900360640190fd5b600354610100900460ff168061080c575061080c613328565b8061081a575060035460ff16155b6108555760405162461bcd60e51b815260040180806020018281038252602e8152602001806145ec602e913960400191505060405180910390fd5b600354610100900460ff16158015610880576003805460ff1961ff0019909116610100171660011790555b6108898361332e565b603c80546001600160a01b0319166001600160a01b03861617905560af60385568056bc75e2d631000006039556108be611c4a565b6108c7826133fb565b6108d261b5bb61352a565b6119f6603b5580156108ea576003805461ff00191690555b50505050565b336001600160a01b0383161480610916575060335461010090046001600160a01b031633145b6040518060600160405280603981526020016144ab60399139906109b85760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561097d578181015183820152602001610965565b50505050905090810190601f1680156109aa5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506001600160a01b03808316600090815260416020908152604080832093851683529290522054610a1a5760405162461bcd60e51b81526004018080602001828103825260238152602001806143606023913960400191505060405180910390fd5b6001600160a01b03808316600081815260416020908152604080832094861680845294909152808220829055517fd7a1b9c3d30d51412b848777bffec951c371bf58a13788d70c12f534f82d4cb39190a35050565b610a7761329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b26035913990610b065760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603580546001600160a01b0319166001600160a01b0383169081179091556040517f373f84f0177a6c2e019f2e0e73c988359e56e111629a261c9bba5c968c383ed190600090a250565b6000610b5b61329d565b610b63613659565b610b6b6136a2565b610b736136e9565b610b7c83613730565b15610bb85760405162461bcd60e51b815260040180806020018281038252603e815260200180614322603e913960400191505060405180910390fd5b60345460408051636c483ff360e01b81526001600160a01b0386811660048301523360248301819052604483018790529251929316918291636c483ff391606480830192600092919082900301818387803b158015610c1657600080fd5b505af1158015610c2a573d6000803e3d6000fd5b50505050610c3882866137b5565b610cc6576001600160a01b038581166000818152603d602090815260408220600201805460018101825581845291832090910180546001600160a01b0319169487169490941790935560385491905290541115610cc65760405162461bcd60e51b815260040180806020018281038252602c815260200180614281602c913960400191505060405180910390fd5b6001600160a01b0385166000908152603d6020526040902054610d5b9083908790610cf7908863ffffffff61383f16565b6001600160a01b038087166000908152603e60209081526040808320938d1683529290522054610d2d908963ffffffff61383f16565b6001600160a01b0387166000908152603f6020526040902054610d56908a63ffffffff61383f16565b6138a0565b6039546001600160a01b038084166000908152603e60209081526040808320938a168352929052205410801590610dc157506001600160a01b038086166000818152604260209081526040808320549487168352603e8252808320938352929052205410155b6040518060600160405280603381526020016144e46033913990610e265760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603554604080516303a378e360e61b81526001600160a01b0388811660048301529151919092169163e8de38c0916024808301926000929190829003018186803b158015610e7457600080fd5b505afa158015610e88573d6000803e3d6000fd5b5050505083856001600160a01b0316836001600160a01b03167f82d701855f3ac4a098fc0249261c5e06d1050d23c8aa351fae8abefc2a464fda60405160405180910390a4506001600160a01b039081166000908152603e602090815260408083209387168352929052205490505b92915050565b610f0561329d565b610f0d613659565b610f156136a2565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b26035913990610fa45760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b5060345460355460408051634b341aed60e01b81526001600160a01b03858116600483015291519382169391909216916000918491634b341aed916024808301926020929190829003018186803b158015610ffe57600080fd5b505afa158015611012573d6000803e3d6000fd5b505050506040513d602081101561102857600080fd5b505190508481101561106b5760405162461bcd60e51b815260040180806020018281038252603e8152602001806147e7603e913960400191505060405180910390fd5b604080516001624d61bb60e11b031981526001600160a01b03868116600483015282516000939186169263ff653c8a926024808301939192829003018186803b1580156110b757600080fd5b505afa1580156110cb573d6000803e3d6000fd5b505050506040513d60408110156110e157600080fd5b50519050801561115c57826001600160a01b03166354350cee866040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b03168152602001915050600060405180830381600087803b15801561114357600080fd5b505af1158015611157573d6000803e3d6000fd5b505050505b6000836001600160a01b031663f273e9a8876040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060c06040518083038186803b1580156111b457600080fd5b505afa1580156111c8573d6000803e3d6000fd5b505050506040513d60c08110156111de57600080fd5b505190508061121e5760405162461bcd60e51b81526004018080602001828103825260308152602001806145bc6030913960400191505060405180910390fd5b846001600160a01b0316633d82e3c188886040518363ffffffff1660e01b815260040180838152602001826001600160a01b03166001600160a01b0316815260200192505050600060405180830381600087803b15801561127e57600080fd5b505af1158015611292573d6000803e3d6000fd5b505050506000856001600160a01b0316634b341aed886040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156112ee57600080fd5b505afa158015611302573d6000803e3d6000fd5b505050506040513d602081101561131857600080fd5b5051604051909150819089906001600160a01b038a16907fe05ad941535eea602efe44ddd7d96e5db6ad9a4865c360257aad8cf4c0a9446990600090a46000805b6001600160a01b0389166000908152603d602052604090206002015481101561159f576001600160a01b0389166000908152603d602052604081206002018054839081106113a357fe5b60009182526020808320909101546001600160a01b03908116808452603e83526040808520928f16855291909252822054909250906113f8896113ec888563ffffffff6138e716565b9063ffffffff61394016565b905061146461140d838363ffffffff61398216565b603e6000866001600160a01b03166001600160a01b0316815260200190815260200160002060008f6001600160a01b03166001600160a01b031681526020019081526020016000205461398290919063ffffffff16565b603e6000856001600160a01b03166001600160a01b0316815260200190815260200160002060008e6001600160a01b03166001600160a01b03168152602001908152602001600020819055506114f4836114ef6114ca848661398290919063ffffffff16565b6001600160a01b0387166000908152603f60205260409020549063ffffffff61398216565b6139c4565b611514611507838363ffffffff61398216565b869063ffffffff61383f16565b6001600160a01b03841660009081526040602081905290206001015490955015611594576001600160a01b0380841660009081526040602081815281832080546001918201549516808552603d909252919092200154909190611588908390611583908463ffffffff61398216565b6139e0565b611591856139ff565b50505b505050600101611359565b506001600160a01b0388166000908152603d60205260409020546115c9908263ffffffff61398216565b6001600160a01b0389166000908152603d60205260408120919091556115f5868463ffffffff61398216565b90506000611609828463ffffffff61398216565b90506001600160a01b03881663b90bc8528b61162b888563ffffffff61398216565b6040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561167a57600080fd5b505af115801561168e573d6000803e3d6000fd5b505050505050505050505050505050565b60006116a961329d565b506001600160a01b03918216600090815260416020908152604080832093909416825291909152205490565b6116dd61329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b2603591399061176c5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603981905560405181907f2a565983434870f0302d93575c6ee07199767028d6f294c9d1d6a1cd0979f1e190600090a250565b6117a861329d565b816001600160a01b0316336001600160a01b03161460405180606001604052806038815260200161474f60389139906118225760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b5060355460408051631e4e7d3560e31b81526001600160a01b0385811660048301529151600093929092169163f273e9a89160248082019260c092909190829003018186803b15801561187457600080fd5b505afa158015611888573d6000803e3d6000fd5b505050506040513d60c081101561189e57600080fd5b506060908101516040805192830190526038808352909250821515919061474f60208301399061190f5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b506001600160a01b038316600081815260426020526040808220859055518492917fb5cbea0eea08e03cbff1c1db26b3125d44b4dd567d36c988c01ca3f6e694aea391a3505050565b61196061329d565b3361196a81613a0d565b6119a55760405162461bcd60e51b81526004018080602001828103825260288152602001806144836028913960400191505060405180910390fd5b6001600160a01b038082166000908152604060208181528183206001808201549154909516808552603d90925291909220909201546119f0908290611583908563ffffffff61398216565b6119f9836139ff565b81816001600160a01b0316846001600160a01b03167fdd2f922d72fb35f887498001c4c6bc61a53f40a51ad38c576e092bc7c688352360405160405180910390a4505050565b611a4761329d565b336001600160a01b0383161480611a6d575060335461010090046001600160a01b031633145b6040518060600160405280603981526020016144ab6039913990611ad25760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b506001600160a01b0380831660009081526041602090815260408083209385168352929052205415611b355760405162461bcd60e51b81526004018080602001828103825260318152602001806146666031913960400191505060405180910390fd5b611b3f81836137b5565b6040518060600160405280603081526020016143836030913990611ba45760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603a546001600160a01b038381166000818152604160209081526040808320948716808452949091528082204390950194859055517fd6f2f5867e98ef295f42626fa37ec5192436d80d6b552dc38c971b9ddbe16e109190a45050565b6000611c0c61329d565b5060335461010090046001600160a01b031690565b6000611c2b61329d565b506001600160a01b03166000908152603d602052604090206001015490565b6000546001600160a01b03163314611ca9576040805162461bcd60e51b815260206004820152601f60248201527f4f6e6c792070726f78792061646d696e2063616e20696e697469616c697a6500604482015290519081900360640190fd5b600354610100900460ff1680611cc25750611cc2613328565b80611cd0575060035460ff16155b611d0b5760405162461bcd60e51b815260040180806020018281038252602e8152602001806145ec602e913960400191505060405180910390fd5b600354610100900460ff16158015611d36576003805460ff1961ff0019909116610100171660011790555b6033805460ff191660011790558015611d55576003805461ff00191690555b50565b6000611d6261329d565b50603a5490565b6000611d7361329d565b506001600160a01b03166000908152603d602052604090205490565b611d9761329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b26035913990611e265760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603881905560405181907f6ba19979a519727673bc99b911e17ce26c5b91bbf7471cfc082fea38eb2a488490600090a250565b6000806000611e6761329d565b611e6f614219565b505050506001600160a01b03908116600090815260406020818152918190208151606081018352815490941680855260018201549385018490526002909101549390910183905292909190565b6000611ec661329d565b506036546001600160a01b031690565b6000611ee061329d565b50603b5490565b6000611ef161329d565b611ef96136e9565b60008211611f385760405162461bcd60e51b815260040180806020018281038252604c81526020018061461a604c913960600191505060405180910390fd5b611f4183613730565b15611f7d5760405162461bcd60e51b81526004018080602001828103825260468152602001806143dd6046913960600191505060405180910390fd5b33611f8881856137b5565b6040518060600160405280603081526020016143836030913990611fed5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50611ff781613a0d565b156120335760405162461bcd60e51b815260040180806020018281038252602b815260200180614787602b913960400191505060405180910390fd5b6001600160a01b038082166000908152603e6020908152604080832093881683529290522054808411156120985760405162461bcd60e51b81526004018080602001828103825260578152602001806146976057913960600191505060405180910390fd5b60006120af6037544361383f90919063ffffffff16565b90506120bd83878784613a79565b6001600160a01b0386166000908152603d60205260409020600101546120ef908790611583908863ffffffff61383f16565b84866001600160a01b0316846001600160a01b03167f0c0ebdfe3f3ccdb3ad070f98a3fb9656a7b8781c299a5c0cd0f37e4d5a02556d846040518082815260200191505060405180910390a46001600160a01b038084166000908152603e60209081526040808320938a1683529290522054612171908663ffffffff61398216565b9695505050505050565b61218361329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b260359139906122125760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603680546001600160a01b0319166001600160a01b0383169081179091556040517f3b3679838ffd21f454712cf443ab98f11d36d5552da016314c5cbe364a10c24390600090a250565b600061226761329d565b506001600160a01b03166000908152603f602052604090205490565b600061228d61329d565b5060395490565b61229c61329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b2603591399061232b5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603b81905560405181907f10c34e4da809ce0e816d31562e6f5a3d38f913c470dd384ed0a73710281b23dd90600090a250565b600061236961329d565b506001600160a01b039182166000908152603e6020908152604080832093909416825291909152205490565b6001600160a01b031660009081526042602052604090205490565b6123b861329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b260359139906124475760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b506124518161332e565b60338054610100600160a81b0319166101006001600160a01b038416908102919091179091556040517fd0e77a42021adb46a85dc0dbcdd75417f2042ed5c51474cb43a25ce0f1049a1e90600090a250565b6124ab61329d565b6124b3613659565b336001600160a01b03831614806124d9575060335461010090046001600160a01b031633145b6040518060600160405280603981526020016144ab603991399061253e5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b506001600160a01b038083166000908152604160209081526040808320938516835292905220546125a05760405162461bcd60e51b81526004018080602001828103825260238152602001806143606023913960400191505060405180910390fd5b6001600160a01b038083166000908152604160209081526040808320938516835292905220544310156126045760405162461bcd60e51b81526004018080602001828103825260278152602001806147286027913960400191505060405180910390fd5b603b546001600160a01b0380841660009081526041602090815260408083209386168352929052205401431061266b5760405162461bcd60e51b815260040180806020018281038252603a8152602001806146ee603a913960400191505060405180910390fd5b6001600160a01b038082166000818152603e60209081526040808320878616808552925280832054603454825163666cc1c560e11b8152600481019490945260248401959095526044830181905290519094939093169263ccd9838a9260648084019391929182900301818387803b1580156126e657600080fd5b505af11580156126fa573d6000803e3d6000fd5b5050506001600160a01b0384166000908152603d602052604090205461278e91508390859061272f908563ffffffff61398216565b6001600160a01b038087166000908152603e60209081526040808320938b1683529290522054612765908663ffffffff61398216565b6001600160a01b0387166000908152603f6020526040902054610d56908763ffffffff61398216565b61279782613a0d565b80156127bf57506001600160a01b038281166000908152604060208190529020548116908416145b15612810576001600160a01b038083166000908152604060208181528183206001908101549488168452603d909152912001546128079185916115839163ffffffff61398216565b612810826139ff565b61281a8383613ad1565b6001600160a01b0380841660008181526041602090815260408083209487168084529490915280822082905551849392917f912ca4f48e16ea4ec940ef9071c9cc3eb57f01c07e052b1f797caaade6504f8b91a4505050565b61287b61329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b2603591399061290a5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50612914816133fb565b60405181907fcb0491a1854ba445c5afa53dcbe6d6224e52d99cb73840cb58b0c5b79cd434bf90600090a250565b61294a61329d565b612952613659565b61295a6136a2565b6129626136e9565b6035546001600160a01b031660008080808061297e8688613c00565b94509450945094509450816000141561299c57505050505050611d55565b6000612a0f888585858b6001600160a01b0316636c75fdf36040518163ffffffff1660e01b815260040160206040518083038186803b1580156129de57600080fd5b505afa1580156129f2573d6000803e3d6000fd5b505050506040513d6020811015612a0857600080fd5b5051613f13565b6001600160a01b0389166000908152603d6020526040902054909150612a3b908263ffffffff61383f16565b6001600160a01b0389166000908152603d6020526040812091909155612a67848363ffffffff61398216565b90506000612a7b878363ffffffff61383f16565b6001600160a01b038b166000908152603d6020526040902054909150612aa890829063ffffffff61383f16565b8814612ae55760405162461bcd60e51b815260040180806020018281038252602d815260200180614855602d913960400191505060405180910390fd5b886001600160a01b031663b90bc8528b836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015612b4557600080fd5b505af1158015612b59573d6000803e3d6000fd5b5050505050505050505050505050565b612b7161329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b26035913990612c005760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603480546001600160a01b0319166001600160a01b0383169081179091556040517f8ae96d8af35324a34b19e4f33e72d620b502f69595bb43870ab5fd7a7de7823990600090a250565b612c5361329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b26035913990612ce25760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50612cec8161352a565b60405181907f6e9686f24e1165005f49d9abb260eb40aed402da21db4894ebd3895a6519a45490600090a250565b6000612d2461329d565b612d2c613659565b612d346136a2565b612d3c6136e9565b33612d4681613a0d565b612d815760405162461bcd60e51b81526004018080602001828103825260288152602001806144836028913960400191505060405180910390fd5b6001600160a01b038116600090815260406020819052902060020154431015612ddb5760405162461bcd60e51b81526004018080602001828103825260278152602001806147286027913960400191505060405180910390fd5b6001600160a01b03808216600090815260406020819052902054612dff9116613730565b15612e3b5760405162461bcd60e51b815260040180806020018281038252603e815260200180614538603e913960400191505060405180910390fd5b6001600160a01b038082166000818152604060208190528082208054600190910154603454835163666cc1c560e11b81529287166004840181905260248401969096526044830182905292519495909492169263ccd9838a9260648084019382900301818387803b158015612eaf57600080fd5b505af1158015612ec3573d6000803e3d6000fd5b5050506001600160a01b0383166000908152603d6020526040902054612f57915084908490612ef8908563ffffffff61398216565b6001600160a01b038088166000908152603e60209081526040808320938a1683529290522054612f2e908663ffffffff61398216565b6001600160a01b0388166000908152603f6020526040902054610d56908763ffffffff61398216565b6039546001600160a01b038085166000908152603e602090815260408083209387168352929052205410801590612fbd57506001600160a01b038083166000818152604260209081526040808320549488168352603e8252808320938352929052205410155b80612feb57506001600160a01b038084166000908152603e6020908152604080832093861683529290522054155b6040518060600160405280603381526020016144e460339139906130505760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b506001600160a01b038084166000908152603e6020908152604080832093861683529290522054613085576130858284613ad1565b6001600160a01b0382166000908152603d60205260409020600101546130b7908390611583908463ffffffff61398216565b6130c0836139ff565b80826001600160a01b0316846001600160a01b03167fdf026d8db1c407002e7abde612fb40b6031db7aa35d4b3b699d07627f891e63160405160405180910390a460355460408051631e4e7d3560e31b81526001600160a01b0385811660048301529151600093929092169163f273e9a89160248082019260c092909190829003018186803b15801561315257600080fd5b505afa158015613166573d6000803e3d6000fd5b505050506040513d60c081101561317c57600080fd5b505160355460408051635c85e42960e11b81526001600160a01b03878116600483015260248201859052915193945091169163b90bc8529160448082019260009290919082900301818387803b1580156131d557600080fd5b505af11580156131e9573d6000803e3d6000fd5b5050506001600160a01b039485166000908152603e60209081526040808320969097168252949094525050502054905090565b606061322661329d565b6001600160a01b0382166000908152603d60209081526040918290206002018054835181840281018401909452808452909183018282801561329157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613273575b50505050509050919050565b6033546040805180820190915260208082527f496e697469616c697a61626c6556323a204e6f7420696e697469616c697a6564908201529060ff161515600114611d555760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b303b1590565b806001600160a01b0316630ea773076040518163ffffffff1660e01b815260040160206040518083038186803b15801561336757600080fd5b505afa15801561337b573d6000803e3d6000fd5b505050506040513d602081101561339157600080fd5b505115156001146133d35760405162461bcd60e51b81526004018080602001828103825260468152602001806145766046913960600191505060405180910390fd5b603380546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b6000603360019054906101000a90046001600160a01b03169050806001600160a01b031663062888856040518163ffffffff1660e01b815260040160206040518083038186803b15801561344e57600080fd5b505afa158015613462573d6000803e3d6000fd5b505050506040513d602081101561347857600080fd5b505160408051633ecc6a4360e01b815290516001600160a01b03841691633ecc6a43916004808301926020929190829003018186803b1580156134ba57600080fd5b505afa1580156134ce573d6000803e3d6000fd5b505050506040513d60208110156134e457600080fd5b50510182116135245760405162461bcd60e51b81526004018080602001828103825260708152602001806148826070913960800191505060405180910390fd5b50603755565b6000603360019054906101000a90046001600160a01b03169050806001600160a01b031663062888856040518163ffffffff1660e01b815260040160206040518083038186803b15801561357d57600080fd5b505afa158015613591573d6000803e3d6000fd5b505050506040513d60208110156135a757600080fd5b505160408051633ecc6a4360e01b815290516001600160a01b03841691633ecc6a43916004808301926020929190829003018186803b1580156135e957600080fd5b505afa1580156135fd573d6000803e3d6000fd5b505050506040513d602081101561361357600080fd5b50510182116136535760405162461bcd60e51b81526004018080602001828103825260758152602001806142ad6075913960800191505060405180910390fd5b50603a55565b6034546001600160a01b03166136a05760405162461bcd60e51b815260040180806020018281038252602a8152602001806143b3602a913960400191505060405180910390fd5b565b6035546001600160a01b03166136a05760405162461bcd60e51b81526004018080602001828103825260398152602001806144236039913960400191505060405180910390fd5b6036546001600160a01b03166136a05760405162461bcd60e51b81526004018080602001828103825260308152602001806148256030913960400191505060405180910390fd5b6036546040805163d017f48360e01b81526001600160a01b03848116600483015291516000939290921691829163d017f483916024808301926020929190829003018186803b15801561378257600080fd5b505afa158015613796573d6000803e3d6000fd5b505050506040513d60208110156137ac57600080fd5b50519392505050565b6000805b6001600160a01b0383166000908152603d6020526040902060020154811015613835576001600160a01b038381166000908152603d602052604090206002018054918616918390811061380857fe5b6000918252602090912001546001600160a01b0316141561382d576001915050610ef7565b6001016137b9565b5060009392505050565b600082820183811015613899576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b6001600160a01b038085166000818152603d602090815260408083208890559389168252603e815283822092825291909152208290556138e085826139c4565b5050505050565b6000826138f657506000610ef7565b8282028284828161390357fe5b04146138995760405162461bcd60e51b81526004018080602001828103825260218152602001806145176021913960400191505060405180910390fd5b600061389983836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061415a565b600061389983836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506141bf565b6001600160a01b039091166000908152603f6020526040902055565b6001600160a01b039091166000908152603d6020526040902060010155565b611d55816000806000613a79565b6001600160a01b03811660009081526040602081905281206002015415801590613a5157506001600160a01b03821660009081526040602081905290206001015415155b8015610ef75750506001600160a01b0390811660009081526040602081905290205416151590565b604080516060810182526001600160a01b03948516815260208082019485528183019384529585166000908152958290529420935184546001600160a01b031916931692909217835551600183015551600290910155565b60005b6001600160a01b0383166000908152603d6020526040902060020154811015613bfb576001600160a01b038381166000908152603d6020526040902060020180549184169183908110613b2357fe5b6000918252602090912001546001600160a01b03161415613bf3576001600160a01b0383166000908152603d6020526040902060020180546000198101908110613b6957fe5b60009182526020808320909101546001600160a01b038681168452603d9092526040909220600201805491909216919083908110613ba357fe5b600091825260208083209190910180546001600160a01b0319166001600160a01b039485161790559185168152603d90915260409020600201805490613bed906000198301614243565b50613bfb565b600101613ad4565b505050565b600080600080600080876001600160a01b031663ff653c8a886040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b03168152602001915050604080518083038186803b158015613c5e57600080fd5b505afa158015613c72573d6000803e3d6000fd5b505050506040513d6040811015613c8857600080fd5b50516001600160a01b0388166000908152603d602052604081206001015491925090613cba908363ffffffff61383f16565b60365460408051631bff085760e21b81526001600160a01b038c811660048301526024820185905291519394506000939190921691636ffc215c91604480830192602092919082900301818787803b158015613d1557600080fd5b505af1158015613d29573d6000803e3d6000fd5b505050506040513d6020811015613d3f57600080fd5b505160345460408051634b341aed60e01b81526001600160a01b038d811660048301529151939450911691634b341aed91602480820192602092909190829003018186803b158015613d9057600080fd5b505afa158015613da4573d6000803e3d6000fd5b505050506040513d6020811015613dba57600080fd5b505160408051631e4e7d3560e31b81526001600160a01b038c811660048301529151929a50908c169163f273e9a89160248082019260c092909190829003018186803b158015613e0957600080fd5b505afa158015613e1d573d6000803e3d6000fd5b505050506040513d60c0811015613e3357600080fd5b5080516020918201516001600160a01b038c166000908152603d90935260408320549199509550613e6b90899063ffffffff61383f16565b9050613e7d818463ffffffff61398216565b9650613e8f898263ffffffff61398216565b8214613ecc5760405162461bcd60e51b815260040180806020018281038252602781526020018061445c6027913960400191505060405180910390fd5b88868b6001600160a01b03167f34fcbac0073d7c3d388e51312faf357774904998eeb8fca628b9e6f65ee1cbf760405160405180910390a450935050509295509295909350565b6000805b6001600160a01b0387166000908152603d6020526040902060020154811015614150576001600160a01b0387166000908152603d60205260408120600201805483908110613f6157fe5b60009182526020808320909101546001600160a01b03908116808452603e835260408085208d84168087529085528186205483875294829052942054909450919291161415613fda576001600160a01b038216600090815260406020819052902060010154613fd790829063ffffffff61398216565b90505b6000613ff0896113ec848b63ffffffff6138e716565b905060006140276140078b8963ffffffff6138e716565b6113ec8a61401b878e63ffffffff6138e716565b9063ffffffff6138e716565b905061409361403c838363ffffffff61398216565b603e6000876001600160a01b03166001600160a01b0316815260200190815260200160002060008e6001600160a01b03166001600160a01b031681526020019081526020016000205461383f90919063ffffffff16565b603e6000866001600160a01b03166001600160a01b0316815260200190815260200160002060008d6001600160a01b03166001600160a01b031681526020019081526020016000208190555061411e846114ef6140f9848661398290919063ffffffff16565b6001600160a01b0388166000908152603f60205260409020549063ffffffff61383f16565b61413e614131838363ffffffff61398216565b879063ffffffff61383f16565b95505060019093019250613f17915050565b5095945050505050565b600081836141a95760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b5060008385816141b557fe5b0495945050505050565b600081848411156142115760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b505050900390565b604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b815481835581811115613bfb57600083815260209020613bfb91810190830161075591905b8082111561427c5760008155600101614268565b509056fe44656c65676174654d616e616765723a204d6178696d756d2064656c656761746f727320657863656564656444656c65676174654d616e616765723a2072656d6f766544656c656761746f724c6f636b75704475726174696f6e206475726174696f6e206d7573742062652067726561746572207468616e20676f7665726e616e636520766f74696e67506572696f64202b20657865637574696f6e44656c617944656c65676174654d616e616765723a2044656c65676174696f6e206e6f74207065726d697474656420666f722053502070656e64696e6720636c61696d44656c65676174654d616e616765723a204e6f2070656e64696e67207265717565737444656c65676174654d616e616765723a2044656c656761746f72206d757374206265207374616b656420666f7220535044656c65676174654d616e616765723a207374616b696e6741646472657373206973206e6f742073657444656c65676174654d616e616765723a20556e64656c65676174652072657175657374206e6f74207065726d697474656420666f722053502070656e64696e6720636c61696d44656c65676174654d616e616765723a207365727669636550726f7669646572466163746f727941646472657373206973206e6f742073657444656c65676174654d616e616765723a2052657761726420616d6f756e74206d69736d6174636844656c65676174654d616e616765723a2050656e64696e67206c6f636b757020657870656374656444656c65676174654d616e616765723a204f6e6c792063616c6c61626c6520627920746172676574205350206f7220676f7665726e616e636544656c65676174654d616e616765723a204d696e696d756d2064656c65676174696f6e20616d6f756e74207265717569726564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7744656c65676174654d616e616765723a20556e64656c6567617465206e6f74207065726d697474656420666f722053502070656e64696e6720636c61696d44656c65676174654d616e616765723a205f676f7665726e616e636541646472657373206973206e6f7420612076616c696420676f7665726e616e636520636f6e747261637444656c65676174654d616e616765723a20536572766963652050726f7669646572207374616b65207265717569726564436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a656444656c65676174654d616e616765723a2052657175657374656420756e64656c6567617465207374616b6520616d6f756e74206d7573742062652067726561746572207468616e207a65726f44656c65676174654d616e616765723a2050656e64696e672072656d6f76652064656c656761746f72207265717565737444656c65676174654d616e616765723a2043616e6e6f742064656372656173652067726561746572207468616e2063757272656e746c79207374616b656420666f722074686973205365727669636550726f766964657244656c65676174654d616e616765723a2052656d6f766544656c656761746f72206576616c756174696f6e2077696e646f77206578706972656444656c65676174654d616e616765723a204c6f636b7570206d757374206265206578706972656444656c65676174654d616e616765723a204f6e6c792063616c6c61626c652062792076616c696420536572766963652050726f766964657244656c65676174654d616e616765723a204e6f2070656e64696e67206c6f636b757020657870656374656444656c65676174654d616e616765723a204f6e6c792063616c6c61626c6520627920476f7665726e616e636520636f6e747261637444656c65676174654d616e616765723a2043616e6e6f7420736c617368206d6f7265207468616e20746f74616c2063757272656e746c79207374616b656444656c65676174654d616e616765723a20636c61696d734d616e6167657241646472657373206973206e6f742073657444656c65676174654d616e616765723a20636c61696d5265776172647320616d6f756e74206d69736d6174636844656c65676174654d616e616765723a20756e64656c65676174654c6f636b75704475726174696f6e206475726174696f6e206d7573742062652067726561746572207468616e20676f7665726e616e636520766f74696e67506572696f64202b20657865637574696f6e44656c6179a265627a7a72315820b09a598ad0fd1e1002c94db815e00a0d1c35ee031853e1155378f5524c8d1c6f64736f6c63430005110032
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102315760003560e01c8063862c95b911610130578063b9ca6067116100b8578063ef5cfb8c1161007c578063ef5cfb8c14610656578063f4e0d9ac1461067c578063f5c081ad146106a2578063feaf8048146106bf578063fed3d1fd146106c757610231565b8063b9ca606714610591578063ca31b4b5146105bf578063cfc16254146105e5578063e0d229ff1461060b578063e37e191c1461063957610231565b8063a7bac487116100ff578063a7bac487146104f4578063aa70d23614610520578063b0303b7514610546578063b11caba51461056c578063b26df5641461057457610231565b8063862c95b9146104795780639336086f14610496578063948e5426146104e45780639d974fb5146104ec57610231565b80634a551fe7116101be578063732524941161018257806373252494146104155780637dc1eeba1461041d5780638129fc1c1461044357806382d51e2c1461044b5780638504f1881461045357610231565b80634a551fe7146103685780635ad15ada1461039657806368579837146103b35780636a53f10f146103df578063721e4221146103e757610231565b80631794bb3c116102055780631794bb3c146102845780631d0f283a146102bc578063201ae9db146102ea5780633c323a1b146103105780633d82e3c11461033c57610231565b80622ae74a1461023657806309a945a01461025a5780630e9ed68b1461027457806315fe40701461027c575b600080fd5b61023e61073d565b604080516001600160a01b039092168252519081900360200190f35b610262610758565b60408051918252519081900360200190f35b61023e610769565b610262610783565b6102ba6004803603606081101561029a57600080fd5b506001600160a01b03813581169160208101359091169060400135610794565b005b6102ba600480360360408110156102d257600080fd5b506001600160a01b03813581169160200135166108f0565b6102ba6004803603602081101561030057600080fd5b50356001600160a01b0316610a6f565b6102626004803603604081101561032657600080fd5b506001600160a01b038135169060200135610b51565b6102ba6004803603604081101561035257600080fd5b50803590602001356001600160a01b0316610efd565b6102626004803603604081101561037e57600080fd5b506001600160a01b038135811691602001351661169f565b6102ba600480360360208110156103ac57600080fd5b50356116d5565b6102ba600480360360408110156103c957600080fd5b506001600160a01b0381351690602001356117a0565b6102ba611958565b6102ba600480360360408110156103fd57600080fd5b506001600160a01b0381358116916020013516611a3f565b61023e611c02565b6102626004803603602081101561043357600080fd5b50356001600160a01b0316611c21565b6102ba611c4a565b610262611d58565b6102626004803603602081101561046957600080fd5b50356001600160a01b0316611d69565b6102ba6004803603602081101561048f57600080fd5b5035611d8f565b6104bc600480360360208110156104ac57600080fd5b50356001600160a01b0316611e5a565b604080516001600160a01b039094168452602084019290925282820152519081900360600190f35b61023e611ebc565b610262611ed6565b6102626004803603604081101561050a57600080fd5b506001600160a01b038135169060200135611ee7565b6102ba6004803603602081101561053657600080fd5b50356001600160a01b031661217b565b6102626004803603602081101561055c57600080fd5b50356001600160a01b031661225d565b610262612283565b6102ba6004803603602081101561058a57600080fd5b5035612294565b610262600480360360408110156105a757600080fd5b506001600160a01b038135811691602001351661235f565b610262600480360360208110156105d557600080fd5b50356001600160a01b0316612395565b6102ba600480360360208110156105fb57600080fd5b50356001600160a01b03166123b0565b6102ba6004803603604081101561062157600080fd5b506001600160a01b03813581169160200135166124a3565b6102ba6004803603602081101561064f57600080fd5b5035612873565b6102ba6004803603602081101561066c57600080fd5b50356001600160a01b0316612942565b6102ba6004803603602081101561069257600080fd5b50356001600160a01b0316612b69565b6102ba600480360360208110156106b857600080fd5b5035612c4b565b610262612d1a565b6106ed600480360360208110156106dd57600080fd5b50356001600160a01b031661321c565b60408051602080825283518183015283519192839290830191858101910280838360005b83811015610729578181015183820152602001610711565b505050509050019250505060405180910390f35b600061074761329d565b506035546001600160a01b03165b90565b600061076261329d565b5060375490565b600061077361329d565b506034546001600160a01b031690565b600061078d61329d565b5060385490565b6000546001600160a01b031633146107f3576040805162461bcd60e51b815260206004820152601f60248201527f4f6e6c792070726f78792061646d696e2063616e20696e697469616c697a6500604482015290519081900360640190fd5b600354610100900460ff168061080c575061080c613328565b8061081a575060035460ff16155b6108555760405162461bcd60e51b815260040180806020018281038252602e8152602001806145ec602e913960400191505060405180910390fd5b600354610100900460ff16158015610880576003805460ff1961ff0019909116610100171660011790555b6108898361332e565b603c80546001600160a01b0319166001600160a01b03861617905560af60385568056bc75e2d631000006039556108be611c4a565b6108c7826133fb565b6108d261b5bb61352a565b6119f6603b5580156108ea576003805461ff00191690555b50505050565b336001600160a01b0383161480610916575060335461010090046001600160a01b031633145b6040518060600160405280603981526020016144ab60399139906109b85760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561097d578181015183820152602001610965565b50505050905090810190601f1680156109aa5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b506001600160a01b03808316600090815260416020908152604080832093851683529290522054610a1a5760405162461bcd60e51b81526004018080602001828103825260238152602001806143606023913960400191505060405180910390fd5b6001600160a01b03808316600081815260416020908152604080832094861680845294909152808220829055517fd7a1b9c3d30d51412b848777bffec951c371bf58a13788d70c12f534f82d4cb39190a35050565b610a7761329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b26035913990610b065760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603580546001600160a01b0319166001600160a01b0383169081179091556040517f373f84f0177a6c2e019f2e0e73c988359e56e111629a261c9bba5c968c383ed190600090a250565b6000610b5b61329d565b610b63613659565b610b6b6136a2565b610b736136e9565b610b7c83613730565b15610bb85760405162461bcd60e51b815260040180806020018281038252603e815260200180614322603e913960400191505060405180910390fd5b60345460408051636c483ff360e01b81526001600160a01b0386811660048301523360248301819052604483018790529251929316918291636c483ff391606480830192600092919082900301818387803b158015610c1657600080fd5b505af1158015610c2a573d6000803e3d6000fd5b50505050610c3882866137b5565b610cc6576001600160a01b038581166000818152603d602090815260408220600201805460018101825581845291832090910180546001600160a01b0319169487169490941790935560385491905290541115610cc65760405162461bcd60e51b815260040180806020018281038252602c815260200180614281602c913960400191505060405180910390fd5b6001600160a01b0385166000908152603d6020526040902054610d5b9083908790610cf7908863ffffffff61383f16565b6001600160a01b038087166000908152603e60209081526040808320938d1683529290522054610d2d908963ffffffff61383f16565b6001600160a01b0387166000908152603f6020526040902054610d56908a63ffffffff61383f16565b6138a0565b6039546001600160a01b038084166000908152603e60209081526040808320938a168352929052205410801590610dc157506001600160a01b038086166000818152604260209081526040808320549487168352603e8252808320938352929052205410155b6040518060600160405280603381526020016144e46033913990610e265760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603554604080516303a378e360e61b81526001600160a01b0388811660048301529151919092169163e8de38c0916024808301926000929190829003018186803b158015610e7457600080fd5b505afa158015610e88573d6000803e3d6000fd5b5050505083856001600160a01b0316836001600160a01b03167f82d701855f3ac4a098fc0249261c5e06d1050d23c8aa351fae8abefc2a464fda60405160405180910390a4506001600160a01b039081166000908152603e602090815260408083209387168352929052205490505b92915050565b610f0561329d565b610f0d613659565b610f156136a2565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b26035913990610fa45760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b5060345460355460408051634b341aed60e01b81526001600160a01b03858116600483015291519382169391909216916000918491634b341aed916024808301926020929190829003018186803b158015610ffe57600080fd5b505afa158015611012573d6000803e3d6000fd5b505050506040513d602081101561102857600080fd5b505190508481101561106b5760405162461bcd60e51b815260040180806020018281038252603e8152602001806147e7603e913960400191505060405180910390fd5b604080516001624d61bb60e11b031981526001600160a01b03868116600483015282516000939186169263ff653c8a926024808301939192829003018186803b1580156110b757600080fd5b505afa1580156110cb573d6000803e3d6000fd5b505050506040513d60408110156110e157600080fd5b50519050801561115c57826001600160a01b03166354350cee866040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b03168152602001915050600060405180830381600087803b15801561114357600080fd5b505af1158015611157573d6000803e3d6000fd5b505050505b6000836001600160a01b031663f273e9a8876040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060c06040518083038186803b1580156111b457600080fd5b505afa1580156111c8573d6000803e3d6000fd5b505050506040513d60c08110156111de57600080fd5b505190508061121e5760405162461bcd60e51b81526004018080602001828103825260308152602001806145bc6030913960400191505060405180910390fd5b846001600160a01b0316633d82e3c188886040518363ffffffff1660e01b815260040180838152602001826001600160a01b03166001600160a01b0316815260200192505050600060405180830381600087803b15801561127e57600080fd5b505af1158015611292573d6000803e3d6000fd5b505050506000856001600160a01b0316634b341aed886040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b0316815260200191505060206040518083038186803b1580156112ee57600080fd5b505afa158015611302573d6000803e3d6000fd5b505050506040513d602081101561131857600080fd5b5051604051909150819089906001600160a01b038a16907fe05ad941535eea602efe44ddd7d96e5db6ad9a4865c360257aad8cf4c0a9446990600090a46000805b6001600160a01b0389166000908152603d602052604090206002015481101561159f576001600160a01b0389166000908152603d602052604081206002018054839081106113a357fe5b60009182526020808320909101546001600160a01b03908116808452603e83526040808520928f16855291909252822054909250906113f8896113ec888563ffffffff6138e716565b9063ffffffff61394016565b905061146461140d838363ffffffff61398216565b603e6000866001600160a01b03166001600160a01b0316815260200190815260200160002060008f6001600160a01b03166001600160a01b031681526020019081526020016000205461398290919063ffffffff16565b603e6000856001600160a01b03166001600160a01b0316815260200190815260200160002060008e6001600160a01b03166001600160a01b03168152602001908152602001600020819055506114f4836114ef6114ca848661398290919063ffffffff16565b6001600160a01b0387166000908152603f60205260409020549063ffffffff61398216565b6139c4565b611514611507838363ffffffff61398216565b869063ffffffff61383f16565b6001600160a01b03841660009081526040602081905290206001015490955015611594576001600160a01b0380841660009081526040602081815281832080546001918201549516808552603d909252919092200154909190611588908390611583908463ffffffff61398216565b6139e0565b611591856139ff565b50505b505050600101611359565b506001600160a01b0388166000908152603d60205260409020546115c9908263ffffffff61398216565b6001600160a01b0389166000908152603d60205260408120919091556115f5868463ffffffff61398216565b90506000611609828463ffffffff61398216565b90506001600160a01b03881663b90bc8528b61162b888563ffffffff61398216565b6040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b15801561167a57600080fd5b505af115801561168e573d6000803e3d6000fd5b505050505050505050505050505050565b60006116a961329d565b506001600160a01b03918216600090815260416020908152604080832093909416825291909152205490565b6116dd61329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b2603591399061176c5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603981905560405181907f2a565983434870f0302d93575c6ee07199767028d6f294c9d1d6a1cd0979f1e190600090a250565b6117a861329d565b816001600160a01b0316336001600160a01b03161460405180606001604052806038815260200161474f60389139906118225760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b5060355460408051631e4e7d3560e31b81526001600160a01b0385811660048301529151600093929092169163f273e9a89160248082019260c092909190829003018186803b15801561187457600080fd5b505afa158015611888573d6000803e3d6000fd5b505050506040513d60c081101561189e57600080fd5b506060908101516040805192830190526038808352909250821515919061474f60208301399061190f5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b506001600160a01b038316600081815260426020526040808220859055518492917fb5cbea0eea08e03cbff1c1db26b3125d44b4dd567d36c988c01ca3f6e694aea391a3505050565b61196061329d565b3361196a81613a0d565b6119a55760405162461bcd60e51b81526004018080602001828103825260288152602001806144836028913960400191505060405180910390fd5b6001600160a01b038082166000908152604060208181528183206001808201549154909516808552603d90925291909220909201546119f0908290611583908563ffffffff61398216565b6119f9836139ff565b81816001600160a01b0316846001600160a01b03167fdd2f922d72fb35f887498001c4c6bc61a53f40a51ad38c576e092bc7c688352360405160405180910390a4505050565b611a4761329d565b336001600160a01b0383161480611a6d575060335461010090046001600160a01b031633145b6040518060600160405280603981526020016144ab6039913990611ad25760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b506001600160a01b0380831660009081526041602090815260408083209385168352929052205415611b355760405162461bcd60e51b81526004018080602001828103825260318152602001806146666031913960400191505060405180910390fd5b611b3f81836137b5565b6040518060600160405280603081526020016143836030913990611ba45760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603a546001600160a01b038381166000818152604160209081526040808320948716808452949091528082204390950194859055517fd6f2f5867e98ef295f42626fa37ec5192436d80d6b552dc38c971b9ddbe16e109190a45050565b6000611c0c61329d565b5060335461010090046001600160a01b031690565b6000611c2b61329d565b506001600160a01b03166000908152603d602052604090206001015490565b6000546001600160a01b03163314611ca9576040805162461bcd60e51b815260206004820152601f60248201527f4f6e6c792070726f78792061646d696e2063616e20696e697469616c697a6500604482015290519081900360640190fd5b600354610100900460ff1680611cc25750611cc2613328565b80611cd0575060035460ff16155b611d0b5760405162461bcd60e51b815260040180806020018281038252602e8152602001806145ec602e913960400191505060405180910390fd5b600354610100900460ff16158015611d36576003805460ff1961ff0019909116610100171660011790555b6033805460ff191660011790558015611d55576003805461ff00191690555b50565b6000611d6261329d565b50603a5490565b6000611d7361329d565b506001600160a01b03166000908152603d602052604090205490565b611d9761329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b26035913990611e265760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603881905560405181907f6ba19979a519727673bc99b911e17ce26c5b91bbf7471cfc082fea38eb2a488490600090a250565b6000806000611e6761329d565b611e6f614219565b505050506001600160a01b03908116600090815260406020818152918190208151606081018352815490941680855260018201549385018490526002909101549390910183905292909190565b6000611ec661329d565b506036546001600160a01b031690565b6000611ee061329d565b50603b5490565b6000611ef161329d565b611ef96136e9565b60008211611f385760405162461bcd60e51b815260040180806020018281038252604c81526020018061461a604c913960600191505060405180910390fd5b611f4183613730565b15611f7d5760405162461bcd60e51b81526004018080602001828103825260468152602001806143dd6046913960600191505060405180910390fd5b33611f8881856137b5565b6040518060600160405280603081526020016143836030913990611fed5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50611ff781613a0d565b156120335760405162461bcd60e51b815260040180806020018281038252602b815260200180614787602b913960400191505060405180910390fd5b6001600160a01b038082166000908152603e6020908152604080832093881683529290522054808411156120985760405162461bcd60e51b81526004018080602001828103825260578152602001806146976057913960600191505060405180910390fd5b60006120af6037544361383f90919063ffffffff16565b90506120bd83878784613a79565b6001600160a01b0386166000908152603d60205260409020600101546120ef908790611583908863ffffffff61383f16565b84866001600160a01b0316846001600160a01b03167f0c0ebdfe3f3ccdb3ad070f98a3fb9656a7b8781c299a5c0cd0f37e4d5a02556d846040518082815260200191505060405180910390a46001600160a01b038084166000908152603e60209081526040808320938a1683529290522054612171908663ffffffff61398216565b9695505050505050565b61218361329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b260359139906122125760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603680546001600160a01b0319166001600160a01b0383169081179091556040517f3b3679838ffd21f454712cf443ab98f11d36d5552da016314c5cbe364a10c24390600090a250565b600061226761329d565b506001600160a01b03166000908152603f602052604090205490565b600061228d61329d565b5060395490565b61229c61329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b2603591399061232b5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603b81905560405181907f10c34e4da809ce0e816d31562e6f5a3d38f913c470dd384ed0a73710281b23dd90600090a250565b600061236961329d565b506001600160a01b039182166000908152603e6020908152604080832093909416825291909152205490565b6001600160a01b031660009081526042602052604090205490565b6123b861329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b260359139906124475760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b506124518161332e565b60338054610100600160a81b0319166101006001600160a01b038416908102919091179091556040517fd0e77a42021adb46a85dc0dbcdd75417f2042ed5c51474cb43a25ce0f1049a1e90600090a250565b6124ab61329d565b6124b3613659565b336001600160a01b03831614806124d9575060335461010090046001600160a01b031633145b6040518060600160405280603981526020016144ab603991399061253e5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b506001600160a01b038083166000908152604160209081526040808320938516835292905220546125a05760405162461bcd60e51b81526004018080602001828103825260238152602001806143606023913960400191505060405180910390fd5b6001600160a01b038083166000908152604160209081526040808320938516835292905220544310156126045760405162461bcd60e51b81526004018080602001828103825260278152602001806147286027913960400191505060405180910390fd5b603b546001600160a01b0380841660009081526041602090815260408083209386168352929052205401431061266b5760405162461bcd60e51b815260040180806020018281038252603a8152602001806146ee603a913960400191505060405180910390fd5b6001600160a01b038082166000818152603e60209081526040808320878616808552925280832054603454825163666cc1c560e11b8152600481019490945260248401959095526044830181905290519094939093169263ccd9838a9260648084019391929182900301818387803b1580156126e657600080fd5b505af11580156126fa573d6000803e3d6000fd5b5050506001600160a01b0384166000908152603d602052604090205461278e91508390859061272f908563ffffffff61398216565b6001600160a01b038087166000908152603e60209081526040808320938b1683529290522054612765908663ffffffff61398216565b6001600160a01b0387166000908152603f6020526040902054610d56908763ffffffff61398216565b61279782613a0d565b80156127bf57506001600160a01b038281166000908152604060208190529020548116908416145b15612810576001600160a01b038083166000908152604060208181528183206001908101549488168452603d909152912001546128079185916115839163ffffffff61398216565b612810826139ff565b61281a8383613ad1565b6001600160a01b0380841660008181526041602090815260408083209487168084529490915280822082905551849392917f912ca4f48e16ea4ec940ef9071c9cc3eb57f01c07e052b1f797caaade6504f8b91a4505050565b61287b61329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b2603591399061290a5760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50612914816133fb565b60405181907fcb0491a1854ba445c5afa53dcbe6d6224e52d99cb73840cb58b0c5b79cd434bf90600090a250565b61294a61329d565b612952613659565b61295a6136a2565b6129626136e9565b6035546001600160a01b031660008080808061297e8688613c00565b94509450945094509450816000141561299c57505050505050611d55565b6000612a0f888585858b6001600160a01b0316636c75fdf36040518163ffffffff1660e01b815260040160206040518083038186803b1580156129de57600080fd5b505afa1580156129f2573d6000803e3d6000fd5b505050506040513d6020811015612a0857600080fd5b5051613f13565b6001600160a01b0389166000908152603d6020526040902054909150612a3b908263ffffffff61383f16565b6001600160a01b0389166000908152603d6020526040812091909155612a67848363ffffffff61398216565b90506000612a7b878363ffffffff61383f16565b6001600160a01b038b166000908152603d6020526040902054909150612aa890829063ffffffff61383f16565b8814612ae55760405162461bcd60e51b815260040180806020018281038252602d815260200180614855602d913960400191505060405180910390fd5b886001600160a01b031663b90bc8528b836040518363ffffffff1660e01b815260040180836001600160a01b03166001600160a01b0316815260200182815260200192505050600060405180830381600087803b158015612b4557600080fd5b505af1158015612b59573d6000803e3d6000fd5b5050505050505050505050505050565b612b7161329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b26035913990612c005760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50603480546001600160a01b0319166001600160a01b0383169081179091556040517f8ae96d8af35324a34b19e4f33e72d620b502f69595bb43870ab5fd7a7de7823990600090a250565b612c5361329d565b603360019054906101000a90046001600160a01b03166001600160a01b0316336001600160a01b0316146040518060600160405280603581526020016147b26035913990612ce25760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b50612cec8161352a565b60405181907f6e9686f24e1165005f49d9abb260eb40aed402da21db4894ebd3895a6519a45490600090a250565b6000612d2461329d565b612d2c613659565b612d346136a2565b612d3c6136e9565b33612d4681613a0d565b612d815760405162461bcd60e51b81526004018080602001828103825260288152602001806144836028913960400191505060405180910390fd5b6001600160a01b038116600090815260406020819052902060020154431015612ddb5760405162461bcd60e51b81526004018080602001828103825260278152602001806147286027913960400191505060405180910390fd5b6001600160a01b03808216600090815260406020819052902054612dff9116613730565b15612e3b5760405162461bcd60e51b815260040180806020018281038252603e815260200180614538603e913960400191505060405180910390fd5b6001600160a01b038082166000818152604060208190528082208054600190910154603454835163666cc1c560e11b81529287166004840181905260248401969096526044830182905292519495909492169263ccd9838a9260648084019382900301818387803b158015612eaf57600080fd5b505af1158015612ec3573d6000803e3d6000fd5b5050506001600160a01b0383166000908152603d6020526040902054612f57915084908490612ef8908563ffffffff61398216565b6001600160a01b038088166000908152603e60209081526040808320938a1683529290522054612f2e908663ffffffff61398216565b6001600160a01b0388166000908152603f6020526040902054610d56908763ffffffff61398216565b6039546001600160a01b038085166000908152603e602090815260408083209387168352929052205410801590612fbd57506001600160a01b038083166000818152604260209081526040808320549488168352603e8252808320938352929052205410155b80612feb57506001600160a01b038084166000908152603e6020908152604080832093861683529290522054155b6040518060600160405280603381526020016144e460339139906130505760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b506001600160a01b038084166000908152603e6020908152604080832093861683529290522054613085576130858284613ad1565b6001600160a01b0382166000908152603d60205260409020600101546130b7908390611583908463ffffffff61398216565b6130c0836139ff565b80826001600160a01b0316846001600160a01b03167fdf026d8db1c407002e7abde612fb40b6031db7aa35d4b3b699d07627f891e63160405160405180910390a460355460408051631e4e7d3560e31b81526001600160a01b0385811660048301529151600093929092169163f273e9a89160248082019260c092909190829003018186803b15801561315257600080fd5b505afa158015613166573d6000803e3d6000fd5b505050506040513d60c081101561317c57600080fd5b505160355460408051635c85e42960e11b81526001600160a01b03878116600483015260248201859052915193945091169163b90bc8529160448082019260009290919082900301818387803b1580156131d557600080fd5b505af11580156131e9573d6000803e3d6000fd5b5050506001600160a01b039485166000908152603e60209081526040808320969097168252949094525050502054905090565b606061322661329d565b6001600160a01b0382166000908152603d60209081526040918290206002018054835181840281018401909452808452909183018282801561329157602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613273575b50505050509050919050565b6033546040805180820190915260208082527f496e697469616c697a61626c6556323a204e6f7420696e697469616c697a6564908201529060ff161515600114611d555760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b303b1590565b806001600160a01b0316630ea773076040518163ffffffff1660e01b815260040160206040518083038186803b15801561336757600080fd5b505afa15801561337b573d6000803e3d6000fd5b505050506040513d602081101561339157600080fd5b505115156001146133d35760405162461bcd60e51b81526004018080602001828103825260468152602001806145766046913960600191505060405180910390fd5b603380546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b6000603360019054906101000a90046001600160a01b03169050806001600160a01b031663062888856040518163ffffffff1660e01b815260040160206040518083038186803b15801561344e57600080fd5b505afa158015613462573d6000803e3d6000fd5b505050506040513d602081101561347857600080fd5b505160408051633ecc6a4360e01b815290516001600160a01b03841691633ecc6a43916004808301926020929190829003018186803b1580156134ba57600080fd5b505afa1580156134ce573d6000803e3d6000fd5b505050506040513d60208110156134e457600080fd5b50510182116135245760405162461bcd60e51b81526004018080602001828103825260708152602001806148826070913960800191505060405180910390fd5b50603755565b6000603360019054906101000a90046001600160a01b03169050806001600160a01b031663062888856040518163ffffffff1660e01b815260040160206040518083038186803b15801561357d57600080fd5b505afa158015613591573d6000803e3d6000fd5b505050506040513d60208110156135a757600080fd5b505160408051633ecc6a4360e01b815290516001600160a01b03841691633ecc6a43916004808301926020929190829003018186803b1580156135e957600080fd5b505afa1580156135fd573d6000803e3d6000fd5b505050506040513d602081101561361357600080fd5b50510182116136535760405162461bcd60e51b81526004018080602001828103825260758152602001806142ad6075913960800191505060405180910390fd5b50603a55565b6034546001600160a01b03166136a05760405162461bcd60e51b815260040180806020018281038252602a8152602001806143b3602a913960400191505060405180910390fd5b565b6035546001600160a01b03166136a05760405162461bcd60e51b81526004018080602001828103825260398152602001806144236039913960400191505060405180910390fd5b6036546001600160a01b03166136a05760405162461bcd60e51b81526004018080602001828103825260308152602001806148256030913960400191505060405180910390fd5b6036546040805163d017f48360e01b81526001600160a01b03848116600483015291516000939290921691829163d017f483916024808301926020929190829003018186803b15801561378257600080fd5b505afa158015613796573d6000803e3d6000fd5b505050506040513d60208110156137ac57600080fd5b50519392505050565b6000805b6001600160a01b0383166000908152603d6020526040902060020154811015613835576001600160a01b038381166000908152603d602052604090206002018054918616918390811061380857fe5b6000918252602090912001546001600160a01b0316141561382d576001915050610ef7565b6001016137b9565b5060009392505050565b600082820183811015613899576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b9392505050565b6001600160a01b038085166000818152603d602090815260408083208890559389168252603e815283822092825291909152208290556138e085826139c4565b5050505050565b6000826138f657506000610ef7565b8282028284828161390357fe5b04146138995760405162461bcd60e51b81526004018080602001828103825260218152602001806145176021913960400191505060405180910390fd5b600061389983836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f00000000000081525061415a565b600061389983836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f7700008152506141bf565b6001600160a01b039091166000908152603f6020526040902055565b6001600160a01b039091166000908152603d6020526040902060010155565b611d55816000806000613a79565b6001600160a01b03811660009081526040602081905281206002015415801590613a5157506001600160a01b03821660009081526040602081905290206001015415155b8015610ef75750506001600160a01b0390811660009081526040602081905290205416151590565b604080516060810182526001600160a01b03948516815260208082019485528183019384529585166000908152958290529420935184546001600160a01b031916931692909217835551600183015551600290910155565b60005b6001600160a01b0383166000908152603d6020526040902060020154811015613bfb576001600160a01b038381166000908152603d6020526040902060020180549184169183908110613b2357fe5b6000918252602090912001546001600160a01b03161415613bf3576001600160a01b0383166000908152603d6020526040902060020180546000198101908110613b6957fe5b60009182526020808320909101546001600160a01b038681168452603d9092526040909220600201805491909216919083908110613ba357fe5b600091825260208083209190910180546001600160a01b0319166001600160a01b039485161790559185168152603d90915260409020600201805490613bed906000198301614243565b50613bfb565b600101613ad4565b505050565b600080600080600080876001600160a01b031663ff653c8a886040518263ffffffff1660e01b815260040180826001600160a01b03166001600160a01b03168152602001915050604080518083038186803b158015613c5e57600080fd5b505afa158015613c72573d6000803e3d6000fd5b505050506040513d6040811015613c8857600080fd5b50516001600160a01b0388166000908152603d602052604081206001015491925090613cba908363ffffffff61383f16565b60365460408051631bff085760e21b81526001600160a01b038c811660048301526024820185905291519394506000939190921691636ffc215c91604480830192602092919082900301818787803b158015613d1557600080fd5b505af1158015613d29573d6000803e3d6000fd5b505050506040513d6020811015613d3f57600080fd5b505160345460408051634b341aed60e01b81526001600160a01b038d811660048301529151939450911691634b341aed91602480820192602092909190829003018186803b158015613d9057600080fd5b505afa158015613da4573d6000803e3d6000fd5b505050506040513d6020811015613dba57600080fd5b505160408051631e4e7d3560e31b81526001600160a01b038c811660048301529151929a50908c169163f273e9a89160248082019260c092909190829003018186803b158015613e0957600080fd5b505afa158015613e1d573d6000803e3d6000fd5b505050506040513d60c0811015613e3357600080fd5b5080516020918201516001600160a01b038c166000908152603d90935260408320549199509550613e6b90899063ffffffff61383f16565b9050613e7d818463ffffffff61398216565b9650613e8f898263ffffffff61398216565b8214613ecc5760405162461bcd60e51b815260040180806020018281038252602781526020018061445c6027913960400191505060405180910390fd5b88868b6001600160a01b03167f34fcbac0073d7c3d388e51312faf357774904998eeb8fca628b9e6f65ee1cbf760405160405180910390a450935050509295509295909350565b6000805b6001600160a01b0387166000908152603d6020526040902060020154811015614150576001600160a01b0387166000908152603d60205260408120600201805483908110613f6157fe5b60009182526020808320909101546001600160a01b03908116808452603e835260408085208d84168087529085528186205483875294829052942054909450919291161415613fda576001600160a01b038216600090815260406020819052902060010154613fd790829063ffffffff61398216565b90505b6000613ff0896113ec848b63ffffffff6138e716565b905060006140276140078b8963ffffffff6138e716565b6113ec8a61401b878e63ffffffff6138e716565b9063ffffffff6138e716565b905061409361403c838363ffffffff61398216565b603e6000876001600160a01b03166001600160a01b0316815260200190815260200160002060008e6001600160a01b03166001600160a01b031681526020019081526020016000205461383f90919063ffffffff16565b603e6000866001600160a01b03166001600160a01b0316815260200190815260200160002060008d6001600160a01b03166001600160a01b031681526020019081526020016000208190555061411e846114ef6140f9848661398290919063ffffffff16565b6001600160a01b0388166000908152603f60205260409020549063ffffffff61383f16565b61413e614131838363ffffffff61398216565b879063ffffffff61383f16565b95505060019093019250613f17915050565b5095945050505050565b600081836141a95760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b5060008385816141b557fe5b0495945050505050565b600081848411156142115760405162461bcd60e51b815260206004820181815283516024840152835190928392604490910191908501908083836000831561097d578181015183820152602001610965565b505050900390565b604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b815481835581811115613bfb57600083815260209020613bfb91810190830161075591905b8082111561427c5760008155600101614268565b509056fe44656c65676174654d616e616765723a204d6178696d756d2064656c656761746f727320657863656564656444656c65676174654d616e616765723a2072656d6f766544656c656761746f724c6f636b75704475726174696f6e206475726174696f6e206d7573742062652067726561746572207468616e20676f7665726e616e636520766f74696e67506572696f64202b20657865637574696f6e44656c617944656c65676174654d616e616765723a2044656c65676174696f6e206e6f74207065726d697474656420666f722053502070656e64696e6720636c61696d44656c65676174654d616e616765723a204e6f2070656e64696e67207265717565737444656c65676174654d616e616765723a2044656c656761746f72206d757374206265207374616b656420666f7220535044656c65676174654d616e616765723a207374616b696e6741646472657373206973206e6f742073657444656c65676174654d616e616765723a20556e64656c65676174652072657175657374206e6f74207065726d697474656420666f722053502070656e64696e6720636c61696d44656c65676174654d616e616765723a207365727669636550726f7669646572466163746f727941646472657373206973206e6f742073657444656c65676174654d616e616765723a2052657761726420616d6f756e74206d69736d6174636844656c65676174654d616e616765723a2050656e64696e67206c6f636b757020657870656374656444656c65676174654d616e616765723a204f6e6c792063616c6c61626c6520627920746172676574205350206f7220676f7665726e616e636544656c65676174654d616e616765723a204d696e696d756d2064656c65676174696f6e20616d6f756e74207265717569726564536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f7744656c65676174654d616e616765723a20556e64656c6567617465206e6f74207065726d697474656420666f722053502070656e64696e6720636c61696d44656c65676174654d616e616765723a205f676f7665726e616e636541646472657373206973206e6f7420612076616c696420676f7665726e616e636520636f6e747261637444656c65676174654d616e616765723a20536572766963652050726f7669646572207374616b65207265717569726564436f6e747261637420696e7374616e63652068617320616c7265616479206265656e20696e697469616c697a656444656c65676174654d616e616765723a2052657175657374656420756e64656c6567617465207374616b6520616d6f756e74206d7573742062652067726561746572207468616e207a65726f44656c65676174654d616e616765723a2050656e64696e672072656d6f76652064656c656761746f72207265717565737444656c65676174654d616e616765723a2043616e6e6f742064656372656173652067726561746572207468616e2063757272656e746c79207374616b656420666f722074686973205365727669636550726f766964657244656c65676174654d616e616765723a2052656d6f766544656c656761746f72206576616c756174696f6e2077696e646f77206578706972656444656c65676174654d616e616765723a204c6f636b7570206d757374206265206578706972656444656c65676174654d616e616765723a204f6e6c792063616c6c61626c652062792076616c696420536572766963652050726f766964657244656c65676174654d616e616765723a204e6f2070656e64696e67206c6f636b757020657870656374656444656c65676174654d616e616765723a204f6e6c792063616c6c61626c6520627920476f7665726e616e636520636f6e747261637444656c65676174654d616e616765723a2043616e6e6f7420736c617368206d6f7265207468616e20746f74616c2063757272656e746c79207374616b656444656c65676174654d616e616765723a20636c61696d734d616e6167657241646472657373206973206e6f742073657444656c65676174654d616e616765723a20636c61696d5265776172647320616d6f756e74206d69736d6174636844656c65676174654d616e616765723a20756e64656c65676174654c6f636b75704475726174696f6e206475726174696f6e206d7573742062652067726561746572207468616e20676f7665726e616e636520766f74696e67506572696f64202b20657865637574696f6e44656c6179a265627a7a72315820b09a598ad0fd1e1002c94db815e00a0d1c35ee031853e1155378f5524c8d1c6f64736f6c63430005110032
Deployed Bytecode Sourcemap
235863:53747:0:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;235863:53747:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;275160:167;;;:::i;:::-;;;;-1:-1:-1;;;;;275160:167:0;;;;;;;;;;;;;;273809;;;:::i;:::-;;;;;;;;;;;;;;;;275580:142;;;:::i;274028:145::-;;;:::i;242558:792::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;242558:792:0;;;;;;;;;;;;;;;;;:::i;:::-;;261815:580;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;261815:580:0;;;;;;;;;;:::i;269857:306::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;269857:306:0;-1:-1:-1;;;;;269857:306:0;;:::i;243655:2520::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;243655:2520:0;;;;;;;;:::i;256060:4445::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;256060:4445:0;;;;;;-1:-1:-1;;;;;256060:4445:0;;:::i;273126:265::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;273126:265:0;;;;;;;;;;:::i;267463:305::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;267463:305:0;;:::i;265664:820::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;265664:820:0;;;;;;;;:::i;248422:898::-;;;:::i;260699:924::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;260699:924:0;;;;;;;;;;:::i;274953:143::-;;;:::i;271781:207::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;271781:207:0;-1:-1:-1;;;;;271781:207:0;;:::i;14353:80::-;;;:::i;274463:177::-;;;:::i;271497:200::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;271497:200:0;-1:-1:-1;;;;;271497:200:0;;:::i;267045:275::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;267045:275:0;;:::i;272479:344::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;272479:344:0;-1:-1:-1;;;;;272479:344:0;;:::i;:::-;;;;-1:-1:-1;;;;;272479:344:0;;;;;;;;;;;;;;;;;;;;;;;;;275382:149;;;:::i;274728:173::-;;;:::i;246464:1888::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;246464:1888:0;;;;;;;;:::i;270362:312::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;270362:312:0;-1:-1:-1;;;;;270362:312:0;;:::i;271236:187::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;271236:187:0;-1:-1:-1;;;;;271236:187:0;;:::i;274228:157::-;;;:::i;268351:302::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;268351:302:0;;:::i;272086:237::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;272086:237:0;;;;;;;;;;:::i;273587:158::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;273587:158:0;-1:-1:-1;;;;;273587:158:0;;:::i;268843:351::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;268843:351:0;-1:-1:-1;;;;;268843:351:0;;:::i;262665:2598::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;;;;;;262665:2598:0;;;;;;;;;;:::i;266619:299::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;266619:299:0;;:::i;253523:2261::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;253523:2261:0;-1:-1:-1;;;;;253523:2261:0;;:::i;269375:276::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;269375:276:0;-1:-1:-1;;;;;269375:276:0;;:::i;267897:314::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;267897:314:0;;:::i;249494:3736::-;;;:::i;270923:183::-;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;270923:183:0;-1:-1:-1;;;;;270923:183:0;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:100:-1;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;270923:183:0;;;;;;;;;;;;;;;;;275160:167;275227:7;275247:23;:21;:23::i;:::-;-1:-1:-1;275290:29:0;;-1:-1:-1;;;;;275290:29:0;275160:167;;:::o;273809:::-;273876:7;273901:23;:21;:23::i;:::-;-1:-1:-1;273944:24:0;;273809:167;:::o;275580:142::-;275632:7;275657:23;:21;:23::i;:::-;-1:-1:-1;275700:14:0;;-1:-1:-1;;;;;275700:14:0;275580:142;:::o;274028:145::-;274084:7;274109:23;:21;:23::i;:::-;-1:-1:-1;274152:13:0;;274028:145;:::o;242558:792::-;12382:10;;-1:-1:-1;;;;;12382:10:0;12368;:24;12360:68;;;;;-1:-1:-1;;;12360:68:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;12443:12;;;;;;;;:31;;;12459:15;:13;:15::i;:::-;12443:47;;;-1:-1:-1;12479:11:0;;;;12478:12;12443:47;12435:106;;;;-1:-1:-1;;;12435:106:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12573:12;;;;;;;12572:13;12592:83;;;;12621:12;:19;;-1:-1:-1;;;;12621:19:0;;;;;12649:18;12636:4;12649:18;;;12592:83;242734:44;242759:18;242734:24;:44::i;:::-;242789:11;:42;;-1:-1:-1;;;;;;242789:42:0;-1:-1:-1;;;;;242789:42:0;;;;;242858:3;242842:13;:19;242954:21;242932:19;:43;242986:28;:26;:28::i;:::-;243027:58;243059:25;243027:31;:58::i;:::-;243182:43;243219:5;243182:36;:43::i;:::-;243338:4;243308:27;:34;12693:57;;;;12722:12;:20;;-1:-1:-1;;12722:20:0;;;12693:57;242558:792;;;;:::o;261815:580::-;261941:10;-1:-1:-1;;;;;261941:30:0;;;;:65;;-1:-1:-1;261989:17:0;;;;;-1:-1:-1;;;;;261989:17:0;261975:10;:31;261941:65;262021:24;;;;;;;;;;;;;;;;;261919:137;;;;;-1:-1:-1;;;261919:137:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:10;8:100;;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;;12:14;261919:137:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;;262089:41:0;;;;;;;:23;:41;;;;;;;;:53;;;;;;;;;;262067:143;;;;-1:-1:-1;;;262067:143:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;262253:41:0;;;262309:1;262253:41;;;:23;:41;;;;;;;;:53;;;;;;;;;;;;;:57;;;262326:61;;;262309:1;262326:61;261815:580;;:::o;269857:306::-;269939:23;:21;:23::i;:::-;269997:17;;;;;;;;;-1:-1:-1;;;;;269997:17:0;-1:-1:-1;;;;;269983:31:0;:10;-1:-1:-1;;;;;269983:31:0;;270016:21;;;;;;;;;;;;;;;;;269975:63;;;;;-1:-1:-1;;;269975:63:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;269975:63:0;-1:-1:-1;270049:29:0;:42;;-1:-1:-1;;;;;;270049:42:0;-1:-1:-1;;;;;270049:42:0;;;;;;;;270107:48;;;;-1:-1:-1;;270107:48:0;269857:306;:::o;243655:2520::-;243757:7;243782:23;:21;:23::i;:::-;243816:29;:27;:29::i;:::-;243856:44;:42;:44::i;:::-;243911:35;:33;:35::i;:::-;243982:24;243996:9;243982:13;:24::i;:::-;243981:25;243959:137;;;;-1:-1:-1;;;243959:137:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;244182:14;;244265:113;;;-1:-1:-1;;;244265:113:0;;-1:-1:-1;;;;;244265:113:0;;;;;;;244127:10;244265:113;;;;;;;;;;;;;;244127:10;;244182:14;;;;244265:32;;:113;;;;;244107:17;;244265:113;;;;;;;244107:17;244182:14;244265:113;;;5:2:-1;;;;30:1;27;20:12;5:2;244265:113:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;244265:113:0;;;;244453:43;244475:9;244486;244453:21;:43::i;:::-;244448:365;;-1:-1:-1;;;;;244568:25:0;;;;;;;:14;:25;;;;;;;:36;;27:10:-1;;39:1;23:18;;45:23;;244568:52:0;;;;;;;;;;;-1:-1:-1;;;;;;244568:52:0;;;;;;;;;;;244708:13;;244661:25;;;:43;;:60;;244635:166;;;;-1:-1:-1;;;244635:166:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;245211:25:0;;;;;;:14;:25;;;;;:45;245127:273;;245163:9;;245187;;245211:58;;245261:7;245211:58;:49;:58;:::i;:::-;-1:-1:-1;;;;;245284:23:0;;;;;;;:12;:23;;;;;;;;:34;;;;;;;;;;:47;;245323:7;245284:47;:38;:47;:::i;:::-;-1:-1:-1;;;;;245346:30:0;;;;;;:19;:30;;;;;;:43;;245381:7;245346:43;:34;:43;:::i;:::-;245127:21;:273::i;:::-;245632:19;;-1:-1:-1;;;;;245594:23:0;;;;;;;:12;:23;;;;;;;;:34;;;;;;;;;;:57;;;;:146;;-1:-1:-1;;;;;;245707:33:0;;;;;;;:22;:33;;;;;;;;;245669:23;;;;;:12;:23;;;;;:34;;;;;;;;:71;;245594:146;245770:24;;;;;;;;;;;;;;;;;245571:234;;;;;-1:-1:-1;;;245571:234:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;245571:234:0;-1:-1:-1;245884:29:0;;245847:116;;;-1:-1:-1;;;245847:116:0;;-1:-1:-1;;;;;245847:116:0;;;;;;;;;245884:29;;;;;245847:105;;:116;;;;;245884:29;;245847:116;;;;;;;245884:29;245847:116;;;5:2:-1;;;;30:1;27;20:12;5:2;245847:116:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;245847:116:0;;;;246066:7;246042:9;-1:-1:-1;;;;;245981:103:0;246018:9;-1:-1:-1;;;;;245981:103:0;;;;;;;;;;;-1:-1:-1;;;;;;246133:23:0;;;;;;;:12;:23;;;;;;;;:34;;;;;;;;;;;-1:-1:-1;243655:2520:0;;;;;:::o;256060:4445::-;256145:23;:21;:23::i;:::-;256179:29;:27;:29::i;:::-;256219:44;:42;:44::i;:::-;256298:17;;;;;;;;;-1:-1:-1;;;;;256298:17:0;-1:-1:-1;;;;;256284:31:0;:10;-1:-1:-1;;;;;256284:31:0;;256317:21;;;;;;;;;;;;;;;;;256276:63;;;;;-1:-1:-1;;;256276:63:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;256276:63:0;-1:-1:-1;256386:14:0;;256470:29;;256609:45;;;-1:-1:-1;;;256609:45:0;;-1:-1:-1;;;;;256609:45:0;;;;;;;;;256386:14;;;;256470:29;;;;;-1:-1:-1;;256386:14:0;;256609:30;;:45;;;;;;;;;;;;;;256386:14;256609:45;;;5:2:-1;;;;30:1;27;20:12;5:2;256609:45:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;256609:45:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;256609:45:0;;-1:-1:-1;256688:40:0;;;;256665:154;;;;-1:-1:-1;;;256665:154:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;256927:55;;;-1:-1:-1;;;;;;256927:55:0;;-1:-1:-1;;;;;256927:55:0;;;;;;;;;256901:21;;256927:40;;;;;;:55;;;;;;;;;;;;:40;:55;;;5:2:-1;;;;30:1;27;20:12;5:2;256927:55:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;256927:55:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;256927:55:0;;-1:-1:-1;256997:17:0;;256993:101;;257031:9;-1:-1:-1;;;;;257031:36:0;;257068:13;257031:51;;;;;;;;;;;;;-1:-1:-1;;;;;257031:51:0;-1:-1:-1;;;;;257031:51:0;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;257031:51:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;257031:51:0;;;;256993:101;257157:31;257212:9;-1:-1:-1;;;;;257212:35:0;;257248:13;257212:50;;;;;;;;;;;;;-1:-1:-1;;;;;257212:50:0;-1:-1:-1;;;;;257212:50:0;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;257212:50:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;257212:50:0;;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;257212:50:0;;-1:-1:-1;257306:27:0;257284:125;;;;-1:-1:-1;;;257284:125:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;257552:15;-1:-1:-1;;;;;257552:21:0;;257574:7;257583:13;257552:45;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;257552:45:0;-1:-1:-1;;;;;257552:45:0;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;257552:45:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;257552:45:0;;;;257608:39;257650:15;-1:-1:-1;;;;;257650:30:0;;257681:13;257650:45;;;;;;;;;;;;;-1:-1:-1;;;;;257650:45:0;-1:-1:-1;;;;;257650:45:0;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;257650:45:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;257650:45:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;257650:45:0;257742:62;;257650:45;;-1:-1:-1;257650:45:0;;257763:7;;-1:-1:-1;;;;;257742:62:0;;;;;;;;257817:35;;258020:1779;-1:-1:-1;;;;;258044:29:0;;;;;;:14;:29;;;;;:40;;:47;258040:51;;258020:1779;;;-1:-1:-1;;;;;258133:29:0;;258113:17;258133:29;;;:14;:29;;;;;:40;;:43;;258174:1;;258133:43;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;258133:43:0;;;258223:23;;;:12;:23;;;;;;:38;;;;;;;;;;;;258133:43;;-1:-1:-1;258223:38:0;258303:124;258397:29;258319:58;:31;258223:38;258319:58;:35;:58;:::i;:::-;258303:93;:124;:93;:124;:::i;:::-;258276:151;-1:-1:-1;258586:87:0;258629:43;:21;258276:151;258629:43;:25;:43;:::i;:::-;258586:12;:23;258599:9;-1:-1:-1;;;;;258586:23:0;-1:-1:-1;;;;;258586:23:0;;;;;;;;;;;;:38;258610:13;-1:-1:-1;;;;;258586:38:0;-1:-1:-1;;;;;258586:38:0;;;;;;;;;;;;;:42;;:87;;;;:::i;:::-;258526:12;:23;258539:9;-1:-1:-1;;;;;258526:23:0;-1:-1:-1;;;;;258526:23:0;;;;;;;;;;;;:38;258550:13;-1:-1:-1;;;;;258526:38:0;-1:-1:-1;;;;;258526:38:0;;;;;;;;;;;;:162;;;;258752:167;258797:9;258825:79;258860:43;258886:16;258860:21;:25;;:43;;;;:::i;:::-;-1:-1:-1;;;;;258825:30:0;;;;;;:19;:30;;;;;;;:79;:34;:79;:::i;:::-;258752:26;:167::i;:::-;259028:76;259060:43;:21;259086:16;259060:43;:25;:43;:::i;:::-;259028:27;;:76;:31;:76;:::i;:::-;-1:-1:-1;;;;;259275:29:0;;;;;;:18;:29;;;;;;:36;;;258979:140;;-1:-1:-1;259275:41:0;259271:517;;-1:-1:-1;;;;;259357:29:0;;;259337:17;259357:29;;;:18;:29;;;;;;;:45;;;259445:36;;;;259357:45;;259632:25;;;:14;:25;;;;;;;:44;;259357:45;;259445:36;259543:171;;259357:45;;259632:63;;259445:36;259632:63;:48;:63;:::i;:::-;259543:34;:171::i;:::-;259733:39;259762:9;259733:28;:39::i;:::-;259271:517;;;-1:-1:-1;;;258093:3:0;;258020:1779;;;-1:-1:-1;;;;;;259924:29:0;;;;;;:14;:29;;;;;:49;:82;;259978:27;259924:82;:53;:82;:::i;:::-;-1:-1:-1;;;;;259857:29:0;;;;;;:14;:29;;;;;:160;;;;260133:66;:29;260167:31;260133:66;:33;:66;:::i;:::-;260089:121;-1:-1:-1;260221:37:0;260276:51;260089:121;260299:27;260276:51;:22;:51;:::i;:::-;260221:117;-1:-1:-1;;;;;;260349:36:0;;;260400:13;260428:58;:23;260221:117;260428:58;:27;:58;:::i;:::-;260349:148;;;;;;;;;;;;;-1:-1:-1;;;;;260349:148:0;-1:-1:-1;;;;;260349:148:0;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;260349:148:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;260349:148:0;;;;256060:4445;;;;;;;;;;;:::o;273126:265::-;273262:7;273287:23;:21;:23::i;:::-;-1:-1:-1;;;;;;273330:41:0;;;;;;;:23;:41;;;;;;;;:53;;;;;;;;;;;;;273126:265::o;267463:305::-;267548:23;:21;:23::i;:::-;267606:17;;;;;;;;;-1:-1:-1;;;;;267606:17:0;-1:-1:-1;;;;;267592:31:0;:10;-1:-1:-1;;;;;267592:31:0;;267625:21;;;;;;;;;;;;;;;;;267584:63;;;;;-1:-1:-1;;;267584:63:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;267584:63:0;-1:-1:-1;267660:19:0;:42;;;267718;;267682:20;;267718:42;;;;;267463:305;:::o;265664:820::-;265804:23;:21;:23::i;:::-;265862:16;-1:-1:-1;;;;;265848:30:0;:10;-1:-1:-1;;;;;265848:30:0;;265880:27;;;;;;;;;;;;;;;;;265840:68;;;;;-1:-1:-1;;;265840:68:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;265840:68:0;-1:-1:-1;266146:29:0;;266123:111;;;-1:-1:-1;;;266123:111:0;;-1:-1:-1;;;;;266123:111:0;;;;;;;;;266082:20;;266146:29;;;;;266123:93;;:111;;;;;;;;;;;;;;;266146:29;266123:111;;;5:2:-1;;;;30:1;27;20:12;5:2;266123:111:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;266123:111:0;;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;266123:111:0;;;;;;266282:27;;;;;;;;;;;266123:111;;-1:-1:-1;266264:16:0;;;;266282:27;;266123:111;266282:27;;;266256:54;;;;;-1:-1:-1;;;266256:54:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;266256:54:0;-1:-1:-1;;;;;;266323:40:0;;;;;;:22;:40;;;;;;:65;;;266406:70;266366:22;;266323:40;266406:70;;;265664:820;;;:::o;248422:898::-;248482:23;:21;:23::i;:::-;248538:10;248628:38;248538:10;248628:27;:38::i;:::-;248606:128;;;;-1:-1:-1;;;248606:128:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;248769:29:0;;;248745:21;248769:29;;;:18;:29;;;;;;;:36;;;;;248840:45;;;;;249061:29;;;:14;:29;;;;;;;:48;;;;248984:155;;248840:45;;249061:67;;248769:36;249061:67;:52;:67;:::i;248984:155::-;249185:39;249214:9;249185:28;:39::i;:::-;249298:13;249283;-1:-1:-1;;;;;249240:72:0;249272:9;-1:-1:-1;;;;;249240:72:0;;;;;;;;;;;248422:898;;;:::o;260699:924::-;260797:23;:21;:23::i;:::-;260855:10;-1:-1:-1;;;;;260855:30:0;;;;:65;;-1:-1:-1;260903:17:0;;;;;-1:-1:-1;;;;;260903:17:0;260889:10;:31;260855:65;260935:24;;;;;;;;;;;;;;;;;260833:137;;;;;-1:-1:-1;;;260833:137:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;260833:137:0;-1:-1:-1;;;;;;261005:41:0;;;;;;;:23;:41;;;;;;;;:53;;;;;;;;;;:58;260983:157;;;;-1:-1:-1;;;260983:157:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;261175:51;261197:10;261209:16;261175:21;:51::i;:::-;261241:21;;;;;;;;;;;;;;;;;261153:120;;;;;-1:-1:-1;;;261153:120:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;261153:120:0;-1:-1:-1;261398:29:0;;-1:-1:-1;;;;;261312:41:0;;;;;;;:23;:41;;;;;;;;:53;;;;;;;;;;;;;261383:12;:44;;;261312:126;;;;261456:159;;;261312:41;261456:159;260699:924;;:::o;274953:143::-;275008:7;275028:23;:21;:23::i;:::-;-1:-1:-1;275071:17:0;;;;;-1:-1:-1;;;;;275071:17:0;;274953:143::o;271781:207::-;271874:7;271899:23;:21;:23::i;:::-;-1:-1:-1;;;;;;271942:19:0;;;;;:14;:19;;;;;:38;;;;271781:207::o;14353:80::-;12382:10;;-1:-1:-1;;;;;12382:10:0;12368;:24;12360:68;;;;;-1:-1:-1;;;12360:68:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;12443:12;;;;;;;;:31;;;12459:15;:13;:15::i;:::-;12443:47;;;-1:-1:-1;12479:11:0;;;;12478:12;12443:47;12435:106;;;;-1:-1:-1;;;12435:106:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;12573:12;;;;;;;12572:13;12592:83;;;;12621:12;:19;;-1:-1:-1;;;;12621:19:0;;;;;12649:18;12636:4;12649:18;;;12592:83;14405:13;:20;;-1:-1:-1;;14405:20:0;14421:4;14405:20;;;12693:57;;;;12722:12;:20;;-1:-1:-1;;12722:20:0;;;12693:57;14353:80;:::o;274463:177::-;274535:7;274560:23;:21;:23::i;:::-;-1:-1:-1;274603:29:0;;274463:177;:::o;271497:200::-;271582:7;271607:23;:21;:23::i;:::-;-1:-1:-1;;;;;;271650:19:0;;;;;:14;:19;;;;;:39;;271497:200::o;267045:275::-;267118:23;:21;:23::i;:::-;267176:17;;;;;;;;;-1:-1:-1;;;;;267176:17:0;-1:-1:-1;;;;;267162:31:0;:10;-1:-1:-1;;;;;267162:31:0;;267195:21;;;;;;;;;;;;;;;;;267154:63;;;;;-1:-1:-1;;;267154:63:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;267154:63:0;-1:-1:-1;267230:13:0;:30;;;267276:36;;267246:14;;267276:36;;;;;267045:275;:::o;272479:344::-;272564:14;272580;272596:25;272639:23;:21;:23::i;:::-;272675:33;;:::i;:::-;-1:-1:-1;;;;;;;;;272711:30:0;;;;;;;:18;:30;;;;;;;;272675:66;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;272479:344::o;275382:149::-;275440:7;275460:23;:21;:23::i;:::-;-1:-1:-1;275503:20:0;;-1:-1:-1;;;;;275503:20:0;275382:149;:::o;274728:173::-;274798:7;274823:23;:21;:23::i;:::-;-1:-1:-1;274866:27:0;;274728:173;:::o;246464:1888::-;246573:7;246598:23;:21;:23::i;:::-;246632:35;:33;:35::i;:::-;246712:1;246702:7;:11;246680:137;;;;-1:-1:-1;;;246680:137:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;246851:22;246865:7;246851:13;:22::i;:::-;246850:23;246828:143;;;;-1:-1:-1;;;246828:143:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;247002:10;247045:41;247002:10;247078:7;247045:21;:41::i;:::-;247101:21;;;;;;;;;;;;;;;;;247023:110;;;;;-1:-1:-1;;;247023:110:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;247023:110:0;;247219:38;247247:9;247219:27;:38::i;:::-;247218:39;247196:132;;;;-1:-1:-1;;;247196:132:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;247406:23:0;;;247373:30;247406:23;;;:12;:23;;;;;;;;:32;;;;;;;;;;247471:33;;;;247449:170;;;;-1:-1:-1;;;247449:170:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;247734:25;247762:42;247779:24;;247762:12;:16;;:42;;;;:::i;:::-;247734:70;;247815:140;247859:9;247883:7;247905;247927:17;247815:29;:140::i;:::-;-1:-1:-1;;;;;248125:23:0;;;;;;:14;:23;;;;;:42;;;248054:137;;248103:7;;248125:55;;248172:7;248125:55;:46;:55;:::i;248054:137::-;248254:7;248245;-1:-1:-1;;;;;248209:72:0;248234:9;-1:-1:-1;;;;;248209:72:0;;248263:17;248209:72;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;248299:23:0;;;;;;;:12;:23;;;;;;;;:32;;;;;;;;;;:45;;248336:7;248299:45;:36;:45;:::i;:::-;248292:52;246464:1888;-1:-1:-1;;;;;;246464:1888:0:o;270362:312::-;270446:23;:21;:23::i;:::-;270504:17;;;;;;;;;-1:-1:-1;;;;;270504:17:0;-1:-1:-1;;;;;270490:31:0;:10;-1:-1:-1;;;;;270490:31:0;;270523:21;;;;;;;;;;;;;;;;;270482:63;;;;;-1:-1:-1;;;270482:63:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;270482:63:0;-1:-1:-1;270556:20:0;:44;;-1:-1:-1;;;;;;270556:44:0;-1:-1:-1;;;;;270556:44:0;;;;;;;;270616:50;;;;-1:-1:-1;;270616:50:0;270362:312;:::o;271236:187::-;271316:7;271341:23;:21;:23::i;:::-;-1:-1:-1;;;;;;271384:31:0;;;;;:19;:31;;;;;;;271236:187::o;274228:157::-;274290:7;274315:23;:21;:23::i;:::-;-1:-1:-1;274358:19:0;;274228:157;:::o;268351:302::-;268433:23;:21;:23::i;:::-;268491:17;;;;;;;;;-1:-1:-1;;;;;268491:17:0;-1:-1:-1;;;;;268477:31:0;:10;-1:-1:-1;;;;;268477:31:0;;268510:21;;;;;;;;;;;;;;;;;268469:63;;;;;-1:-1:-1;;;268469:63:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;268469:63:0;-1:-1:-1;268545:27:0;:39;;;268600:45;;268575:9;;268600:45;;;;;268351:302;:::o;272086:237::-;272205:7;272230:23;:21;:23::i;:::-;-1:-1:-1;;;;;;272273:24:0;;;;;;;:12;:24;;;;;;;;:42;;;;;;;;;;;;;272086:237::o;273587:158::-;-1:-1:-1;;;;;273697:40:0;273670:7;273697:40;;;:22;:40;;;;;;;273587:158::o;268843:351::-;268921:23;:21;:23::i;:::-;268979:17;;;;;;;;;-1:-1:-1;;;;;268979:17:0;-1:-1:-1;;;;;268965:31:0;:10;-1:-1:-1;;;;;268965:31:0;;268998:21;;;;;;;;;;;;;;;;;268957:63;;;;;-1:-1:-1;;;268957:63:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;268957:63:0;;269033:44;269058:18;269033:24;:44::i;:::-;269088:17;:38;;-1:-1:-1;;;;;;269088:38:0;;-1:-1:-1;;;;;269088:38:0;;;;;;;;;;;;269142:44;;;;-1:-1:-1;;269142:44:0;268843:351;:::o;262665:2598::-;262756:23;:21;:23::i;:::-;262790:29;:27;:29::i;:::-;262854:10;-1:-1:-1;;;;;262854:30:0;;;;:65;;-1:-1:-1;262902:17:0;;;;;-1:-1:-1;;;;;262902:17:0;262888:10;:31;262854:65;262934:24;;;;;;;;;;;;;;;;;262832:137;;;;;-1:-1:-1;;;262832:137:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;262832:137:0;-1:-1:-1;;;;;;263004:41:0;;;;;;;:23;:41;;;;;;;;:53;;;;;;;;;;262982:143;;;;-1:-1:-1;;;262982:143:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;263216:41:0;;;;;;;:23;:41;;;;;;;;:53;;;;;;;;;;263200:12;:69;;263178:158;;;;-1:-1:-1;;;263178:158:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;263492:27;;-1:-1:-1;;;;;263436:41:0;;;;;;;:23;:41;;;;;;;;:53;;;;;;;;;;:83;263421:12;:98;263399:206;;;;-1:-1:-1;;;263399:206:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;263642:24:0;;;263618:21;263642:24;;;:12;:24;;;;;;;;:42;;;;;;;;;;;;263760:14;;263752:137;;-1:-1:-1;;;263752:137:0;;;;;;;;;;;;;;;;;;;;;;;;263642:42;;263760:14;;;;;263752:42;;:137;;;;;263618:21;;263752:137;;;;;;263618:21;263760:14;263752:137;;;5:2:-1;;;;30:1;27;20:12;5:2;263752:137:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;;;;;;;264233:32:0;;;;;;:14;:32;;;;;:52;264141:315;;-1:-1:-1;264177:10:0;;264202:16;;264233:71;;264290:13;264233:71;:56;:71;:::i;:::-;-1:-1:-1;;;;;264319:24:0;;;;;;;:12;:24;;;;;;;;:42;;;;;;;;;;:61;;264366:13;264319:61;:46;:61;:::i;:::-;-1:-1:-1;;;;;264395:31:0;;;;;;:19;:31;;;;;;:50;;264431:13;264395:50;:35;:50;:::i;264141:315::-;264487:39;264515:10;264487:27;:39::i;:::-;:122;;;;-1:-1:-1;;;;;;264543:30:0;;;;;;;:18;:30;;;;;;:46;;;:66;;;;264487:122;264469:482;;;-1:-1:-1;;;;;264831:30:0;;;;;;;:18;:30;;;;;;;:37;;;;;264775:32;;;;;:14;:32;;;;;:51;;264687:197;;264740:16;;264775:94;;;:55;:94;:::i;264687:197::-;264899:40;264928:10;264899:28;:40::i;:::-;265006:55;265032:16;265050:10;265006:25;:55::i;:::-;-1:-1:-1;;;;;265106:41:0;;;265162:1;265106:41;;;:23;:41;;;;;;;;:53;;;;;;;;;;;;;:57;;;265179:76;265241:13;;265106:53;:41;265179:76;;;262665:2598;;;:::o;266619:299::-;266698:23;:21;:23::i;:::-;266756:17;;;;;;;;;-1:-1:-1;;;;;266756:17:0;-1:-1:-1;;;;;266742:31:0;:10;-1:-1:-1;;;;;266742:31:0;;266775:21;;;;;;;;;;;;;;;;;266734:63;;;;;-1:-1:-1;;;266734:63:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;266734:63:0;;266810:42;266842:9;266810:31;:42::i;:::-;266868;;266900:9;;266868:42;;;;;266619:299;:::o;253523:2261::-;253591:23;:21;:23::i;:::-;253625:29;:27;:29::i;:::-;253665:44;:42;:44::i;:::-;253720:35;:33;:35::i;:::-;253826:29;;-1:-1:-1;;;;;253826:29:0;253768:32;;;;;254190:50;253826:29;254223:16;254190:21;:50::i;:::-;253978:262;;;;;;;;;;254400:12;254416:1;254400:17;254396:56;;;254434:7;;;;;;;;254396:56;254464:35;254502:212;254543:16;254574;254605:12;254632:11;254658:9;-1:-1:-1;;;;;254658:43:0;;:45;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;254658:45:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;254658:45:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;254658:45:0;254502:26;:212::i;:::-;-1:-1:-1;;;;;254843:32:0;;;;;;:14;:32;;;;;:52;254464:250;;-1:-1:-1;254843:85:0;;254464:250;254843:85;:56;:85;:::i;:::-;-1:-1:-1;;;;;254773:32:0;;;;;;:14;:32;;;;;:166;;;;255239:45;:12;255256:27;255239:45;:16;:45;:::i;:::-;255215:69;-1:-1:-1;255369:27:0;255399:42;:23;255215:69;255399:42;:27;:42;:::i;:::-;-1:-1:-1;;;;;255525:32:0;;;;;;:14;:32;;;;;:52;255369:72;;-1:-1:-1;255501:77:0;;255369:72;;255501:77;:23;:77;:::i;:::-;255476:21;:102;255454:197;;;;-1:-1:-1;;;255454:197:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;255664:9;-1:-1:-1;;;;;255664:36:0;;255715:16;255746:19;255664:112;;;;;;;;;;;;;-1:-1:-1;;;;;255664:112:0;-1:-1:-1;;;;;255664:112:0;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;255664:112:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;255664:112:0;;;;253523:2261;;;;;;;;;;:::o;269375:276::-;269447:23;:21;:23::i;:::-;269505:17;;;;;;;;;-1:-1:-1;;;;;269505:17:0;-1:-1:-1;;;;;269491:31:0;:10;-1:-1:-1;;;;;269491:31:0;;269524:21;;;;;;;;;;;;;;;;;269483:63;;;;;-1:-1:-1;;;269483:63:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;269483:63:0;-1:-1:-1;269557:14:0;:32;;-1:-1:-1;;;;;;269557:32:0;-1:-1:-1;;;;;269557:32:0;;;;;;;;269605:38;;;;-1:-1:-1;;269605:38:0;269375:276;:::o;267897:314::-;267981:23;:21;:23::i;:::-;268039:17;;;;;;;;;-1:-1:-1;;;;;268039:17:0;-1:-1:-1;;;;;268025:31:0;:10;-1:-1:-1;;;;;268025:31:0;;268058:21;;;;;;;;;;;;;;;;;268017:63;;;;;-1:-1:-1;;;268017:63:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;268017:63:0;;268093:47;268130:9;268093:36;:47::i;:::-;268156;;268193:9;;268156:47;;;;;267897:314;:::o;249494:3736::-;249539:7;249559:23;:21;:23::i;:::-;249593:29;:27;:29::i;:::-;249633:44;:42;:44::i;:::-;249688:35;:33;:35::i;:::-;249756:10;249848:38;249756:10;249848:27;:38::i;:::-;249826:128;;;;-1:-1:-1;;;249826:128:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;250035:29:0;;;;;;:18;:29;;;;;;:47;;;250086:12;-1:-1:-1;250035:63:0;250013:152;;;;-1:-1:-1;;;250013:152:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;250278:29:0;;;;;;;:18;:29;;;;;;:45;250264:60;;250278:45;250264:13;:60::i;:::-;250263:61;250241:173;;;;-1:-1:-1;;;250241:173:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;250453:29:0;;;250427:23;250453:29;;;:18;:29;;;;;;;:45;;;250533:36;;;;250647:14;;250639:135;;-1:-1:-1;;;250639:135:0;;250453:45;;;250639:135;;;;;;;;;;;;;;;;;;;;;250453:45;;250533:36;;250647:14;;;250639:42;;:135;;;;;;;;;;250427:23;250647:14;250639:135;;;5:2:-1;;;;30:1;27;20:12;5:2;250639:135:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;;;;;;;251118:31:0;;;;;;:14;:31;;;;;:51;251028:309;;-1:-1:-1;251064:9:0;;251088:15;;251118:70;;251174:13;251118:70;:55;:70;:::i;:::-;-1:-1:-1;;;;;251203:23:0;;;;;;;:12;:23;;;;;;;;:40;;;;;;;;;;:59;;251248:13;251203:59;:44;:59;:::i;:::-;-1:-1:-1;;;;;251277:30:0;;;;;;:19;:30;;;;;;:49;;251312:13;251277:49;:34;:49;:::i;251028:309::-;251662:19;;-1:-1:-1;;;;;251618:23:0;;;;;;;:12;:23;;;;;;;;:40;;;;;;;;;;:63;;;;:167;;-1:-1:-1;;;;;;251746:39:0;;;;;;;:22;:39;;;;;;;;;251702:23;;;;;:12;:23;;;;;:40;;;;;;;;:83;;251618:167;251599:250;;;-1:-1:-1;;;;;;251804:23:0;;;;;;;:12;:23;;;;;;;;:40;;;;;;;;;;:45;251599:250;251864:24;;;;;;;;;;;;;;;;;251577:322;;;;;-1:-1:-1;;;251577:322:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;251577:322:0;-1:-1:-1;;;;;;251988:23:0;;;;;;;:12;:23;;;;;;;;:40;;;;;;;;;;251984:131;;252050:53;252076:15;252093:9;252050:25;:53::i;:::-;-1:-1:-1;;;;;252294:31:0;;;;;;:14;:31;;;;;:50;;;252215:159;;252264:15;;252294:69;;252349:13;252294:69;:54;:69;:::i;252215:159::-;252422:39;252451:9;252422:28;:39::i;:::-;252579:13;252549:15;-1:-1:-1;;;;;252479:124:0;252525:9;-1:-1:-1;;;;;252479:124:0;;;;;;;;;;;252900:29;;252877:96;;;-1:-1:-1;;;252877:96:0;;-1:-1:-1;;;;;252877:96:0;;;;;;;;;252830:23;;252900:29;;;;;252877:79;;:96;;;;;;;;;;;;;;;252900:29;252877:96;;;5:2:-1;;;;30:1;27;20:12;5:2;252877:96:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;252877:96:0;;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;252877:96:0;253018:29;;252877:96;252995:138;;-1:-1:-1;;;252995:138:0;;-1:-1:-1;;;;;252995:138:0;;;;;;;;;;;;;;;252877:96;;-1:-1:-1;253018:29:0;;;252995:80;;:138;;;;;253018:29;;252995:138;;;;;;;;253018:29;;252995:138;;;5:2:-1;;;;30:1;27;20:12;5:2;252995:138:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;;;;;;;253182:23:0;;;;;;;:12;:23;;;;;;;;:40;;;;;;;;;;-1:-1:-1;;;253182:40:0;;;-1:-1:-1;249494:3736:0;:::o;270923:183::-;270991:16;271025:23;:21;:23::i;:::-;-1:-1:-1;;;;;271068:19:0;;;;;;:14;:19;;;;;;;;;:30;;271061:37;;;;;;;;;;;;;;;;;271068:30;;271061:37;;271068:30;271061:37;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;271061:37:0;;;;;;;;;;;;;;;;;;;;;;;270923:183;;;:::o;14625:119::-;14691:13;;14714:21;;;;;;;;;;;;;;;;;;;14691:13;;:21;;:13;:21;14683:53;;;;-1:-1:-1;;;14683:53:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;12844:508:0;13261:4;13307:17;13339:7;12844:508;:::o;285584:319::-;285699:18;-1:-1:-1;;;;;285688:50:0;;:52;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;285688:52:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;285688:52:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;285688:52:0;:60;;285744:4;285688:60;285666:180;;;;-1:-1:-1;;;285666:180:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;285857:17;:38;;-1:-1:-1;;;;;285857:38:0;;;;;-1:-1:-1;;;;;;285857:38:0;;;;;;;;;285584:319::o;286721:434::-;286801:21;286836:17;;;;;;;;;-1:-1:-1;;;;;286836:17:0;286801:53;;286930:10;-1:-1:-1;;;;;286930:28:0;;:30;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;286930:30:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;286930:30:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;286930:30:0;286899:28;;;-1:-1:-1;;;286899:28:0;;;;-1:-1:-1;;;;;286899:26:0;;;;;:28;;;;;286930:30;;286899:28;;;;;;;:26;:28;;;5:2:-1;;;;30:1;27;20:12;5:2;286899:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;286899:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;286899:28:0;:61;286887:73;;286865:235;;;;-1:-1:-1;;;286865:235:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;287111:24:0;:36;286721:434::o;286090:449::-;286175:21;286210:17;;;;;;;;;-1:-1:-1;;;;;286210:17:0;286175:53;;286304:10;-1:-1:-1;;;;;286304:28:0;;:30;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;286304:30:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;286304:30:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;286304:30:0;286273:28;;;-1:-1:-1;;;286273:28:0;;;;-1:-1:-1;;;;;286273:26:0;;;;;:28;;;;;286304:30;;286273:28;;;;;;;:26;:28;;;5:2:-1;;;;30:1;27;20:12;5:2;286273:28:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;286273:28:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;286273:28:0;:61;286261:73;;286239:240;;;;-1:-1:-1;;;286239:240:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;286490:29:0;:41;286090:449::o;288946:194::-;289031:14;;-1:-1:-1;;;;;289031:14:0;289009:123;;;;-1:-1:-1;;;289009:123:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;288946:194::o;289148:239::-;289248:29;;-1:-1:-1;;;;;289248:29:0;289226:153;;;;-1:-1:-1;;;289226:153:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;289395:212;289486:20;;-1:-1:-1;;;;;289486:20:0;289464:135;;;;-1:-1:-1;;;289464:135:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;288075:198;288195:20;;288234:31;;;-1:-1:-1;;;288234:31:0;;-1:-1:-1;;;;;288234:31:0;;;;;;;;;288134:4;;288195:20;;;;;;;288234:26;;:31;;;;;;;;;;;;;;288195:20;288234:31;;;5:2:-1;;;;30:1;27;20:12;5:2;288234:31:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;288234:31:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;288234:31:0;;288075:198;-1:-1:-1;;;288075:198:0:o;287446:418::-;287571:4;;287593:219;-1:-1:-1;;;;;287617:32:0;;;;;;:14;:32;;;;;:43;;:50;287613:54;;287593:219;;;-1:-1:-1;;;;;287693:32:0;;;;;;;:14;:32;;;;;:43;;:46;;:60;;;;287737:1;;287693:46;;;;;;;;;;;;;;;;-1:-1:-1;;;;;287693:46:0;:60;287689:112;;;287781:4;287774:11;;;;;287689:112;287669:3;;287593:219;;;-1:-1:-1;287851:5:0;;287446:418;-1:-1:-1;;;287446:418:0:o;3861:181::-;3919:7;3951:5;;;3975:6;;;;3967:46;;;;;-1:-1:-1;;;3967:46:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;4033:1;3861:181;-1:-1:-1;;;3861:181:0:o;278857:707::-;-1:-1:-1;;;;;279168:32:0;;;;;;;:14;:32;;;;;;;;:90;;;279353:24;;;;;:12;:24;;;;;:42;;;;;;;;:75;;;279496:60;279366:10;279535:20;279496:26;:60::i;:::-;278857:707;;;;;:::o;5233:471::-;5291:7;5536:6;5532:47;;-1:-1:-1;5566:1:0;5559:8;;5532:47;5603:5;;;5607:1;5603;:5;:1;5627:5;;;;;:10;5619:56;;;;-1:-1:-1;;;5619:56:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6172:132;6230:7;6257:39;6261:1;6264;6257:39;;;;;;;;;;;;;;;;;:3;:39::i;4317:136::-;4375:7;4402:43;4406:1;4409;4402:43;;;;;;;;;;;;;;;;;:3;:43::i;280816:147::-;-1:-1:-1;;;;;280914:31:0;;;;;;;:19;:31;;;;;:41;280816:147::o;281188:232::-;-1:-1:-1;;;;;281338:32:0;;;;;;;:14;:32;;;;;:51;;:74;281188:232::o;279694:150::-;279777:59;279807:10;279827:1;279831;279834;279777:29;:59::i;288488:338::-;-1:-1:-1;;;;;288613:30:0;;288568:4;288613:30;;;:18;:30;;;;;;:48;;;:53;;;;288612:116;;-1:-1:-1;;;;;;288685:30:0;;;;;;:18;:30;;;;;;:37;;;:42;;288612:116;:195;;;;-1:-1:-1;;;;;;;288746:30:0;;;288804:1;288746:30;;;:18;:30;;;;;;:46;;:60;;;288488:338::o;280181:442::-;280450:165;;;;;;;;-1:-1:-1;;;;;280450:165:0;;;;;;;;;;;;;;;;;;280417:30;;;-1:-1:-1;280417:30:0;;;;;;;;;:198;;;;-1:-1:-1;;;;;;280417:198:0;;;;;;;;;;-1:-1:-1;280417:198:0;;;;;;;;;280181:442::o;281428:621::-;281539:9;281534:508;-1:-1:-1;;;;;281558:32:0;;;;;;:14;:32;;;;;:43;;:50;281554:54;;281534:508;;;-1:-1:-1;;;;;281634:32:0;;;;;;;:14;:32;;;;;:43;;:46;;:60;;;;281678:1;;281634:46;;;;;;;;;;;;;;;;-1:-1:-1;;;;;281634:46:0;:60;281630:401;;;-1:-1:-1;;;;;281821:32:0;;;;;;:14;:32;;;;;:43;;281865:50;;-1:-1:-1;;281865:54:0;;;281821:99;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;281772:32:0;;;;;:14;:32;;;;;;;:43;;:46;;281821:99;;;;;281772:43;281816:1;;281772:46;;;;;;;;;;;;;;;;;;:148;;-1:-1:-1;;;;;;281772:148:0;-1:-1:-1;;;;;281772:148:0;;;;;;281939:32;;;;;:14;:32;;;;;;:43;;:52;;;;;-1:-1:-1;;281939:52:0;;;:::i;:::-;;282010:5;;281630:401;281610:3;;281534:508;;;;281428:621;;:::o;276257:2113::-;276381:29;276421:31;276463:24;276498:20;276529:19;276650:21;276676:9;-1:-1:-1;;;;;276676:40:0;;276717:16;276676:58;;;;;;;;;;;;;-1:-1:-1;;;;;276676:58:0;-1:-1:-1;;;;;276676:58:0;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;276676:58:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;276676:58:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;276676:58:0;-1:-1:-1;;;;;276789:32:0;;276745:26;276789:32;;;:14;276676:58;276789:32;276676:58;276789:32;;:51;;;276676:58;;-1:-1:-1;276745:26:0;276789:70;;276676:58;276789:70;:55;:70;:::i;:::-;277069:20;;277055:123;;;-1:-1:-1;;;277055:123:0;;-1:-1:-1;;;;;277055:123:0;;;;;;;;;;;;;;;276745:125;;-1:-1:-1;277031:21:0;;277069:20;;;;;277055:48;;:123;;;;;;;;;;;;;;277031:21;277069:20;277055:123;;;5:2:-1;;;;30:1;27;20:12;5:2;277055:123:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;277055:123:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;277055:123:0;277279:14;;277271:56;;;-1:-1:-1;;;277271:56:0;;-1:-1:-1;;;;;277271:56:0;;;;;;;;;277055:123;;-1:-1:-1;277279:14:0;;;277271:38;;:56;;;;;277055:123;;277271:56;;;;;;;;277279:14;277271:56;;;5:2:-1;;;;30:1;27;20:12;5:2;277271:56:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;277271:56:0;;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;-1:-1;277271:56:0;277481:53;;;-1:-1:-1;;;277481:53:0;;-1:-1:-1;;;;;277481:53:0;;;;;;;;;277271:56;;-1:-1:-1;277481:35:0;;;;;;:53;;;;;;;;;;;;;;;:35;:53;;;5:2:-1;;;;30:1;27;20:12;5:2;277481:53:0;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;277481:53:0;;;;;;;13:3:-1;8;5:12;2:2;;;30:1;27;20:12;2:2;-1:-1;277481:53:0;;;;;;;-1:-1:-1;;;;;277747:32:0;;277667:34;277747:32;;;:14;:32;;;277481:53;277747:32;;:52;277481:53;;-1:-1:-1;277481:53:0;-1:-1:-1;277719:81:0;;277481:53;;277719:81;:27;:81;:::i;:::-;277667:144;-1:-1:-1;277843:50:0;277667:144;277874:18;277843:50;:30;:50;:::i;:::-;277824:69;-1:-1:-1;277945:53:0;:21;277971:26;277945:53;:25;:53;:::i;:::-;277928:13;:70;277906:159;;;;-1:-1:-1;;;277906:159:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;278150:21;278136:12;278118:16;-1:-1:-1;;;;;278112:60:0;;;;;;;;;;;-1:-1:-1;278312:13:0;-1:-1:-1;;;276257:2113:0;;;;;;;;:::o;282654:2762::-;282876:35;;283079:2281;-1:-1:-1;;;;;283103:19:0;;;;;;:14;:19;;;;;:30;;:37;283099:41;;283079:2281;;;-1:-1:-1;;;;;283182:19:0;;283162:17;283182:19;;;:14;:19;;;;;:30;;:33;;283213:1;;283182:33;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;283182:33:0;;;283258:23;;;:12;:23;;;;;;:28;;;;;;;;;;;;;283352:29;;;;;;;;;:45;283182:33;;-1:-1:-1;283258:28:0;;283352:45;;:52;283348:172;;;-1:-1:-1;;;;;283467:29:0;;;;;;:18;:29;;;;;;:36;;;283445:59;;:17;;:59;:21;:59;:::i;:::-;283425:79;;283348:172;283629:27;283659:91;283732:17;283676:36;:17;283698:13;283676:36;:21;:36;:::i;283659:91::-;283629:121;-1:-1:-1;284413:21:0;284437:167;284550:39;:17;284572:16;284550:39;:21;:39;:::i;:::-;284456:56;284499:12;284457:36;:17;284479:13;284457:36;:21;:36;:::i;:::-;284456:42;:56;:42;:56;:::i;284437:167::-;284413:191;-1:-1:-1;284882:72:0;284915:38;:19;284413:191;284915:38;:23;:38;:::i;:::-;284882:12;:23;284895:9;-1:-1:-1;;;;;284882:23:0;-1:-1:-1;;;;;284882:23:0;;;;;;;;;;;;:28;284906:3;-1:-1:-1;;;;;284882:28:0;-1:-1:-1;;;;;284882:28:0;;;;;;;;;;;;;:32;;:72;;;;:::i;:::-;284832:12;:23;284845:9;-1:-1:-1;;;;;284832:23:0;-1:-1:-1;;;;;284832:23:0;;;;;;;;;;;;:28;284856:3;-1:-1:-1;;;;;284832:28:0;-1:-1:-1;;;;;284832:28:0;;;;;;;;;;;;:137;;;;285034:162;285079:9;285107:74;285142:38;285166:13;285142:19;:23;;:38;;;;:::i;:::-;-1:-1:-1;;;;;285107:30:0;;;;;;:19;:30;;;;;;;:74;:34;:74;:::i;285034:162::-;285262:71;285294:38;:19;285318:13;285294:38;:23;:38;:::i;:::-;285262:27;;:71;:31;:71;:::i;:::-;285213:135;-1:-1:-1;;283142:3:0;;;;;-1:-1:-1;283079:2281:0;;-1:-1:-1;;283079:2281:0;;-1:-1:-1;282654:2762:0;;;;;;;:::o;6834:345::-;6920:7;7022:12;7015:5;7007:28;;;;-1:-1:-1;;;7007:28:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;7007:28:0;;7046:9;7062:1;7058;:5;;;;;;;6834:345;-1:-1:-1;;;;;6834:345:0:o;4790:192::-;4876:7;4912:12;4904:6;;;;4896:29;;;;-1:-1:-1;;;4896:29:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;27:10:-1;;8:100;;90:11;;;84:18;71:11;;;64:39;52:2;45:10;8:100;;4896:29:0;-1:-1:-1;;;4948:5:0;;;4790:192::o;235863:53747::-;;;;;;;;;;-1:-1:-1;;;;;235863:53747:0;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Swarm Source
bzzr://b09a598ad0fd1e1002c94db815e00a0d1c35ee031853e1155378f5524c8d1c6f
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.