Source Code
Overview
MNT Balance
MNT Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
DelegationManager
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 1500 runs
Other Settings:
berlin EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: LGPL-3.0
pragma solidity 0.8.20;
import '@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol';
import '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
import '@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol';
import '../permissions/Pausable.sol';
import '../libraries/EIP1271SignatureUtils.sol';
import './DelegationManagerStorage.sol';
/**
* @title DelegationManager
* @notice This is the contract for delegation in Pell. The main functionalities of this contract are
* - enabling anyone to register as an operator in Pell
* - allowing operators to specify parameters related to stakers who delegate to them
* - enabling any staker to delegate its stake to the operator of its choice (a given staker can only delegate to a single operator at a time)
* - enabling a staker to undelegate its assets from the operator it is delegated to (performed as part of the withdrawal process, initiated through the StrategyManager)
*/
contract DelegationManager is Initializable, OwnableUpgradeable, Pausable, DelegationManagerStorage, ReentrancyGuardUpgradeable {
// @dev Index for flag that pauses new delegations when set
uint8 internal constant PAUSED_NEW_DELEGATION = 0;
// @dev Index for flag that pauses queuing new withdrawals when set.
uint8 internal constant PAUSED_ENTER_WITHDRAWAL_QUEUE = 1;
// @dev Index for flag that pauses completing existing withdrawals when set.
uint8 internal constant PAUSED_EXIT_WITHDRAWAL_QUEUE = 2;
// @dev Chain ID at the time of contract deployment
uint256 internal immutable ORIGINAL_CHAIN_ID;
// @dev Maximum Value for `stakerOptOutWindow`. Approximately equivalent to 6 months.
uint256 public constant MAX_STAKER_OPT_OUT_WINDOW = 180 days;
// @notice Simple permission for functions that are only callable by the StrategyManager contract
modifier onlyStrategyManager() {
require(msg.sender == address(strategyManager), 'DelegationManager: onlyStrategyManager');
_;
}
/*******************************************************************************
INITIALIZING FUNCTIONS
*******************************************************************************/
/**
* @dev Initializes the immutable addresses of the strategy mananger and slasher.
*/
constructor(IStrategyManager _strategyManager, ISlasher _slasher) DelegationManagerStorage(_strategyManager, _slasher) {
_disableInitializers();
ORIGINAL_CHAIN_ID = block.chainid;
}
/**
* @dev Initializes the addresses of the initial owner, pauser registry, and paused status.
* minWithdrawalDelay is set only once here
*/
function initialize(
address initialOwner,
IPauserRegistry _pauserRegistry,
uint256 initialPausedStatus,
uint256 _minWithdrawalDelay,
IStrategy[] calldata _strategies,
uint256[] calldata _withdrawalDelay
) external initializer {
_initializePauser(_pauserRegistry, initialPausedStatus);
_DOMAIN_SEPARATOR = _calculateDomainSeparator();
_transferOwnership(initialOwner);
_setMinWithdrawalDelay(_minWithdrawalDelay);
_setStrategyWithdrawalDelay(_strategies, _withdrawalDelay);
}
/*******************************************************************************
EXTERNAL FUNCTIONS
*******************************************************************************/
/**
* @notice Registers the caller as an operator in Pell.
* @param registeringOperatorDetails is the `OperatorDetails` for the operator.
* @param metadataURI is a URI for the operator's metadata, i.e. a link providing more details on the operator.
*
* @dev Once an operator is registered, they cannot 'deregister' as an operator, and they will forever be considered "delegated to themself".
* @dev This function will revert if the caller attempts to set their `earningsReceiver` to address(0).
* @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event
*/
function registerAsOperator(OperatorDetails calldata registeringOperatorDetails, string calldata metadataURI) external {
require(
_operatorDetails[msg.sender].earningsReceiver == address(0),
'DelegationManager.registerAsOperator: operator has already registered'
);
_setOperatorDetails(msg.sender, registeringOperatorDetails);
SignatureWithExpiry memory emptySignatureAndExpiry;
// delegate from the operator to themselves
_delegate(msg.sender, msg.sender, emptySignatureAndExpiry, bytes32(0));
// emit events
emit OperatorRegistered(msg.sender, registeringOperatorDetails);
emit OperatorMetadataURIUpdated(msg.sender, metadataURI);
}
/**
* @notice Updates an operator's stored `OperatorDetails`.
* @param newOperatorDetails is the updated `OperatorDetails` for the operator, to replace their current OperatorDetails`.
*
* @dev The caller must have previously registered as an operator in Pell.
* @dev This function will revert if the caller attempts to set their `earningsReceiver` to address(0).
*/
function modifyOperatorDetails(OperatorDetails calldata newOperatorDetails) external {
require(isOperator(msg.sender), 'DelegationManager.modifyOperatorDetails: caller must be an operator');
_setOperatorDetails(msg.sender, newOperatorDetails);
}
/**
* @notice Called by an operator to emit an `OperatorMetadataURIUpdated` event indicating the information has updated.
* @param metadataURI The URI for metadata associated with an operator
*/
function updateOperatorMetadataURI(string calldata metadataURI) external {
require(isOperator(msg.sender), 'DelegationManager.updateOperatorMetadataURI: caller must be an operator');
emit OperatorMetadataURIUpdated(msg.sender, metadataURI);
}
/**
* @notice Caller delegates their stake to an operator.
* @param operator The account (`msg.sender`) is delegating its assets to for use in serving applications built on Pell.
* @param approverSignatureAndExpiry Verifies the operator approves of this delegation
* @param approverSalt A unique single use value tied to an individual signature.
* @dev The approverSignatureAndExpiry is used in the event that:
* 1) the operator's `delegationApprover` address is set to a non-zero value.
* AND
* 2) neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator
* or their delegationApprover is the `msg.sender`, then approval is assumed.
* @dev In the event that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input
* in this case to save on complexity + gas costs
*/
function delegateTo(address operator, SignatureWithExpiry memory approverSignatureAndExpiry, bytes32 approverSalt) external {
// go through the internal delegation flow, checking the `approverSignatureAndExpiry` if applicable
_delegate(msg.sender, operator, approverSignatureAndExpiry, approverSalt);
}
/**
* @notice Caller delegates a staker's stake to an operator with valid signatures from both parties.
* @param staker The account delegating stake to an `operator` account
* @param operator The account (`staker`) is delegating its assets to for use in serving applications built on Pell.
* @param stakerSignatureAndExpiry Signed data from the staker authorizing delegating stake to an operator
* @param approverSignatureAndExpiry is a parameter that will be used for verifying that the operator approves of this delegation action in the event that:
* @param approverSalt Is a salt used to help guarantee signature uniqueness. Each salt can only be used once by a given approver.
*
* @dev If `staker` is an EOA, then `stakerSignature` is verified to be a valid ECDSA stakerSignature from `staker`, indicating their intention for this action.
* @dev If `staker` is a contract, then `stakerSignature` will be checked according to EIP-1271.
* @dev the operator's `delegationApprover` address is set to a non-zero value.
* @dev neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator or their delegationApprover
* is the `msg.sender`, then approval is assumed.
* @dev This function will revert if the current `block.timestamp` is equal to or exceeds the expiry
* @dev In the case that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input
* in this case to save on complexity + gas costs
*/
function delegateToBySignature(
address staker,
address operator,
SignatureWithExpiry memory stakerSignatureAndExpiry,
SignatureWithExpiry memory approverSignatureAndExpiry,
bytes32 approverSalt
) external {
// check the signature expiry
require(stakerSignatureAndExpiry.expiry >= block.timestamp, 'DelegationManager.delegateToBySignature: staker signature expired');
// calculate the digest hash, then increment `staker`'s nonce
uint256 currentStakerNonce = stakerNonce[staker];
bytes32 stakerDigestHash = calculateStakerDelegationDigestHash(staker, currentStakerNonce, operator, stakerSignatureAndExpiry.expiry);
unchecked {
stakerNonce[staker] = currentStakerNonce + 1;
}
// actually check that the signature is valid
EIP1271SignatureUtils.checkSignature_EIP1271(staker, stakerDigestHash, stakerSignatureAndExpiry.signature);
// go through the internal delegation flow, checking the `approverSignatureAndExpiry` if applicable
_delegate(staker, operator, approverSignatureAndExpiry, approverSalt);
}
/**
* Allows the staker, the staker's operator, or that operator's delegationApprover to undelegate
* a staker from their operator. Undelegation immediately removes ALL active shares/strategies from
* both the staker and operator, and places the shares and strategies in the withdrawal queue
*/
function undelegate(address staker) external onlyWhenNotPaused(PAUSED_ENTER_WITHDRAWAL_QUEUE) returns (bytes32[] memory withdrawalRoots) {
require(isDelegated(staker), 'DelegationManager.undelegate: staker must be delegated to undelegate');
require(!isOperator(staker), 'DelegationManager.undelegate: operators cannot be undelegated');
require(staker != address(0), 'DelegationManager.undelegate: cannot undelegate zero address');
address operator = delegatedTo[staker];
require(
msg.sender == staker || msg.sender == operator || msg.sender == _operatorDetails[operator].delegationApprover,
'DelegationManager.undelegate: caller cannot undelegate staker'
);
// Gather strategies and shares to remove from staker/operator during undelegation
// Undelegation removes ALL currently-active strategies and shares
(IStrategy[] memory strategies, uint256[] memory shares) = getDelegatableShares(staker);
// emit an event if this action was not initiated by the staker themselves
if (msg.sender != staker) {
emit StakerForceUndelegated(staker, operator);
}
// undelegate the staker
emit StakerUndelegated(staker, operator);
delegatedTo[staker] = address(0);
// if no delegatable shares, return an empty array, and don't queue a withdrawal
if (strategies.length == 0) {
withdrawalRoots = new bytes32[](0);
} else {
withdrawalRoots = new bytes32[](strategies.length);
for (uint256 i = 0; i < strategies.length; i++) {
IStrategy[] memory singleStrategy = new IStrategy[](1);
uint256[] memory singleShare = new uint256[](1);
singleStrategy[0] = strategies[i];
singleShare[0] = shares[i];
withdrawalRoots[i] = _removeSharesAndQueueWithdrawal({
staker: staker,
operator: operator,
withdrawer: staker,
strategies: singleStrategy,
shares: singleShare
});
}
}
return withdrawalRoots;
}
/**
* Allows a staker to withdraw some shares. Withdrawn shares/strategies are immediately removed
* from the staker. If the staker is delegated, withdrawn shares/strategies are also removed from
* their operator.
*
* All withdrawn shares/strategies are placed in a queue and can be fully withdrawn after a delay.
*/
function queueWithdrawals(
QueuedWithdrawalParams[] calldata queuedWithdrawalParams
) external onlyWhenNotPaused(PAUSED_ENTER_WITHDRAWAL_QUEUE) returns (bytes32[] memory) {
bytes32[] memory withdrawalRoots = new bytes32[](queuedWithdrawalParams.length);
address operator = delegatedTo[msg.sender];
for (uint256 i = 0; i < queuedWithdrawalParams.length; i++) {
require(
queuedWithdrawalParams[i].strategies.length == queuedWithdrawalParams[i].shares.length,
'DelegationManager.queueWithdrawal: input length mismatch'
);
require(queuedWithdrawalParams[i].withdrawer == msg.sender, 'DelegationManager.queueWithdrawal: withdrawer must be staker');
// Remove shares from staker's strategies and place strategies/shares in queue.
// If the staker is delegated to an operator, the operator's delegated shares are also reduced
// NOTE: This will fail if the staker doesn't have the shares implied by the input parameters
withdrawalRoots[i] = _removeSharesAndQueueWithdrawal({
staker: msg.sender,
operator: operator,
withdrawer: queuedWithdrawalParams[i].withdrawer,
strategies: queuedWithdrawalParams[i].strategies,
shares: queuedWithdrawalParams[i].shares
});
}
return withdrawalRoots;
}
/**
* @notice Used to complete the specified `withdrawal`. The caller must match `withdrawal.withdrawer`
* @param withdrawal The Withdrawal to complete.
* @param tokens Array in which the i-th entry specifies the `token` input to the 'withdraw' function of the i-th Strategy in the `withdrawal.strategies` array.
* This input can be provided with zero length if `receiveAsTokens` is set to 'false' (since in that case, this input will be unused)
* @param middlewareTimesIndex is the index in the operator that the staker who triggered the withdrawal was delegated to's middleware times array
* @param receiveAsTokens If true, the shares specified in the withdrawal will be withdrawn from the specified strategies themselves
* and sent to the caller, through calls to `withdrawal.strategies[i].withdraw`. If false, then the shares in the specified strategies
* will simply be transferred to the caller directly.
* @dev middlewareTimesIndex is unused, but will be used in the Slasher eventually
* @dev beaconChainETHStrategy shares are non-transferrable, so if `receiveAsTokens = false` and `withdrawal.withdrawer != withdrawal.staker`, note that
* any beaconChainETHStrategy shares in the `withdrawal` will be _returned to the staker_, rather than transferred to the withdrawer, unlike shares in
* any other strategies, which will be transferred to the withdrawer.
*/
function completeQueuedWithdrawal(
Withdrawal calldata withdrawal,
IERC20[] calldata tokens,
uint256 middlewareTimesIndex,
bool receiveAsTokens
) external onlyWhenNotPaused(PAUSED_EXIT_WITHDRAWAL_QUEUE) nonReentrant {
_completeQueuedWithdrawal(withdrawal, tokens, middlewareTimesIndex, receiveAsTokens);
}
/**
* @notice Array-ified version of `completeQueuedWithdrawal`.
* Used to complete the specified `withdrawals`. The function caller must match `withdrawals[...].withdrawer`
* @param withdrawals The Withdrawals to complete.
* @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array.
* @param middlewareTimesIndexes One index to reference per Withdrawal. See `completeQueuedWithdrawal` for the usage of a single index.
* @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean.
* @dev See `completeQueuedWithdrawal` for relevant dev tags
*/
function completeQueuedWithdrawals(
Withdrawal[] calldata withdrawals,
IERC20[][] calldata tokens,
uint256[] calldata middlewareTimesIndexes,
bool[] calldata receiveAsTokens
) external onlyWhenNotPaused(PAUSED_EXIT_WITHDRAWAL_QUEUE) nonReentrant {
for (uint256 i = 0; i < withdrawals.length; ++i) {
_completeQueuedWithdrawal(withdrawals[i], tokens[i], middlewareTimesIndexes[i], receiveAsTokens[i]);
}
}
/**
* @notice Increases a staker's delegated share balance in a strategy.
* @param staker The address to increase the delegated shares for their operator.
* @param strategy The strategy in which to increase the delegated shares.
* @param shares The number of shares to increase.
*
* @dev *If the staker is actively delegated*, then increases the `staker`'s delegated shares in `strategy` by `shares`. Otherwise does nothing.
* @dev Callable only by the StrategyManager.
*/
function increaseDelegatedShares(address staker, IStrategy strategy, uint256 shares) external onlyStrategyManager {
// if the staker is delegated to an operator
if (isDelegated(staker)) {
address operator = delegatedTo[staker];
// add strategy shares to delegate's shares
_increaseOperatorShares({operator: operator, staker: staker, strategy: strategy, shares: shares});
}
}
/**
* @notice Decreases a staker's delegated share balance in a strategy.
* @param staker The address to increase the delegated shares for their operator.
* @param strategy The strategy in which to decrease the delegated shares.
* @param shares The number of shares to decrease.
*
* @dev *If the staker is actively delegated*, then decreases the `staker`'s delegated shares in `strategy` by `shares`. Otherwise does nothing.
* @dev Callable only by the StrategyManager or EigenPodManager.
*/
function decreaseDelegatedShares(address staker, IStrategy strategy, uint256 shares) external onlyStrategyManager {
// if the staker is delegated to an operator
if (isDelegated(staker)) {
address operator = delegatedTo[staker];
// subtract strategy shares from delegate's shares
_decreaseOperatorShares({operator: operator, staker: staker, strategy: strategy, shares: shares});
}
}
/**
* @notice Owner-only function for modifying the value of the `minWithdrawalDelay` variable.
* @param newMinWithdrawalDelay new value of `minWithdrawalDelay`.
*/
function setMinWithdrawalDelay(uint256 newMinWithdrawalDelay) external onlyOwner {
_setMinWithdrawalDelay(newMinWithdrawalDelay);
}
/**
* @notice Called by owner to set the minimum withdrawal delay for each passed in strategy
* Note that the min cooldown to complete a withdrawal of a strategy is
* MAX(minWithdrawalDelay, strategyWithdrawalDelay[strategy])
* @param strategies The strategies to set the minimum withdrawal delay for
* @param withdrawalDelay The minimum withdrawal delay to set for each strategy
*/
function setStrategyWithdrawalDelay(IStrategy[] calldata strategies, uint256[] calldata withdrawalDelay) external onlyOwner {
_setStrategyWithdrawalDelay(strategies, withdrawalDelay);
}
/*******************************************************************************
INTERNAL FUNCTIONS
*******************************************************************************/
/**
* @notice Sets operator parameters in the `_operatorDetails` mapping.
* @param operator The account registered as an operator updating their operatorDetails
* @param newOperatorDetails The new parameters for the operator
*
* @dev This function will revert if the operator attempts to set their `earningsReceiver` to address(0).
*/
function _setOperatorDetails(address operator, OperatorDetails calldata newOperatorDetails) internal {
require(
newOperatorDetails.earningsReceiver != address(0),
'DelegationManager._setOperatorDetails: cannot set `earningsReceiver` to zero address'
);
require(
newOperatorDetails.stakerOptOutWindow <= MAX_STAKER_OPT_OUT_WINDOW,
'DelegationManager._setOperatorDetails: stakerOptOutWindow cannot be > MAX_STAKER_OPT_OUT_WINDOW'
);
require(
newOperatorDetails.stakerOptOutWindow >= _operatorDetails[operator].stakerOptOutWindow,
'DelegationManager._setOperatorDetails: stakerOptOutWindow cannot be decreased'
);
_operatorDetails[operator] = newOperatorDetails;
emit OperatorDetailsModified(msg.sender, newOperatorDetails);
}
/**
* @notice Delegates *from* a `staker` *to* an `operator`.
* @param staker The address to delegate *from* -- this address is delegating control of its own assets.
* @param operator The address to delegate *to* -- this address is being given power to place the `staker`'s assets at risk on services
* @param approverSignatureAndExpiry Verifies the operator approves of this delegation
* @param approverSalt Is a salt used to help guarantee signature uniqueness. Each salt can only be used once by a given approver.
* @dev Ensures that:
* 1) the `staker` is not already delegated to an operator
* 2) the `operator` has indeed registered as an operator in Pell
* 3) if applicable, that the approver signature is valid and non-expired
*/
function _delegate(
address staker,
address operator,
SignatureWithExpiry memory approverSignatureAndExpiry,
bytes32 approverSalt
) internal onlyWhenNotPaused(PAUSED_NEW_DELEGATION) {
require(!isDelegated(staker), 'DelegationManager._delegate: staker is already actively delegated');
require(isOperator(operator), 'DelegationManager._delegate: operator is not registered in Pell');
// fetch the operator's `delegationApprover` address and store it in memory in case we need to use it multiple times
address _delegationApprover = _operatorDetails[operator].delegationApprover;
/**
* Check the `_delegationApprover`'s signature, if applicable.
* If the `_delegationApprover` is the zero address, then the operator allows all stakers to delegate to them and this verification is skipped.
* If the `_delegationApprover` or the `operator` themselves is the caller, then approval is assumed and signature verification is skipped as well.
*/
if (_delegationApprover != address(0) && msg.sender != _delegationApprover && msg.sender != operator) {
// check the signature expiry
require(approverSignatureAndExpiry.expiry >= block.timestamp, 'DelegationManager._delegate: approver signature expired');
// check that the salt hasn't been used previously, then mark the salt as spent
require(!delegationApproverSaltIsSpent[_delegationApprover][approverSalt], 'DelegationManager._delegate: approverSalt already spent');
delegationApproverSaltIsSpent[_delegationApprover][approverSalt] = true;
// calculate the digest hash
bytes32 approverDigestHash = calculateDelegationApprovalDigestHash(
staker,
operator,
_delegationApprover,
approverSalt,
approverSignatureAndExpiry.expiry
);
// actually check that the signature is valid
EIP1271SignatureUtils.checkSignature_EIP1271(_delegationApprover, approverDigestHash, approverSignatureAndExpiry.signature);
}
// record the delegation relation between the staker and operator, and emit an event
delegatedTo[staker] = operator;
emit StakerDelegated(staker, operator);
(IStrategy[] memory strategies, uint256[] memory shares) = getDelegatableShares(staker);
for (uint256 i = 0; i < strategies.length; ) {
_increaseOperatorShares({operator: operator, staker: staker, strategy: strategies[i], shares: shares[i]});
unchecked {
++i;
}
}
}
/**
* @dev commented-out param (middlewareTimesIndex) is the index in the operator that the staker who triggered the withdrawal was delegated to's middleware times array
* This param is intended to be passed on to the Slasher contract, but is unused in the M2 release of these contracts, and is thus commented-out.
*/
function _completeQueuedWithdrawal(
Withdrawal calldata withdrawal,
IERC20[] calldata tokens,
uint256 /*middlewareTimesIndex*/,
bool receiveAsTokens
) internal {
bytes32 withdrawalRoot = calculateWithdrawalRoot(withdrawal);
require(pendingWithdrawals[withdrawalRoot], 'DelegationManager._completeQueuedWithdrawal: action is not in queue');
require(
withdrawal.startTimestamp + minWithdrawalDelay <= block.timestamp,
'DelegationManager._completeQueuedWithdrawal: minWithdrawalDelay period has not yet passed'
);
require(msg.sender == withdrawal.withdrawer, 'DelegationManager._completeQueuedWithdrawal: only withdrawer can complete action');
if (receiveAsTokens) {
require(tokens.length == withdrawal.strategies.length, 'DelegationManager._completeQueuedWithdrawal: input length mismatch');
}
// Remove `withdrawalRoot` from pending roots
delete pendingWithdrawals[withdrawalRoot];
// Finalize action by converting shares to tokens for each strategy, or
// by re-awarding shares in each strategy.
if (receiveAsTokens) {
for (uint256 i = 0; i < withdrawal.strategies.length; ) {
require(
withdrawal.startTimestamp + strategyWithdrawalDelay[withdrawal.strategies[i]] <= block.timestamp,
'DelegationManager._completeQueuedWithdrawal: withdrawalDelay period has not yet passed for this strategy'
);
_withdrawSharesAsTokens({
staker: withdrawal.staker,
withdrawer: msg.sender,
strategy: withdrawal.strategies[i],
shares: withdrawal.shares[i],
token: tokens[i]
});
unchecked {
++i;
}
}
// Award shares back in StrategyManager/EigenPodManager. If withdrawer is delegated, increase the shares delegated to the operator
} else {
address currentOperator = delegatedTo[msg.sender];
for (uint256 i = 0; i < withdrawal.strategies.length; ) {
require(
withdrawal.startTimestamp + strategyWithdrawalDelay[withdrawal.strategies[i]] <= block.timestamp,
'DelegationManager._completeQueuedWithdrawal: withdrawalDelay period has not yet passed for this strategy'
);
strategyManager.addShares(msg.sender, tokens[i], withdrawal.strategies[i], withdrawal.shares[i]);
// Similar to `isDelegated` logic
if (currentOperator != address(0)) {
_increaseOperatorShares({
operator: currentOperator,
// the 'staker' here is the address receiving new shares
staker: msg.sender,
strategy: withdrawal.strategies[i],
shares: withdrawal.shares[i]
});
}
unchecked {
++i;
}
}
}
emit WithdrawalCompleted(withdrawalRoot);
}
// @notice Increases `operator`s delegated shares in `strategy` by `shares` and emits an `OperatorSharesIncreased` event
function _increaseOperatorShares(address operator, address staker, IStrategy strategy, uint256 shares) internal {
operatorShares[operator][strategy] += shares;
emit OperatorSharesIncreased(operator, staker, strategy, shares);
}
// @notice Decreases `operator`s delegated shares in `strategy` by `shares` and emits an `OperatorSharesDecreased` event
function _decreaseOperatorShares(address operator, address staker, IStrategy strategy, uint256 shares) internal {
// This will revert on underflow, so no check needed
operatorShares[operator][strategy] -= shares;
emit OperatorSharesDecreased(operator, staker, strategy, shares);
}
/**
* @notice Removes `shares` in `strategies` from `staker` who is currently delegated to `operator` and queues a withdrawal to the `withdrawer`.
* @dev If the `operator` is indeed an operator, then the operator's delegated shares in the `strategies` are also decreased appropriately.
* @dev If `withdrawer` is not the same address as `staker`, then thirdPartyTransfersForbidden[strategy] must be set to false in the StrategyManager.
*/
function _removeSharesAndQueueWithdrawal(
address staker,
address operator,
address withdrawer,
IStrategy[] memory strategies,
uint256[] memory shares
) internal returns (bytes32) {
require(staker != address(0), 'DelegationManager._removeSharesAndQueueWithdrawal: staker cannot be zero address');
require(strategies.length != 0, 'DelegationManager._removeSharesAndQueueWithdrawal: strategies cannot be empty');
// Remove shares from staker and operator
// Each of these operations fail if we attempt to remove more shares than exist
for (uint256 i = 0; i < strategies.length; ) {
// Similar to `isDelegated` logic
if (operator != address(0)) {
_decreaseOperatorShares({operator: operator, staker: staker, strategy: strategies[i], shares: shares[i]});
}
require(
staker == withdrawer || !strategyManager.thirdPartyTransfersForbidden(strategies[i]),
'DelegationManager._removeSharesAndQueueWithdrawal: withdrawer must be same address as staker if thirdPartyTransfersForbidden are set'
);
// this call will revert if `shares[i]` exceeds the Staker's current shares in `strategies[i]`
strategyManager.removeShares(staker, strategies[i], shares[i]);
unchecked {
++i;
}
}
// Create queue entry and increment withdrawal nonce
uint256 nonce = cumulativeWithdrawalsQueued[staker];
cumulativeWithdrawalsQueued[staker]++;
Withdrawal memory withdrawal = Withdrawal({
staker: staker,
delegatedTo: operator,
withdrawer: withdrawer,
nonce: nonce,
startTimestamp: uint32(block.timestamp),
strategies: strategies,
shares: shares
});
bytes32 withdrawalRoot = calculateWithdrawalRoot(withdrawal);
// Place withdrawal in queue
pendingWithdrawals[withdrawalRoot] = true;
emit WithdrawalQueued(withdrawalRoot, withdrawal);
return withdrawalRoot;
}
/**
* @notice Withdraws `shares` in `strategy` to `withdrawer`. Call is ultimately forwarded to the `strategy` with info on the `token`.
*/
function _withdrawSharesAsTokens(address staker, address withdrawer, IStrategy strategy, uint256 shares, IERC20 token) internal {
strategyManager.withdrawSharesAsTokens(withdrawer, strategy, shares, token);
}
function _setMinWithdrawalDelay(uint256 _minWithdrawalDelay) internal {
require(
_minWithdrawalDelay <= MAX_WITHDRAWAL_DELAY,
'DelegationManager._setMinWithdrawalDelay: _minWithdrawalDelay cannot be > MAX_WITHDRAWAL_DELAY'
);
emit MinWithdrawalDelaySet(minWithdrawalDelay, _minWithdrawalDelay);
minWithdrawalDelay = _minWithdrawalDelay;
}
/**
* @notice Sets the withdrawal delay for each strategy in `_strategies` to `_withdrawalDelay`.
* gets called when initializing contract or by calling `setStrategyWithdrawalDelay`
*/
function _setStrategyWithdrawalDelay(IStrategy[] calldata _strategies, uint256[] calldata _withdrawalDelay) internal {
require(_strategies.length == _withdrawalDelay.length, 'DelegationManager._setStrategyWithdrawalDelay: input length mismatch');
uint256 numStrats = _strategies.length;
for (uint256 i = 0; i < numStrats; ++i) {
IStrategy strategy = _strategies[i];
uint256 prevStrategyWithdrawalDelay = strategyWithdrawalDelay[strategy];
uint256 newStrategyWithdrawalDelay = _withdrawalDelay[i];
require(
newStrategyWithdrawalDelay <= MAX_WITHDRAWAL_DELAY,
'DelegationManager._setStrategyWithdrawalDelay: _withdrawalDelay cannot be > MAX_WITHDRAWAL_DELAY'
);
// set the new withdrawal delay
strategyWithdrawalDelay[strategy] = newStrategyWithdrawalDelay;
emit StrategyWithdrawalDelaySet(strategy, prevStrategyWithdrawalDelay, newStrategyWithdrawalDelay);
}
}
/*******************************************************************************
VIEW FUNCTIONS
*******************************************************************************/
/**
* @notice Getter function for the current EIP-712 domain separator for this contract.
*
* @dev The domain separator will change in the event of a fork that changes the ChainID.
* @dev By introducing a domain separator the DApp developers are guaranteed that there can be no signature collision.
* for more detailed information please read EIP-712.
*/
function domainSeparator() public view returns (bytes32) {
if (block.chainid == ORIGINAL_CHAIN_ID) {
return _DOMAIN_SEPARATOR;
} else {
return _calculateDomainSeparator();
}
}
/**
* @notice Returns 'true' if `staker` *is* actively delegated, and 'false' otherwise.
*/
function isDelegated(address staker) public view returns (bool) {
return (delegatedTo[staker] != address(0));
}
/**
* @notice Returns true is an operator has previously registered for delegation.
*/
function isOperator(address operator) public view returns (bool) {
return (_operatorDetails[operator].earningsReceiver != address(0));
}
/**
* @notice Returns the OperatorDetails struct associated with an `operator`.
*/
function operatorDetails(address operator) external view returns (OperatorDetails memory) {
return _operatorDetails[operator];
}
/*
* @notice Returns the earnings receiver address for an operator
*/
function earningsReceiver(address operator) external view returns (address) {
return _operatorDetails[operator].earningsReceiver;
}
/**
* @notice Returns the delegationApprover account for an operator
*/
function delegationApprover(address operator) external view returns (address) {
return _operatorDetails[operator].delegationApprover;
}
/**
* @notice Returns the stakerOptOutWindow for an operator
*/
function stakerOptOutWindow(address operator) external view returns (uint256) {
return _operatorDetails[operator].stakerOptOutWindow;
}
/// @notice Given array of strategies, returns array of shares for the operator
function getOperatorShares(address operator, IStrategy[] memory strategies) public view returns (uint256[] memory) {
uint256[] memory shares = new uint256[](strategies.length);
for (uint256 i = 0; i < strategies.length; ++i) {
shares[i] = operatorShares[operator][strategies[i]];
}
return shares;
}
/**
* @notice Returns the number of actively-delegatable shares a staker has across all strategies.
* @dev Returns two empty arrays in the case that the Staker has no actively-delegateable shares.
*/
function getDelegatableShares(address staker) public view returns (IStrategy[] memory, uint256[] memory) {
// Get currently active shares and strategies for `staker`
(IStrategy[] memory strategyManagerStrats, uint256[] memory strategyManagerShares) = strategyManager.getDeposits(staker);
return (strategyManagerStrats, strategyManagerShares);
}
/**
* @notice Given a list of strategies, return the minimum cooldown that must pass to withdraw
* from all the inputted strategies. Return value is >= minWithdrawalDelay as this is the global min withdrawal delay.
* @param strategies The strategies to check withdrawal delays for
*/
function getWithdrawalDelay(IStrategy[] calldata strategies) public view returns (uint256) {
uint256 withdrawalDelay = minWithdrawalDelay;
for (uint256 i = 0; i < strategies.length; ++i) {
uint256 currWithdrawalDelay = strategyWithdrawalDelay[strategies[i]];
if (currWithdrawalDelay > withdrawalDelay) {
withdrawalDelay = currWithdrawalDelay;
}
}
return withdrawalDelay;
}
/// @notice Returns the keccak256 hash of `withdrawal`.
function calculateWithdrawalRoot(Withdrawal memory withdrawal) public pure returns (bytes32) {
return keccak256(abi.encode(withdrawal));
}
/**
* @notice Calculates the digestHash for a `staker` to sign to delegate to an `operator`
* @param staker The signing staker
* @param operator The operator who is being delegated to
* @param expiry The desired expiry time of the staker's signature
*/
function calculateCurrentStakerDelegationDigestHash(address staker, address operator, uint256 expiry) external view returns (bytes32) {
// fetch the staker's current nonce
uint256 currentStakerNonce = stakerNonce[staker];
// calculate the digest hash
return calculateStakerDelegationDigestHash(staker, currentStakerNonce, operator, expiry);
}
/**
* @notice Calculates the digest hash to be signed and used in the `delegateToBySignature` function
* @param staker The signing staker
* @param _stakerNonce The nonce of the staker. In practice we use the staker's current nonce, stored at `stakerNonce[staker]`
* @param operator The operator who is being delegated to
* @param expiry The desired expiry time of the staker's signature
*/
function calculateStakerDelegationDigestHash(
address staker,
uint256 _stakerNonce,
address operator,
uint256 expiry
) public view returns (bytes32) {
// calculate the struct hash
bytes32 stakerStructHash = keccak256(abi.encode(STAKER_DELEGATION_TYPEHASH, staker, operator, _stakerNonce, expiry));
// calculate the digest hash
bytes32 stakerDigestHash = keccak256(abi.encodePacked('\x19\x01', domainSeparator(), stakerStructHash));
return stakerDigestHash;
}
/**
* @notice Calculates the digest hash to be signed by the operator's delegationApprove and used in the `delegateTo` and `delegateToBySignature` functions.
* @param staker The account delegating their stake
* @param operator The account receiving delegated stake
* @param _delegationApprover the operator's `delegationApprover` who will be signing the delegationHash (in general)
* @param approverSalt A unique and single use value associated with the approver signature.
* @param expiry Time after which the approver's signature becomes invalid
*/
function calculateDelegationApprovalDigestHash(
address staker,
address operator,
address _delegationApprover,
bytes32 approverSalt,
uint256 expiry
) public view returns (bytes32) {
// calculate the struct hash
bytes32 approverStructHash = keccak256(
abi.encode(DELEGATION_APPROVAL_TYPEHASH, _delegationApprover, staker, operator, approverSalt, expiry)
);
// calculate the digest hash
bytes32 approverDigestHash = keccak256(abi.encodePacked('\x19\x01', domainSeparator(), approverStructHash));
return approverDigestHash;
}
/**
* @dev Recalculates the domain separator when the chainid changes due to a fork.
*/
function _calculateDomainSeparator() internal view returns (bytes32) {
return keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes('Pell')), block.chainid, address(this)));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*
* _Available since v4.1._
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/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.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/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.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
/**
* @dev Returns an Ethereum Signed Data with intended validator, created from a
* `validator` and `data` according to the version 0 of EIP-191.
*
* See {recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: LGPL-3.0
pragma solidity 0.8.20;
import '../interfaces/IStrategyManager.sol';
import '../interfaces/IDelegationManager.sol';
import '../interfaces/ISlasher.sol';
/**
* @title Storage variables for the `DelegationManager` contract.
* @notice This storage contract is separate from the logic to simplify the upgrade process.
*/
abstract contract DelegationManagerStorage is IDelegationManager {
/// @notice The EIP-712 typehash for the contract's domain
bytes32 public constant DOMAIN_TYPEHASH = keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)');
/// @notice The EIP-712 typehash for the `StakerDelegation` struct used by the contract
bytes32 public constant STAKER_DELEGATION_TYPEHASH =
keccak256('StakerDelegation(address staker,address operator,uint256 nonce,uint256 expiry)');
/// @notice The EIP-712 typehash for the `DelegationApproval` struct used by the contract
bytes32 public constant DELEGATION_APPROVAL_TYPEHASH =
keccak256('DelegationApproval(address delegationApprover,address staker,address operator,bytes32 salt,uint256 expiry)');
/**
* @notice Original EIP-712 Domain separator for this contract.
* @dev The domain separator may change in the event of a fork that modifies the ChainID.
* Use the getter function `domainSeparator` to get the current domain separator for this contract.
*/
bytes32 internal _DOMAIN_SEPARATOR;
/// @notice The StrategyManager contract for Pell
IStrategyManager public immutable strategyManager;
/// @notice The Slasher contract for Pell
ISlasher public immutable slasher;
// 30 days (60 * 60 * 24 * 30 = 2,592,000)
uint256 public constant MAX_WITHDRAWAL_DELAY = 2592000;
/**
* @notice returns the total number of shares in `strategy` that are delegated to `operator`.
* @notice Mapping: operator => strategy => total number of shares in the strategy delegated to the operator.
* @dev By design, the following invariant should hold for each Strategy:
* (operator's shares in delegation manager) = sum (shares above zero of all stakers delegated to operator)
* = sum (delegateable shares of all stakers delegated to the operator)
*/
mapping(address => mapping(IStrategy => uint256)) public operatorShares;
/**
* @notice Mapping: operator => OperatorDetails struct
* @dev This struct is internal with an external getter so we can return an `OperatorDetails memory` object
*/
mapping(address => OperatorDetails) internal _operatorDetails;
/**
* @notice Mapping: staker => operator whom the staker is currently delegated to.
* @dev Note that returning address(0) indicates that the staker is not actively delegated to any operator.
*/
mapping(address => address) public delegatedTo;
/// @notice Mapping: staker => number of signed messages (used in `delegateToBySignature`) from the staker that this contract has already checked.
mapping(address => uint256) public stakerNonce;
/**
* @notice Mapping: delegationApprover => 32-byte salt => whether or not the salt has already been used by the delegationApprover.
* @dev Salts are used in the `delegateTo` and `delegateToBySignature` functions. Note that these functions only process the delegationApprover's
* signature + the provided salt if the operator being delegated to has specified a nonzero address as their `delegationApprover`.
*/
mapping(address => mapping(bytes32 => bool)) public delegationApproverSaltIsSpent;
/**
* @notice Global minimum withdrawal delay for all strategy withdrawals.
* In a prior Goerli release, we only had a global min withdrawal delay across all strategies.
* In addition, we now also configure withdrawal delays on a per-strategy basis.
* To withdraw from a strategy, max(minWithdrawalDelay, strategyWithdrawalDelay[strategy]) number of timestamp must have passed.
* See mapping strategyWithdrawalDelay below for per-strategy withdrawal delays.
*/
uint256 public minWithdrawalDelay;
/// @notice Mapping: hash of withdrawal inputs, aka 'withdrawalRoot' => whether the withdrawal is pending
mapping(bytes32 => bool) public pendingWithdrawals;
/// @notice Mapping: staker => cumulative number of queued withdrawals they have ever initiated.
/// @dev This only increments (doesn't decrement), and is used to help ensure that otherwise identical withdrawals have unique hashes.
mapping(address => uint256) public cumulativeWithdrawalsQueued;
/**
* @notice Minimum delay enforced by this contract per Strategy for completing queued withdrawals. Measured in timestamp, and adjustable by this contract's owner,
* up to a maximum of `MAX_WITHDRAWAL_DELAY`. Minimum value is 0 (i.e. no delay enforced).
*/
mapping(IStrategy => uint256) public strategyWithdrawalDelay;
constructor(IStrategyManager _strategyManager, ISlasher _slasher) {
strategyManager = _strategyManager;
slasher = _slasher;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: LGPL-3.0
pragma solidity 0.8.20;
import './IStrategy.sol';
import './ISignatureUtils.sol';
import './IStrategyManager.sol';
/**
* @title DelegationManager
* @notice This is the contract for delegation in Pell. The main functionalities of this contract are
* - enabling anyone to register as an operator in Pell
* - allowing operators to specify parameters related to stakers who delegate to them
* - enabling any staker to delegate its stake to the operator of its choice (a given staker can only delegate to a single operator at a time)
* - enabling a staker to undelegate its assets from the operator it is delegated to (performed as part of the withdrawal process, initiated through the StrategyManager)
*/
interface IDelegationManager is ISignatureUtils {
// @notice Struct used for storing information about a single operator who has registered with Pell
struct OperatorDetails {
// @notice address to receive the rewards that the operator earns via serving applications built on Pell.
address earningsReceiver;
/**
* @notice Address to verify signatures when a staker wishes to delegate to the operator, as well as controlling "forced undelegations".
* @dev Signature verification follows these rules:
* 1) If this address is left as address(0), then any staker will be free to delegate to the operator, i.e. no signature verification will be performed.
* 2) If this address is an EOA (i.e. it has no code), then we follow standard ECDSA signature verification for delegations to the operator.
* 3) If this address is a contract (i.e. it has code) then we forward a call to the contract and verify that it returns the correct EIP-1271 "magic value".
*/
address delegationApprover;
/**
* @notice A minimum delay -- enforced between:
* 1) the operator signalling their intent to register for a service, via calling `Slasher.optIntoSlashing`
* and
* 2) the operator completing registration for the service, via the service ultimately calling `Slasher.recordFirstStakeUpdate`
* @dev note that for a specific operator, this value *cannot decrease*, i.e. if the operator wishes to modify their OperatorDetails,
* then they are only allowed to either increase this value or keep it the same.
*/
uint32 stakerOptOutWindow;
}
/**
* @notice Abstract struct used in calculating an EIP712 signature for a staker to approve that they (the staker themselves) delegate to a specific operator.
* @dev Used in computing the `STAKER_DELEGATION_TYPEHASH` and as a reference in the computation of the stakerDigestHash in the `delegateToBySignature` function.
*/
struct StakerDelegation {
// the staker who is delegating
address staker;
// the operator being delegated to
address operator;
// the staker's nonce
uint256 nonce;
// the expiration timestamp (UTC) of the signature
uint256 expiry;
}
/**
* @notice Abstract struct used in calculating an EIP712 signature for an operator's delegationApprover to approve that a specific staker delegate to the operator.
* @dev Used in computing the `DELEGATION_APPROVAL_TYPEHASH` and as a reference in the computation of the approverDigestHash in the `_delegate` function.
*/
struct DelegationApproval {
// the staker who is delegating
address staker;
// the operator being delegated to
address operator;
// the operator's provided salt
bytes32 salt;
// the expiration timestamp (UTC) of the signature
uint256 expiry;
}
/**
* Struct type used to specify an existing queued withdrawal. Rather than storing the entire struct, only a hash is stored.
* In functions that operate on existing queued withdrawals -- e.g. completeQueuedWithdrawal`, the data is resubmitted and the hash of the submitted
* data is computed by `calculateWithdrawalRoot` and checked against the stored hash in order to confirm the integrity of the submitted data.
*/
struct Withdrawal {
// The address that originated the Withdrawal
address staker;
// The address that the staker was delegated to at the time that the Withdrawal was created
address delegatedTo;
// The address that can complete the Withdrawal + will receive funds when completing the withdrawal
address withdrawer;
// Nonce used to guarantee that otherwise identical withdrawals have unique hashes
uint256 nonce;
// Block timestamp when the Withdrawal was created
uint32 startTimestamp;
// Array of strategies that the Withdrawal contains
IStrategy[] strategies;
// Array containing the amount of shares in each Strategy in the `strategies` array
uint256[] shares;
}
struct QueuedWithdrawalParams {
// Array of strategies that the QueuedWithdrawal contains
IStrategy[] strategies;
// Array containing the amount of shares in each Strategy in the `strategies` array
uint256[] shares;
// The address of the withdrawer
address withdrawer;
}
// @notice Emitted when a new operator registers in Pell and provides their OperatorDetails.
event OperatorRegistered(address indexed operator, OperatorDetails operatorDetails);
/// @notice Emitted when an operator updates their OperatorDetails to @param newOperatorDetails
event OperatorDetailsModified(address indexed operator, OperatorDetails newOperatorDetails);
/**
* @notice Emitted when @param operator indicates that they are updating their MetadataURI string
* @dev Note that these strings are *never stored in storage* and are instead purely emitted in events for off-chain indexing
*/
event OperatorMetadataURIUpdated(address indexed operator, string metadataURI);
/// @notice Emitted whenever an operator's shares are increased for a given strategy. Note that shares is the delta in the operator's shares.
event OperatorSharesIncreased(address indexed operator, address staker, IStrategy strategy, uint256 shares);
/// @notice Emitted whenever an operator's shares are decreased for a given strategy. Note that shares is the delta in the operator's shares.
event OperatorSharesDecreased(address indexed operator, address staker, IStrategy strategy, uint256 shares);
/// @notice Emitted when @param staker delegates to @param operator.
event StakerDelegated(address indexed staker, address indexed operator);
/// @notice Emitted when @param staker undelegates from @param operator.
event StakerUndelegated(address indexed staker, address indexed operator);
/// @notice Emitted when @param staker is undelegated via a call not originating from the staker themself
event StakerForceUndelegated(address indexed staker, address indexed operator);
/**
* @notice Emitted when a new withdrawal is queued.
* @param withdrawalRoot Is the hash of the `withdrawal`.
* @param withdrawal Is the withdrawal itself.
*/
event WithdrawalQueued(bytes32 withdrawalRoot, Withdrawal withdrawal);
/// @notice Emitted when a queued withdrawal is completed
event WithdrawalCompleted(bytes32 withdrawalRoot);
/// @notice Emitted when the `minWithdrawalDelay` variable is modified from `previousValue` to `newValue`.
event MinWithdrawalDelaySet(uint256 previousValue, uint256 newValue);
/// @notice Emitted when the `strategyWithdrawalDelay` variable is modified from `previousValue` to `newValue`.
event StrategyWithdrawalDelaySet(IStrategy strategy, uint256 previousValue, uint256 newValue);
event UpdateWrappedTokenGateway(address previousGateway, address currentGateway);
/**
* @notice Registers the caller as an operator in Pell.
* @param registeringOperatorDetails is the `OperatorDetails` for the operator.
* @param metadataURI is a URI for the operator's metadata, i.e. a link providing more details on the operator.
*
* @dev Once an operator is registered, they cannot 'deregister' as an operator, and they will forever be considered "delegated to themself".
* @dev This function will revert if the caller attempts to set their `earningsReceiver` to address(0).
* @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event
*/
function registerAsOperator(OperatorDetails calldata registeringOperatorDetails, string calldata metadataURI) external;
/**
* @notice Updates an operator's stored `OperatorDetails`.
* @param newOperatorDetails is the updated `OperatorDetails` for the operator, to replace their current OperatorDetails`.
*
* @dev The caller must have previously registered as an operator in Pell.
* @dev This function will revert if the caller attempts to set their `earningsReceiver` to address(0).
*/
function modifyOperatorDetails(OperatorDetails calldata newOperatorDetails) external;
/**
* @notice Called by an operator to emit an `OperatorMetadataURIUpdated` event indicating the information has updated.
* @param metadataURI The URI for metadata associated with an operator
* @dev Note that the `metadataURI` is *never stored * and is only emitted in the `OperatorMetadataURIUpdated` event
*/
function updateOperatorMetadataURI(string calldata metadataURI) external;
/**
* @notice Caller delegates their stake to an operator.
* @param operator The account (`msg.sender`) is delegating its assets to for use in serving applications built on Pell.
* @param approverSignatureAndExpiry Verifies the operator approves of this delegation
* @param approverSalt A unique single use value tied to an individual signature.
* @dev The approverSignatureAndExpiry is used in the event that:
* 1) the operator's `delegationApprover` address is set to a non-zero value.
* AND
* 2) neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator
* or their delegationApprover is the `msg.sender`, then approval is assumed.
* @dev In the event that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input
* in this case to save on complexity + gas costs
*/
function delegateTo(address operator, SignatureWithExpiry memory approverSignatureAndExpiry, bytes32 approverSalt) external;
/**
* @notice Caller delegates a staker's stake to an operator with valid signatures from both parties.
* @param staker The account delegating stake to an `operator` account
* @param operator The account (`staker`) is delegating its assets to for use in serving applications built on Pell.
* @param stakerSignatureAndExpiry Signed data from the staker authorizing delegating stake to an operator
* @param approverSignatureAndExpiry is a parameter that will be used for verifying that the operator approves of this delegation action in the event that:
* @param approverSalt Is a salt used to help guarantee signature uniqueness. Each salt can only be used once by a given approver.
*
* @dev If `staker` is an EOA, then `stakerSignature` is verified to be a valid ECDSA stakerSignature from `staker`, indicating their intention for this action.
* @dev If `staker` is a contract, then `stakerSignature` will be checked according to EIP-1271.
* @dev the operator's `delegationApprover` address is set to a non-zero value.
* @dev neither the operator nor their `delegationApprover` is the `msg.sender`, since in the event that the operator or their delegationApprover
* is the `msg.sender`, then approval is assumed.
* @dev This function will revert if the current `block.timestamp` is equal to or exceeds the expiry
* @dev In the case that `approverSignatureAndExpiry` is not checked, its content is ignored entirely; it's recommended to use an empty input
* in this case to save on complexity + gas costs
*/
function delegateToBySignature(
address staker,
address operator,
SignatureWithExpiry memory stakerSignatureAndExpiry,
SignatureWithExpiry memory approverSignatureAndExpiry,
bytes32 approverSalt
) external;
/**
* @notice Undelegates the staker from the operator who they are delegated to. Puts the staker into the "undelegation limbo" mode of the EigenPodManager
* and queues a withdrawal of all of the staker's shares in the StrategyManager (to the staker), if necessary.
* @param staker The account to be undelegated.
* @return withdrawalRoot The root of the newly queued withdrawal, if a withdrawal was queued. Otherwise just bytes32(0).
*
* @dev Reverts if the `staker` is also an operator, since operators are not allowed to undelegate from themselves.
* @dev Reverts if the caller is not the staker, nor the operator who the staker is delegated to, nor the operator's specified "delegationApprover"
* @dev Reverts if the `staker` is already undelegated.
*/
function undelegate(address staker) external returns (bytes32[] memory withdrawalRoot);
/**
* Allows a staker to withdraw some shares. Withdrawn shares/strategies are immediately removed
* from the staker. If the staker is delegated, withdrawn shares/strategies are also removed from
* their operator.
*
* All withdrawn shares/strategies are placed in a queue and can be fully withdrawn after a delay.
*/
function queueWithdrawals(QueuedWithdrawalParams[] calldata queuedWithdrawalParams) external returns (bytes32[] memory);
/**
* @notice Used to complete the specified `withdrawal`. The caller must match `withdrawal.withdrawer`
* @param withdrawal The Withdrawal to complete.
* @param tokens Array in which the i-th entry specifies the `token` input to the 'withdraw' function of the i-th Strategy in the `withdrawal.strategies` array.
* This input can be provided with zero length if `receiveAsTokens` is set to 'false' (since in that case, this input will be unused)
* @param middlewareTimesIndex is the index in the operator that the staker who triggered the withdrawal was delegated to's middleware times array
* @param receiveAsTokens If true, the shares specified in the withdrawal will be withdrawn from the specified strategies themselves
* and sent to the caller, through calls to `withdrawal.strategies[i].withdraw`. If false, then the shares in the specified strategies
* will simply be transferred to the caller directly.
* @dev middlewareTimesIndex should be calculated off chain before calling this function by finding the first index that satisfies `slasher.canWithdraw`
* @dev beaconChainETHStrategy shares are non-transferrable, so if `receiveAsTokens = false` and `withdrawal.withdrawer != withdrawal.staker`, note that
* any beaconChainETHStrategy shares in the `withdrawal` will be _returned to the staker_, rather than transferred to the withdrawer, unlike shares in
* any other strategies, which will be transferred to the withdrawer.
*/
function completeQueuedWithdrawal(
Withdrawal calldata withdrawal,
IERC20[] calldata tokens,
uint256 middlewareTimesIndex,
bool receiveAsTokens
) external;
/**
* @notice Array-ified version of `completeQueuedWithdrawal`.
* Used to complete the specified `withdrawals`. The function caller must match `withdrawals[...].withdrawer`
* @param withdrawals The Withdrawals to complete.
* @param tokens Array of tokens for each Withdrawal. See `completeQueuedWithdrawal` for the usage of a single array.
* @param middlewareTimesIndexes One index to reference per Withdrawal. See `completeQueuedWithdrawal` for the usage of a single index.
* @param receiveAsTokens Whether or not to complete each withdrawal as tokens. See `completeQueuedWithdrawal` for the usage of a single boolean.
* @dev See `completeQueuedWithdrawal` for relevant dev tags
*/
function completeQueuedWithdrawals(
Withdrawal[] calldata withdrawals,
IERC20[][] calldata tokens,
uint256[] calldata middlewareTimesIndexes,
bool[] calldata receiveAsTokens
) external;
/**
* @notice Increases a staker's delegated share balance in a strategy.
* @param staker The address to increase the delegated shares for their operator.
* @param strategy The strategy in which to increase the delegated shares.
* @param shares The number of shares to increase.
*
* @dev *If the staker is actively delegated*, then increases the `staker`'s delegated shares in `strategy` by `shares`. Otherwise does nothing.
* @dev Callable only by the StrategyManager or EigenPodManager.
*/
function increaseDelegatedShares(address staker, IStrategy strategy, uint256 shares) external;
/**
* @notice Decreases a staker's delegated share balance in a strategy.
* @param staker The address to increase the delegated shares for their operator.
* @param strategy The strategy in which to decrease the delegated shares.
* @param shares The number of shares to decrease.
*
* @dev *If the staker is actively delegated*, then decreases the `staker`'s delegated shares in `strategy` by `shares`. Otherwise does nothing.
* @dev Callable only by the StrategyManager or EigenPodManager.
*/
function decreaseDelegatedShares(address staker, IStrategy strategy, uint256 shares) external;
/**
* @notice returns the address of the operator that `staker` is delegated to.
* @notice Mapping: staker => operator whom the staker is currently delegated to.
* @dev Note that returning address(0) indicates that the staker is not actively delegated to any operator.
*/
function delegatedTo(address staker) external view returns (address);
/**
* @notice Returns the OperatorDetails struct associated with an `operator`.
*/
function operatorDetails(address operator) external view returns (OperatorDetails memory);
/*
* @notice Returns the earnings receiver address for an operator
*/
function earningsReceiver(address operator) external view returns (address);
/**
* @notice Returns the delegationApprover account for an operator
*/
function delegationApprover(address operator) external view returns (address);
/**
* @notice Returns the stakerOptOutWindow for an operator
*/
function stakerOptOutWindow(address operator) external view returns (uint256);
/**
* @notice Given array of strategies, returns array of shares for the operator
*/
function getOperatorShares(address operator, IStrategy[] memory strategies) external view returns (uint256[] memory);
/**
* @notice Given a list of strategies, return the minimum cooldown that must pass to withdraw
* from all the inputted strategies. Return value is >= minWithdrawalDelay as this is the global min withdrawal delay.
* @param strategies The strategies to check withdrawal delays for
*/
function getWithdrawalDelay(IStrategy[] calldata strategies) external view returns (uint256);
/**
* @notice returns the total number of shares in `strategy` that are delegated to `operator`.
* @notice Mapping: operator => strategy => total number of shares in the strategy delegated to the operator.
* @dev By design, the following invariant should hold for each Strategy:
* (operator's shares in delegation manager) = sum (shares above zero of all stakers delegated to operator)
* = sum (delegateable shares of all stakers delegated to the operator)
*/
function operatorShares(address operator, IStrategy strategy) external view returns (uint256);
/**
* @notice Returns 'true' if `staker` *is* actively delegated, and 'false' otherwise.
*/
function isDelegated(address staker) external view returns (bool);
/**
* @notice Returns true is an operator has previously registered for delegation.
*/
function isOperator(address operator) external view returns (bool);
/// @notice Mapping: staker => number of signed delegation nonces (used in `delegateToBySignature`) from the staker that the contract has already checked
function stakerNonce(address staker) external view returns (uint256);
/**
* @notice Mapping: delegationApprover => 32-byte salt => whether or not the salt has already been used by the delegationApprover.
* @dev Salts are used in the `delegateTo` and `delegateToBySignature` functions. Note that these functions only process the delegationApprover's
* signature + the provided salt if the operator being delegated to has specified a nonzero address as their `delegationApprover`.
*/
function delegationApproverSaltIsSpent(address _delegationApprover, bytes32 salt) external view returns (bool);
/**
* @notice Minimum delay enforced by this contract for completing queued withdrawals. Cooldown, and adjustable by this contract's owner,
* up to a maximum of `MAX_WITHDRAWAL_DELAY`. Minimum value is 0 (i.e. no delay enforced).
* Note that strategies each have a separate withdrawal delay, which can be greater than this value. So the minimum cooldown that must pass
* to withdraw a strategy is MAX(minWithdrawalDelay, strategyWithdrawalDelay[strategy])
*/
function minWithdrawalDelay() external view returns (uint256);
/**
* @notice Minimum delay enforced by this contract per Strategy for completing queued withdrawals. Cooldown, and adjustable by this contract's owner,
* up to a maximum of `MAX_WITHDRAWAL_DELAY`. Minimum value is 0 (i.e. no delay enforced).
*/
function strategyWithdrawalDelay(IStrategy strategy) external view returns (uint256);
/**
* @notice Calculates the digestHash for a `staker` to sign to delegate to an `operator`
* @param staker The signing staker
* @param operator The operator who is being delegated to
* @param expiry The desired expiry time of the staker's signature
*/
function calculateCurrentStakerDelegationDigestHash(address staker, address operator, uint256 expiry) external view returns (bytes32);
/**
* @notice Calculates the digest hash to be signed and used in the `delegateToBySignature` function
* @param staker The signing staker
* @param _stakerNonce The nonce of the staker. In practice we use the staker's current nonce, stored at `stakerNonce[staker]`
* @param operator The operator who is being delegated to
* @param expiry The desired expiry time of the staker's signature
*/
function calculateStakerDelegationDigestHash(
address staker,
uint256 _stakerNonce,
address operator,
uint256 expiry
) external view returns (bytes32);
/**
* @notice Calculates the digest hash to be signed by the operator's delegationApprove and used in the `delegateTo` and `delegateToBySignature` functions.
* @param staker The account delegating their stake
* @param operator The account receiving delegated stake
* @param _delegationApprover the operator's `delegationApprover` who will be signing the delegationHash (in general)
* @param approverSalt A unique and single use value associated with the approver signature.
* @param expiry Time after which the approver's signature becomes invalid
*/
function calculateDelegationApprovalDigestHash(
address staker,
address operator,
address _delegationApprover,
bytes32 approverSalt,
uint256 expiry
) external view returns (bytes32);
/// @notice The EIP-712 typehash for the contract's domain
function DOMAIN_TYPEHASH() external view returns (bytes32);
/// @notice The EIP-712 typehash for the StakerDelegation struct used by the contract
function STAKER_DELEGATION_TYPEHASH() external view returns (bytes32);
/// @notice The EIP-712 typehash for the DelegationApproval struct used by the contract
function DELEGATION_APPROVAL_TYPEHASH() external view returns (bytes32);
/**
* @notice Getter function for the current EIP-712 domain separator for this contract.
*
* @dev The domain separator will change in the event of a fork that changes the ChainID.
* @dev By introducing a domain separator the DApp developers are guaranteed that there can be no signature collision.
* for more detailed information please read EIP-712.
*/
function domainSeparator() external view returns (bytes32);
/// @notice Mapping: staker => cumulative number of queued withdrawals they have ever initiated.
/// @dev This only increments (doesn't decrement), and is used to help ensure that otherwise identical withdrawals have unique hashes.
function cumulativeWithdrawalsQueued(address staker) external view returns (uint256);
/// @notice Returns the keccak256 hash of `withdrawal`.
function calculateWithdrawalRoot(Withdrawal memory withdrawal) external pure returns (bytes32);
}// SPDX-License-Identifier: LGPL-3.0
pragma solidity 0.8.20;
import '../interfaces/IPauserRegistry.sol';
/**
* @title Adds pausability to a contract, with pausing & unpausing controlled by the `pauser` and `unpauser` of a PauserRegistry contract.
* @notice Contracts that inherit from this contract may define their own `pause` and `unpause` (and/or related) functions.
* These functions should be permissioned as "onlyPauser" which defers to a `PauserRegistry` for determining access control.
* @dev Pausability is implemented using a uint256, which allows up to 256 different single bit-flags; each bit can potentially pause different functionality.
* Inspiration for this was taken from the NearBridge design here https://etherscan.io/address/0x3FEFc5A4B1c02f21cBc8D3613643ba0635b9a873#code.
* For the `pause` and `unpause` functions we've implemented, if you pause, you can only flip (any number of) switches to on/1 (aka "paused"), and if you unpause,
* you can only flip (any number of) switches to off/0 (aka "paused").
* If you want a pauseXYZ function that just flips a single bit / "pausing flag", it will:
* 1) 'bit-wise and' (aka `&`) a flag with the current paused state (as a uint256)
* 2) update the paused state to this new value
* @dev We note as well that we have chosen to identify flags by their *bit index* as opposed to their numerical value, so, e.g. defining `DEPOSITS_PAUSED = 3`
* indicates specifically that if the *third bit* of `_paused` is flipped -- i.e. it is a '1' -- then deposits should be paused
*/
interface IPausable {
/// @notice Emitted when the `pauserRegistry` is set to `newPauserRegistry`.
event PauserRegistrySet(IPauserRegistry pauserRegistry, IPauserRegistry newPauserRegistry);
/// @notice Emitted when the pause is triggered by `account`, and changed to `newPausedStatus`.
event Paused(address indexed account, uint256 newPausedStatus);
/// @notice Emitted when the pause is lifted by `account`, and changed to `newPausedStatus`.
event Unpaused(address indexed account, uint256 newPausedStatus);
/// @notice Address of the `PauserRegistry` contract that this contract defers to for determining access control (for pausing).
function pauserRegistry() external view returns (IPauserRegistry);
/**
* @notice This function is used to pause an Pell contract's functionality.
* It is permissioned to the `pauser` address, which is expected to be a low threshold multisig.
* @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
* @dev This function can only pause functionality, and thus cannot 'unflip' any bit in `_paused` from 1 to 0.
*/
function pause(uint256 newPausedStatus) external;
/**
* @notice Alias for `pause(type(uint256).max)`.
*/
function pauseAll() external;
/**
* @notice This function is used to unpause an Pell contract's functionality.
* It is permissioned to the `unpauser` address, which is expected to be a high threshold multisig or governance contract.
* @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
* @dev This function can only unpause functionality, and thus cannot 'flip' any bit in `_paused` from 0 to 1.
*/
function unpause(uint256 newPausedStatus) external;
/// @notice Returns the current paused status as a uint256.
function paused() external view returns (uint256);
/// @notice Returns 'true' if the `indexed`th bit of `_paused` is 1, and 'false' otherwise
function paused(uint8 index) external view returns (bool);
/// @notice Allows the unpauser to set a new pauser registry
function setPauserRegistry(IPauserRegistry newPauserRegistry) external;
}// SPDX-License-Identifier: LGPL-3.0
pragma solidity 0.8.20;
/**
* @title Interface for the `PauserRegistry` contract.
*/
interface IPauserRegistry {
event PauserStatusChanged(address pauser, bool canPause);
event UnpauserChanged(address previousUnpauser, address newUnpauser);
/// @notice Mapping of addresses to whether they hold the pauser role.
function isPauser(address pauser) external view returns (bool);
/// @notice Unique address that holds the unpauser role. Capable of changing *both* the pauser and unpauser addresses.
function unpauser() external view returns (address);
}// SPDX-License-Identifier: LGPL-3.0
pragma solidity 0.8.20;
/**
* @title The interface for common signature utilities.
*/
interface ISignatureUtils {
// @notice Struct that bundles together a signature and an expiration time for the signature. Used primarily for stack management.
struct SignatureWithExpiry {
// the signature itself, formatted as a single bytes object
bytes signature;
// the expiration timestamp (UTC) of the signature
uint256 expiry;
}
// @notice Struct that bundles together a signature, a salt for uniqueness, and an expiration time for the signature. Used primarily for stack management.
struct SignatureWithSaltAndExpiry {
// the signature itself, formatted as a single bytes object
bytes signature;
// the salt used to generate the signature
bytes32 salt;
// the expiration timestamp (UTC) of the signature
uint256 expiry;
}
}// SPDX-License-Identifier: LGPL-3.0
pragma solidity 0.8.20;
import './IStrategyManager.sol';
import './IDelegationManager.sol';
/**
* @title Interface for the primary 'slashing' contract for Pell.
* @notice See the `Slasher` contract itself for implementation details.
*/
interface ISlasher {
// struct used to store information about the current state of an operator's obligations to middlewares they are serving
struct MiddlewareTimes {
// The update timestamp for the middleware whose most recent update was earliest, i.e. the 'stalest' update out of all middlewares the operator is serving
uint32 stalestUpdateTimestamp;
// The latest 'serveUntilTimestamp' from all of the middleware that the operator is serving
uint32 latestServeUntilTimestamp;
}
// struct used to store details relevant to a single middleware that an operator has opted-in to serving
struct MiddlewareDetails {
// the timestamp at which the contract begins being able to finalize the operator's registration with the service via calling `recordFirstStakeUpdate`
uint32 registrationMayBeginAtTimestamp;
// the timestamp before which the contract is allowed to slash the user
uint32 contractCanSlashOperatorUntilTimestamp;
// the timestamp at which the middleware's view of the operator's stake was most recently updated
uint32 latestUpdateTimestamp;
}
/// @notice Emitted when a middleware times is added to `operator`'s array.
event MiddlewareTimesAdded(address operator, uint256 index, uint32 stalestUpdateTimestamp, uint32 latestServeUntilTimestamp);
/// @notice Emitted when `operator` begins to allow `contractAddress` to slash them.
event OptedIntoSlashing(address indexed operator, address indexed contractAddress);
/// @notice Emitted when `contractAddress` signals that it will no longer be able to slash `operator` after the `contractCanSlashOperatorUntilTimestamp`.
event SlashingAbilityRevoked(address indexed operator, address indexed contractAddress, uint32 contractCanSlashOperatorUntilTimestamp);
/**
* @notice Emitted when `slashingContract` 'freezes' the `slashedOperator`.
* @dev The `slashingContract` must have permission to slash the `slashedOperator`, i.e. `canSlash(slasherOperator, slashingContract)` must return 'true'.
*/
event OperatorFrozen(address indexed slashedOperator, address indexed slashingContract);
/// @notice Emitted when `previouslySlashedAddress` is 'unfrozen', allowing them to again move deposited funds within Pell.
event FrozenStatusReset(address indexed previouslySlashedAddress);
/**
* @notice Gives the `contractAddress` permission to slash the funds of the caller.
* @dev Typically, this function must be called prior to registering for a middleware.
*/
function optIntoSlashing(address contractAddress) external;
/**
* @notice Used for 'slashing' a certain operator.
* @param toBeFrozen The operator to be frozen.
* @dev Technically the operator is 'frozen' (hence the name of this function), and then subject to slashing pending a decision by a human-in-the-loop.
* @dev The operator must have previously given the caller (which should be a contract) the ability to slash them, through a call to `optIntoSlashing`.
*/
function freezeOperator(address toBeFrozen) external;
/**
* @notice Removes the 'frozen' status from each of the `frozenAddresses`
* @dev Callable only by the contract owner (i.e. governance).
*/
function resetFrozenStatus(address[] calldata frozenAddresses) external;
/**
* @notice this function is a called by middlewares during an operator's registration to make sure the operator's stake at registration
* is slashable until serveUntil
* @param operator the operator whose stake update is being recorded
* @param serveUntilTimestamp the timestamp until which the operator's stake at the current timestamp is slashable
* @dev adds the middleware's slashing contract to the operator's linked list
*/
function recordFirstStakeUpdate(address operator, uint32 serveUntilTimestamp) external;
/**
* @notice this function is a called by middlewares during a stake update for an operator (perhaps to free pending withdrawals)
* to make sure the operator's stake at updateTimestamp is slashable until serveUntil
* @param operator the operator whose stake update is being recorded
* @param updateTimestamp the timestamp for which the stake update is being recorded
* @param serveUntilTimestamp the timestamp until which the operator's stake at updateTimestamp is slashable
* @param insertAfter the element of the operators linked list that the currently updating middleware should be inserted after
* @dev insertAfter should be calculated offchain before making the transaction that calls this. this is subject to race conditions,
* but it is anticipated to be rare and not detrimental.
*/
function recordStakeUpdate(address operator, uint32 updateTimestamp, uint32 serveUntilTimestamp, uint256 insertAfter) external;
/**
* @notice this function is a called by middlewares during an operator's deregistration to make sure the operator's stake at deregistration
* is slashable until serveUntil
* @param operator the operator whose stake update is being recorded
* @param serveUntilTimestamp the timestamp until which the operator's stake at the current timestamp is slashable
* @dev removes the middleware's slashing contract to the operator's linked list and revokes the middleware's (i.e. caller's) ability to
* slash `operator` once `serveUntil` is reached
*/
function recordLastStakeUpdateAndRevokeSlashingAbility(address operator, uint32 serveUntilTimestamp) external;
/// @notice The StrategyManager contract of Pell
function strategyManager() external view returns (IStrategyManager);
/// @notice The DelegationManager contract of Pell
function delegation() external view returns (IDelegationManager);
/**
* @notice Used to determine whether `staker` is actively 'frozen'. If a staker is frozen, then they are potentially subject to
* slashing of their funds, and cannot cannot deposit or withdraw from the strategyManager until the slashing process is completed
* and the staker's status is reset (to 'unfrozen').
* @param staker The staker of interest.
* @return Returns 'true' if `staker` themselves has their status set to frozen, OR if the staker is delegated
* to an operator who has their status set to frozen. Otherwise returns 'false'.
*/
function isFrozen(address staker) external view returns (bool);
/// @notice Returns true if `slashingContract` is currently allowed to slash `toBeSlashed`.
function canSlash(address toBeSlashed, address slashingContract) external view returns (bool);
/// @notice Returns the timestamp until which `serviceContract` is allowed to slash the `operator`.
function contractCanSlashOperatorUntilTimestamp(address operator, address serviceContract) external view returns (uint32);
/// @notice Returns the timestamp at which the `serviceContract` last updated its view of the `operator`'s stake
function latestUpdateTimestamp(address operator, address serviceContract) external view returns (uint32);
/// @notice A search routine for finding the correct input value of `insertAfter` to `recordStakeUpdate` / `_updateMiddlewareList`.
function getCorrectValueForInsertAfter(address operator, uint32 updateTimestamp) external view returns (uint256);
/**
* @notice Returns 'true' if `operator` can currently complete a withdrawal started at the `withdrawalStartTimestamp`, with `middlewareTimesIndex` used
* to specify the index of a `MiddlewareTimes` struct in the operator's list (i.e. an index in `operatorToMiddlewareTimes[operator]`). The specified
* struct is consulted as proof of the `operator`'s ability (or lack thereof) to complete the withdrawal.
* This function will return 'false' if the operator cannot currently complete a withdrawal started at the `withdrawalStartTimestamp`, *or* in the event
* that an incorrect `middlewareTimesIndex` is supplied, even if one or more correct inputs exist.
* @param operator Either the operator who queued the withdrawal themselves, or if the withdrawing party is a staker who delegated to an operator,
* this address is the operator *who the staker was delegated to* at the time of the `withdrawalStartTimestamp`.
* @param withdrawalStartTimestamp The timestamp at which the withdrawal was initiated.
* @param middlewareTimesIndex Indicates an index in `operatorToMiddlewareTimes[operator]` to consult as proof of the `operator`'s ability to withdraw
* @dev The correct `middlewareTimesIndex` input should be computable off-chain.
*/
function canWithdraw(address operator, uint32 withdrawalStartTimestamp, uint256 middlewareTimesIndex) external returns (bool);
/**
* operator =>
* [
* (
* the least recent update timestamp of all of the middlewares it's serving/served,
* latest time that the stake bonded at that update needed to serve until
* )
* ]
*/
function operatorToMiddlewareTimes(address operator, uint256 arrayIndex) external view returns (MiddlewareTimes memory);
/// @notice Getter function for fetching `operatorToMiddlewareTimes[operator].length`
function middlewareTimesLength(address operator) external view returns (uint256);
/// @notice Getter function for fetching `operatorToMiddlewareTimes[operator][index].stalestUpdateTimestamp`.
function getMiddlewareTimesIndexStalestUpdateTimestamp(address operator, uint32 index) external view returns (uint32);
/// @notice Getter function for fetching `operatorToMiddlewareTimes[operator][index].latestServeUntil`.
function getMiddlewareTimesIndexServeUntilTimestamp(address operator, uint32 index) external view returns (uint32);
/// @notice Getter function for fetching `_operatorToWhitelistedContractsByUpdate[operator].size`.
function operatorWhitelistedContractsLinkedListSize(address operator) external view returns (uint256);
/// @notice Getter function for fetching a single node in the operator's linked list (`_operatorToWhitelistedContractsByUpdate[operator]`).
function operatorWhitelistedContractsLinkedListEntry(address operator, address node) external view returns (bool, uint256, uint256);
}// SPDX-License-Identifier: LGPL-3.0
pragma solidity 0.8.20;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
/**
* @title Minimal interface for an `Strategy` contract.
* @notice Custom `Strategy` implementations may expand extensively on this interface.
*/
interface IStrategy {
/**
* @notice Used to deposit tokens into this Strategy
* @param token is the ERC20 token being deposited
* @param amount is the amount of token being deposited
* @dev This function is only callable by the strategyManager contract. It is invoked inside of the strategyManager's
* `depositIntoStrategy` function, and individual share balances are recorded in the strategyManager as well.
* @return newShares is the number of new shares issued at the current exchange ratio.
*/
function deposit(IERC20 token, uint256 amount) external returns (uint256);
/**
* @notice Used to withdraw tokens from this Strategy, to the `recipient`'s address
* @param recipient is the address to receive the withdrawn funds
* @param token is the ERC20 token being transferred out
* @param amountShares is the amount of shares being withdrawn
* @dev This function is only callable by the strategyManager contract. It is invoked inside of the strategyManager's
* other functions, and individual share balances are recorded in the strategyManager as well.
*/
function withdraw(address recipient, IERC20 token, uint256 amountShares) external;
/**
* @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy.
* @notice In contrast to `sharesToUnderlyingView`, this function **may** make state modifications
* @param amountShares is the amount of shares to calculate its conversion into the underlying token
* @return The amount of underlying tokens corresponding to the input `amountShares`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function sharesToUnderlying(uint256 amountShares) external returns (uint256);
/**
* @notice Used to convert an amount of underlying tokens to the equivalent amount of shares in this strategy.
* @notice In contrast to `underlyingToSharesView`, this function **may** make state modifications
* @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into strategy shares
* @return The amount of underlying tokens corresponding to the input `amountShares`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function underlyingToShares(uint256 amountUnderlying) external returns (uint256);
/**
* @notice convenience function for fetching the current underlying value of all of the `user`'s shares in
* this strategy. In contrast to `userUnderlyingView`, this function **may** make state modifications
*/
function userUnderlying(address user) external returns (uint256);
/**
* @notice convenience function for fetching the current total shares of `user` in this strategy, by
* querying the `strategyManager` contract
*/
function shares(address user) external view returns (uint256);
/**
* @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this strategy.
* @notice In contrast to `sharesToUnderlying`, this function guarantees no state modifications
* @param amountShares is the amount of shares to calculate its conversion into the underlying token
* @return The amount of shares corresponding to the input `amountUnderlying`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function sharesToUnderlyingView(uint256 amountShares) external view returns (uint256);
/**
* @notice Used to convert an amount of underlying tokens to the equivalent amount of shares in this strategy.
* @notice In contrast to `underlyingToShares`, this function guarantees no state modifications
* @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into strategy shares
* @return The amount of shares corresponding to the input `amountUnderlying`
* @dev Implementation for these functions in particular may vary significantly for different strategies
*/
function underlyingToSharesView(uint256 amountUnderlying) external view returns (uint256);
/**
* @notice convenience function for fetching the current underlying value of all of the `user`'s shares in
* this strategy. In contrast to `userUnderlying`, this function guarantees no state modifications
*/
function userUnderlyingView(address user) external view returns (uint256);
/// @notice The underlying token for shares in this Strategy
function underlyingToken() external view returns (IERC20);
/// @notice The total number of extant shares in this Strategy
function totalShares() external view returns (uint256);
/// @notice Returns either a brief string explaining the strategy's goal & purpose, or a link to metadata that explains in more detail.
function explanation() external view returns (string memory);
}// SPDX-License-Identifier: LGPL-3.0
pragma solidity 0.8.20;
import './IStrategy.sol';
import './ISlasher.sol';
import './IDelegationManager.sol';
/**
* @title Interface for the primary entrypoint for funds into Pell.
* @notice See the `StrategyManager` contract itself for implementation details.
*/
interface IStrategyManager {
/**
* @notice Emitted when a new deposit occurs on behalf of `staker`.
* @param staker Is the staker who is depositing funds into Pell.
* @param strategy Is the strategy that `staker` has deposited into.
* @param token Is the token that `staker` deposited.
* @param shares Is the number of new shares `staker` has been granted in `strategy`.
*/
event Deposit(address staker, IERC20 token, IStrategy strategy, uint256 shares);
/// @notice Emitted when `thirdPartyTransfersForbidden` is updated for a strategy and value by the owner
event UpdatedThirdPartyTransfersForbidden(IStrategy strategy, bool value);
/// @notice Emitted when the `strategyWhitelister` is changed
event StrategyWhitelisterChanged(address previousAddress, address newAddress);
/// @notice Emitted when a strategy is added to the approved list of strategies for deposit
event StrategyAddedToDepositWhitelist(IStrategy strategy);
/// @notice Emitted when a strategy is removed from the approved list of strategies for deposit
event StrategyRemovedFromDepositWhitelist(IStrategy strategy);
/**
* @notice Deposits `amount` of `token` into the specified `strategy`, with the resultant shares credited to `msg.sender`
* @param strategy is the specified strategy where deposit is to be made,
* @param token is the denomination in which the deposit is to be made,
* @param amount is the amount of token to be deposited in the strategy by the staker
* @return shares The amount of new shares in the `strategy` created as part of the action.
* @dev The `msg.sender` must have previously approved this contract to transfer at least `amount` of `token` on their behalf.
* @dev Cannot be called by an address that is 'frozen' (this function will revert if the `msg.sender` is frozen).
*
* WARNING: Depositing tokens that allow reentrancy (eg. ERC-777) into a strategy is not recommended. This can lead to attack vectors
* where the token balance and corresponding strategy shares are not in sync upon reentrancy.
*/
function depositIntoStrategy(IStrategy strategy, IERC20 token, uint256 amount) external returns (uint256 shares);
/**
* @notice Used for depositing an asset into the specified strategy with the resultant shares credited to `staker`,
* who must sign off on the action.
* Note that the assets are transferred out/from the `msg.sender`, not from the `staker`; this function is explicitly designed
* purely to help one address deposit 'for' another.
* @param strategy is the specified strategy where deposit is to be made,
* @param token is the denomination in which the deposit is to be made,
* @param amount is the amount of token to be deposited in the strategy by the staker
* @param staker the staker that the deposited assets will be credited to
* @param expiry the timestamp at which the signature expires
* @param signature is a valid signature from the `staker`. either an ECDSA signature if the `staker` is an EOA, or data to forward
* following EIP-1271 if the `staker` is a contract
* @return shares The amount of new shares in the `strategy` created as part of the action.
* @dev The `msg.sender` must have previously approved this contract to transfer at least `amount` of `token` on their behalf.
* @dev A signature is required for this function to eliminate the possibility of griefing attacks, specifically those
* targeting stakers who may be attempting to undelegate.
* @dev Cannot be called if thirdPartyTransfersForbidden is set to true for this strategy
*
* WARNING: Depositing tokens that allow reentrancy (eg. ERC-777) into a strategy is not recommended. This can lead to attack vectors
* where the token balance and corresponding strategy shares are not in sync upon reentrancy
*/
function depositIntoStrategyWithSignature(
IStrategy strategy,
IERC20 token,
uint256 amount,
address staker,
uint256 expiry,
bytes memory signature
) external returns (uint256 shares);
/// @notice Used by the DelegationManager to remove a Staker's shares from a particular strategy when entering the withdrawal queue
function removeShares(address staker, IStrategy strategy, uint256 shares) external;
/// @notice Used by the DelegationManager to award a Staker some shares that have passed through the withdrawal queue
function addShares(address staker, IERC20 token, IStrategy strategy, uint256 shares) external;
/// @notice Used by the DelegationManager to convert withdrawn shares to tokens and send them to a recipient
function withdrawSharesAsTokens(address recipient, IStrategy strategy, uint256 shares, IERC20 token) external;
/// @notice Returns the current shares of `user` in `strategy`
function stakerStrategyShares(address user, IStrategy strategy) external view returns (uint256 shares);
/**
* @notice Get all details on the staker's deposits and corresponding shares
* @return (staker's strategies, shares in these strategies)
*/
function getDeposits(address staker) external view returns (IStrategy[] memory, uint256[] memory);
/// @notice Simple getter function that returns `stakerStrategyList[staker].length`.
function stakerStrategyListLength(address staker) external view returns (uint256);
/**
* @notice Owner-only function that adds the provided Strategies to the 'whitelist' of strategies that stakers can deposit into
* @param strategiesToWhitelist Strategies that will be added to the `strategyIsWhitelistedForDeposit` mapping (if they aren't in it already)
* @param thirdPartyTransfersForbiddenValues bool values to set `thirdPartyTransfersForbidden` to for each strategy
*/
function addStrategiesToDepositWhitelist(
IStrategy[] calldata strategiesToWhitelist,
bool[] calldata thirdPartyTransfersForbiddenValues
) external;
/**
* @notice Owner-only function that removes the provided Strategies from the 'whitelist' of strategies that stakers can deposit into
* @param strategiesToRemoveFromWhitelist Strategies that will be removed to the `strategyIsWhitelistedForDeposit` mapping (if they are in it)
*/
function removeStrategiesFromDepositWhitelist(IStrategy[] calldata strategiesToRemoveFromWhitelist) external;
/// @notice Returns the single, central Delegation contract of Pell
function delegation() external view returns (IDelegationManager);
/// @notice Returns the single, central Slasher contract of Pell
function slasher() external view returns (ISlasher);
/// @notice Returns the address of the `strategyWhitelister`
function strategyWhitelister() external view returns (address);
/**
* @notice Returns bool for whether or not `strategy` enables credit transfers. i.e enabling
* depositIntoStrategyWithSignature calls or queueing withdrawals to a different address than the staker.
*/
function thirdPartyTransfersForbidden(IStrategy strategy) external view returns (bool);
}// SPDX-License-Identifier: LGPL-3.0
pragma solidity 0.8.20;
import '@openzeppelin/contracts/interfaces/IERC1271.sol';
import '@openzeppelin/contracts/utils/Address.sol';
import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
/**
* @title Library of utilities for making EIP1271-compliant signature checks.
*/
library EIP1271SignatureUtils {
// bytes4(keccak256("isValidSignature(bytes32,bytes)")
bytes4 internal constant EIP1271_MAGICVALUE = 0x1626ba7e;
/**
* @notice Checks @param signature is a valid signature of @param digestHash from @param signer.
* If the `signer` contains no code -- i.e. it is not (yet, at least) a contract address, then checks using standard ECDSA logic
* Otherwise, passes on the signature to the signer to verify the signature and checks that it returns the `EIP1271_MAGICVALUE`.
*/
function checkSignature_EIP1271(address signer, bytes32 digestHash, bytes memory signature) internal view {
/**
* check validity of signature:
* 1) if `signer` is an EOA, then `signature` must be a valid ECDSA signature from `signer`,
* indicating their intention for this action
* 2) if `signer` is a contract, then `signature` must will be checked according to EIP-1271
*/
if (Address.isContract(signer)) {
require(
IERC1271(signer).isValidSignature(digestHash, signature) == EIP1271_MAGICVALUE,
'EIP1271SignatureUtils.checkSignature_EIP1271: ERC1271 signature verification failed'
);
} else {
require(ECDSA.recover(digestHash, signature) == signer, 'EIP1271SignatureUtils.checkSignature_EIP1271: signature not from signer');
}
}
}// SPDX-License-Identifier: LGPL-3.0
pragma solidity 0.8.20;
import '../interfaces/IPausable.sol';
/**
* @title Adds pausability to a contract, with pausing & unpausing controlled by the `pauser` and `unpauser` of a PauserRegistry contract.
* @notice Contracts that inherit from this contract may define their own `pause` and `unpause` (and/or related) functions.
* These functions should be permissioned as "onlyPauser" which defers to a `PauserRegistry` for determining access control.
* @dev Pausability is implemented using a uint256, which allows up to 256 different single bit-flags; each bit can potentially pause different functionality.
* Inspiration for this was taken from the NearBridge design here https://etherscan.io/address/0x3FEFc5A4B1c02f21cBc8D3613643ba0635b9a873#code.
* For the `pause` and `unpause` functions we've implemented, if you pause, you can only flip (any number of) switches to on/1 (aka "paused"), and if you unpause,
* you can only flip (any number of) switches to off/0 (aka "paused").
* If you want a pauseXYZ function that just flips a single bit / "pausing flag", it will:
* 1) 'bit-wise and' (aka `&`) a flag with the current paused state (as a uint256)
* 2) update the paused state to this new value
* @dev We note as well that we have chosen to identify flags by their *bit index* as opposed to their numerical value, so, e.g. defining `DEPOSITS_PAUSED = 3`
* indicates specifically that if the *third bit* of `_paused` is flipped -- i.e. it is a '1' -- then deposits should be paused
*/
contract Pausable is IPausable {
/// @notice Address of the `PauserRegistry` contract that this contract defers to for determining access control (for pausing).
IPauserRegistry public pauserRegistry;
/// @dev whether or not the contract is currently paused
uint256 private _paused;
uint256 internal constant UNPAUSE_ALL = 0;
uint256 internal constant PAUSE_ALL = type(uint256).max;
/// @notice
modifier onlyPauser() {
require(pauserRegistry.isPauser(msg.sender), 'msg.sender is not permissioned as pauser');
_;
}
modifier onlyUnpauser() {
require(msg.sender == pauserRegistry.unpauser(), 'msg.sender is not permissioned as unpauser');
_;
}
/// @notice Throws if the contract is paused, i.e. if any of the bits in `_paused` is flipped to 1.
modifier whenNotPaused() {
require(_paused == 0, 'Pausable: contract is paused');
_;
}
/// @notice Throws if the `indexed`th bit of `_paused` is 1, i.e. if the `index`th pause switch is flipped.
modifier onlyWhenNotPaused(uint8 index) {
require(!paused(index), 'Pausable: index is paused');
_;
}
/// @notice One-time function for setting the `pauserRegistry` and initializing the value of `_paused`.
function _initializePauser(IPauserRegistry _pauserRegistry, uint256 initPausedStatus) internal {
require(
address(pauserRegistry) == address(0) && address(_pauserRegistry) != address(0),
'Pausable._initializePauser: _initializePauser() can only be called once'
);
_paused = initPausedStatus;
emit Paused(msg.sender, initPausedStatus);
_setPauserRegistry(_pauserRegistry);
}
/**
* @notice This function is used to pause an Pell contract's functionality.
* It is permissioned to the `pauser` address, which is expected to be a low threshold multisig.
* @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
* @dev This function can only pause functionality, and thus cannot 'unflip' any bit in `_paused` from 1 to 0.
*/
function pause(uint256 newPausedStatus) external onlyPauser {
// verify that the `newPausedStatus` does not *unflip* any bits (i.e. doesn't unpause anything, all 1 bits remain)
require((_paused & newPausedStatus) == _paused, 'Pausable.pause: invalid attempt to unpause functionality');
_paused = newPausedStatus;
emit Paused(msg.sender, newPausedStatus);
}
/**
* @notice Alias for `pause(type(uint256).max)`.
*/
function pauseAll() external onlyPauser {
_paused = type(uint256).max;
emit Paused(msg.sender, type(uint256).max);
}
/**
* @notice This function is used to unpause an Pell contract's functionality.
* It is permissioned to the `unpauser` address, which is expected to be a high threshold multisig or governance contract.
* @param newPausedStatus represents the new value for `_paused` to take, which means it may flip several bits at once.
* @dev This function can only unpause functionality, and thus cannot 'flip' any bit in `_paused` from 0 to 1.
*/
function unpause(uint256 newPausedStatus) external onlyUnpauser {
// verify that the `newPausedStatus` does not *flip* any bits (i.e. doesn't pause anything, all 0 bits remain)
require(((~_paused) & (~newPausedStatus)) == (~_paused), 'Pausable.unpause: invalid attempt to pause functionality');
_paused = newPausedStatus;
emit Unpaused(msg.sender, newPausedStatus);
}
/// @notice Returns the current paused status as a uint256.
function paused() public view virtual returns (uint256) {
return _paused;
}
/// @notice Returns 'true' if the `indexed`th bit of `_paused` is 1, and 'false' otherwise
function paused(uint8 index) public view virtual returns (bool) {
uint256 mask = 1 << index;
return ((_paused & mask) == mask);
}
/// @notice Allows the unpauser to set a new pauser registry
function setPauserRegistry(IPauserRegistry newPauserRegistry) external onlyUnpauser {
_setPauserRegistry(newPauserRegistry);
}
/// internal function for setting pauser registry
function _setPauserRegistry(IPauserRegistry newPauserRegistry) internal {
require(address(newPauserRegistry) != address(0), 'Pausable._setPauserRegistry: newPauserRegistry cannot be the zero address');
emit PauserRegistrySet(pauserRegistry, newPauserRegistry);
pauserRegistry = newPauserRegistry;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}{
"evmVersion": "berlin",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 1500
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract IStrategyManager","name":"_strategyManager","type":"address"},{"internalType":"contract ISlasher","name":"_slasher","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"MinWithdrawalDelaySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"components":[{"internalType":"address","name":"earningsReceiver","type":"address"},{"internalType":"address","name":"delegationApprover","type":"address"},{"internalType":"uint32","name":"stakerOptOutWindow","type":"uint32"}],"indexed":false,"internalType":"struct IDelegationManager.OperatorDetails","name":"newOperatorDetails","type":"tuple"}],"name":"OperatorDetailsModified","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"string","name":"metadataURI","type":"string"}],"name":"OperatorMetadataURIUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"components":[{"internalType":"address","name":"earningsReceiver","type":"address"},{"internalType":"address","name":"delegationApprover","type":"address"},{"internalType":"uint32","name":"stakerOptOutWindow","type":"uint32"}],"indexed":false,"internalType":"struct IDelegationManager.OperatorDetails","name":"operatorDetails","type":"tuple"}],"name":"OperatorRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"contract IStrategy","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"OperatorSharesDecreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"contract IStrategy","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"OperatorSharesIncreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"newPausedStatus","type":"uint256"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IPauserRegistry","name":"pauserRegistry","type":"address"},{"indexed":false,"internalType":"contract IPauserRegistry","name":"newPauserRegistry","type":"address"}],"name":"PauserRegistrySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"StakerDelegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"StakerForceUndelegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"}],"name":"StakerUndelegated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IStrategy","name":"strategy","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newValue","type":"uint256"}],"name":"StrategyWithdrawalDelaySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"newPausedStatus","type":"uint256"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousGateway","type":"address"},{"indexed":false,"internalType":"address","name":"currentGateway","type":"address"}],"name":"UpdateWrappedTokenGateway","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"withdrawalRoot","type":"bytes32"}],"name":"WithdrawalCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"withdrawalRoot","type":"bytes32"},{"components":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"address","name":"delegatedTo","type":"address"},{"internalType":"address","name":"withdrawer","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint32","name":"startTimestamp","type":"uint32"},{"internalType":"contract IStrategy[]","name":"strategies","type":"address[]"},{"internalType":"uint256[]","name":"shares","type":"uint256[]"}],"indexed":false,"internalType":"struct IDelegationManager.Withdrawal","name":"withdrawal","type":"tuple"}],"name":"WithdrawalQueued","type":"event"},{"inputs":[],"name":"DELEGATION_APPROVAL_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_STAKER_OPT_OUT_WINDOW","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_WITHDRAWAL_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STAKER_DELEGATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"calculateCurrentStakerDelegationDigestHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"_delegationApprover","type":"address"},{"internalType":"bytes32","name":"approverSalt","type":"bytes32"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"calculateDelegationApprovalDigestHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"_stakerNonce","type":"uint256"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"name":"calculateStakerDelegationDigestHash","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"address","name":"delegatedTo","type":"address"},{"internalType":"address","name":"withdrawer","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint32","name":"startTimestamp","type":"uint32"},{"internalType":"contract IStrategy[]","name":"strategies","type":"address[]"},{"internalType":"uint256[]","name":"shares","type":"uint256[]"}],"internalType":"struct IDelegationManager.Withdrawal","name":"withdrawal","type":"tuple"}],"name":"calculateWithdrawalRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"address","name":"delegatedTo","type":"address"},{"internalType":"address","name":"withdrawer","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint32","name":"startTimestamp","type":"uint32"},{"internalType":"contract IStrategy[]","name":"strategies","type":"address[]"},{"internalType":"uint256[]","name":"shares","type":"uint256[]"}],"internalType":"struct IDelegationManager.Withdrawal","name":"withdrawal","type":"tuple"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256","name":"middlewareTimesIndex","type":"uint256"},{"internalType":"bool","name":"receiveAsTokens","type":"bool"}],"name":"completeQueuedWithdrawal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"address","name":"delegatedTo","type":"address"},{"internalType":"address","name":"withdrawer","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint32","name":"startTimestamp","type":"uint32"},{"internalType":"contract IStrategy[]","name":"strategies","type":"address[]"},{"internalType":"uint256[]","name":"shares","type":"uint256[]"}],"internalType":"struct IDelegationManager.Withdrawal[]","name":"withdrawals","type":"tuple[]"},{"internalType":"contract IERC20[][]","name":"tokens","type":"address[][]"},{"internalType":"uint256[]","name":"middlewareTimesIndexes","type":"uint256[]"},{"internalType":"bool[]","name":"receiveAsTokens","type":"bool[]"}],"name":"completeQueuedWithdrawals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"cumulativeWithdrawalsQueued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"contract IStrategy","name":"strategy","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"decreaseDelegatedShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"components":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"internalType":"struct ISignatureUtils.SignatureWithExpiry","name":"approverSignatureAndExpiry","type":"tuple"},{"internalType":"bytes32","name":"approverSalt","type":"bytes32"}],"name":"delegateTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"components":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"internalType":"struct ISignatureUtils.SignatureWithExpiry","name":"stakerSignatureAndExpiry","type":"tuple"},{"components":[{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"expiry","type":"uint256"}],"internalType":"struct ISignatureUtils.SignatureWithExpiry","name":"approverSignatureAndExpiry","type":"tuple"},{"internalType":"bytes32","name":"approverSalt","type":"bytes32"}],"name":"delegateToBySignature","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"delegatedTo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"delegationApprover","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"delegationApproverSaltIsSpent","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"domainSeparator","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"earningsReceiver","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getDelegatableShares","outputs":[{"internalType":"contract IStrategy[]","name":"","type":"address[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"contract IStrategy[]","name":"strategies","type":"address[]"}],"name":"getOperatorShares","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IStrategy[]","name":"strategies","type":"address[]"}],"name":"getWithdrawalDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"contract IStrategy","name":"strategy","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"increaseDelegatedShares","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"contract IPauserRegistry","name":"_pauserRegistry","type":"address"},{"internalType":"uint256","name":"initialPausedStatus","type":"uint256"},{"internalType":"uint256","name":"_minWithdrawalDelay","type":"uint256"},{"internalType":"contract IStrategy[]","name":"_strategies","type":"address[]"},{"internalType":"uint256[]","name":"_withdrawalDelay","type":"uint256[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"isDelegated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minWithdrawalDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"earningsReceiver","type":"address"},{"internalType":"address","name":"delegationApprover","type":"address"},{"internalType":"uint32","name":"stakerOptOutWindow","type":"uint32"}],"internalType":"struct IDelegationManager.OperatorDetails","name":"newOperatorDetails","type":"tuple"}],"name":"modifyOperatorDetails","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"operatorDetails","outputs":[{"components":[{"internalType":"address","name":"earningsReceiver","type":"address"},{"internalType":"address","name":"delegationApprover","type":"address"},{"internalType":"uint32","name":"stakerOptOutWindow","type":"uint32"}],"internalType":"struct IDelegationManager.OperatorDetails","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"contract IStrategy","name":"","type":"address"}],"name":"operatorShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"newPausedStatus","type":"uint256"}],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"index","type":"uint8"}],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pauserRegistry","outputs":[{"internalType":"contract IPauserRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"pendingWithdrawals","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract IStrategy[]","name":"strategies","type":"address[]"},{"internalType":"uint256[]","name":"shares","type":"uint256[]"},{"internalType":"address","name":"withdrawer","type":"address"}],"internalType":"struct IDelegationManager.QueuedWithdrawalParams[]","name":"queuedWithdrawalParams","type":"tuple[]"}],"name":"queueWithdrawals","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"earningsReceiver","type":"address"},{"internalType":"address","name":"delegationApprover","type":"address"},{"internalType":"uint32","name":"stakerOptOutWindow","type":"uint32"}],"internalType":"struct IDelegationManager.OperatorDetails","name":"registeringOperatorDetails","type":"tuple"},{"internalType":"string","name":"metadataURI","type":"string"}],"name":"registerAsOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newMinWithdrawalDelay","type":"uint256"}],"name":"setMinWithdrawalDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPauserRegistry","name":"newPauserRegistry","type":"address"}],"name":"setPauserRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IStrategy[]","name":"strategies","type":"address[]"},{"internalType":"uint256[]","name":"withdrawalDelay","type":"uint256[]"}],"name":"setStrategyWithdrawalDelay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"slasher","outputs":[{"internalType":"contract ISlasher","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakerNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"stakerOptOutWindow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"strategyManager","outputs":[{"internalType":"contract IStrategyManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IStrategy","name":"","type":"address"}],"name":"strategyWithdrawalDelay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"undelegate","outputs":[{"internalType":"bytes32[]","name":"withdrawalRoots","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newPausedStatus","type":"uint256"}],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"metadataURI","type":"string"}],"name":"updateOperatorMetadataURI","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60e06040523480156200001157600080fd5b506040516200591838038062005918833981016040819052620000349162000137565b6001600160a01b03808316608052811660a052620000516200005d565b50504660c05262000176565b600054610100900460ff1615620000ca5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff908116146200011c576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b6001600160a01b03811681146200013457600080fd5b50565b600080604083850312156200014b57600080fd5b825162000158816200011e565b60208401519092506200016b816200011e565b809150509250929050565b60805160a05160c051615741620001d7600039600061240d01526000610797015260008181610504015281816110d80152818161154901528181611cfa01528181612834015281816129d001528181613f69015261440601526157416000f3fe608060405234801561001057600080fd5b50600436106103575760003560e01c806365187810116101c8578063b134427111610104578063da8be864116100a2578063f16172b01161007c578063f16172b01461093d578063f2fde38b14610950578063f698da2514610963578063fabc1cbc1461096b57600080fd5b8063da8be864146108f7578063e9007a521461090a578063eea9064b1461092a57600080fd5b8063be0525d7116100de578063be0525d71461080a578063c5e480db1461081d578063c94b5111146108c3578063cf80873e146108d657600080fd5b8063b134427114610792578063b7f06ebe146107b9578063bb45fef2146107dc57600080fd5b8063886f119511610171578063973a67961161014b578063973a67961461074b57806399be81c814610755578063a178848414610768578063a238f9df1461078857600080fd5b8063886f1195146107075780638da5cb5b1461071a578063900413471461072b57600080fd5b8063715018a6116101a2578063715018a6146106c1578063778e55f3146106c95780637f548071146106f457600080fd5b8063651878101461065757806365da12641461066a5780636d70f7ae1461069357600080fd5b806328a573ae1161029757806343377382116102405780635ac86ab71161021a5780635ac86ab7146105ed5780635c975abb146106105780635f966f141461061857806360d7faed1461064457600080fd5b806343377382146105ab578063595c6a67146105d2578063597b36da146105da57600080fd5b806339b70e381161027157806339b70e38146104ff5780633cdeb5e01461053e5780633e28391d1461056d57600080fd5b806328a573ae146104b957806329c77d4f146104cc57806333404396146104ec57600080fd5b806310d67a2f116103045780631bbce091116102de5780631bbce091146104335780631d1bf7f21461044657806320606b701461047f57806322bf40e4146104a657600080fd5b806310d67a2f146103fa578063132d49671461040d578063136439dd1461042057600080fd5b80630d5b0067116103355780630d5b0067146103bc5780630dd8dd02146103c55780630f589e59146103e557600080fd5b80630449ca391461035c57806304a4f979146103825780630b9f487a146103a9575b600080fd5b61036f61036a36600461472d565b61097e565b6040519081526020015b60405180910390f35b61036f7f14bde674c9f64b2ad00eaaee4a8bed1fabef35c7507e3c5b9cfc9436909a2dad81565b61036f6103b7366004614788565b610a05565b61036f609e5481565b6103d86103d336600461472d565b610ac7565b60405161037991906147e3565b6103f86103f3366004614881565b610e5c565b005b6103f86104083660046148d5565b610fc4565b6103f861041b3660046148f9565b6110cd565b6103f861042e36600461493a565b6111a2565b61036f6104413660046148f9565b611337565b61036f6104543660046148d5565b6001600160a01b03166000908152609a6020526040902060010154600160a01b900463ffffffff1690565b61036f7f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b6103f86104b4366004614953565b611365565b6103f86104c73660046148f9565b61153e565b61036f6104da3660046148d5565b609c6020526000908152604090205481565b6103f86104fa3660046149fb565b61160c565b6105267f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610379565b61052661054c3660046148d5565b6001600160a01b039081166000908152609a60205260409020600101541690565b61059b61057b3660046148d5565b6001600160a01b039081166000908152609b602052604090205416151590565b6040519015158152602001610379565b61036f7f39111bc4a4d688e1f685123d7497d4615370152a8ee4a0593e647bd06ad8bb0b81565b6103f8611722565b61036f6105e8366004614cfe565b61183f565b61059b6105fb366004614d3b565b606654600160ff9092169190911b9081161490565b60665461036f565b6105266106263660046148d5565b6001600160a01b039081166000908152609a60205260409020541690565b6103f8610652366004614d6c565b61186f565b6103f8610665366004614dfc565b6118ee565b6105266106783660046148d5565b609b602052600090815260409020546001600160a01b031681565b61059b6106a13660046148d5565b6001600160a01b039081166000908152609a602052604090205416151590565b6103f8611902565b61036f6106d7366004614e68565b609960209081526000928352604080842090915290825290205481565b6103f8610702366004614f4a565b611916565b606554610526906001600160a01b031681565b6033546001600160a01b0316610526565b61073e610739366004614fdb565b611a1b565b6040516103799190615066565b61036f62ed4e0081565b6103f8610763366004615079565b611af6565b61036f6107763660046148d5565b60a06020526000908152604090205481565b61036f62278d0081565b6105267f000000000000000000000000000000000000000000000000000000000000000081565b61059b6107c736600461493a565b609f6020526000908152604090205460ff1681565b61059b6107ea3660046150af565b609d60209081526000928352604080842090915290825290205460ff1681565b6103f861081836600461493a565b611bee565b61088d61082b3660046148d5565b6040805160608082018352600080835260208084018290529284018190526001600160a01b039485168152609a8352839020835191820184528054851682526001015493841691810191909152600160a01b90920463ffffffff169082015290565b6040805182516001600160a01b039081168252602080850151909116908201529181015163ffffffff1690820152606001610379565b61036f6108d13660046150db565b611bff565b6108e96108e43660046148d5565b611cb8565b60405161037992919061515c565b6103d86109053660046148d5565b611d75565b61036f6109183660046148d5565b60a16020526000908152604090205481565b6103f8610938366004615181565b6122b5565b6103f861094b3660046151da565b6122c1565b6103f861095e3660046148d5565b61237c565b61036f612409565b6103f861097936600461493a565b6124cb565b609e54600090815b838110156109fb57600060a160008787858181106109a6576109a66151f6565b90506020020160208101906109bb91906148d5565b6001600160a01b03166001600160a01b03168152602001908152602001600020549050828111156109ea578092505b506109f481615222565b9050610986565b5090505b92915050565b604080517f14bde674c9f64b2ad00eaaee4a8bed1fabef35c7507e3c5b9cfc9436909a2dad6020808301919091526001600160a01b038681168385015288811660608401528716608083015260a0820185905260c08083018590528351808403909101815260e0909201909252805191012060009081610a83612409565b60405161190160f01b602082015260228101919091526042810183905260620160408051808303601f19018152919052805160209091012098975050505050505050565b606654606090600190600290811603610b275760405162461bcd60e51b815260206004820152601960248201527f5061757361626c653a20696e646578206973207061757365640000000000000060448201526064015b60405180910390fd5b60008367ffffffffffffffff811115610b4257610b42614a9e565b604051908082528060200260200182016040528015610b6b578160200160208202803683370190505b50336000908152609b60205260408120549192506001600160a01b03909116905b85811015610e5157868682818110610ba657610ba66151f6565b9050602002810190610bb8919061523b565b610bc690602081019061525b565b9050878783818110610bda57610bda6151f6565b9050602002810190610bec919061523b565b610bf6908061525b565b905014610c6b5760405162461bcd60e51b815260206004820152603860248201527f44656c65676174696f6e4d616e616765722e717565756557697468647261776160448201527f6c3a20696e707574206c656e677468206d69736d6174636800000000000000006064820152608401610b1e565b33878783818110610c7e57610c7e6151f6565b9050602002810190610c90919061523b565b610ca19060608101906040016148d5565b6001600160a01b031614610d1d5760405162461bcd60e51b815260206004820152603c60248201527f44656c65676174696f6e4d616e616765722e717565756557697468647261776160448201527f6c3a2077697468647261776572206d757374206265207374616b6572000000006064820152608401610b1e565b610e223383898985818110610d3457610d346151f6565b9050602002810190610d46919061523b565b610d579060608101906040016148d5565b8a8a86818110610d6957610d696151f6565b9050602002810190610d7b919061523b565b610d85908061525b565b808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508e92508d9150889050818110610dcb57610dcb6151f6565b9050602002810190610ddd919061523b565b610deb90602081019061525b565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061267d92505050565b838281518110610e3457610e346151f6565b602090810291909101015280610e4981615222565b915050610b8c565b509095945050505050565b336000908152609a60205260409020546001600160a01b031615610f0e5760405162461bcd60e51b815260206004820152604560248201527f44656c65676174696f6e4d616e616765722e726567697374657241734f70657260448201527f61746f723a206f70657261746f722068617320616c726561647920726567697360648201527f7465726564000000000000000000000000000000000000000000000000000000608482015260a401610b1e565b610f183384612bb1565b604080518082019091526060815260006020820152610f3a3380836000612e4a565b336001600160a01b03167f8e8485583a2310d41f7c82b9427d0bd49bad74bb9cff9d3402a29d8f9b28a0e285604051610f7391906152a5565b60405180910390a2336001600160a01b03167f02a919ed0e2acad1dd90f17ef2fa4ae5462ee1339170034a8531cca4b67080908484604051610fb69291906152f7565b60405180910390a250505050565b606560009054906101000a90046001600160a01b03166001600160a01b031663eab66d7a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611017573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061103b9190615326565b6001600160a01b0316336001600160a01b0316146110c15760405162461bcd60e51b815260206004820152602a60248201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160448201527f7320756e706175736572000000000000000000000000000000000000000000006064820152608401610b1e565b6110ca81613241565b50565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146111545760405162461bcd60e51b815260206004820152602660248201527f44656c65676174696f6e4d616e616765723a206f6e6c7953747261746567794d60448201526530b730b3b2b960d11b6064820152608401610b1e565b6001600160a01b038084166000908152609b6020526040902054161561119d576001600160a01b038084166000908152609b60205260409020541661119b81858585613359565b505b505050565b60655460405163237dfb4760e11b81523360048201526001600160a01b03909116906346fbf68e90602401602060405180830381865afa1580156111ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061120e9190615343565b6112805760405162461bcd60e51b815260206004820152602860248201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160448201527f73207061757365720000000000000000000000000000000000000000000000006064820152608401610b1e565b606654818116146112f95760405162461bcd60e51b815260206004820152603860248201527f5061757361626c652e70617573653a20696e76616c696420617474656d70742060448201527f746f20756e70617573652066756e6374696f6e616c69747900000000000000006064820152608401610b1e565b606681905560405181815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d906020015b60405180910390a250565b6001600160a01b0383166000908152609c602052604081205461135c85828686611bff565b95945050505050565b600054610100900460ff16158080156113855750600054600160ff909116105b8061139f5750303b15801561139f575060005460ff166001145b6114115760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610b1e565b6000805460ff191660011790558015611434576000805461ff0019166101001790555b61143e88886133e2565b604080518082018252600481526314195b1b60e21b60209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f0e0f546b3087f7e4405d37b8de121d34252412874bf985c456311dd7eca663b681840152466060820152306080808301919091528351808303909101815260a090910190925281519101206098556114d8896134e2565b6114e186613541565b6114ed85858585613621565b8015611533576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146115c55760405162461bcd60e51b815260206004820152602660248201527f44656c65676174696f6e4d616e616765723a206f6e6c7953747261746567794d60448201526530b730b3b2b960d11b6064820152608401610b1e565b6001600160a01b038084166000908152609b6020526040902054161561119d576001600160a01b038084166000908152609b60205260409020541661119b81858585613837565b6066546002906004908116036116645760405162461bcd60e51b815260206004820152601960248201527f5061757361626c653a20696e64657820697320706175736564000000000000006044820152606401610b1e565b61166c6138c0565b60005b88811015611717576117078a8a8381811061168c5761168c6151f6565b905060200281019061169e9190615360565b8989848181106116b0576116b06151f6565b90506020028101906116c2919061525b565b8989868181106116d4576116d46151f6565b905060200201358888878181106116ed576116ed6151f6565b90506020020160208101906117029190615376565b613919565b61171081615222565b905061166f565b50611533600160d355565b60655460405163237dfb4760e11b81523360048201526001600160a01b03909116906346fbf68e90602401602060405180830381865afa15801561176a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178e9190615343565b6118005760405162461bcd60e51b815260206004820152602860248201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160448201527f73207061757365720000000000000000000000000000000000000000000000006064820152608401610b1e565b600019606681905560405190815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d9060200160405180910390a2565b6000816040516020016118529190615408565b604051602081830303815290604052805190602001209050919050565b6066546002906004908116036118c75760405162461bcd60e51b815260206004820152601960248201527f5061757361626c653a20696e64657820697320706175736564000000000000006044820152606401610b1e565b6118cf6138c0565b6118dc8686868686613919565b6118e6600160d355565b505050505050565b6118f6614152565b61119b84848484613621565b61190a614152565b61191460006134e2565b565b428360200151101561199a5760405162461bcd60e51b815260206004820152604160248201527f44656c65676174696f6e4d616e616765722e64656c6567617465546f4279536960448201527f676e61747572653a207374616b6572207369676e6174757265206578706972656064820152601960fa1b608482015260a401610b1e565b6000609c6000876001600160a01b03166001600160a01b0316815260200190815260200160002054905060006119d68783888860200151611bff565b6001600160a01b0388166000908152609c602052604090206001840190558551909150611a0690889083906141ac565b611a1287878686612e4a565b50505050505050565b60606000825167ffffffffffffffff811115611a3957611a39614a9e565b604051908082528060200260200182016040528015611a62578160200160208202803683370190505b50905060005b83518110156109fb576001600160a01b03851660009081526099602052604081208551909190869084908110611aa057611aa06151f6565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002054828281518110611adb57611adb6151f6565b6020908102919091010152611aef81615222565b9050611a68565b336000908152609a60205260409020546001600160a01b0316611ba75760405162461bcd60e51b815260206004820152604760248201527f44656c65676174696f6e4d616e616765722e7570646174654f70657261746f7260448201527f4d657461646174615552493a2063616c6c6572206d75737420626520616e206f60648201527f70657261746f7200000000000000000000000000000000000000000000000000608482015260a401610b1e565b336001600160a01b03167f02a919ed0e2acad1dd90f17ef2fa4ae5462ee1339170034a8531cca4b67080908383604051611be29291906152f7565b60405180910390a25050565b611bf6614152565b6110ca81613541565b604080517f39111bc4a4d688e1f685123d7497d4615370152a8ee4a0593e647bd06ad8bb0b6020808301919091526001600160a01b0387811683850152851660608301526080820186905260a08083018590528351808403909101815260c0909201909252805191012060009081611c75612409565b60405161190160f01b602082015260228101919091526042810183905260620160408051808303601f190181529190528051602090910120979650505050505050565b6040517f94f649dd0000000000000000000000000000000000000000000000000000000081526001600160a01b038281166004830152606091829160009182917f000000000000000000000000000000000000000000000000000000000000000016906394f649dd90602401600060405180830381865afa158015611d41573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611d699190810190615476565b90969095509350505050565b606654606090600190600290811603611dd05760405162461bcd60e51b815260206004820152601960248201527f5061757361626c653a20696e64657820697320706175736564000000000000006044820152606401610b1e565b6001600160a01b038084166000908152609b602052604090205416611e845760405162461bcd60e51b8152602060048201526044602482018190527f44656c65676174696f6e4d616e616765722e756e64656c65676174653a207374908201527f616b6572206d7573742062652064656c65676174656420746f20756e64656c6560648201527f6761746500000000000000000000000000000000000000000000000000000000608482015260a401610b1e565b6001600160a01b038084166000908152609a60205260409020541615611f125760405162461bcd60e51b815260206004820152603d60248201527f44656c65676174696f6e4d616e616765722e756e64656c65676174653a206f7060448201527f657261746f72732063616e6e6f7420626520756e64656c6567617465640000006064820152608401610b1e565b6001600160a01b038316611f8e5760405162461bcd60e51b815260206004820152603c60248201527f44656c65676174696f6e4d616e616765722e756e64656c65676174653a20636160448201527f6e6e6f7420756e64656c6567617465207a65726f2061646472657373000000006064820152608401610b1e565b6001600160a01b038084166000818152609b602052604090205490911690331480611fc15750336001600160a01b038216145b80611fe857506001600160a01b038181166000908152609a60205260409020600101541633145b61205a5760405162461bcd60e51b815260206004820152603d60248201527f44656c65676174696f6e4d616e616765722e756e64656c65676174653a20636160448201527f6c6c65722063616e6e6f7420756e64656c6567617465207374616b65720000006064820152608401610b1e565b60008061206686611cb8565b9092509050336001600160a01b038716146120bc57826001600160a01b0316866001600160a01b03167ff0eddf07e6ea14f388b47e1e94a0f464ecbd9eed4171130e0fc0e99fb4030a8a60405160405180910390a35b826001600160a01b0316866001600160a01b03167ffee30966a256b71e14bc0ebfc94315e28ef4a97a7131a9e2b7a310a73af4467660405160405180910390a36001600160a01b0386166000908152609b60205260408120805473ffffffffffffffffffffffffffffffffffffffff191690558251900361214d5760408051600081526020810190915294506122ac565b815167ffffffffffffffff81111561216757612167614a9e565b604051908082528060200260200182016040528015612190578160200160208202803683370190505b50945060005b82518110156122aa576040805160018082528183019092526000916020808301908036833750506040805160018082528183019092529293506000929150602080830190803683370190505090508483815181106121f6576121f66151f6565b602002602001015182600081518110612211576122116151f6565b60200260200101906001600160a01b031690816001600160a01b031681525050838381518110612243576122436151f6565b60200260200101518160008151811061225e5761225e6151f6565b60200260200101818152505061227789878b858561267d565b888481518110612289576122896151f6565b602002602001018181525050505080806122a290615222565b915050612196565b505b50505050919050565b61119d33848484612e4a565b336000908152609a60205260409020546001600160a01b03166123725760405162461bcd60e51b815260206004820152604360248201527f44656c65676174696f6e4d616e616765722e6d6f646966794f70657261746f7260448201527f44657461696c733a2063616c6c6572206d75737420626520616e206f7065726160648201527f746f720000000000000000000000000000000000000000000000000000000000608482015260a401610b1e565b6110ca3382612bb1565b612384614152565b6001600160a01b0381166124005760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610b1e565b6110ca816134e2565b60007f00000000000000000000000000000000000000000000000000000000000000004603612439575060985490565b50604080518082018252600481526314195b1b60e21b60209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f0e0f546b3087f7e4405d37b8de121d34252412874bf985c456311dd7eca663b681840152466060820152306080808301919091528351808303909101815260a0909101909252815191012090565b606560009054906101000a90046001600160a01b03166001600160a01b031663eab66d7a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561251e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125429190615326565b6001600160a01b0316336001600160a01b0316146125c85760405162461bcd60e51b815260206004820152602a60248201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160448201527f7320756e706175736572000000000000000000000000000000000000000000006064820152608401610b1e565b6066541981196066541916146126465760405162461bcd60e51b815260206004820152603860248201527f5061757361626c652e756e70617573653a20696e76616c696420617474656d7060448201527f7420746f2070617573652066756e6374696f6e616c69747900000000000000006064820152608401610b1e565b606681905560405181815233907f3582d1828e26bf56bd801502bc021ac0bc8afb57c826e4986b45593c8fad389c9060200161132c565b60006001600160a01b0386166127215760405162461bcd60e51b815260206004820152605060248201527f44656c65676174696f6e4d616e616765722e5f72656d6f76655368617265734160448201527f6e6451756575655769746864726177616c3a207374616b65722063616e6e6f7460648201527f206265207a65726f206164647265737300000000000000000000000000000000608482015260a401610b1e565b82516000036127be5760405162461bcd60e51b815260206004820152604d60248201527f44656c65676174696f6e4d616e616765722e5f72656d6f76655368617265734160448201527f6e6451756575655769746864726177616c3a207374726174656769657320636160648201527f6e6e6f7420626520656d70747900000000000000000000000000000000000000608482015260a401610b1e565b60005b8351811015612abf576001600160a01b038616156128175761281786888684815181106127f0576127f06151f6565b602002602001015186858151811061280a5761280a6151f6565b6020026020010151613359565b846001600160a01b0316876001600160a01b031614806128e957507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316639b4da03d858381518110612873576128736151f6565b60200260200101516040518263ffffffff1660e01b81526004016128a691906001600160a01b0391909116815260200190565b602060405180830381865afa1580156128c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128e79190615343565b155b6129ce5760405162461bcd60e51b8152602060048201526084602482018190527f44656c65676174696f6e4d616e616765722e5f72656d6f76655368617265734160448301527f6e6451756575655769746864726177616c3a2077697468647261776572206d7560648301527f73742062652073616d652061646472657373206173207374616b657220696620908201527f746869726450617274795472616e7366657273466f7262696464656e2061726560a48201527f207365740000000000000000000000000000000000000000000000000000000060c482015260e401610b1e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638c80d4e588868481518110612a1057612a106151f6565b6020026020010151868581518110612a2a57612a2a6151f6565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b158015612a9c57600080fd5b505af1158015612ab0573d6000803e3d6000fd5b505050508060010190506127c1565b506001600160a01b038616600090815260a060205260408120805491829190612ae783615222565b919050555060006040518060e00160405280896001600160a01b03168152602001886001600160a01b03168152602001876001600160a01b031681526020018381526020014263ffffffff1681526020018681526020018581525090506000612b4f8261183f565b6000818152609f602052604090819020805460ff19166001179055519091507f9009ab153e8014fbfb02f2217f5cde7aa7f9ad734ae85ca3ee3f4ca2fdd499f990612b9d9083908590615531565b60405180910390a198975050505050505050565b6000612bc060208301836148d5565b6001600160a01b031603612c625760405162461bcd60e51b815260206004820152605460248201527f44656c65676174696f6e4d616e616765722e5f7365744f70657261746f72446560448201527f7461696c733a2063616e6e6f742073657420606561726e696e6773526563656960648201527f7665726020746f207a65726f2061646472657373000000000000000000000000608482015260a401610b1e565b62ed4e00612c76606083016040840161554a565b63ffffffff161115612d165760405162461bcd60e51b815260206004820152605f60248201527f44656c65676174696f6e4d616e616765722e5f7365744f70657261746f72446560448201527f7461696c733a207374616b65724f70744f757457696e646f772063616e6e6f7460648201527f206265203e204d41585f5354414b45525f4f50545f4f55545f57494e444f5700608482015260a401610b1e565b6001600160a01b0382166000908152609a60205260409081902060010154600160a01b900463ffffffff1690612d52906060840190840161554a565b63ffffffff161015612df25760405162461bcd60e51b815260206004820152604d60248201527f44656c65676174696f6e4d616e616765722e5f7365744f70657261746f72446560448201527f7461696c733a207374616b65724f70744f757457696e646f772063616e6e6f7460648201527f2062652064656372656173656400000000000000000000000000000000000000608482015260a401610b1e565b6001600160a01b0382166000908152609a602052604090208190612e168282615567565b505060405133907ffebe5cd24b2cbc7b065b9d0fdeb904461e4afcff57dd57acda1e7832031ba7ac90611be29084906152a5565b606654600090600190811603612ea25760405162461bcd60e51b815260206004820152601960248201527f5061757361626c653a20696e64657820697320706175736564000000000000006044820152606401610b1e565b6001600160a01b038086166000908152609b60205260409020541615612f3a5760405162461bcd60e51b815260206004820152604160248201527f44656c65676174696f6e4d616e616765722e5f64656c65676174653a2073746160448201527f6b657220697320616c7265616479206163746976656c792064656c65676174656064820152601960fa1b608482015260a401610b1e565b6001600160a01b038085166000908152609a602052604090205416612fc75760405162461bcd60e51b815260206004820152603f60248201527f44656c65676174696f6e4d616e616765722e5f64656c65676174653a206f706560448201527f7261746f72206973206e6f74207265676973746572656420696e2050656c6c006064820152608401610b1e565b6001600160a01b038085166000908152609a6020526040902060010154168015801590612ffd5750336001600160a01b03821614155b80156130125750336001600160a01b03861614155b1561317f5742846020015110156130915760405162461bcd60e51b815260206004820152603760248201527f44656c65676174696f6e4d616e616765722e5f64656c65676174653a2061707060448201527f726f766572207369676e617475726520657870697265640000000000000000006064820152608401610b1e565b6001600160a01b0381166000908152609d6020908152604080832086845290915290205460ff161561312b5760405162461bcd60e51b815260206004820152603760248201527f44656c65676174696f6e4d616e616765722e5f64656c65676174653a2061707060448201527f726f76657253616c7420616c7265616479207370656e740000000000000000006064820152608401610b1e565b6001600160a01b0381166000908152609d6020908152604080832086845282528220805460ff1916600117905585015161316c908890889085908890610a05565b905061317d828287600001516141ac565b505b6001600160a01b038681166000818152609b6020526040808220805473ffffffffffffffffffffffffffffffffffffffff1916948a169485179055517fc3ee9f2e5fda98e8066a1f745b2df9285f416fe98cf2559cd21484b3d87433049190a36000806131eb88611cb8565b9150915060005b825181101561153357613239888a858481518110613212576132126151f6565b602002602001015185858151811061322c5761322c6151f6565b6020026020010151613837565b6001016131f2565b6001600160a01b0381166132e35760405162461bcd60e51b815260206004820152604960248201527f5061757361626c652e5f73657450617573657252656769737472793a206e657760448201527f50617573657252656769737472792063616e6e6f7420626520746865207a657260648201527f6f20616464726573730000000000000000000000000000000000000000000000608482015260a401610b1e565b606554604080516001600160a01b03928316815291831660208301527f6e9fcd539896fca60e8b0f01dd580233e48a6b0f7df013b89ba7f565869acdb6910160405180910390a16065805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6001600160a01b0380851660009081526099602090815260408083209386168352929052908120805483929061339090849061562b565b9091555050604080516001600160a01b0385811682528481166020830152918101839052908516907f6909600037b75d7b4733aedd815442b5ec018a827751c832aaff64eba5d6d2dd90606001610fb6565b6065546001600160a01b031615801561340357506001600160a01b03821615155b61349b5760405162461bcd60e51b815260206004820152604760248201527f5061757361626c652e5f696e697469616c697a655061757365723a205f696e6960448201527f7469616c697a6550617573657228292063616e206f6e6c792062652063616c6c60648201527f6564206f6e636500000000000000000000000000000000000000000000000000608482015260a401610b1e565b606681905560405181815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d9060200160405180910390a26134de82613241565b5050565b603380546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b62278d008111156135e05760405162461bcd60e51b815260206004820152605e60248201527f44656c65676174696f6e4d616e616765722e5f7365744d696e5769746864726160448201527f77616c44656c61793a205f6d696e5769746864726177616c44656c617920636160648201527f6e6e6f74206265203e204d41585f5749544844524157414c5f44454c41590000608482015260a401610b1e565b609e5460408051918252602082018390527f338caf1431dddfb34caa16bfc51573f97922fa2f8eb6d70d27476d8b4a89d5c3910160405180910390a1609e55565b8281146136bd5760405162461bcd60e51b8152602060048201526044602482018190527f44656c65676174696f6e4d616e616765722e5f73657453747261746567795769908201527f746864726177616c44656c61793a20696e707574206c656e677468206d69736d60648201527f6174636800000000000000000000000000000000000000000000000000000000608482015260a401610b1e565b8260005b818110156118e65760008686838181106136dd576136dd6151f6565b90506020020160208101906136f291906148d5565b6001600160a01b038116600090815260a16020526040812054919250868685818110613720576137206151f6565b90506020020135905062278d008111156137c85760405162461bcd60e51b815260206004820152606060248201527f44656c65676174696f6e4d616e616765722e5f7365745374726174656779576960448201527f746864726177616c44656c61793a205f7769746864726177616c44656c61792060648201527f63616e6e6f74206265203e204d41585f5749544844524157414c5f44454c4159608482015260a401610b1e565b6001600160a01b038316600081815260a160209081526040918290208490558151928352820184905281018290527f4be295c8d739bae6e60f607ccde1aa068970dc43209682f7290d10726efc02e59060600160405180910390a15050508061383090615222565b90506136c1565b6001600160a01b0380851660009081526099602090815260408083209386168352929052908120805483929061386e90849061563e565b9091555050604080516001600160a01b0385811682528481166020830152918101839052908516907f1ec042c965e2edd7107b51188ee0f383e22e76179041ab3a9d18ff151405166c90606001610fb6565b600260d354036139125760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610b1e565b600260d355565b60006139276105e887615651565b6000818152609f602052604090205490915060ff166139d45760405162461bcd60e51b815260206004820152604360248201527f44656c65676174696f6e4d616e616765722e5f636f6d706c657465517565756560448201527f645769746864726177616c3a20616374696f6e206973206e6f7420696e20717560648201527f6575650000000000000000000000000000000000000000000000000000000000608482015260a401610b1e565b609e5442906139e960a0890160808a0161554a565b63ffffffff166139f9919061563e565b1115613a935760405162461bcd60e51b815260206004820152605960248201527f44656c65676174696f6e4d616e616765722e5f636f6d706c657465517565756560448201527f645769746864726177616c3a206d696e5769746864726177616c44656c61792060648201527f706572696f6420686173206e6f74207965742070617373656400000000000000608482015260a401610b1e565b613aa360608701604088016148d5565b6001600160a01b0316336001600160a01b031614613b4f5760405162461bcd60e51b815260206004820152605060248201527f44656c65676174696f6e4d616e616765722e5f636f6d706c657465517565756560448201527f645769746864726177616c3a206f6e6c7920776974686472617765722063616e60648201527f20636f6d706c65746520616374696f6e00000000000000000000000000000000608482015260a401610b1e565b8115613bfe57613b6260a087018761525b565b85149050613bfe5760405162461bcd60e51b815260206004820152604260248201527f44656c65676174696f6e4d616e616765722e5f636f6d706c657465517565756560448201527f645769746864726177616c3a20696e707574206c656e677468206d69736d617460648201527f6368000000000000000000000000000000000000000000000000000000000000608482015260a401610b1e565b6000818152609f60205260409020805460ff191690558115613e055760005b613c2a60a088018861525b565b9050811015613dff574260a16000613c4560a08b018b61525b565b85818110613c5557613c556151f6565b9050602002016020810190613c6a91906148d5565b6001600160a01b03168152602081019190915260400160002054613c9460a08a0160808b0161554a565b63ffffffff16613ca4919061563e565b1115613d645760405162461bcd60e51b815260206004820152606860248201527f44656c65676174696f6e4d616e616765722e5f636f6d706c657465517565756560448201527f645769746864726177616c3a207769746864726177616c44656c61792070657260648201527f696f6420686173206e6f74207965742070617373656420666f7220746869732060848201527f737472617465677900000000000000000000000000000000000000000000000060a482015260c401610b1e565b613df7613d7460208901896148d5565b33613d8260a08b018b61525b565b85818110613d9257613d926151f6565b9050602002016020810190613da791906148d5565b613db460c08c018c61525b565b86818110613dc457613dc46151f6565b905060200201358a8a87818110613ddd57613ddd6151f6565b9050602002016020810190613df291906148d5565b6143b7565b600101613c1d565b50614117565b336000908152609b60205260408120546001600160a01b0316905b613e2d60a089018961525b565b9050811015614114574260a16000613e4860a08c018c61525b565b85818110613e5857613e586151f6565b9050602002016020810190613e6d91906148d5565b6001600160a01b03168152602081019190915260400160002054613e9760a08b0160808c0161554a565b63ffffffff16613ea7919061563e565b1115613f675760405162461bcd60e51b815260206004820152606860248201527f44656c65676174696f6e4d616e616765722e5f636f6d706c657465517565756560448201527f645769746864726177616c3a207769746864726177616c44656c61792070657260648201527f696f6420686173206e6f74207965742070617373656420666f7220746869732060848201527f737472617465677900000000000000000000000000000000000000000000000060a482015260c401610b1e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c4623ea133898985818110613fa957613fa96151f6565b9050602002016020810190613fbe91906148d5565b613fcb60a08d018d61525b565b86818110613fdb57613fdb6151f6565b9050602002016020810190613ff091906148d5565b613ffd60c08e018e61525b565b8781811061400d5761400d6151f6565b60405160e088901b7fffffffff000000000000000000000000000000000000000000000000000000001681526001600160a01b03968716600482015294861660248601529290941660448401526020909102013560648201526084019050600060405180830381600087803b15801561408557600080fd5b505af1158015614099573d6000803e3d6000fd5b505050506001600160a01b0382161561410c5761410c82336140be60a08c018c61525b565b858181106140ce576140ce6151f6565b90506020020160208101906140e391906148d5565b6140f060c08d018d61525b565b86818110614100576141006151f6565b90506020020135613837565b600101613e20565b50505b6040518181527fc97098c2f658800b4df29001527f7324bcdffcf6e8751a699ab920a1eced5b1d9060200160405180910390a1505050505050565b6033546001600160a01b031633146119145760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b1e565b6001600160a01b0383163b15614301576040517f1626ba7e00000000000000000000000000000000000000000000000000000000808252906001600160a01b03851690631626ba7e90614205908690869060040161565d565b602060405180830381865afa158015614222573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061424691906156b3565b7fffffffff00000000000000000000000000000000000000000000000000000000161461119d5760405162461bcd60e51b815260206004820152605360248201527f454950313237315369676e61747572655574696c732e636865636b5369676e6160448201527f747572655f454950313237313a2045524331323731207369676e61747572652060648201527f766572696669636174696f6e206661696c656400000000000000000000000000608482015260a401610b1e565b826001600160a01b0316614315838361445e565b6001600160a01b03161461119d5760405162461bcd60e51b815260206004820152604760248201527f454950313237315369676e61747572655574696c732e636865636b5369676e6160448201527f747572655f454950313237313a207369676e6174757265206e6f742066726f6d60648201527f207369676e657200000000000000000000000000000000000000000000000000608482015260a401610b1e565b6040517fc608c7f30000000000000000000000000000000000000000000000000000000081526001600160a01b03858116600483015284811660248301526044820184905282811660648301527f0000000000000000000000000000000000000000000000000000000000000000169063c608c7f390608401600060405180830381600087803b15801561444a57600080fd5b505af1158015611533573d6000803e3d6000fd5b600080600061446d858561447a565b915091506109fb816144bf565b60008082516041036144b05760208301516040840151606085015160001a6144a487828585614624565b945094505050506144b8565b506000905060025b9250929050565b60008160048111156144d3576144d36156f5565b036144db5750565b60018160048111156144ef576144ef6156f5565b0361453c5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610b1e565b6002816004811115614550576145506156f5565b0361459d5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610b1e565b60038160048111156145b1576145b16156f5565b036110ca5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401610b1e565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561465b57506000905060036146df565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156146af573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166146d8576000600192509250506146df565b9150600090505b94509492505050565b60008083601f8401126146fa57600080fd5b50813567ffffffffffffffff81111561471257600080fd5b6020830191508360208260051b85010111156144b857600080fd5b6000806020838503121561474057600080fd5b823567ffffffffffffffff81111561475757600080fd5b611d69858286016146e8565b6001600160a01b03811681146110ca57600080fd5b803561478381614763565b919050565b600080600080600060a086880312156147a057600080fd5b85356147ab81614763565b945060208601356147bb81614763565b935060408601356147cb81614763565b94979396509394606081013594506080013592915050565b6020808252825182820181905260009190848201906040850190845b8181101561481b578351835292840192918401916001016147ff565b50909695505050505050565b60006060828403121561483957600080fd5b50919050565b60008083601f84011261485157600080fd5b50813567ffffffffffffffff81111561486957600080fd5b6020830191508360208285010111156144b857600080fd5b60008060006080848603121561489657600080fd5b6148a08585614827565b9250606084013567ffffffffffffffff8111156148bc57600080fd5b6148c88682870161483f565b9497909650939450505050565b6000602082840312156148e757600080fd5b81356148f281614763565b9392505050565b60008060006060848603121561490e57600080fd5b833561491981614763565b9250602084013561492981614763565b929592945050506040919091013590565b60006020828403121561494c57600080fd5b5035919050565b60008060008060008060008060c0898b03121561496f57600080fd5b883561497a81614763565b9750602089013561498a81614763565b96506040890135955060608901359450608089013567ffffffffffffffff808211156149b557600080fd5b6149c18c838d016146e8565b909650945060a08b01359150808211156149da57600080fd5b506149e78b828c016146e8565b999c989b5096995094979396929594505050565b6000806000806000806000806080898b031215614a1757600080fd5b883567ffffffffffffffff80821115614a2f57600080fd5b614a3b8c838d016146e8565b909a50985060208b0135915080821115614a5457600080fd5b614a608c838d016146e8565b909850965060408b0135915080821115614a7957600080fd5b614a858c838d016146e8565b909650945060608b01359150808211156149da57600080fd5b634e487b7160e01b600052604160045260246000fd5b60405160e0810167ffffffffffffffff81118282101715614ad757614ad7614a9e565b60405290565b6040805190810167ffffffffffffffff81118282101715614ad757614ad7614a9e565b604051601f8201601f1916810167ffffffffffffffff81118282101715614b2957614b29614a9e565b604052919050565b63ffffffff811681146110ca57600080fd5b803561478381614b31565b600067ffffffffffffffff821115614b6857614b68614a9e565b5060051b60200190565b600082601f830112614b8357600080fd5b81356020614b98614b9383614b4e565b614b00565b82815260059290921b84018101918181019086841115614bb757600080fd5b8286015b84811015614bdb578035614bce81614763565b8352918301918301614bbb565b509695505050505050565b600082601f830112614bf757600080fd5b81356020614c07614b9383614b4e565b82815260059290921b84018101918181019086841115614c2657600080fd5b8286015b84811015614bdb5780358352918301918301614c2a565b600060e08284031215614c5357600080fd5b614c5b614ab4565b9050614c6682614778565b8152614c7460208301614778565b6020820152614c8560408301614778565b604082015260608201356060820152614ca060808301614b43565b608082015260a082013567ffffffffffffffff80821115614cc057600080fd5b614ccc85838601614b72565b60a084015260c0840135915080821115614ce557600080fd5b50614cf284828501614be6565b60c08301525092915050565b600060208284031215614d1057600080fd5b813567ffffffffffffffff811115614d2757600080fd5b614d3384828501614c41565b949350505050565b600060208284031215614d4d57600080fd5b813560ff811681146148f257600080fd5b80151581146110ca57600080fd5b600080600080600060808688031215614d8457600080fd5b853567ffffffffffffffff80821115614d9c57600080fd5b9087019060e0828a031215614db057600080fd5b90955060208701359080821115614dc657600080fd5b50614dd3888289016146e8565b909550935050604086013591506060860135614dee81614d5e565b809150509295509295909350565b60008060008060408587031215614e1257600080fd5b843567ffffffffffffffff80821115614e2a57600080fd5b614e36888389016146e8565b90965094506020870135915080821115614e4f57600080fd5b50614e5c878288016146e8565b95989497509550505050565b60008060408385031215614e7b57600080fd5b8235614e8681614763565b91506020830135614e9681614763565b809150509250929050565b600060408284031215614eb357600080fd5b614ebb614add565b9050813567ffffffffffffffff80821115614ed557600080fd5b818401915084601f830112614ee957600080fd5b8135602082821115614efd57614efd614a9e565b614f0f601f8301601f19168201614b00565b92508183528681838601011115614f2557600080fd5b8181850182850137600081838501015282855280860135818601525050505092915050565b600080600080600060a08688031215614f6257600080fd5b8535614f6d81614763565b94506020860135614f7d81614763565b9350604086013567ffffffffffffffff80821115614f9a57600080fd5b614fa689838a01614ea1565b94506060880135915080821115614fbc57600080fd5b50614fc988828901614ea1565b95989497509295608001359392505050565b60008060408385031215614fee57600080fd5b8235614ff981614763565b9150602083013567ffffffffffffffff81111561501557600080fd5b61502185828601614b72565b9150509250929050565b600081518084526020808501945080840160005b8381101561505b5781518752958201959082019060010161503f565b509495945050505050565b6020815260006148f2602083018461502b565b6000806020838503121561508c57600080fd5b823567ffffffffffffffff8111156150a357600080fd5b611d698582860161483f565b600080604083850312156150c257600080fd5b82356150cd81614763565b946020939093013593505050565b600080600080608085870312156150f157600080fd5b84356150fc81614763565b935060208501359250604085013561511381614763565b9396929550929360600135925050565b600081518084526020808501945080840160005b8381101561505b5781516001600160a01b031687529582019590820190600101615137565b60408152600061516f6040830185615123565b828103602084015261135c818561502b565b60008060006060848603121561519657600080fd5b83356151a181614763565b9250602084013567ffffffffffffffff8111156151bd57600080fd5b6151c986828701614ea1565b925050604084013590509250925092565b6000606082840312156151ec57600080fd5b6148f28383614827565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016152345761523461520c565b5060010190565b60008235605e1983360301811261525157600080fd5b9190910192915050565b6000808335601e1984360301811261527257600080fd5b83018035915067ffffffffffffffff82111561528d57600080fd5b6020019150600581901b36038213156144b857600080fd5b6060810182356152b481614763565b6001600160a01b0390811683526020840135906152d082614763565b16602083015260408301356152e481614b31565b63ffffffff811660408401525092915050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b60006020828403121561533857600080fd5b81516148f281614763565b60006020828403121561535557600080fd5b81516148f281614d5e565b6000823560de1983360301811261525157600080fd5b60006020828403121561538857600080fd5b81356148f281614d5e565b60006001600160a01b03808351168452806020840151166020850152806040840151166040850152506060820151606084015263ffffffff608083015116608084015260a082015160e060a08501526153ef60e0850182615123565b905060c083015184820360c086015261135c828261502b565b6020815260006148f26020830184615393565b600082601f83011261542c57600080fd5b8151602061543c614b9383614b4e565b82815260059290921b8401810191818101908684111561545b57600080fd5b8286015b84811015614bdb578051835291830191830161545f565b6000806040838503121561548957600080fd5b825167ffffffffffffffff808211156154a157600080fd5b818501915085601f8301126154b557600080fd5b815160206154c5614b9383614b4e565b82815260059290921b840181019181810190898411156154e457600080fd5b948201945b8386101561550b5785516154fc81614763565b825294820194908201906154e9565b9188015191965090935050508082111561552457600080fd5b506150218582860161541b565b828152604060208201526000614d336040830184615393565b60006020828403121561555c57600080fd5b81356148f281614b31565b813561557281614763565b815473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038216178255506001810160208301356155ab81614763565b815473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0382161782555060408301356155e081614b31565b81547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff1660a09190911b77ffffffff0000000000000000000000000000000000000000161790555050565b818103818111156109ff576109ff61520c565b808201808211156109ff576109ff61520c565b60006109ff3683614c41565b82815260006020604081840152835180604085015260005b8181101561569157858101830151858201606001528201615675565b506000606082860101526060601f19601f830116850101925050509392505050565b6000602082840312156156c557600080fd5b81517fffffffff00000000000000000000000000000000000000000000000000000000811681146148f257600080fd5b634e487b7160e01b600052602160045260246000fdfea2646970667358221220678255252209f66deed740922c1ffddfe296ba6f5b36a8d1c8ecc2c00c3b7a8e64736f6c6343000814003300000000000000000000000000b67e4805138325ce871d5e27dc15f994681bc1000000000000000000000000f5a3ee38ed6a60fb9632f2e894421ce84a6a78b8
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103575760003560e01c806365187810116101c8578063b134427111610104578063da8be864116100a2578063f16172b01161007c578063f16172b01461093d578063f2fde38b14610950578063f698da2514610963578063fabc1cbc1461096b57600080fd5b8063da8be864146108f7578063e9007a521461090a578063eea9064b1461092a57600080fd5b8063be0525d7116100de578063be0525d71461080a578063c5e480db1461081d578063c94b5111146108c3578063cf80873e146108d657600080fd5b8063b134427114610792578063b7f06ebe146107b9578063bb45fef2146107dc57600080fd5b8063886f119511610171578063973a67961161014b578063973a67961461074b57806399be81c814610755578063a178848414610768578063a238f9df1461078857600080fd5b8063886f1195146107075780638da5cb5b1461071a578063900413471461072b57600080fd5b8063715018a6116101a2578063715018a6146106c1578063778e55f3146106c95780637f548071146106f457600080fd5b8063651878101461065757806365da12641461066a5780636d70f7ae1461069357600080fd5b806328a573ae1161029757806343377382116102405780635ac86ab71161021a5780635ac86ab7146105ed5780635c975abb146106105780635f966f141461061857806360d7faed1461064457600080fd5b806343377382146105ab578063595c6a67146105d2578063597b36da146105da57600080fd5b806339b70e381161027157806339b70e38146104ff5780633cdeb5e01461053e5780633e28391d1461056d57600080fd5b806328a573ae146104b957806329c77d4f146104cc57806333404396146104ec57600080fd5b806310d67a2f116103045780631bbce091116102de5780631bbce091146104335780631d1bf7f21461044657806320606b701461047f57806322bf40e4146104a657600080fd5b806310d67a2f146103fa578063132d49671461040d578063136439dd1461042057600080fd5b80630d5b0067116103355780630d5b0067146103bc5780630dd8dd02146103c55780630f589e59146103e557600080fd5b80630449ca391461035c57806304a4f979146103825780630b9f487a146103a9575b600080fd5b61036f61036a36600461472d565b61097e565b6040519081526020015b60405180910390f35b61036f7f14bde674c9f64b2ad00eaaee4a8bed1fabef35c7507e3c5b9cfc9436909a2dad81565b61036f6103b7366004614788565b610a05565b61036f609e5481565b6103d86103d336600461472d565b610ac7565b60405161037991906147e3565b6103f86103f3366004614881565b610e5c565b005b6103f86104083660046148d5565b610fc4565b6103f861041b3660046148f9565b6110cd565b6103f861042e36600461493a565b6111a2565b61036f6104413660046148f9565b611337565b61036f6104543660046148d5565b6001600160a01b03166000908152609a6020526040902060010154600160a01b900463ffffffff1690565b61036f7f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b6103f86104b4366004614953565b611365565b6103f86104c73660046148f9565b61153e565b61036f6104da3660046148d5565b609c6020526000908152604090205481565b6103f86104fa3660046149fb565b61160c565b6105267f00000000000000000000000000b67e4805138325ce871d5e27dc15f994681bc181565b6040516001600160a01b039091168152602001610379565b61052661054c3660046148d5565b6001600160a01b039081166000908152609a60205260409020600101541690565b61059b61057b3660046148d5565b6001600160a01b039081166000908152609b602052604090205416151590565b6040519015158152602001610379565b61036f7f39111bc4a4d688e1f685123d7497d4615370152a8ee4a0593e647bd06ad8bb0b81565b6103f8611722565b61036f6105e8366004614cfe565b61183f565b61059b6105fb366004614d3b565b606654600160ff9092169190911b9081161490565b60665461036f565b6105266106263660046148d5565b6001600160a01b039081166000908152609a60205260409020541690565b6103f8610652366004614d6c565b61186f565b6103f8610665366004614dfc565b6118ee565b6105266106783660046148d5565b609b602052600090815260409020546001600160a01b031681565b61059b6106a13660046148d5565b6001600160a01b039081166000908152609a602052604090205416151590565b6103f8611902565b61036f6106d7366004614e68565b609960209081526000928352604080842090915290825290205481565b6103f8610702366004614f4a565b611916565b606554610526906001600160a01b031681565b6033546001600160a01b0316610526565b61073e610739366004614fdb565b611a1b565b6040516103799190615066565b61036f62ed4e0081565b6103f8610763366004615079565b611af6565b61036f6107763660046148d5565b60a06020526000908152604090205481565b61036f62278d0081565b6105267f000000000000000000000000f5a3ee38ed6a60fb9632f2e894421ce84a6a78b881565b61059b6107c736600461493a565b609f6020526000908152604090205460ff1681565b61059b6107ea3660046150af565b609d60209081526000928352604080842090915290825290205460ff1681565b6103f861081836600461493a565b611bee565b61088d61082b3660046148d5565b6040805160608082018352600080835260208084018290529284018190526001600160a01b039485168152609a8352839020835191820184528054851682526001015493841691810191909152600160a01b90920463ffffffff169082015290565b6040805182516001600160a01b039081168252602080850151909116908201529181015163ffffffff1690820152606001610379565b61036f6108d13660046150db565b611bff565b6108e96108e43660046148d5565b611cb8565b60405161037992919061515c565b6103d86109053660046148d5565b611d75565b61036f6109183660046148d5565b60a16020526000908152604090205481565b6103f8610938366004615181565b6122b5565b6103f861094b3660046151da565b6122c1565b6103f861095e3660046148d5565b61237c565b61036f612409565b6103f861097936600461493a565b6124cb565b609e54600090815b838110156109fb57600060a160008787858181106109a6576109a66151f6565b90506020020160208101906109bb91906148d5565b6001600160a01b03166001600160a01b03168152602001908152602001600020549050828111156109ea578092505b506109f481615222565b9050610986565b5090505b92915050565b604080517f14bde674c9f64b2ad00eaaee4a8bed1fabef35c7507e3c5b9cfc9436909a2dad6020808301919091526001600160a01b038681168385015288811660608401528716608083015260a0820185905260c08083018590528351808403909101815260e0909201909252805191012060009081610a83612409565b60405161190160f01b602082015260228101919091526042810183905260620160408051808303601f19018152919052805160209091012098975050505050505050565b606654606090600190600290811603610b275760405162461bcd60e51b815260206004820152601960248201527f5061757361626c653a20696e646578206973207061757365640000000000000060448201526064015b60405180910390fd5b60008367ffffffffffffffff811115610b4257610b42614a9e565b604051908082528060200260200182016040528015610b6b578160200160208202803683370190505b50336000908152609b60205260408120549192506001600160a01b03909116905b85811015610e5157868682818110610ba657610ba66151f6565b9050602002810190610bb8919061523b565b610bc690602081019061525b565b9050878783818110610bda57610bda6151f6565b9050602002810190610bec919061523b565b610bf6908061525b565b905014610c6b5760405162461bcd60e51b815260206004820152603860248201527f44656c65676174696f6e4d616e616765722e717565756557697468647261776160448201527f6c3a20696e707574206c656e677468206d69736d6174636800000000000000006064820152608401610b1e565b33878783818110610c7e57610c7e6151f6565b9050602002810190610c90919061523b565b610ca19060608101906040016148d5565b6001600160a01b031614610d1d5760405162461bcd60e51b815260206004820152603c60248201527f44656c65676174696f6e4d616e616765722e717565756557697468647261776160448201527f6c3a2077697468647261776572206d757374206265207374616b6572000000006064820152608401610b1e565b610e223383898985818110610d3457610d346151f6565b9050602002810190610d46919061523b565b610d579060608101906040016148d5565b8a8a86818110610d6957610d696151f6565b9050602002810190610d7b919061523b565b610d85908061525b565b808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152508e92508d9150889050818110610dcb57610dcb6151f6565b9050602002810190610ddd919061523b565b610deb90602081019061525b565b8080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525061267d92505050565b838281518110610e3457610e346151f6565b602090810291909101015280610e4981615222565b915050610b8c565b509095945050505050565b336000908152609a60205260409020546001600160a01b031615610f0e5760405162461bcd60e51b815260206004820152604560248201527f44656c65676174696f6e4d616e616765722e726567697374657241734f70657260448201527f61746f723a206f70657261746f722068617320616c726561647920726567697360648201527f7465726564000000000000000000000000000000000000000000000000000000608482015260a401610b1e565b610f183384612bb1565b604080518082019091526060815260006020820152610f3a3380836000612e4a565b336001600160a01b03167f8e8485583a2310d41f7c82b9427d0bd49bad74bb9cff9d3402a29d8f9b28a0e285604051610f7391906152a5565b60405180910390a2336001600160a01b03167f02a919ed0e2acad1dd90f17ef2fa4ae5462ee1339170034a8531cca4b67080908484604051610fb69291906152f7565b60405180910390a250505050565b606560009054906101000a90046001600160a01b03166001600160a01b031663eab66d7a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611017573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061103b9190615326565b6001600160a01b0316336001600160a01b0316146110c15760405162461bcd60e51b815260206004820152602a60248201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160448201527f7320756e706175736572000000000000000000000000000000000000000000006064820152608401610b1e565b6110ca81613241565b50565b336001600160a01b037f00000000000000000000000000b67e4805138325ce871d5e27dc15f994681bc116146111545760405162461bcd60e51b815260206004820152602660248201527f44656c65676174696f6e4d616e616765723a206f6e6c7953747261746567794d60448201526530b730b3b2b960d11b6064820152608401610b1e565b6001600160a01b038084166000908152609b6020526040902054161561119d576001600160a01b038084166000908152609b60205260409020541661119b81858585613359565b505b505050565b60655460405163237dfb4760e11b81523360048201526001600160a01b03909116906346fbf68e90602401602060405180830381865afa1580156111ea573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061120e9190615343565b6112805760405162461bcd60e51b815260206004820152602860248201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160448201527f73207061757365720000000000000000000000000000000000000000000000006064820152608401610b1e565b606654818116146112f95760405162461bcd60e51b815260206004820152603860248201527f5061757361626c652e70617573653a20696e76616c696420617474656d70742060448201527f746f20756e70617573652066756e6374696f6e616c69747900000000000000006064820152608401610b1e565b606681905560405181815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d906020015b60405180910390a250565b6001600160a01b0383166000908152609c602052604081205461135c85828686611bff565b95945050505050565b600054610100900460ff16158080156113855750600054600160ff909116105b8061139f5750303b15801561139f575060005460ff166001145b6114115760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a65640000000000000000000000000000000000006064820152608401610b1e565b6000805460ff191660011790558015611434576000805461ff0019166101001790555b61143e88886133e2565b604080518082018252600481526314195b1b60e21b60209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f0e0f546b3087f7e4405d37b8de121d34252412874bf985c456311dd7eca663b681840152466060820152306080808301919091528351808303909101815260a090910190925281519101206098556114d8896134e2565b6114e186613541565b6114ed85858585613621565b8015611533576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050505050565b336001600160a01b037f00000000000000000000000000b67e4805138325ce871d5e27dc15f994681bc116146115c55760405162461bcd60e51b815260206004820152602660248201527f44656c65676174696f6e4d616e616765723a206f6e6c7953747261746567794d60448201526530b730b3b2b960d11b6064820152608401610b1e565b6001600160a01b038084166000908152609b6020526040902054161561119d576001600160a01b038084166000908152609b60205260409020541661119b81858585613837565b6066546002906004908116036116645760405162461bcd60e51b815260206004820152601960248201527f5061757361626c653a20696e64657820697320706175736564000000000000006044820152606401610b1e565b61166c6138c0565b60005b88811015611717576117078a8a8381811061168c5761168c6151f6565b905060200281019061169e9190615360565b8989848181106116b0576116b06151f6565b90506020028101906116c2919061525b565b8989868181106116d4576116d46151f6565b905060200201358888878181106116ed576116ed6151f6565b90506020020160208101906117029190615376565b613919565b61171081615222565b905061166f565b50611533600160d355565b60655460405163237dfb4760e11b81523360048201526001600160a01b03909116906346fbf68e90602401602060405180830381865afa15801561176a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061178e9190615343565b6118005760405162461bcd60e51b815260206004820152602860248201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160448201527f73207061757365720000000000000000000000000000000000000000000000006064820152608401610b1e565b600019606681905560405190815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d9060200160405180910390a2565b6000816040516020016118529190615408565b604051602081830303815290604052805190602001209050919050565b6066546002906004908116036118c75760405162461bcd60e51b815260206004820152601960248201527f5061757361626c653a20696e64657820697320706175736564000000000000006044820152606401610b1e565b6118cf6138c0565b6118dc8686868686613919565b6118e6600160d355565b505050505050565b6118f6614152565b61119b84848484613621565b61190a614152565b61191460006134e2565b565b428360200151101561199a5760405162461bcd60e51b815260206004820152604160248201527f44656c65676174696f6e4d616e616765722e64656c6567617465546f4279536960448201527f676e61747572653a207374616b6572207369676e6174757265206578706972656064820152601960fa1b608482015260a401610b1e565b6000609c6000876001600160a01b03166001600160a01b0316815260200190815260200160002054905060006119d68783888860200151611bff565b6001600160a01b0388166000908152609c602052604090206001840190558551909150611a0690889083906141ac565b611a1287878686612e4a565b50505050505050565b60606000825167ffffffffffffffff811115611a3957611a39614a9e565b604051908082528060200260200182016040528015611a62578160200160208202803683370190505b50905060005b83518110156109fb576001600160a01b03851660009081526099602052604081208551909190869084908110611aa057611aa06151f6565b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002054828281518110611adb57611adb6151f6565b6020908102919091010152611aef81615222565b9050611a68565b336000908152609a60205260409020546001600160a01b0316611ba75760405162461bcd60e51b815260206004820152604760248201527f44656c65676174696f6e4d616e616765722e7570646174654f70657261746f7260448201527f4d657461646174615552493a2063616c6c6572206d75737420626520616e206f60648201527f70657261746f7200000000000000000000000000000000000000000000000000608482015260a401610b1e565b336001600160a01b03167f02a919ed0e2acad1dd90f17ef2fa4ae5462ee1339170034a8531cca4b67080908383604051611be29291906152f7565b60405180910390a25050565b611bf6614152565b6110ca81613541565b604080517f39111bc4a4d688e1f685123d7497d4615370152a8ee4a0593e647bd06ad8bb0b6020808301919091526001600160a01b0387811683850152851660608301526080820186905260a08083018590528351808403909101815260c0909201909252805191012060009081611c75612409565b60405161190160f01b602082015260228101919091526042810183905260620160408051808303601f190181529190528051602090910120979650505050505050565b6040517f94f649dd0000000000000000000000000000000000000000000000000000000081526001600160a01b038281166004830152606091829160009182917f00000000000000000000000000b67e4805138325ce871d5e27dc15f994681bc116906394f649dd90602401600060405180830381865afa158015611d41573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611d699190810190615476565b90969095509350505050565b606654606090600190600290811603611dd05760405162461bcd60e51b815260206004820152601960248201527f5061757361626c653a20696e64657820697320706175736564000000000000006044820152606401610b1e565b6001600160a01b038084166000908152609b602052604090205416611e845760405162461bcd60e51b8152602060048201526044602482018190527f44656c65676174696f6e4d616e616765722e756e64656c65676174653a207374908201527f616b6572206d7573742062652064656c65676174656420746f20756e64656c6560648201527f6761746500000000000000000000000000000000000000000000000000000000608482015260a401610b1e565b6001600160a01b038084166000908152609a60205260409020541615611f125760405162461bcd60e51b815260206004820152603d60248201527f44656c65676174696f6e4d616e616765722e756e64656c65676174653a206f7060448201527f657261746f72732063616e6e6f7420626520756e64656c6567617465640000006064820152608401610b1e565b6001600160a01b038316611f8e5760405162461bcd60e51b815260206004820152603c60248201527f44656c65676174696f6e4d616e616765722e756e64656c65676174653a20636160448201527f6e6e6f7420756e64656c6567617465207a65726f2061646472657373000000006064820152608401610b1e565b6001600160a01b038084166000818152609b602052604090205490911690331480611fc15750336001600160a01b038216145b80611fe857506001600160a01b038181166000908152609a60205260409020600101541633145b61205a5760405162461bcd60e51b815260206004820152603d60248201527f44656c65676174696f6e4d616e616765722e756e64656c65676174653a20636160448201527f6c6c65722063616e6e6f7420756e64656c6567617465207374616b65720000006064820152608401610b1e565b60008061206686611cb8565b9092509050336001600160a01b038716146120bc57826001600160a01b0316866001600160a01b03167ff0eddf07e6ea14f388b47e1e94a0f464ecbd9eed4171130e0fc0e99fb4030a8a60405160405180910390a35b826001600160a01b0316866001600160a01b03167ffee30966a256b71e14bc0ebfc94315e28ef4a97a7131a9e2b7a310a73af4467660405160405180910390a36001600160a01b0386166000908152609b60205260408120805473ffffffffffffffffffffffffffffffffffffffff191690558251900361214d5760408051600081526020810190915294506122ac565b815167ffffffffffffffff81111561216757612167614a9e565b604051908082528060200260200182016040528015612190578160200160208202803683370190505b50945060005b82518110156122aa576040805160018082528183019092526000916020808301908036833750506040805160018082528183019092529293506000929150602080830190803683370190505090508483815181106121f6576121f66151f6565b602002602001015182600081518110612211576122116151f6565b60200260200101906001600160a01b031690816001600160a01b031681525050838381518110612243576122436151f6565b60200260200101518160008151811061225e5761225e6151f6565b60200260200101818152505061227789878b858561267d565b888481518110612289576122896151f6565b602002602001018181525050505080806122a290615222565b915050612196565b505b50505050919050565b61119d33848484612e4a565b336000908152609a60205260409020546001600160a01b03166123725760405162461bcd60e51b815260206004820152604360248201527f44656c65676174696f6e4d616e616765722e6d6f646966794f70657261746f7260448201527f44657461696c733a2063616c6c6572206d75737420626520616e206f7065726160648201527f746f720000000000000000000000000000000000000000000000000000000000608482015260a401610b1e565b6110ca3382612bb1565b612384614152565b6001600160a01b0381166124005760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610b1e565b6110ca816134e2565b60007f00000000000000000000000000000000000000000000000000000000000013884603612439575060985490565b50604080518082018252600481526314195b1b60e21b60209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f0e0f546b3087f7e4405d37b8de121d34252412874bf985c456311dd7eca663b681840152466060820152306080808301919091528351808303909101815260a0909101909252815191012090565b606560009054906101000a90046001600160a01b03166001600160a01b031663eab66d7a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561251e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125429190615326565b6001600160a01b0316336001600160a01b0316146125c85760405162461bcd60e51b815260206004820152602a60248201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160448201527f7320756e706175736572000000000000000000000000000000000000000000006064820152608401610b1e565b6066541981196066541916146126465760405162461bcd60e51b815260206004820152603860248201527f5061757361626c652e756e70617573653a20696e76616c696420617474656d7060448201527f7420746f2070617573652066756e6374696f6e616c69747900000000000000006064820152608401610b1e565b606681905560405181815233907f3582d1828e26bf56bd801502bc021ac0bc8afb57c826e4986b45593c8fad389c9060200161132c565b60006001600160a01b0386166127215760405162461bcd60e51b815260206004820152605060248201527f44656c65676174696f6e4d616e616765722e5f72656d6f76655368617265734160448201527f6e6451756575655769746864726177616c3a207374616b65722063616e6e6f7460648201527f206265207a65726f206164647265737300000000000000000000000000000000608482015260a401610b1e565b82516000036127be5760405162461bcd60e51b815260206004820152604d60248201527f44656c65676174696f6e4d616e616765722e5f72656d6f76655368617265734160448201527f6e6451756575655769746864726177616c3a207374726174656769657320636160648201527f6e6e6f7420626520656d70747900000000000000000000000000000000000000608482015260a401610b1e565b60005b8351811015612abf576001600160a01b038616156128175761281786888684815181106127f0576127f06151f6565b602002602001015186858151811061280a5761280a6151f6565b6020026020010151613359565b846001600160a01b0316876001600160a01b031614806128e957507f00000000000000000000000000b67e4805138325ce871d5e27dc15f994681bc16001600160a01b0316639b4da03d858381518110612873576128736151f6565b60200260200101516040518263ffffffff1660e01b81526004016128a691906001600160a01b0391909116815260200190565b602060405180830381865afa1580156128c3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128e79190615343565b155b6129ce5760405162461bcd60e51b8152602060048201526084602482018190527f44656c65676174696f6e4d616e616765722e5f72656d6f76655368617265734160448301527f6e6451756575655769746864726177616c3a2077697468647261776572206d7560648301527f73742062652073616d652061646472657373206173207374616b657220696620908201527f746869726450617274795472616e7366657273466f7262696464656e2061726560a48201527f207365740000000000000000000000000000000000000000000000000000000060c482015260e401610b1e565b7f00000000000000000000000000b67e4805138325ce871d5e27dc15f994681bc16001600160a01b0316638c80d4e588868481518110612a1057612a106151f6565b6020026020010151868581518110612a2a57612a2a6151f6565b60209081029190910101516040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b1681526001600160a01b0393841660048201529290911660248301526044820152606401600060405180830381600087803b158015612a9c57600080fd5b505af1158015612ab0573d6000803e3d6000fd5b505050508060010190506127c1565b506001600160a01b038616600090815260a060205260408120805491829190612ae783615222565b919050555060006040518060e00160405280896001600160a01b03168152602001886001600160a01b03168152602001876001600160a01b031681526020018381526020014263ffffffff1681526020018681526020018581525090506000612b4f8261183f565b6000818152609f602052604090819020805460ff19166001179055519091507f9009ab153e8014fbfb02f2217f5cde7aa7f9ad734ae85ca3ee3f4ca2fdd499f990612b9d9083908590615531565b60405180910390a198975050505050505050565b6000612bc060208301836148d5565b6001600160a01b031603612c625760405162461bcd60e51b815260206004820152605460248201527f44656c65676174696f6e4d616e616765722e5f7365744f70657261746f72446560448201527f7461696c733a2063616e6e6f742073657420606561726e696e6773526563656960648201527f7665726020746f207a65726f2061646472657373000000000000000000000000608482015260a401610b1e565b62ed4e00612c76606083016040840161554a565b63ffffffff161115612d165760405162461bcd60e51b815260206004820152605f60248201527f44656c65676174696f6e4d616e616765722e5f7365744f70657261746f72446560448201527f7461696c733a207374616b65724f70744f757457696e646f772063616e6e6f7460648201527f206265203e204d41585f5354414b45525f4f50545f4f55545f57494e444f5700608482015260a401610b1e565b6001600160a01b0382166000908152609a60205260409081902060010154600160a01b900463ffffffff1690612d52906060840190840161554a565b63ffffffff161015612df25760405162461bcd60e51b815260206004820152604d60248201527f44656c65676174696f6e4d616e616765722e5f7365744f70657261746f72446560448201527f7461696c733a207374616b65724f70744f757457696e646f772063616e6e6f7460648201527f2062652064656372656173656400000000000000000000000000000000000000608482015260a401610b1e565b6001600160a01b0382166000908152609a602052604090208190612e168282615567565b505060405133907ffebe5cd24b2cbc7b065b9d0fdeb904461e4afcff57dd57acda1e7832031ba7ac90611be29084906152a5565b606654600090600190811603612ea25760405162461bcd60e51b815260206004820152601960248201527f5061757361626c653a20696e64657820697320706175736564000000000000006044820152606401610b1e565b6001600160a01b038086166000908152609b60205260409020541615612f3a5760405162461bcd60e51b815260206004820152604160248201527f44656c65676174696f6e4d616e616765722e5f64656c65676174653a2073746160448201527f6b657220697320616c7265616479206163746976656c792064656c65676174656064820152601960fa1b608482015260a401610b1e565b6001600160a01b038085166000908152609a602052604090205416612fc75760405162461bcd60e51b815260206004820152603f60248201527f44656c65676174696f6e4d616e616765722e5f64656c65676174653a206f706560448201527f7261746f72206973206e6f74207265676973746572656420696e2050656c6c006064820152608401610b1e565b6001600160a01b038085166000908152609a6020526040902060010154168015801590612ffd5750336001600160a01b03821614155b80156130125750336001600160a01b03861614155b1561317f5742846020015110156130915760405162461bcd60e51b815260206004820152603760248201527f44656c65676174696f6e4d616e616765722e5f64656c65676174653a2061707060448201527f726f766572207369676e617475726520657870697265640000000000000000006064820152608401610b1e565b6001600160a01b0381166000908152609d6020908152604080832086845290915290205460ff161561312b5760405162461bcd60e51b815260206004820152603760248201527f44656c65676174696f6e4d616e616765722e5f64656c65676174653a2061707060448201527f726f76657253616c7420616c7265616479207370656e740000000000000000006064820152608401610b1e565b6001600160a01b0381166000908152609d6020908152604080832086845282528220805460ff1916600117905585015161316c908890889085908890610a05565b905061317d828287600001516141ac565b505b6001600160a01b038681166000818152609b6020526040808220805473ffffffffffffffffffffffffffffffffffffffff1916948a169485179055517fc3ee9f2e5fda98e8066a1f745b2df9285f416fe98cf2559cd21484b3d87433049190a36000806131eb88611cb8565b9150915060005b825181101561153357613239888a858481518110613212576132126151f6565b602002602001015185858151811061322c5761322c6151f6565b6020026020010151613837565b6001016131f2565b6001600160a01b0381166132e35760405162461bcd60e51b815260206004820152604960248201527f5061757361626c652e5f73657450617573657252656769737472793a206e657760448201527f50617573657252656769737472792063616e6e6f7420626520746865207a657260648201527f6f20616464726573730000000000000000000000000000000000000000000000608482015260a401610b1e565b606554604080516001600160a01b03928316815291831660208301527f6e9fcd539896fca60e8b0f01dd580233e48a6b0f7df013b89ba7f565869acdb6910160405180910390a16065805473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0392909216919091179055565b6001600160a01b0380851660009081526099602090815260408083209386168352929052908120805483929061339090849061562b565b9091555050604080516001600160a01b0385811682528481166020830152918101839052908516907f6909600037b75d7b4733aedd815442b5ec018a827751c832aaff64eba5d6d2dd90606001610fb6565b6065546001600160a01b031615801561340357506001600160a01b03821615155b61349b5760405162461bcd60e51b815260206004820152604760248201527f5061757361626c652e5f696e697469616c697a655061757365723a205f696e6960448201527f7469616c697a6550617573657228292063616e206f6e6c792062652063616c6c60648201527f6564206f6e636500000000000000000000000000000000000000000000000000608482015260a401610b1e565b606681905560405181815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d9060200160405180910390a26134de82613241565b5050565b603380546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b62278d008111156135e05760405162461bcd60e51b815260206004820152605e60248201527f44656c65676174696f6e4d616e616765722e5f7365744d696e5769746864726160448201527f77616c44656c61793a205f6d696e5769746864726177616c44656c617920636160648201527f6e6e6f74206265203e204d41585f5749544844524157414c5f44454c41590000608482015260a401610b1e565b609e5460408051918252602082018390527f338caf1431dddfb34caa16bfc51573f97922fa2f8eb6d70d27476d8b4a89d5c3910160405180910390a1609e55565b8281146136bd5760405162461bcd60e51b8152602060048201526044602482018190527f44656c65676174696f6e4d616e616765722e5f73657453747261746567795769908201527f746864726177616c44656c61793a20696e707574206c656e677468206d69736d60648201527f6174636800000000000000000000000000000000000000000000000000000000608482015260a401610b1e565b8260005b818110156118e65760008686838181106136dd576136dd6151f6565b90506020020160208101906136f291906148d5565b6001600160a01b038116600090815260a16020526040812054919250868685818110613720576137206151f6565b90506020020135905062278d008111156137c85760405162461bcd60e51b815260206004820152606060248201527f44656c65676174696f6e4d616e616765722e5f7365745374726174656779576960448201527f746864726177616c44656c61793a205f7769746864726177616c44656c61792060648201527f63616e6e6f74206265203e204d41585f5749544844524157414c5f44454c4159608482015260a401610b1e565b6001600160a01b038316600081815260a160209081526040918290208490558151928352820184905281018290527f4be295c8d739bae6e60f607ccde1aa068970dc43209682f7290d10726efc02e59060600160405180910390a15050508061383090615222565b90506136c1565b6001600160a01b0380851660009081526099602090815260408083209386168352929052908120805483929061386e90849061563e565b9091555050604080516001600160a01b0385811682528481166020830152918101839052908516907f1ec042c965e2edd7107b51188ee0f383e22e76179041ab3a9d18ff151405166c90606001610fb6565b600260d354036139125760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610b1e565b600260d355565b60006139276105e887615651565b6000818152609f602052604090205490915060ff166139d45760405162461bcd60e51b815260206004820152604360248201527f44656c65676174696f6e4d616e616765722e5f636f6d706c657465517565756560448201527f645769746864726177616c3a20616374696f6e206973206e6f7420696e20717560648201527f6575650000000000000000000000000000000000000000000000000000000000608482015260a401610b1e565b609e5442906139e960a0890160808a0161554a565b63ffffffff166139f9919061563e565b1115613a935760405162461bcd60e51b815260206004820152605960248201527f44656c65676174696f6e4d616e616765722e5f636f6d706c657465517565756560448201527f645769746864726177616c3a206d696e5769746864726177616c44656c61792060648201527f706572696f6420686173206e6f74207965742070617373656400000000000000608482015260a401610b1e565b613aa360608701604088016148d5565b6001600160a01b0316336001600160a01b031614613b4f5760405162461bcd60e51b815260206004820152605060248201527f44656c65676174696f6e4d616e616765722e5f636f6d706c657465517565756560448201527f645769746864726177616c3a206f6e6c7920776974686472617765722063616e60648201527f20636f6d706c65746520616374696f6e00000000000000000000000000000000608482015260a401610b1e565b8115613bfe57613b6260a087018761525b565b85149050613bfe5760405162461bcd60e51b815260206004820152604260248201527f44656c65676174696f6e4d616e616765722e5f636f6d706c657465517565756560448201527f645769746864726177616c3a20696e707574206c656e677468206d69736d617460648201527f6368000000000000000000000000000000000000000000000000000000000000608482015260a401610b1e565b6000818152609f60205260409020805460ff191690558115613e055760005b613c2a60a088018861525b565b9050811015613dff574260a16000613c4560a08b018b61525b565b85818110613c5557613c556151f6565b9050602002016020810190613c6a91906148d5565b6001600160a01b03168152602081019190915260400160002054613c9460a08a0160808b0161554a565b63ffffffff16613ca4919061563e565b1115613d645760405162461bcd60e51b815260206004820152606860248201527f44656c65676174696f6e4d616e616765722e5f636f6d706c657465517565756560448201527f645769746864726177616c3a207769746864726177616c44656c61792070657260648201527f696f6420686173206e6f74207965742070617373656420666f7220746869732060848201527f737472617465677900000000000000000000000000000000000000000000000060a482015260c401610b1e565b613df7613d7460208901896148d5565b33613d8260a08b018b61525b565b85818110613d9257613d926151f6565b9050602002016020810190613da791906148d5565b613db460c08c018c61525b565b86818110613dc457613dc46151f6565b905060200201358a8a87818110613ddd57613ddd6151f6565b9050602002016020810190613df291906148d5565b6143b7565b600101613c1d565b50614117565b336000908152609b60205260408120546001600160a01b0316905b613e2d60a089018961525b565b9050811015614114574260a16000613e4860a08c018c61525b565b85818110613e5857613e586151f6565b9050602002016020810190613e6d91906148d5565b6001600160a01b03168152602081019190915260400160002054613e9760a08b0160808c0161554a565b63ffffffff16613ea7919061563e565b1115613f675760405162461bcd60e51b815260206004820152606860248201527f44656c65676174696f6e4d616e616765722e5f636f6d706c657465517565756560448201527f645769746864726177616c3a207769746864726177616c44656c61792070657260648201527f696f6420686173206e6f74207965742070617373656420666f7220746869732060848201527f737472617465677900000000000000000000000000000000000000000000000060a482015260c401610b1e565b7f00000000000000000000000000b67e4805138325ce871d5e27dc15f994681bc16001600160a01b031663c4623ea133898985818110613fa957613fa96151f6565b9050602002016020810190613fbe91906148d5565b613fcb60a08d018d61525b565b86818110613fdb57613fdb6151f6565b9050602002016020810190613ff091906148d5565b613ffd60c08e018e61525b565b8781811061400d5761400d6151f6565b60405160e088901b7fffffffff000000000000000000000000000000000000000000000000000000001681526001600160a01b03968716600482015294861660248601529290941660448401526020909102013560648201526084019050600060405180830381600087803b15801561408557600080fd5b505af1158015614099573d6000803e3d6000fd5b505050506001600160a01b0382161561410c5761410c82336140be60a08c018c61525b565b858181106140ce576140ce6151f6565b90506020020160208101906140e391906148d5565b6140f060c08d018d61525b565b86818110614100576141006151f6565b90506020020135613837565b600101613e20565b50505b6040518181527fc97098c2f658800b4df29001527f7324bcdffcf6e8751a699ab920a1eced5b1d9060200160405180910390a1505050505050565b6033546001600160a01b031633146119145760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b1e565b6001600160a01b0383163b15614301576040517f1626ba7e00000000000000000000000000000000000000000000000000000000808252906001600160a01b03851690631626ba7e90614205908690869060040161565d565b602060405180830381865afa158015614222573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061424691906156b3565b7fffffffff00000000000000000000000000000000000000000000000000000000161461119d5760405162461bcd60e51b815260206004820152605360248201527f454950313237315369676e61747572655574696c732e636865636b5369676e6160448201527f747572655f454950313237313a2045524331323731207369676e61747572652060648201527f766572696669636174696f6e206661696c656400000000000000000000000000608482015260a401610b1e565b826001600160a01b0316614315838361445e565b6001600160a01b03161461119d5760405162461bcd60e51b815260206004820152604760248201527f454950313237315369676e61747572655574696c732e636865636b5369676e6160448201527f747572655f454950313237313a207369676e6174757265206e6f742066726f6d60648201527f207369676e657200000000000000000000000000000000000000000000000000608482015260a401610b1e565b6040517fc608c7f30000000000000000000000000000000000000000000000000000000081526001600160a01b03858116600483015284811660248301526044820184905282811660648301527f00000000000000000000000000b67e4805138325ce871d5e27dc15f994681bc1169063c608c7f390608401600060405180830381600087803b15801561444a57600080fd5b505af1158015611533573d6000803e3d6000fd5b600080600061446d858561447a565b915091506109fb816144bf565b60008082516041036144b05760208301516040840151606085015160001a6144a487828585614624565b945094505050506144b8565b506000905060025b9250929050565b60008160048111156144d3576144d36156f5565b036144db5750565b60018160048111156144ef576144ef6156f5565b0361453c5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610b1e565b6002816004811115614550576145506156f5565b0361459d5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610b1e565b60038160048111156145b1576145b16156f5565b036110ca5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401610b1e565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561465b57506000905060036146df565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156146af573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166146d8576000600192509250506146df565b9150600090505b94509492505050565b60008083601f8401126146fa57600080fd5b50813567ffffffffffffffff81111561471257600080fd5b6020830191508360208260051b85010111156144b857600080fd5b6000806020838503121561474057600080fd5b823567ffffffffffffffff81111561475757600080fd5b611d69858286016146e8565b6001600160a01b03811681146110ca57600080fd5b803561478381614763565b919050565b600080600080600060a086880312156147a057600080fd5b85356147ab81614763565b945060208601356147bb81614763565b935060408601356147cb81614763565b94979396509394606081013594506080013592915050565b6020808252825182820181905260009190848201906040850190845b8181101561481b578351835292840192918401916001016147ff565b50909695505050505050565b60006060828403121561483957600080fd5b50919050565b60008083601f84011261485157600080fd5b50813567ffffffffffffffff81111561486957600080fd5b6020830191508360208285010111156144b857600080fd5b60008060006080848603121561489657600080fd5b6148a08585614827565b9250606084013567ffffffffffffffff8111156148bc57600080fd5b6148c88682870161483f565b9497909650939450505050565b6000602082840312156148e757600080fd5b81356148f281614763565b9392505050565b60008060006060848603121561490e57600080fd5b833561491981614763565b9250602084013561492981614763565b929592945050506040919091013590565b60006020828403121561494c57600080fd5b5035919050565b60008060008060008060008060c0898b03121561496f57600080fd5b883561497a81614763565b9750602089013561498a81614763565b96506040890135955060608901359450608089013567ffffffffffffffff808211156149b557600080fd5b6149c18c838d016146e8565b909650945060a08b01359150808211156149da57600080fd5b506149e78b828c016146e8565b999c989b5096995094979396929594505050565b6000806000806000806000806080898b031215614a1757600080fd5b883567ffffffffffffffff80821115614a2f57600080fd5b614a3b8c838d016146e8565b909a50985060208b0135915080821115614a5457600080fd5b614a608c838d016146e8565b909850965060408b0135915080821115614a7957600080fd5b614a858c838d016146e8565b909650945060608b01359150808211156149da57600080fd5b634e487b7160e01b600052604160045260246000fd5b60405160e0810167ffffffffffffffff81118282101715614ad757614ad7614a9e565b60405290565b6040805190810167ffffffffffffffff81118282101715614ad757614ad7614a9e565b604051601f8201601f1916810167ffffffffffffffff81118282101715614b2957614b29614a9e565b604052919050565b63ffffffff811681146110ca57600080fd5b803561478381614b31565b600067ffffffffffffffff821115614b6857614b68614a9e565b5060051b60200190565b600082601f830112614b8357600080fd5b81356020614b98614b9383614b4e565b614b00565b82815260059290921b84018101918181019086841115614bb757600080fd5b8286015b84811015614bdb578035614bce81614763565b8352918301918301614bbb565b509695505050505050565b600082601f830112614bf757600080fd5b81356020614c07614b9383614b4e565b82815260059290921b84018101918181019086841115614c2657600080fd5b8286015b84811015614bdb5780358352918301918301614c2a565b600060e08284031215614c5357600080fd5b614c5b614ab4565b9050614c6682614778565b8152614c7460208301614778565b6020820152614c8560408301614778565b604082015260608201356060820152614ca060808301614b43565b608082015260a082013567ffffffffffffffff80821115614cc057600080fd5b614ccc85838601614b72565b60a084015260c0840135915080821115614ce557600080fd5b50614cf284828501614be6565b60c08301525092915050565b600060208284031215614d1057600080fd5b813567ffffffffffffffff811115614d2757600080fd5b614d3384828501614c41565b949350505050565b600060208284031215614d4d57600080fd5b813560ff811681146148f257600080fd5b80151581146110ca57600080fd5b600080600080600060808688031215614d8457600080fd5b853567ffffffffffffffff80821115614d9c57600080fd5b9087019060e0828a031215614db057600080fd5b90955060208701359080821115614dc657600080fd5b50614dd3888289016146e8565b909550935050604086013591506060860135614dee81614d5e565b809150509295509295909350565b60008060008060408587031215614e1257600080fd5b843567ffffffffffffffff80821115614e2a57600080fd5b614e36888389016146e8565b90965094506020870135915080821115614e4f57600080fd5b50614e5c878288016146e8565b95989497509550505050565b60008060408385031215614e7b57600080fd5b8235614e8681614763565b91506020830135614e9681614763565b809150509250929050565b600060408284031215614eb357600080fd5b614ebb614add565b9050813567ffffffffffffffff80821115614ed557600080fd5b818401915084601f830112614ee957600080fd5b8135602082821115614efd57614efd614a9e565b614f0f601f8301601f19168201614b00565b92508183528681838601011115614f2557600080fd5b8181850182850137600081838501015282855280860135818601525050505092915050565b600080600080600060a08688031215614f6257600080fd5b8535614f6d81614763565b94506020860135614f7d81614763565b9350604086013567ffffffffffffffff80821115614f9a57600080fd5b614fa689838a01614ea1565b94506060880135915080821115614fbc57600080fd5b50614fc988828901614ea1565b95989497509295608001359392505050565b60008060408385031215614fee57600080fd5b8235614ff981614763565b9150602083013567ffffffffffffffff81111561501557600080fd5b61502185828601614b72565b9150509250929050565b600081518084526020808501945080840160005b8381101561505b5781518752958201959082019060010161503f565b509495945050505050565b6020815260006148f2602083018461502b565b6000806020838503121561508c57600080fd5b823567ffffffffffffffff8111156150a357600080fd5b611d698582860161483f565b600080604083850312156150c257600080fd5b82356150cd81614763565b946020939093013593505050565b600080600080608085870312156150f157600080fd5b84356150fc81614763565b935060208501359250604085013561511381614763565b9396929550929360600135925050565b600081518084526020808501945080840160005b8381101561505b5781516001600160a01b031687529582019590820190600101615137565b60408152600061516f6040830185615123565b828103602084015261135c818561502b565b60008060006060848603121561519657600080fd5b83356151a181614763565b9250602084013567ffffffffffffffff8111156151bd57600080fd5b6151c986828701614ea1565b925050604084013590509250925092565b6000606082840312156151ec57600080fd5b6148f28383614827565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600182016152345761523461520c565b5060010190565b60008235605e1983360301811261525157600080fd5b9190910192915050565b6000808335601e1984360301811261527257600080fd5b83018035915067ffffffffffffffff82111561528d57600080fd5b6020019150600581901b36038213156144b857600080fd5b6060810182356152b481614763565b6001600160a01b0390811683526020840135906152d082614763565b16602083015260408301356152e481614b31565b63ffffffff811660408401525092915050565b60208152816020820152818360408301376000818301604090810191909152601f909201601f19160101919050565b60006020828403121561533857600080fd5b81516148f281614763565b60006020828403121561535557600080fd5b81516148f281614d5e565b6000823560de1983360301811261525157600080fd5b60006020828403121561538857600080fd5b81356148f281614d5e565b60006001600160a01b03808351168452806020840151166020850152806040840151166040850152506060820151606084015263ffffffff608083015116608084015260a082015160e060a08501526153ef60e0850182615123565b905060c083015184820360c086015261135c828261502b565b6020815260006148f26020830184615393565b600082601f83011261542c57600080fd5b8151602061543c614b9383614b4e565b82815260059290921b8401810191818101908684111561545b57600080fd5b8286015b84811015614bdb578051835291830191830161545f565b6000806040838503121561548957600080fd5b825167ffffffffffffffff808211156154a157600080fd5b818501915085601f8301126154b557600080fd5b815160206154c5614b9383614b4e565b82815260059290921b840181019181810190898411156154e457600080fd5b948201945b8386101561550b5785516154fc81614763565b825294820194908201906154e9565b9188015191965090935050508082111561552457600080fd5b506150218582860161541b565b828152604060208201526000614d336040830184615393565b60006020828403121561555c57600080fd5b81356148f281614b31565b813561557281614763565b815473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b038216178255506001810160208301356155ab81614763565b815473ffffffffffffffffffffffffffffffffffffffff19166001600160a01b0382161782555060408301356155e081614b31565b81547fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff1660a09190911b77ffffffff0000000000000000000000000000000000000000161790555050565b818103818111156109ff576109ff61520c565b808201808211156109ff576109ff61520c565b60006109ff3683614c41565b82815260006020604081840152835180604085015260005b8181101561569157858101830151858201606001528201615675565b506000606082860101526060601f19601f830116850101925050509392505050565b6000602082840312156156c557600080fd5b81517fffffffff00000000000000000000000000000000000000000000000000000000811681146148f257600080fd5b634e487b7160e01b600052602160045260246000fdfea2646970667358221220678255252209f66deed740922c1ffddfe296ba6f5b36a8d1c8ecc2c00c3b7a8e64736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000b67e4805138325ce871d5e27dc15f994681bc1000000000000000000000000f5a3ee38ed6a60fb9632f2e894421ce84a6a78b8
-----Decoded View---------------
Arg [0] : _strategyManager (address): 0x00B67E4805138325ce871D5E27DC15f994681bC1
Arg [1] : _slasher (address): 0xF5A3ee38Ed6A60FB9632f2E894421cE84A6A78b8
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000000b67e4805138325ce871d5e27dc15f994681bc1
Arg [1] : 000000000000000000000000f5a3ee38ed6a60fb9632f2e894421ce84a6a78b8
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in MNT
Multichain Portfolio | 35 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.