Feature Tip: Add private address tag to any address under My Name Tag !
Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
TssStakingSlashing
Compiler Version
v0.8.9+commit.e5eed63a
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {DelegationShareBase} from "../delegation/DelegationShareBase.sol";
import {DelegationCallbackBase} from "../delegation/DelegationCallbackBase.sol";
import {IDelegationManager} from "../delegation/interfaces/IDelegationManager.sol";
import {IDelegationShare} from "../delegation/interfaces/IDelegation.sol";
import {IDelegation} from "../delegation/interfaces/IDelegation.sol";
import {CrossDomainEnabled} from "../../libraries/bridge/CrossDomainEnabled.sol";
import {ITssRewardContract} from "../../L2/predeploys/iTssRewardContract.sol";
import {TssDelegationManager} from "./delegation/TssDelegationManager.sol";
import {TssDelegation} from "./delegation/TssDelegation.sol";
import {WhiteList} from "../delegation/WhiteListBase.sol";
import {Lib_Address} from "../../libraries/utils/Lib_Address.sol";
import "./ITssGroupManager.sol";
import "./ITssStakingSlashing.sol";
contract TssStakingSlashing is
Initializable,
OwnableUpgradeable,
ReentrancyGuardUpgradeable,
IStakingSlashing,
DelegationShareBase,
DelegationCallbackBase,
CrossDomainEnabled
{
enum SlashType {
uptime,
animus
}
struct SlashMsg {
uint256 batchIndex;
address jailNode;
address[] tssNodes;
SlashType slashType;
}
// tss group contract address
address public tssGroupContract;
//tss delegation manager address
address public tssDelegationManagerContract;
//tss delegation address
address public tssDelegationContract;
// storage operator infos (key:staker address)
mapping(address => bytes) public operators;
// slashing parameter settings
// record the quit request
address[] public quitRequestList;
// slashing amount of type uptime and animus (0:uptime, 1:animus)
uint256[2] public slashAmount;
// record the slash operate (map[batchIndex] -> (map[staker] -> slashed))
mapping(uint256 => mapping(address => bool)) slashRecord;
//EOA address
address public regulatoryAccount;
//msg sender => withdraw event
mapping(address => bytes32) public withdrawalRoots;
//msg sender => withdrawal
mapping(address => IDelegationManager.QueuedWithdrawal) public withdrawals;
//operator => stakers
mapping(address => address[]) public stakers;
//staker => operator
mapping(address => address) public delegators;
//operator => claimer
mapping(address => address) public operatorClaimers;
//claimer => operator
mapping(address => address) public claimerOperators;
bool public isSetParam;
address public tssManager;
address public constant DEFUND_ADDRESS = 0x2F44BD2a54aC3fB20cd7783cF94334069641daC9;
/**
* @notice slash tssnode
* @param 0 slashed address
* @param 1 slash type
*/
event Slashing(address, SlashType);
event WithdrawQueue(address,uint256);
event Defund(address, address, uint256);
constructor() CrossDomainEnabled(address(0)) {
_disableInitializers();
}
/**
* @notice initializes the contract setting and the deployer as the initial owner
* @param _mantleToken mantle token contract address
* @param _tssGroupContract address tss group manager contract address
*/
function initialize(address _mantleToken,
address _tssGroupContract,
address _delegationManager,
address _delegation,
address _l1messenger,
address _regulatoryAccount,
address _tssManager
) public initializer {
__Ownable_init();
__ReentrancyGuard_init();
underlyingToken = IERC20(_mantleToken);
tssGroupContract = _tssGroupContract;
tssDelegationManagerContract = _delegationManager;
tssDelegationContract = _delegation;
//initialize delegation
delegationManager = IDelegationManager(_delegationManager);
delegation = IDelegation(_delegation);
messenger = _l1messenger;
regulatoryAccount = _regulatoryAccount;
tssManager = _tssManager;
}
/**
* @notice change the mantle token and tssGroup contract address
* @param _token the erc20 mantle token contract address
*/
function setTokenAddress(address _token) public onlyOwner {
require(_token != address(0),"Invalid address");
underlyingToken = IERC20(_token);
}
function setTssGroupAddress(address _tssGroup) public onlyOwner{
require(_tssGroup != address(0),"Invalid address");
tssGroupContract = _tssGroup;
}
function setRegulatoryAccount(address _account) public onlyOwner {
require(_account != address(0),"Invalid address");
regulatoryAccount = _account;
}
function setTssManager(address _tssManager) public onlyOwner {
require(_tssManager != address(0),"Invalid address");
tssManager = _tssManager;
}
function setClaimer(
address _operator,
address _claimer
) external {
require(msg.sender == _operator, "msg sender is diff with operator address");
require(delegation.isOperator(msg.sender), "msg sender is not registered operator");
require(claimerOperators[_claimer] == address(0), "the claimer has been used");
if (operatorClaimers[_operator] != address(0)) {
delete claimerOperators[operatorClaimers[_operator]];
}
operatorClaimers[_operator] = _claimer;
claimerOperators[_claimer] = _operator;
bytes memory message = abi.encodeWithSelector(
ITssRewardContract.setClaimer.selector,
_operator,
_claimer
);
// send call data into L2, hardcode address
sendCrossDomainMessage(
address(0x4200000000000000000000000000000000000020),
2000000,
message
);
}
/**
* @notice set the slashing params (0 -> uptime , 1 -> animus)
* @param _slashAmount the amount to be deducted for each type
*/
function setSlashingParams(uint256[2] calldata _slashAmount)
public
onlyOwner
{
require(_slashAmount[1] > _slashAmount[0], "invalid param slashAmount, animus <= uptime");
for (uint256 i = 0; i < 2; i++) {
require(_slashAmount[i] > 0, "invalid amount");
slashAmount[i] = _slashAmount[i];
}
isSetParam = true;
}
/**
* @notice set the slashing params (0 -> uptime, 1 -> animus)
*/
function getSlashingParams() public view returns (uint256[2] memory) {
return slashAmount;
}
/**
* @notice send quit request for the next election
*/
function quitRequest() public nonReentrant {
require(delegation.operatorShares(msg.sender, this) > 0, "do not have deposit");
// when not in consensus period
require(
ITssGroupManager(tssGroupContract).memberExistInActive(operators[msg.sender]) ||
ITssGroupManager(tssGroupContract).memberExistActive(operators[msg.sender]),
"not at the inactive group or active group"
);
// is active member
for (uint256 i = 0; i < quitRequestList.length; i++) {
require(quitRequestList[i] != msg.sender, "already in quitRequestList");
}
quitRequestList.push(msg.sender);
}
/**
* @notice return the quit list
*/
function getQuitRequestList() public view returns (address[] memory) {
return quitRequestList;
}
/**
* @notice clear the quit list
*/
function clearQuitRequestList() public onlyOwner {
delete quitRequestList;
}
/**
* @notice verify the slash message then slash
* @param _messageBytes the message that abi encode by type SlashMsg
* @param _sig the signature of the hash keccak256(_messageBytes)
*/
function slashing(bytes calldata _messageBytes, bytes calldata _sig) public nonReentrant {
require(tssManager == msg.sender,"TssStakingSlashing: msg.sender is not tssManager");
SlashMsg memory message = abi.decode(_messageBytes, (SlashMsg));
// verify tss member state not at jailed status
require(!isJailed(message.jailNode), "the node already jailed");
// have not slash before
require(!slashRecord[message.batchIndex][message.jailNode], "already slashed");
slashRecord[message.batchIndex][message.jailNode] = true;
require(
ITssGroupManager(tssGroupContract).verifySign(keccak256(_messageBytes), _sig),
"signer not tss group pub key"
);
// slash tokens
slash(message);
emit Slashing(message.jailNode, message.slashType);
}
/**
* @notice slash the staker and distribute rewards to voters
* @param message the message about the slash infos
*/
function slash(SlashMsg memory message) internal {
// slashing params check
require(isSetParam,"have not set the slash amount");
bytes memory jailNodePubKey = operators[message.jailNode];
if (message.slashType == SlashType.uptime) {
// jail and transfer deposits
ITssGroupManager(tssGroupContract).memberJail(jailNodePubKey);
transformDeposit(message.jailNode, 0);
} else if (message.slashType == SlashType.animus) {
// remove the member and transfer deposits
ITssGroupManager(tssGroupContract).memberJail(jailNodePubKey);
transformDeposit(message.jailNode, 1);
} else {
revert("err type for slashing");
}
}
/**
* @notice distribute rewards to voters
* @param deduction address of the punished
* @param slashType the type to punished
*/
function transformDeposit(
address deduction,
uint256 slashType
) internal {
uint256 deductedAmountShare;
uint256 totalBalance = _tokenBalance();
require(
(delegation.operatorShares(deduction, this) * totalBalance) / totalShares >= slashAmount[slashType],
"do not have enought shares"
);
// record total penalty
deductedAmountShare = (slashAmount[slashType] * totalShares) / totalBalance;
uint256 operatorShare = delegation.operatorShares(deduction, this);
IDelegationShare[] memory delegationShares = new IDelegationShare[](1);
delegationShares[0] = this;
uint256[] memory delegationShareIndexes = new uint256[](1);
delegationShareIndexes[0] = 0;
IERC20[] memory tokens = new IERC20[](1);
tokens[0] = underlyingToken;
address[] memory stakerS = stakers[deduction];
for (uint256 i = 0; i < stakerS.length; i++){
uint256 share = shares(stakerS[i]);
uint256[] memory shareAmounts = new uint256[](1);
shareAmounts[0] = deductedAmountShare * share / operatorShare;
TssDelegationManager(tssDelegationManagerContract).slashShares(stakerS[i], regulatoryAccount, delegationShares,tokens, delegationShareIndexes, shareAmounts);
}
}
/**
* @notice set tss node status unjail
*/
function unJail() public {
// slashing params check
require(isSetParam, "have not set the slash amount");
require(isJailed(msg.sender), "An unjailed user doesn't need to call this method");
uint256 totalBalance = _tokenBalance();
require((delegation.operatorShares(msg.sender, this) * totalBalance) / totalShares >= slashAmount[1], "Insufficient balance");
ITssGroupManager(tssGroupContract).memberUnJail(operators[msg.sender]);
}
/**
* @notice get the slash record
* @param batchIndex the index of batch
* @param user address of the staker
*/
function getSlashRecord(uint256 batchIndex, address user) public view returns (bool) {
return slashRecord[batchIndex][user];
}
/**
* @notice check the tssnode status
* @param user address of the staker
*/
function isJailed(address user) public returns (bool) {
ITssGroupManager.TssMember memory tssMember = ITssGroupManager(tssGroupContract)
.getTssMember(operators[user]);
require(tssMember.publicKey.length == 64, "tss member not exist");
return tssMember.status == ITssGroupManager.MemberStatus.jail;
}
function isCanOperator(address _addr) public returns (bool) {
return TssDelegationManager(tssDelegationManagerContract).isCanOperator(_addr, this);
}
function deposit(uint256 amount) public returns (uint256) {
uint256 shares = TssDelegationManager(tssDelegationManagerContract).depositInto(this, underlyingToken, amount, msg.sender);
return shares;
}
function withdraw() external {
require(delegation.isDelegated(msg.sender),"not delegator");
require(
withdrawalRoots[msg.sender] == bytes32(0),
"msg sender already request withdraws"
);
uint256[] memory delegationIndexes = new uint256[](1);
delegationIndexes[0] = 0;
IDelegationShare[] memory delegationShares = new IDelegationShare[](1);
delegationShares[0] = this;
IERC20[] memory tokens = new IERC20[](1);
tokens[0] = underlyingToken;
uint256[] memory sharesA = new uint256[](1);
sharesA[0] = shares(msg.sender);
uint256 nonce = TssDelegationManager(tssDelegationManagerContract).getWithdrawNonce(msg.sender);
IDelegationManager.WithdrawerAndNonce memory withdrawerAndNonce = IDelegationManager.WithdrawerAndNonce({
withdrawer: msg.sender,
nonce: SafeCast.toUint96(nonce)
});
address operator = delegation.delegatedTo(msg.sender);
IDelegationManager.QueuedWithdrawal memory queuedWithdrawal = IDelegationManager.QueuedWithdrawal({
delegations: delegationShares,
tokens: tokens,
shares: sharesA,
depositor: msg.sender,
withdrawerAndNonce: withdrawerAndNonce,
delegatedAddress: operator
});
withdrawals[msg.sender] = queuedWithdrawal;
bytes32 withdrawRoot = TssDelegationManager(tssDelegationManagerContract).queueWithdrawal(msg.sender,delegationIndexes,delegationShares,tokens,sharesA,withdrawerAndNonce);
withdrawalRoots[msg.sender] = withdrawRoot;
emit WithdrawQueue(msg.sender, sharesA[0]);
}
function startWithdraw() external {
require(
withdrawalRoots[msg.sender] != bytes32(0),
"msg sender must request withdraw first"
);
bytes32 withdrawRoot = withdrawalRoots[msg.sender];
TssDelegationManager(tssDelegationManagerContract).startQueuedWithdrawalWaitingPeriod(withdrawRoot,msg.sender,0);
}
function canCompleteQueuedWithdrawal() external returns (bool) {
require(
withdrawalRoots[msg.sender] != bytes32(0),
"msg sender did not request withdraws"
);
IDelegationManager.QueuedWithdrawal memory queuedWithdrawal = withdrawals[msg.sender];
return delegationManager.canCompleteQueuedWithdrawal(queuedWithdrawal);
}
function completeWithdraw() external {
require(
withdrawalRoots[msg.sender] != bytes32(0),
"msg sender did not request withdraws"
);
IDelegationManager.QueuedWithdrawal memory queuedWithdrawal = withdrawals[msg.sender];
TssDelegationManager(tssDelegationManagerContract).completeQueuedWithdrawal(msg.sender, queuedWithdrawal, true);
delete withdrawalRoots[msg.sender];
delete withdrawals[msg.sender];
}
function registerAsOperator(bytes calldata _pubKey) external {
require(msg.sender == Lib_Address.publicKeyToAddress(_pubKey), "public key not match");
TssDelegation(tssDelegationContract).registerAsOperator(this, msg.sender);
operators[msg.sender] = _pubKey;
}
function delegateTo(address _operator) external {
TssDelegation(tssDelegationContract).delegateTo(_operator, msg.sender);
}
function onDelegationReceived(
address delegator,
address operator,
IDelegationShare[] memory delegationShares,
uint256[] memory investorShares
)external override onlyDelegation {
uint256 delegationLength = delegationShares.length;
require(delegationLength == 1,"delegation only for tss");
require(investorShares.length == 1,"delegation share only for tss");
require(address(delegationShares[0]) == address(this),"must use current contract");
if (delegators[delegator] == address(0)) {
delegators[delegator] = operator;
stakers[operator].push(delegator);
}
}
function onDelegationWithdrawn(
address delegator,
address operator,
IDelegationShare[] memory delegationShares,
uint256[] memory investorShares
) external override onlyDelegation {
uint256 delegationLength = delegationShares.length;
require(delegationLength == 1,"delegation only for tss");
require(investorShares.length == 1,"delegation share only for tss");
require(address(delegationShares[0]) == address(this),"must use current contract");
if (TssDelegationManager(tssDelegationManagerContract).getDelegationShares(delegator, delegationShares[0]) == investorShares[0]){
address[] memory staker = stakers[operator];
for (uint256 j = 0; j < staker.length; j++) {
if (staker[j] == delegator) {
stakers[operator][j] = stakers[operator][staker.length -1];
stakers[operator].pop();
delete delegators[delegator];
}
}
}
}
function defund() external returns (uint256) {
uint256 amount = underlyingToken.balanceOf(address(this));
require(amount > 0, "Not sufficient funds");
require(DEFUND_ADDRESS != address(0),"Invalid DEFUND_ADDRESS address");
underlyingToken.transfer(DEFUND_ADDRESS, amount);
emit Defund(address(underlyingToken), DEFUND_ADDRESS, amount);
return amount;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "./interfaces/IDelegationManager.sol";
/**
* @title Base implementation of `IDelegationShare` interface, designed to be inherited from by more complex strategies.
* @author Layr Labs, Inc.
* @notice Simple, basic, "do-nothing" DelegationShare that holds a single underlying token and returns it on withdrawals.
* Implements minimal versions of the IDelegationShare functions, this contract is designed to be inherited by
* more complex delegation contracts, which can then override its functions as necessary.
*/
abstract contract DelegationShareBase is Initializable, PausableUpgradeable, IDelegationShare {
using SafeERC20 for IERC20;
/// @notice DelegationManager contract
IDelegationManager public delegationManager;
/// @notice The underyling token for shares in this DelegationShare
IERC20 public underlyingToken;
/// @notice The total number of extant shares in the DelegationShare
uint256 public totalShares;
event Deposit(address depositor, address token, uint256 amount);
event Withdraw(address depositor, address token, uint256 amount);
/// @notice Simply checks that the `msg.sender` is the `DelegationManager`, which is an address stored immutably at construction.
modifier onlyDelegationManager() {
require(msg.sender == address(delegationManager), "DelegationShareBase.onlyDelegationManager");
_;
}
/**
* @notice Used to deposit tokens into this DelegationShare
* @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 DelegationManager contract. It is invoked inside of the delegationManager's
* `depositIntoStrategy` function, and individual share balances are recorded in the delegationManager as well.
* @return newShares is the number of new shares issued at the current exchange ratio.
*/
function deposit(address depositor, IERC20 token, uint256 amount)
external
virtual
override
whenNotPaused
onlyDelegationManager
returns (uint256 newShares)
{
require(token == underlyingToken, "DelegationShareBase.deposit: Can only deposit underlyingToken");
// be ware of lines below, if min amount is too small there will be a share calculation exploit problem
(bool success, bytes memory data) = address(token).call(
abi.encodeWithSignature("decimals()")
);
require(success, "underlyingToken have no method with decimals");
uint256 decimals = uint256(bytes32(data));
require(amount >= 1*10**decimals, "amount must gt 1 unit");
/**
* @notice calculation of newShares *mirrors* `underlyingToShares(amount)`, but is different since the balance of `underlyingToken`
* has already been increased due to the `delegationManager` transferring tokens to this delegation contract prior to calling this function
*/
uint256 priorTokenBalance = _tokenBalance() - amount;
if (priorTokenBalance == 0 || totalShares == 0) {
newShares = amount;
} else {
newShares = (amount * totalShares) / priorTokenBalance;
}
totalShares += newShares;
emit Deposit(depositor, address(token), amount);
return newShares;
}
/**
* @notice Used to withdraw tokens from this DelegationShare, to the `depositor`'s address
* @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 delegationManager contract. It is invoked inside of the delegationManager's
* other functions, and individual share balances are recorded in the delegationManager as well.
*/
function withdraw(address depositor, IERC20 token, uint256 amountShares)
external
virtual
override
whenNotPaused
onlyDelegationManager
{
require(token == underlyingToken, "DelegationShareBase.withdraw: Can only withdraw the strategy token");
require(
amountShares <= totalShares,
"DelegationShareBase.withdraw: amountShares must be less than or equal to totalShares"
);
// copy `totalShares` value prior to decrease
uint256 priorTotalShares = totalShares;
// Decrease `totalShares` to reflect withdrawal. Unchecked arithmetic since we just checked this above.
unchecked {
totalShares -= amountShares;
}
/**
* @notice calculation of amountToSend *mirrors* `sharesToUnderlying(amountShares)`, but is different since the `totalShares` has already
* been decremented
*/
uint256 amountToSend;
if (priorTotalShares == amountShares) {
amountToSend = _tokenBalance();
} else {
amountToSend = (_tokenBalance() * amountShares) / priorTotalShares;
}
underlyingToken.safeTransfer(depositor, amountToSend);
emit Withdraw(depositor, address(token), amountToSend);
}
/**
* @notice Currently returns a brief string explaining the strategy's goal & purpose, but for more complex
* strategies, may be a link to metadata that explains in more detail.
*/
function explanation() external pure virtual override returns (string memory) {
// return "Base DelegationShare implementation to inherit from for more complex implementations";
return "Mantle token DelegationShare implementation for submodules as an example";
}
/**
* @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
* @dev Implementation for these functions in particular may vary signifcantly for different strategies
*/
function sharesToUnderlyingView(uint256 amountShares) public view virtual override returns (uint256) {
if (totalShares == 0) {
return amountShares;
} else {
return (_tokenBalance() * amountShares) / totalShares;
}
}
/**
* @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
* @dev Implementation for these functions in particular may vary signifcantly for different strategies
*/
function sharesToUnderlying(uint256 amountShares) public view virtual override returns (uint256) {
return sharesToUnderlyingView(amountShares);
}
/**
* @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
* @dev Implementation for these functions in particular may vary signifcantly for different strategies
*/
function underlyingToSharesView(uint256 amountUnderlying) public view virtual returns (uint256) {
uint256 tokenBalance = _tokenBalance();
if (tokenBalance == 0 || totalShares == 0) {
return amountUnderlying;
} else {
return (amountUnderlying * totalShares) / tokenBalance;
}
}
/**
* @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
* @dev Implementation for these functions in particular may vary signifcantly for different strategies
*/
function underlyingToShares(uint256 amountUnderlying) external view virtual returns (uint256) {
return underlyingToSharesView(amountUnderlying);
}
/**
* @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 virtual returns (uint256) {
return sharesToUnderlyingView(shares(user));
}
/**
* @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 virtual returns (uint256) {
return sharesToUnderlying(shares(user));
}
/**
* @notice convenience function for fetching the current total shares of `user` in this strategy, by
* querying the `delegationManager` contract
*/
function shares(address user) public view virtual returns (uint256) {
return IDelegationManager(delegationManager).investorDelegationShares(user, IDelegationShare(address(this)));
}
/// @notice Internal function used to fetch this contract's current balance of `underlyingToken`.
// slither-disable-next-line dead-code
function _tokenBalance() internal view virtual returns (uint256) {
return underlyingToken.balanceOf(address(this));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/IDelegationCallback.sol";
import "./interfaces/IDelegation.sol";
/**
* @title Base implementation of `IInvestmentStrategy` interface, designed to be inherited from by more complex strategies.
* @notice Simple, basic, "do-nothing" InvestmentStrategy that holds a single underlying token and returns it on withdrawals.
* Implements minimal versions of the IInvestmentStrategy functions, this contract is designed to be inherited by
* more complex investment strategies, which can then override its functions as necessary.
*/
abstract contract DelegationCallbackBase is Initializable, PausableUpgradeable, IDelegationCallback {
/// @notice DelegationManager contract
IDelegation public delegation;
/// @notice Simply checks that the `msg.sender` is the `DelegationManager`, which is an address stored immutably at construction.
modifier onlyDelegation() {
require(msg.sender == address(delegation), "DelegationShareBase.onlyDelegationManager");
_;
}
function payForService(IERC20 token, uint256 amount) external payable {}
function onDelegationWithdrawn(
address delegator,
IDelegationShare[] memory investorDelegationShares,
uint256[] memory investorShares
) external {}
function onDelegationReceived(
address delegator,
IDelegationShare[] memory investorDelegationShares,
uint256[] memory investorShares
) external {}
}// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.9.0;
/* Interface Imports */
import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol";
/**
* @title CrossDomainEnabled
* @dev Helper contract for contracts performing cross-domain communications
*
* Compiler used: defined by inheriting contract
*/
contract CrossDomainEnabled {
/*************
* Variables *
*************/
// Messenger contract used to send and recieve messages from the other domain.
address public messenger;
/***************
* Constructor *
***************/
/**
* @param _messenger Address of the CrossDomainMessenger on the current layer.
*/
constructor(address _messenger) {
messenger = _messenger;
}
/**********************
* Function Modifiers *
**********************/
/**
* Enforces that the modified function is only callable by a specific cross-domain account.
* @param _sourceDomainAccount The only account on the originating domain which is
* authenticated to call this function.
*/
modifier onlyFromCrossDomainAccount(address _sourceDomainAccount) {
require(
msg.sender == address(getCrossDomainMessenger()),
"BVM_XCHAIN: messenger contract unauthenticated"
);
require(
getCrossDomainMessenger().xDomainMessageSender() == _sourceDomainAccount,
"BVM_XCHAIN: wrong sender of cross-domain message"
);
_;
}
/**********************
* Internal Functions *
**********************/
/**
* Gets the messenger, usually from storage. This function is exposed in case a child contract
* needs to override.
* @return The address of the cross-domain messenger contract which should be used.
*/
function getCrossDomainMessenger() internal virtual returns (ICrossDomainMessenger) {
return ICrossDomainMessenger(messenger);
}
/**q
* Sends a message to an account on another domain
* @param _crossDomainTarget The intended recipient on the destination domain
* @param _message The data to send to the target (usually calldata to a function with
* `onlyFromCrossDomainAccount()`)
* @param _gasLimit The gasLimit for the receipt of the message on the target domain.
*/
function sendCrossDomainMessage(
address _crossDomainTarget,
uint32 _gasLimit,
bytes memory _message
) internal {
// slither-disable-next-line reentrancy-events, reentrancy-benign
getCrossDomainMessenger().sendMessage(_crossDomainTarget, _message, _gasLimit);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/**
* @title ITssRewardContract
*/
interface ITssRewardContract {
/**********
* Events *
**********/
event DistributeTssReward(
uint256 lastBatchTime,
uint256 batchTime,
uint256 amount,
address[] tssMembers
);
event DistributeTssRewardByBlock(
uint256 blockStartHeight,
uint32 length,
uint256 amount,
address[] tssMembers
);
event Claim(
address owner,
uint256 amount
);
/********************
* Public Functions *
********************/
/**
* @dev Query total undistributed balance.
* @return Amount of undistributed rewards.
*/
function queryReward() external view returns (uint256);
/**
* @dev Auto distribute reward to tss members.
* @param _blockStartHeight L2 rollup batch block start height.
* @param _length Rollup batch length.
* @param _batchTime rollup batch time.
* @param _tssMembers Tss member address array.
*/
function claimReward(uint256 _blockStartHeight, uint32 _length, uint256 _batchTime, address[] calldata _tssMembers) external;
/**
* @dev clear contract(canonical).
*/
function withdraw() external;
/**
* @dev Claim reward and withdraw
*/
function claim() external;
/**
* @dev default claimer == staker, if staker is multi-signature address,must set claimer
* @param _staker the address of staker
* @param _claimer the address for staker to claim reward
*/
function setClaimer(address _staker, address _claimer) external;
/**
* @dev Initiate a request to claim
*/
function requestClaim() external returns (bool);
/**
* @dev Query the remaining time required to claim
*/
function queryClaimTime() external returns (uint256);
function setSccAddr(address sccAddr) external;
function setStakeSlashAddr(address ssAddr) external;
function setSendAmountPerYear(uint256) external;
function setWaitingTime(uint256) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
abstract contract WhiteList is OwnableUpgradeable {
modifier whitelistOnly(address checkAddr) {
if (!whitelist[checkAddr]) {
revert("NOT_IN_WHITELIST");
}
_;
}
mapping(address => bool) public whitelist;
/**
* @notice Add to whitelist
*/
function addToWhitelist(address[] calldata toAddAddresses) external onlyOwner {
for (uint i = 0; i < toAddAddresses.length; i++) {
whitelist[toAddAddresses[i]] = true;
}
}
/**
* @notice Remove from whitelist
*/
function removeFromWhitelist(address[] calldata toRemoveAddresses) external onlyOwner {
for (uint i = 0; i < toRemoveAddresses.length; i++) {
delete whitelist[toRemoveAddresses[i]];
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/**
* @title Lib_Address
* @dev This library for convert bytes publicKey to address
*/
library Lib_Address {
function publicKeyToAddress(bytes memory publicKey) internal pure returns (address) {
require(publicKey.length == 64, "public key length must 64 bytes");
return address(uint160(uint256(keccak256(publicKey))));
}
}// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.9.0;
interface ITssGroupManager {
enum MemberStatus {
unJail,
jail
}
struct TssMember {
bytes publicKey;
address nodeAddress;
MemberStatus status;
}
function setTssGroupMember(uint256 _threshold, bytes[] memory _batchPublicKey) external;
function setGroupPublicKey(bytes memory _publicKey, bytes memory _groupPublicKey) external;
function getTssGroupInfo() external returns (uint256, uint256, bytes memory, bytes[] memory);
function getTssInactiveGroupInfo() external returns (uint256, uint256, bytes[] memory);
function memberJail(bytes memory _publicKey) external;
function memberUnJail(bytes memory _publicKey) external;
function removeMember(bytes memory _publicKey) external;
function getTssGroupUnJailMembers() external returns (address[] memory);
function getTssGroupMembers() external returns (bytes[] memory);
function getTssMember(bytes memory _publicKey) external returns (TssMember memory);
function memberExistActive(bytes memory _publicKey) external returns (bool);
function memberExistInActive(bytes memory _publicKey) external returns (bool);
function inActiveIsEmpty() external returns (bool);
function verifySign(bytes32 _message, bytes memory _sig) external returns (bool);
function isTssGroupUnJailMembers(address _addr) external returns (bool);
function memberExistActive(address _addr) external returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.9.0;
interface IStakingSlashing {
// tx
function setTokenAddress(address) external;
function setTssGroupAddress(address) external;
function setRegulatoryAccount(address) external;
function setClaimer(address, address) external;
function setSlashingParams(uint256[2] calldata) external;
function setTssManager(address) external;
function quitRequest() external;
function clearQuitRequestList() external;
function slashing(bytes calldata, bytes calldata) external;
function unJail() external;
// query
function getSlashingParams() external view returns (uint256[2] memory);
function getQuitRequestList() external view returns (address[] memory);
function getSlashRecord(uint256, address) external view returns (bool);
function isJailed(address) external returns (bool);
function isCanOperator(address) external returns (bool);
//fund
function deposit(uint256 amount) external returns (uint256);
function withdraw() external;
function completeWithdraw() external;
function startWithdraw() external;
function canCompleteQueuedWithdrawal() external returns (bool);
//delegation
function registerAsOperator(bytes calldata) external;
function delegateTo(address) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "./IDelegationCallback.sol";
/**
* @title Interface for the primary delegation contract.
* @notice See the `Delegation` contract itself for implementation details.
*/
interface IDelegation {
enum DelegationStatus {
UNDELEGATED,
DELEGATED
}
/**
* @notice This will be called by an operator to register itself as an operator that stakers can choose to delegate to.
* @param dt is the `DelegationTerms` contract that the operator has for those who delegate to them.
* @dev An operator can set `dt` equal to their own address (or another EOA address), in the event that they want to split payments
* in a more 'trustful' manner.
* @dev In the present design, once set, there is no way for an operator to ever modify the address of their DelegationTerms contract.
*/
function registerAsOperator(IDelegationCallback dt) external;
/**
* @notice This will be called by a staker to delegate its assets to some operator.
* @param operator is the operator to whom staker (msg.sender) is delegating its assets
*/
function delegateTo(address operator) external;
/**
* @notice Delegates from `staker` to `operator`.
* @dev requires that r, vs are a valid ECSDA signature from `staker` indicating their intention for this action
*/
function delegateToSignature(address staker, address operator, uint256 expiry, bytes32 r, bytes32 vs) external;
/**
* @notice Undelegates `staker` from the operator who they are delegated to.
* @notice Callable only by the InvestmentManager
* @dev Should only ever be called in the event that the `staker` has no active deposits.
*/
function undelegate(address staker) external;
/// @notice returns the address of the operator that `staker` is delegated to.
function delegatedTo(address staker) external view returns (address);
/// @notice returns the delegationCallback of the `operator`, which may mediate their interactions with stakers who delegate to them.
function delegationCallback(address operator) external view returns (IDelegationCallback);
/// @notice returns the total number of shares in `DelegationShare` that are delegated to `operator`.
function operatorShares(address operator, IDelegationShare delegationShare) 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' if `staker` is *not* actively delegated, and 'false' otherwise.
function isNotDelegated(address staker) external returns (bool);
/// @notice Returns if an operator can be delegated to, i.e. it has called `registerAsOperator`.
function isOperator(address operator) external view returns (bool);
/**
* @notice Increases the `staker`'s delegated shares in `delegationShare` by `shares`, typically called when the staker has further deposits.
* @dev Callable only by the DelegationManager
*/
function increaseDelegatedShares(address staker, IDelegationShare delegationShare, uint256 shares) external;
/**
* @notice Decreases the `staker`'s delegated shares in `delegationShare` by `shares, typically called when the staker withdraws
* @dev Callable only by the DelegationManager
*/
function decreaseDelegatedShares(address staker, IDelegationShare delegationShare, uint256 shares) external;
/// @notice Version of `decreaseDelegatedShares` that accepts an array of inputs.
function decreaseDelegatedShares(
address staker,
IDelegationShare[] calldata delegationShares,
uint256[] calldata shares
) external;
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
import "./IDelegationShare.sol";
import "./IDelegationSlasher.sol";
import "./IDelegation.sol";
/**
* @title Interface for the primary entrypoint for funds.
* @author Layr Labs, Inc.
* @notice See the `DelegationManager` contract itself for implementation details.
*/
interface IDelegationManager {
// used for storing details of queued withdrawals
struct WithdrawalStorage {
uint32 initTimestamp;
uint32 unlockTimestamp;
address withdrawer;
}
// packed struct for queued withdrawals
struct WithdrawerAndNonce {
address withdrawer;
uint96 nonce;
}
/**
* 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. `startQueuedWithdrawalWaitingPeriod` or `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 QueuedWithdrawal {
IDelegationShare[] delegations;
IERC20[] tokens;
uint256[] shares;
address depositor;
WithdrawerAndNonce withdrawerAndNonce;
address delegatedAddress;
}
/**
* @notice Deposits `amount` of `token` into the specified `DelegationShare`, with the resultant shares credited to `depositor`
* @param delegationShare is the specified shares record where investment is to be made,
* @param token is the ERC20 token in which the investment is to be made,
* @param amount is the amount of token to be invested in the delegationShare by the depositor
*/
function depositInto(IDelegationShare delegationShare, IERC20 token, uint256 amount)
external
returns (uint256);
/// @notice Returns the current shares of `user` in `delegationShare`
function investorDelegationShares(address user, IDelegationShare delegationShare) external view returns (uint256 shares);
/**
* @notice Get all details on the depositor's investments and corresponding shares
* @return (depositor's delegationShare record, shares in these DelegationShare contract)
*/
function getDeposits(address depositor) external view returns (IDelegationShare[] memory, uint256[] memory);
/// @notice Simple getter function that returns `investorDelegations[staker].length`.
function investorDelegationLength(address staker) external view returns (uint256);
/**
* @notice Called by a staker to queue a withdraw in the given token and shareAmount from each of the respective given strategies.
* @dev Stakers will complete their withdrawal by calling the 'completeQueuedWithdrawal' function.
* User shares are decreased in this function, but the total number of shares in each delegation strategy remains the same.
* The total number of shares is decremented in the 'completeQueuedWithdrawal' function instead, which is where
* the funds are actually sent to the user through use of the delegation strategies' 'withdrawal' function. This ensures
* that the value per share reported by each strategy will remain consistent, and that the shares will continue
* to accrue gains during the enforced WITHDRAWAL_WAITING_PERIOD.
* @param delegationShareIndexes is a list of the indices in `investorDelegationShare[msg.sender]` that correspond to the delegation strategies
* for which `msg.sender` is withdrawing 100% of their shares
* @dev strategies are removed from `delegationShare` by swapping the last entry with the entry to be removed, then
* popping off the last entry in `delegationShares`. The simplest way to calculate the correct `delegationShareIndexes` to input
* is to order the strategies *for which `msg.sender` is withdrawing 100% of their shares* from highest index in
* `delegationShares` to lowest index
*/
function queueWithdrawal(
uint256[] calldata delegationShareIndexes,
IDelegationShare[] calldata delegationShares,
IERC20[] calldata tokens,
uint256[] calldata shareAmounts,
WithdrawerAndNonce calldata withdrawerAndNonce,
bool undelegateIfPossible
)
external returns(bytes32);
function startQueuedWithdrawalWaitingPeriod(
bytes32 withdrawalRoot,
uint32 stakeInactiveAfter
) external;
/**
* @notice Used to complete the specified `queuedWithdrawal`. The function caller must match `queuedWithdrawal.withdrawer`
* @param queuedWithdrawal The QueuedWithdrawal to complete.
* @param receiveAsTokens If true, the shares specified in the queued withdrawal will be withdrawn from the specified delegation strategies themselves
* and sent to the caller, through calls to `queuedWithdrawal.strategies[i].withdraw`. If false, then the shares in the specified delegation strategies
* will simply be transferred to the caller directly.
*/
function completeQueuedWithdrawal(
QueuedWithdrawal calldata queuedWithdrawal,
bool receiveAsTokens
)
external;
/**
* @notice Slashes the shares of 'frozen' operator (or a staker delegated to one)
* @param slashedAddress is the frozen address that is having its shares slashes
* @param delegationShareIndexes is a list of the indices in `investorStrats[msg.sender]` that correspond to the strategies
* for which `msg.sender` is withdrawing 100% of their shares
* @param recipient The slashed funds are withdrawn as tokens to this address.
* @dev strategies are removed from `investorStrats` by swapping the last entry with the entry to be removed, then
* popping off the last entry in `investorStrats`. The simplest way to calculate the correct `strategyIndexes` to input
* is to order the strategies *for which `msg.sender` is withdrawing 100% of their shares* from highest index in
* `investorStrats` to lowest index
*/
function slashShares(
address slashedAddress,
address recipient,
IDelegationShare[] calldata delegationShares,
IERC20[] calldata tokens,
uint256[] calldata delegationShareIndexes,
uint256[] calldata shareAmounts
)
external;
function slashQueuedWithdrawal(
address recipient,
QueuedWithdrawal calldata queuedWithdrawal
)
external;
/**
* @notice Used to check if a queued withdrawal can be completed. Returns 'true' if the withdrawal can be immediately
* completed, and 'false' otherwise.
* @dev This function will revert if the specified `queuedWithdrawal` does not exist
*/
function canCompleteQueuedWithdrawal(
QueuedWithdrawal calldata queuedWithdrawal
)
external
returns (bool);
/// @notice Returns the keccak256 hash of `queuedWithdrawal`.
function calculateWithdrawalRoot(
QueuedWithdrawal memory queuedWithdrawal
)
external
pure
returns (bytes32);
/// @notice Returns the single, central Delegation contract
function delegation() external view returns (IDelegation);
/// @notice Returns the single, central DelegationSlasher contract
function delegationSlasher() external view returns (IDelegationSlasher);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "../../delegation/DelegationManager.sol";
import "../ITssGroupManager.sol";
import "../TssStakingSlashing.sol";
/**
* @title The primary entry- and exit-point for funds into and out.
* @notice This contract is for managing investments in different strategies. The main
* functionalities are:
* - adding and removing investment strategies that any delegator can invest into
* - enabling deposit of assets into specified investment delegation(s)
* - enabling removal of assets from specified investment delegation(s)
* - recording deposit of ETH into settlement layer
* - recording deposit for securing
* - slashing of assets for permissioned strategies
*/
contract TssDelegationManager is DelegationManager {
address public stakingSlash;
address public tssGroupManager;
uint256 public minStakeAmount;
/**
* @param _delegation The delegation contract.
* @param _delegationSlasher The primary slashing contract.
*/
constructor(IDelegation _delegation, IDelegationSlasher _delegationSlasher)
DelegationManager(_delegation, _delegationSlasher)
{
_disableInitializers();
}
function initializeT(
address _stakingSlashing,
address _tssGroupManager,
uint256 _minStakeAmount,
address initialOwner
) public initializer {
DOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, bytes("Mantle"), block.chainid, address(this)));
_transferOwnership(initialOwner);
stakingSlash = _stakingSlashing;
tssGroupManager = _tssGroupManager;
minStakeAmount = _minStakeAmount;
}
modifier onlyStakingSlash() {
require(msg.sender == stakingSlash, "contract call is not staking slashing");
_;
}
function setStakingSlash(address _address) public onlyOwner {
stakingSlash = _address;
}
function setMinStakeAmount(uint256 _amount) public onlyOwner {
minStakeAmount = _amount;
}
function setTssGroupManager(address _addr) public onlyOwner {
tssGroupManager = _addr;
}
/**
* @notice Slashes the shares of a 'frozen' operator (or a staker delegated to one)
* @param slashedAddress is the frozen address that is having its shares slashed
* @param delegationIndexes is a list of the indices in `investorStrats[msg.sender]` that correspond to the strategies
* for which `msg.sender` is withdrawing 100% of their shares
* @param recipient The slashed funds are withdrawn as tokens to this address.
* @dev delegationShares are removed from `investorStrats` by swapping the last entry with the entry to be removed, then
* popping off the last entry in `investorStrats`. The simplest way to calculate the correct `delegationIndexes` to input
* is to order the strategies *for which `msg.sender` is withdrawing 100% of their shares* from highest index in
* `investorStrats` to lowest index
*/
function slashShares(
address slashedAddress,
address recipient,
IDelegationShare[] calldata delegationShares,
IERC20[] calldata tokens,
uint256[] calldata delegationIndexes,
uint256[] calldata shareAmounts
)
external
override
whenNotPaused
onlyStakingSlash
nonReentrant
{
uint256 delegationIndex;
uint256 strategiesLength = delegationShares.length;
for (uint256 i = 0; i < strategiesLength;) {
// the internal function will return 'true' in the event the delegation contract was
// removed from the slashedAddress's array of strategies -- i.e. investorStrats[slashedAddress]
if (_removeShares(slashedAddress, delegationIndexes[delegationIndex], delegationShares[i], shareAmounts[i])) {
unchecked {
++delegationIndex;
}
}
// withdraw the shares and send funds to the recipient
delegationShares[i].withdraw(recipient, tokens[i], shareAmounts[i]);
// increment the loop
unchecked {
++i;
}
}
// modify delegated shares accordingly, if applicable
delegation.decreaseDelegatedShares(slashedAddress, delegationShares, shareAmounts);
}
function queueWithdrawal(
uint256[] calldata delegationIndexes,
IDelegationShare[] calldata delegationShares,
IERC20[] calldata tokens,
uint256[] calldata shares,
WithdrawerAndNonce calldata withdrawerAndNonce,
bool undelegateIfPossible
)
external
virtual
override
whenNotPaused
onlyNotFrozen(msg.sender)
nonReentrant
returns (bytes32)
{
revert("TssDelegationManager: queueWithdrawal is disabled ");
}
function isCanOperator(address _addr, IDelegationShare delegationShare) external returns (bool) {
if (delegation.isOperator(_addr)) {
uint256 share = delegation.operatorShares(_addr, delegationShare);
uint256 balance = delegationShare.sharesToUnderlying(share);
if (balance > minStakeAmount) {
return true;
}
}
return false;
}
function depositInto(IDelegationShare delegationShare, IERC20 token, uint256 amount, address sender)
external
onlyNotFrozen(sender)
nonReentrant
whitelistOnly(address(delegationShare))
onlyStakingSlash
returns (uint256 shares)
{
shares = _depositInto(sender, delegationShare, token, amount);
}
function queueWithdrawal(
address sender,
uint256[] calldata delegationIndexes,
IDelegationShare[] calldata delegationShares,
IERC20[] calldata tokens,
uint256[] calldata shares,
WithdrawerAndNonce calldata withdrawerAndNonce
)
external
whenNotPaused
onlyNotFrozen(sender)
onlyStakingSlash
nonReentrant
returns (bytes32)
{
require(
withdrawerAndNonce.nonce == numWithdrawalsQueued[sender],
"InvestmentManager.queueWithdrawal: provided nonce incorrect"
);
require(delegationShares.length == 1, "only tss delegation share");
require(shares.length == 1,"only tss delegation share");
// increment the numWithdrawalsQueued of the sender
unchecked {
++numWithdrawalsQueued[sender];
}
address operator = delegation.delegatedTo(sender);
_checkMinStakeAmount(sender, delegationShares[0], shares[0]);
// modify delegated shares accordingly, if applicable
delegation.decreaseDelegatedShares(sender, delegationShares, shares);
// the internal function will return 'true' in the event the delegation contrat was
// removed from the depositor's array of strategies -- i.e. investorStrats[depositor]
_removeShares(sender, delegationIndexes[0], delegationShares[0], shares[0]);
// copy arguments into struct and pull delegation info
QueuedWithdrawal memory queuedWithdrawal = QueuedWithdrawal({
delegations: delegationShares,
tokens: tokens,
shares: shares,
depositor: sender,
withdrawerAndNonce: withdrawerAndNonce,
delegatedAddress: operator
});
// calculate the withdrawal root
bytes32 withdrawalRoot = calculateWithdrawalRoot(queuedWithdrawal);
//update storage in mapping of queued withdrawals
queuedWithdrawals[withdrawalRoot] = WithdrawalStorage({
/**
* @dev We add `REASONABLE_STAKES_UPDATE_PERIOD` to the current time here to account for the fact that it may take some time for
* the operator's stake to be updated on all the middlewares. New tasks created between now at this 'initTimestamp' may still
* subject the `msg.sender` to slashing!
*/
initTimestamp: uint32(block.timestamp + REASONABLE_STAKES_UPDATE_PERIOD),
withdrawer: withdrawerAndNonce.withdrawer,
unlockTimestamp: QUEUED_WITHDRAWAL_INITIALIZED_VALUE
});
address staker = sender;
// If the `msg.sender` has withdrawn all of their funds in this transaction, then they can choose to also undelegate
/**
* Checking that `investorStrats[msg.sender].length == 0` is not strictly necessary here, but prevents reverting very late in logic,
* in the case that 'undelegate' is set to true but the `msg.sender` still has active deposits.
*/
if (investorDelegations[staker].length == 0) {
_undelegate(staker);
}
emit WithdrawalQueued(staker, withdrawerAndNonce.withdrawer, operator, withdrawalRoot);
return withdrawalRoot;
}
function startQueuedWithdrawalWaitingPeriod(bytes32 withdrawalRoot, address sender, uint32 stakeInactiveAfter) external onlyStakingSlash {
require(
queuedWithdrawals[withdrawalRoot].unlockTimestamp == QUEUED_WITHDRAWAL_INITIALIZED_VALUE,
"InvestmentManager.startQueuedWithdrawalWaitingPeriod: Withdrawal stake inactive claim has already been made"
);
require(
queuedWithdrawals[withdrawalRoot].withdrawer == sender,
"InvestmentManager.startQueuedWithdrawalWaitingPeriod: Sender is not the withdrawer"
);
require(
block.timestamp > queuedWithdrawals[withdrawalRoot].initTimestamp,
"InvestmentManager.startQueuedWithdrawalWaitingPeriod: Stake may still be subject to slashing based on new tasks. Wait to set stakeInactiveAfter."
);
//they can only unlock after a withdrawal waiting period or after they are claiming their stake is inactive
queuedWithdrawals[withdrawalRoot].unlockTimestamp = max((uint32(block.timestamp) + WITHDRAWAL_WAITING_PERIOD), stakeInactiveAfter);
}
function completeQueuedWithdrawal(address sender, QueuedWithdrawal calldata queuedWithdrawal, bool receiveAsTokens)
external
whenNotPaused
// check that the address that the staker *was delegated to* – at the time that they queued the withdrawal – is not frozen
onlyNotFrozen(queuedWithdrawal.delegatedAddress)
nonReentrant
onlyStakingSlash
{
// find the withdrawalRoot
bytes32 withdrawalRoot = calculateWithdrawalRoot(queuedWithdrawal);
// copy storage to memory
WithdrawalStorage memory withdrawalStorageCopy = queuedWithdrawals[withdrawalRoot];
// verify that the queued withdrawal actually exists
require(
withdrawalStorageCopy.unlockTimestamp != 0,
"InvestmentManager.completeQueuedWithdrawal: withdrawal does not exist"
);
require(
uint32(block.timestamp) >= withdrawalStorageCopy.unlockTimestamp
|| (queuedWithdrawal.delegatedAddress == address(0)),
"InvestmentManager.completeQueuedWithdrawal: withdrawal waiting period has not yet passed and depositor was delegated when withdrawal initiated"
);
// TODO: add testing coverage for this
require(
sender == queuedWithdrawal.withdrawerAndNonce.withdrawer,
"InvestmentManager.completeQueuedWithdrawal: only specified withdrawer can complete a queued withdrawal"
);
// reset the storage slot in mapping of queued withdrawals
delete queuedWithdrawals[withdrawalRoot];
// store length for gas savings
uint256 strategiesLength = queuedWithdrawal.delegations.length;
// if the withdrawer has flagged to receive the funds as tokens, withdraw from strategies
if (receiveAsTokens) {
// actually withdraw the funds
for (uint256 i = 0; i < strategiesLength;) {
// tell the delegation to send the appropriate amount of funds to the depositor
queuedWithdrawal.delegations[i].withdraw(
withdrawalStorageCopy.withdrawer, queuedWithdrawal.tokens[i], queuedWithdrawal.shares[i]
);
unchecked {
++i;
}
}
} else {
// else increase their shares
for (uint256 i = 0; i < strategiesLength;) {
_addShares(withdrawalStorageCopy.withdrawer, queuedWithdrawal.delegations[i], queuedWithdrawal.shares[i]);
unchecked {
++i;
}
}
}
emit WithdrawalCompleted(queuedWithdrawal.depositor, withdrawalStorageCopy.withdrawer, withdrawalRoot);
}
function getWithdrawNonce(address staker) external view onlyStakingSlash returns (uint256) {
return numWithdrawalsQueued[staker];
}
function getDelegationShares(address staker,IDelegationShare delegationShare) external view onlyStakingSlash returns (uint256) {
return investorDelegationShares[staker][delegationShare];
}
function _checkMinStakeAmount(address sender,IDelegationShare delegationShare, uint256 shares) internal {
address operator = delegation.delegatedTo(sender);
// check if the operator is still mpc node, if the remaining shares meet the mini requirement
if (delegation.isDelegated(sender)){
if (ITssGroupManager(tssGroupManager).memberExistActive(operator)){
require(!TssStakingSlashing(stakingSlash).isJailed(operator),"the operator is not in jail status");
uint256 rest= delegation.operatorShares(operator, delegationShare) - shares;
uint256 balance = delegationShare.sharesToUnderlying(rest);
if (ITssGroupManager(tssGroupManager).isTssGroupUnJailMembers(operator)) {
require(balance > minStakeAmount,"unable withdraw due to operator's rest shares smaller than mini requirement");
}
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "../../delegation/Delegation.sol";
import "../../delegation/WhiteListBase.sol";
/**
* @title The primary delegation contract.
* @notice This is the contract for delegation. The main functionalities of this contract are
* - for enabling any staker to register as a delegate and specify the delegation terms it has agreed to
* - for enabling anyone to register as an operator
* - for a registered staker to delegate its stake to the operator of its agreed upon delegation terms contract
* - for a staker to undelegate its assets
* - for anyone to challenge a staker's claim to have fulfilled all its obligation before undelegation
*/
contract TssDelegation is Delegation {
address public stakingSlash;
// INITIALIZING FUNCTIONS
constructor(IDelegationManager _delegationManager)
Delegation(_delegationManager)
{
_disableInitializers();
}
function initializeT(
address _stakingSlashing,
address initialOwner
) external initializer {
DOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, bytes("Mantle"), block.chainid, address(this)));
stakingSlash = _stakingSlashing;
_transferOwnership(initialOwner);
}
modifier onlyStakingSlash() {
require(msg.sender == stakingSlash, "contract call is not staking slashing");
_;
}
function setStakingSlash(address _address) public onlyOwner {
stakingSlash = _address;
}
function registerAsOperator(IDelegationCallback dt, address sender) external whitelistOnly(sender) onlyStakingSlash {
require(
address(delegationCallback[sender]) == address(0),
"Delegation.registerAsOperator: Delegate has already registered"
);
// store the address of the delegation contract that the operator is providing.
delegationCallback[sender] = dt;
_delegate(sender, sender);
emit RegisterOperator(address(dt), sender);
}
function delegateTo(address operator, address staker) external onlyStakingSlash whenNotPaused {
_delegate(staker, operator);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../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 anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @dev 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 v4.4.1 (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
import "../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() {
// On the first call to nonReentrant, _notEntered will be true
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_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
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
return uint128(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
return uint96(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
return uint64(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
return uint32(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits.
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128) {
require(value >= type(int128).min && value <= type(int128).max, "SafeCast: value doesn't fit in 128 bits");
return int128(value);
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64) {
require(value >= type(int64).min && value <= type(int64).max, "SafeCast: value doesn't fit in 64 bits");
return int64(value);
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32) {
require(value >= type(int32).min && value <= type(int32).max, "SafeCast: value doesn't fit in 32 bits");
return int32(value);
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16) {
require(value >= type(int16).min && value <= type(int16).max, "SafeCast: value doesn't fit in 16 bits");
return int16(value);
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits.
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8) {
require(value >= type(int8).min && value <= type(int8).max, "SafeCast: value doesn't fit in 8 bits");
return int8(value);
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
return int256(value);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
/**
* @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.7.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]
* ```
* 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. Equivalent to `reinitializer(1)`.
*/
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.
*
* `initializer` is equivalent to `reinitializer(1)`, so 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.
*
* 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.
*/
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.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized < type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title Minimal interface for an `IDelegationShares` contract.
* @notice Custom `DelegationShares` implementations may expand extensively on this interface.
*/
interface IDelegationShare {
/**
* @notice Used to deposit tokens into this DelegationShares
* @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 delegationManager contract. It is invoked inside of the delegationManager's
* `depositInto` function, and individual share balances are recorded in the delegationManager as well.
* @return newShares is the number of new shares issued at the current exchange ratio.
*/
function deposit(address depositor, IERC20 token, uint256 amount) external returns (uint256);
/**
* @notice Used to withdraw tokens from this DelegationLedger, to the `depositor`'s address
* @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 delegationManager contract. It is invoked inside of the delegationManager's
* other functions, and individual share balances are recorded in the delegationManager as well.
*/
function withdraw(address depositor, 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
* @dev Implementation for these functions in particular may vary signifcantly 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 ledger.
* @notice In contrast to `underlyingToSharesView`, this function **may** make state modifications
* @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into ledger shares
* @dev Implementation for these functions in particular may vary signifcantly for different ledgers
*/
function underlyingToShares(uint256 amountUnderlying) external view returns (uint256);
/**
* @notice convenience function for fetching the current underlying value of all of the `user`'s shares in
* this ledger. In contrast to `userUnderlyingView`, this function **may** make state modifications
*/
function userUnderlying(address user) external returns (uint256);
/**
* @notice Used to convert a number of shares to the equivalent amount of underlying tokens for this ledger.
* @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
* @dev Implementation for these functions in particular may vary signifcantly for different ledgers
*/
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 ledger.
* @notice In contrast to `underlyingToShares`, this function guarantees no state modifications
* @param amountUnderlying is the amount of `underlyingToken` to calculate its conversion into ledger shares
* @dev Implementation for these functions in particular may vary signifcantly for different ledgers
*/
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 ledger. In contrast to `userUnderlying`, this function guarantees no state modifications
*/
function userUnderlyingView(address user) external view returns (uint256);
/// @notice The underyling token for shares in this DelegationShares
function underlyingToken() external view returns (IERC20);
/// @notice The total number of extant shares in thie InvestmentStrategy
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: UNLICENSED
pragma solidity ^0.8.9;
/**
* @title Interface for the primary 'slashing' contract for EigenLayr.
* @author Layr Labs, Inc.
* @notice See the `Slasher` contract itself for implementation details.
*/
interface IDelegationSlasher {
/**
* @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 allowToSlash(address contractAddress) external;
/// @notice Called by a contract to revoke its ability to slash `operator`, once `unbondedAfter` is reached.
function revokeSlashingAbility(address operator, uint32 unbondedAfter) 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 `allowToSlash`.
*/
function freezeOperator(address toBeFrozen) external;
/**
* @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 investmentManager until the slashing process is completed
* and the staker's status is reset (to 'unfrozen').
* @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 UTC timestamp until which `slashingContract` is allowed to slash the `operator`.
function bondedUntil(address operator, address slashingContract) external view returns (uint32);
/**
* @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 Used to give global slashing permission to `contracts`.
* @dev Callable only by the contract owner (i.e. governance).
*/
function addGloballyPermissionedContracts(address[] calldata contracts) external;
/**
* @notice Used to revoke global slashing permission from `contracts`.
* @dev Callable only by the contract owner (i.e. governance).
*/
function removeGloballyPermissionedContracts(address[] calldata contracts) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "./IDelegationShare.sol";
/**
* @title Abstract interface for a contract that helps structure the delegation relationship.
* @notice The gas budget provided to this contract in calls from contracts is limited.
*/
//TODO: discuss if we can structure the inputs of these functions better
interface IDelegationCallback {
function payForService(IERC20 token, uint256 amount) external payable;
function onDelegationReceived(
address delegator,
address operator,
IDelegationShare[] memory delegationShares,
uint256[] memory investorShares
) external;
function onDelegationWithdrawn(
address delegator,
address operator,
IDelegationShare[] memory delegationShares,
uint256[] memory investorShares
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
import "../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;
}
/**
* @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.7.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
* ====
*
* [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://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
assembly {
size := extcodesize(account)
}
return size > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.9.0;
/**
* @title ICrossDomainMessenger
*/
interface ICrossDomainMessenger {
/**********
* Events *
**********/
event SentMessage(
address indexed target,
address sender,
bytes message,
uint256 messageNonce,
uint256 gasLimit
);
event RelayedMessage(bytes32 indexed msgHash);
event FailedRelayedMessage(bytes32 indexed msgHash);
/*************
* Variables *
*************/
function xDomainMessageSender() external view returns (address);
/********************
* Public Functions *
********************/
/**
* Sends a cross domain message to the target messenger.
* @param _target Target contract address.
* @param _message Message to send to the target.
* @param _gasLimit Gas limit for the provided message.
*/
function sendMessage(
address _target,
bytes calldata _message,
uint32 _gasLimit
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "./interfaces/IDelegation.sol";
import "./DelegationManagerStorage.sol";
import "./WhiteListBase.sol";
/**
* @title The primary entry- and exit-point for funds into and out.
* @author Layr Labs, Inc.
* @notice This contract is for managing investments in different strategies. The main
* functionalities are:
* - adding and removing investment strategies that any delegator can invest into
* - enabling deposit of assets into specified investment delegation(s)
* - enabling removal of assets from specified investment delegation(s)
* - recording deposit of ETH into settlement layer
* - recording deposit for securing
* - slashing of assets for permissioned strategies
*/
abstract contract DelegationManager is
Initializable,
OwnableUpgradeable,
PausableUpgradeable,
ReentrancyGuardUpgradeable,
DelegationManagerStorage,
WhiteList
{
using SafeERC20 for IERC20;
/**
* @notice Value to which `initTimestamp` and `unlockTimestamp` to is set to indicate a withdrawal is queued/initialized,
* but has not yet had its waiting period triggered
*/
uint32 internal constant QUEUED_WITHDRAWAL_INITIALIZED_VALUE = type(uint32).max;
/**
* @notice Emitted when a new withdrawal is queued by `depositor`.
* @param depositor Is the staker who is withdrawing funds.
* @param withdrawer Is the party specified by `staker` who will be able to complete the queued withdrawal and receive the withdrawn funds.
* @param delegatedAddress Is the party who the `staker` was delegated to at the time of creating the queued withdrawal
* @param withdrawalRoot Is a hash of the input data for the withdrawal.
*/
event WithdrawalQueued(
address indexed depositor, address indexed withdrawer, address indexed delegatedAddress, bytes32 withdrawalRoot
);
/// @notice Emitted when a queued withdrawal is completed
event WithdrawalCompleted(address indexed depositor, address indexed withdrawer, bytes32 withdrawalRoot);
modifier onlyNotFrozen(address staker) {
require(
!delegationSlasher.isFrozen(staker),
"DelegationManager.onlyNotFrozen: staker has been frozen and may be subject to slashing"
);
_;
}
modifier onlyFrozen(address staker) {
require(delegationSlasher.isFrozen(staker), "DelegationManager.onlyFrozen: staker has not been frozen");
_;
}
/**
* @param _delegation The delegation contract.
* @param _delegationSlasher The primary slashing contract.
*/
constructor(IDelegation _delegation, IDelegationSlasher _delegationSlasher)
DelegationManagerStorage(_delegation, _delegationSlasher)
{
_disableInitializers();
}
// EXTERNAL FUNCTIONS
/**
* @notice Initializes the investment manager contract. Sets the `pauserRegistry` (currently **not** modifiable after being set),
* and transfers contract ownership to the specified `initialOwner`.
* @param initialOwner Ownership of this contract is transferred to this address.
*/
function initialize(address initialOwner)
external
initializer
{
DOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, bytes("Mantle"), block.chainid, address(this)));
_transferOwnership(initialOwner);
}
/**
* @notice Deposits `amount` of `token` into the specified `delegationShare`, with the resultant shares credited to `depositor`
* @param delegationShare is the specified delegation contract where investment is to be made,
* @param token is the denomination in which the investment is to be made,
* @param amount is the amount of token to be invested in the delegation contract by the depositor
* @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).
*/
function depositInto(IDelegationShare delegationShare, IERC20 token, uint256 amount)
external
onlyNotFrozen(msg.sender)
nonReentrant
whitelistOnly(address(delegationShare))
returns (uint256 shares)
{
shares = _depositInto(msg.sender, delegationShare, token, amount);
}
/**
* @notice Called by a staker to undelegate entirely. The staker must first withdraw all of their existing deposits
* (through use of the `queueWithdrawal` function), or else otherwise have never deposited prior to delegating.
*/
function undelegate() external {
_undelegate(msg.sender);
}
/**
* @notice Called by a staker to queue a withdraw in the given token and shareAmount from each of the respective given strategies.
* @dev Stakers will complete their withdrawal by calling the 'completeQueuedWithdrawal' function.
* User shares are decreased in this function, but the total number of shares in each delegation contract remains the same.
* The total number of shares is decremented in the 'completeQueuedWithdrawal' function instead, which is where
* the funds are actually sent to the user through use of the strategies' 'withdrawal' function. This ensures
* that the value per share reported by each delegation contract will remain consistent, and that the shares will continue
* to accrue gains during the enforced WITHDRAWAL_WAITING_PERIOD.
* @param delegationIndexes is a list of the indices in `investorStrats[msg.sender]` that correspond to the strategies
* for which `msg.sender` is withdrawing 100% of their shares
* @dev strategies are removed from `investorStrats` by swapping the last entry with the entry to be removed, then
* popping off the last entry in `investorStrats`. The simplest way to calculate the correct `delegationIndexes` to input
* is to order the strategies *for which `msg.sender` is withdrawing 100% of their shares* from highest index in
* `investorStrats` to lowest index
*/
function queueWithdrawal(
uint256[] calldata delegationIndexes,
IDelegationShare[] calldata delegationShares,
IERC20[] calldata tokens,
uint256[] calldata shares,
WithdrawerAndNonce calldata withdrawerAndNonce,
bool undelegateIfPossible
)
external
virtual
whenNotPaused
onlyNotFrozen(msg.sender)
nonReentrant
returns (bytes32)
{
require(
withdrawerAndNonce.nonce == numWithdrawalsQueued[msg.sender],
"DelegationManager.queueWithdrawal: provided nonce incorrect"
);
// increment the numWithdrawalsQueued of the sender
unchecked {
++numWithdrawalsQueued[msg.sender];
}
uint256 delegationIndex;
// modify delegated shares accordingly, if applicable
delegation.decreaseDelegatedShares(msg.sender, delegationShares, shares);
for (uint256 i = 0; i < delegationShares.length;) {
// the internal function will return 'true' in the event the delegation contrat was
// removed from the depositor's array of strategies -- i.e. investorStrats[depositor]
if (_removeShares(msg.sender, delegationIndexes[delegationIndex], delegationShares[i], shares[i])) {
unchecked {
++delegationIndex;
}
}
//increment the loop
unchecked {
++i;
}
}
// fetch the address that the `msg.sender` is delegated to
address delegatedAddress = delegation.delegatedTo(msg.sender);
// copy arguments into struct and pull delegation info
QueuedWithdrawal memory queuedWithdrawal = QueuedWithdrawal({
delegations: delegationShares,
tokens: tokens,
shares: shares,
depositor: msg.sender,
withdrawerAndNonce: withdrawerAndNonce,
delegatedAddress: delegatedAddress
});
// calculate the withdrawal root
bytes32 withdrawalRoot = calculateWithdrawalRoot(queuedWithdrawal);
//update storage in mapping of queued withdrawals
queuedWithdrawals[withdrawalRoot] = WithdrawalStorage({
/**
* @dev We add `REASONABLE_STAKES_UPDATE_PERIOD` to the current time here to account for the fact that it may take some time for
* the operator's stake to be updated on all the middlewares. New tasks created between now at this 'initTimestamp' may still
* subject the `msg.sender` to slashing!
*/
initTimestamp: uint32(block.timestamp + REASONABLE_STAKES_UPDATE_PERIOD),
withdrawer: withdrawerAndNonce.withdrawer,
unlockTimestamp: QUEUED_WITHDRAWAL_INITIALIZED_VALUE
});
// If the `msg.sender` has withdrawn all of their funds in this transaction, then they can choose to also undelegate
/**
* Checking that `investorStrats[msg.sender].length == 0` is not strictly necessary here, but prevents reverting very late in logic,
* in the case that 'undelegate' is set to true but the `msg.sender` still has active deposits.
*/
if (undelegateIfPossible && investorDelegations[msg.sender].length == 0) {
_undelegate(msg.sender);
}
emit WithdrawalQueued(msg.sender, withdrawerAndNonce.withdrawer, delegatedAddress, withdrawalRoot);
return withdrawalRoot;
}
/*
*
* @notice The withdrawal flow is:
* - Depositor starts a queued withdrawal, setting the receiver of the withdrawn funds as withdrawer
* - Withdrawer then waits for the queued withdrawal tx to be included in the chain, and then sets the stakeInactiveAfter. This cannot
* be set when starting the queued withdrawal, as it is there may be transactions the increase the tasks upon which the stake is active
* that get mined before the withdrawal.
* - The withdrawer completes the queued withdrawal after the stake is inactive or a withdrawal fraud proof period has passed,
* whichever is longer. They specify whether they would like the withdrawal in shares or in tokens.
*/
function startQueuedWithdrawalWaitingPeriod(bytes32 withdrawalRoot, uint32 stakeInactiveAfter) external virtual {
require(
queuedWithdrawals[withdrawalRoot].unlockTimestamp == QUEUED_WITHDRAWAL_INITIALIZED_VALUE,
"DelegationManager.startQueuedWithdrawalWaitingPeriod: Withdrawal stake inactive claim has already been made"
);
require(
queuedWithdrawals[withdrawalRoot].withdrawer == msg.sender,
"DelegationManager.startQueuedWithdrawalWaitingPeriod: Sender is not the withdrawer"
);
require(
block.timestamp > queuedWithdrawals[withdrawalRoot].initTimestamp,
"DelegationManager.startQueuedWithdrawalWaitingPeriod: Stake may still be subject to slashing based on new tasks. Wait to set stakeInactiveAfter."
);
//they can only unlock after a withdrawal waiting period or after they are claiming their stake is inactive
queuedWithdrawals[withdrawalRoot].unlockTimestamp = max((uint32(block.timestamp) + WITHDRAWAL_WAITING_PERIOD), stakeInactiveAfter);
}
/**
* @notice Used to complete the specified `queuedWithdrawal`. The function caller must match `queuedWithdrawal.withdrawer`
* @param queuedWithdrawal The QueuedWithdrawal to complete.
* @param receiveAsTokens If true, the shares specified in the queued withdrawal will be withdrawn from the specified strategies themselves
* and sent to the caller, through calls to `queuedWithdrawal.delegations[i].withdraw`. If false, then the shares in the specified strategies
* will simply be transferred to the caller directly.
*/
function completeQueuedWithdrawal(QueuedWithdrawal calldata queuedWithdrawal, bool receiveAsTokens)
external
whenNotPaused
// check that the address that the staker *was delegated to* – at the time that they queued the withdrawal – is not frozen
onlyNotFrozen(queuedWithdrawal.delegatedAddress)
nonReentrant
{
// find the withdrawalRoot
bytes32 withdrawalRoot = calculateWithdrawalRoot(queuedWithdrawal);
// copy storage to memory
WithdrawalStorage memory withdrawalStorageCopy = queuedWithdrawals[withdrawalRoot];
// verify that the queued withdrawal actually exists
require(
withdrawalStorageCopy.unlockTimestamp != 0,
"DelegationManager.completeQueuedWithdrawal: withdrawal does not exist"
);
require(
uint32(block.timestamp) >= withdrawalStorageCopy.unlockTimestamp
|| (queuedWithdrawal.delegatedAddress == address(0)),
"DelegationManager.completeQueuedWithdrawal: withdrawal waiting period has not yet passed and depositor was delegated when withdrawal initiated"
);
// TODO: add testing coverage for this
require(
msg.sender == queuedWithdrawal.withdrawerAndNonce.withdrawer,
"DelegationManager.completeQueuedWithdrawal: only specified withdrawer can complete a queued withdrawal"
);
// reset the storage slot in mapping of queued withdrawals
delete queuedWithdrawals[withdrawalRoot];
// store length for gas savings
uint256 strategiesLength = queuedWithdrawal.delegations.length;
// if the withdrawer has flagged to receive the funds as tokens, withdraw from strategies
if (receiveAsTokens) {
// actually withdraw the funds
for (uint256 i = 0; i < strategiesLength;) {
// tell the delegation to send the appropriate amount of funds to the depositor
queuedWithdrawal.delegations[i].withdraw(
withdrawalStorageCopy.withdrawer, queuedWithdrawal.tokens[i], queuedWithdrawal.shares[i]
);
unchecked {
++i;
}
}
} else {
// else increase their shares
for (uint256 i = 0; i < strategiesLength;) {
_addShares(withdrawalStorageCopy.withdrawer, queuedWithdrawal.delegations[i], queuedWithdrawal.shares[i]);
unchecked {
++i;
}
}
}
emit WithdrawalCompleted(queuedWithdrawal.depositor, withdrawalStorageCopy.withdrawer, withdrawalRoot);
}
/**
* @notice Slashes the shares of a 'frozen' operator (or a staker delegated to one)
* @param slashedAddress is the frozen address that is having its shares slashed
* @param delegationIndexes is a list of the indices in `investorStrats[msg.sender]` that correspond to the strategies
* for which `msg.sender` is withdrawing 100% of their shares
* @param recipient The slashed funds are withdrawn as tokens to this address.
* @dev delegationShares are removed from `investorStrats` by swapping the last entry with the entry to be removed, then
* popping off the last entry in `investorStrats`. The simplest way to calculate the correct `delegationIndexes` to input
* is to order the strategies *for which `msg.sender` is withdrawing 100% of their shares* from highest index in
* `investorStrats` to lowest index
*/
function slashShares(
address slashedAddress,
address recipient,
IDelegationShare[] calldata delegationShares,
IERC20[] calldata tokens,
uint256[] calldata delegationIndexes,
uint256[] calldata shareAmounts
)
external
virtual
whenNotPaused
onlyOwner
onlyFrozen(slashedAddress)
nonReentrant
{
uint256 delegationIndex;
uint256 strategiesLength = delegationShares.length;
for (uint256 i = 0; i < strategiesLength;) {
// the internal function will return 'true' in the event the delegation contract was
// removed from the slashedAddress's array of strategies -- i.e. investorStrats[slashedAddress]
if (_removeShares(slashedAddress, delegationIndexes[delegationIndex], delegationShares[i], shareAmounts[i])) {
unchecked {
++delegationIndex;
}
}
// withdraw the shares and send funds to the recipient
delegationShares[i].withdraw(recipient, tokens[i], shareAmounts[i]);
// increment the loop
unchecked {
++i;
}
}
// modify delegated shares accordingly, if applicable
delegation.decreaseDelegatedShares(slashedAddress, delegationShares, shareAmounts);
}
/**
* @notice Slashes an existing queued withdrawal that was created by a 'frozen' operator (or a staker delegated to one)
* @param recipient The funds in the slashed withdrawal are withdrawn as tokens to this address.
*/
function slashQueuedWithdrawal(address recipient, QueuedWithdrawal calldata queuedWithdrawal)
external
whenNotPaused
onlyOwner
nonReentrant
{
// find the withdrawalRoot
bytes32 withdrawalRoot = calculateWithdrawalRoot(queuedWithdrawal);
// verify that the queued withdrawal actually exists
require(
queuedWithdrawals[withdrawalRoot].unlockTimestamp != 0,
"DelegationManager.slashQueuedWithdrawal: withdrawal does not exist"
);
// verify that *either* the queued withdrawal has been successfully challenged, *or* the `depositor` has been frozen
require(
queuedWithdrawals[withdrawalRoot].withdrawer == address(0) || delegationSlasher.isFrozen(queuedWithdrawal.depositor),
"DelegationManager.slashQueuedWithdrawal: withdrawal has not been successfully challenged or depositor is not frozen"
);
// reset the storage slot in mapping of queued withdrawals
delete queuedWithdrawals[withdrawalRoot];
uint256 strategiesLength = queuedWithdrawal.delegations.length;
for (uint256 i = 0; i < strategiesLength;) {
// tell the delegation contract to send the appropriate amount of funds to the recipient
queuedWithdrawal.delegations[i].withdraw(recipient, queuedWithdrawal.tokens[i], queuedWithdrawal.shares[i]);
unchecked {
++i;
}
}
}
// INTERNAL FUNCTIONS
/**
* @notice This function adds `shares` for a given `delegationShare` to the `depositor` and runs through the necessary update logic.
* @dev In particular, this function calls `delegation.increaseDelegatedShares(depositor, delegationShare, shares)` to ensure that all
* delegated shares are tracked, increases the stored share amount in `investorStratShares[depositor][delegationShare]`, and adds `delegationShare`
* to the `depositor`'s list of strategies, if it is not in the list already.
*/
function _addShares(address depositor, IDelegationShare delegationShare, uint256 shares) internal {
// sanity check on `shares` input
require(shares != 0, "DelegationManager._addShares: shares should not be zero!");
// if they dont have existing shares of this delegation contract, add it to their strats
if (investorDelegationShares[depositor][delegationShare] == 0) {
require(
investorDelegations[depositor].length <= MAX_INVESTOR_DELEGATION_LENGTH,
"DelegationManager._addShares: deposit would exceed MAX_INVESTOR_DELEGATION_LENGTH"
);
investorDelegations[depositor].push(delegationShare);
}
// add the returned shares to their existing shares for this delegation contract
investorDelegationShares[depositor][delegationShare] += shares;
// if applicable, increase delegated shares accordingly
delegation.increaseDelegatedShares(depositor, delegationShare, shares);
}
/**
* @notice Internal function in which `amount` of ERC20 `token` is transferred from `msg.sender` to the InvestmentDelegation-type contract
* `delegationShare`, with the resulting shares credited to `depositor`.
* @return shares The amount of *new* shares in `delegationShare` that have been credited to the `depositor`.
*/
function _depositInto(address depositor, IDelegationShare delegationShare, IERC20 token, uint256 amount)
internal
returns (uint256 shares)
{
// transfer tokens from the sender to the delegation contract
token.safeTransferFrom(depositor, address(delegationShare), amount);
// deposit the assets into the specified delegation contract and get the equivalent amount of shares in that delegation contract
shares = delegationShare.deposit(depositor, token, amount);
// add the returned shares to the depositor's existing shares for this delegation contract
_addShares(depositor, delegationShare, shares);
return shares;
}
/**
* @notice Decreases the shares that `depositor` holds in `delegationShare` by `shareAmount`.
* @dev If the amount of shares represents all of the depositor`s shares in said delegation contract,
* then the delegation contract is removed from investorStrats[depositor] and 'true' is returned. Otherwise 'false' is returned.
*/
function _removeShares(address depositor, uint256 delegationIndex, IDelegationShare delegationShare, uint256 shareAmount)
internal
returns (bool)
{
// sanity check on `shareAmount` input
require(shareAmount != 0, "DelegationManager._removeShares: shareAmount should not be zero!");
//check that the user has sufficient shares
uint256 userShares = investorDelegationShares[depositor][delegationShare];
require(shareAmount <= userShares, "DelegationManager._removeShares: shareAmount too high");
//unchecked arithmetic since we just checked this above
unchecked {
userShares = userShares - shareAmount;
}
// subtract the shares from the depositor's existing shares for this delegation contract
investorDelegationShares[depositor][delegationShare] = userShares;
// if no existing shares, remove is from this investors strats
if (userShares == 0) {
// remove the delegation contract from the depositor's dynamic array of strategies
_removeDelegationFromInvestorDelegations(depositor, delegationIndex, delegationShare);
// return true in the event that the delegation contract was removed from investorStrats[depositor]
return true;
}
// return false in the event that the delegation contract was *not* removed from investorStrats[depositor]
return false;
}
/**
* @notice Removes `delegationShare` from `depositor`'s dynamic array of strategies, i.e. from `investorStrats[depositor]`
* @dev the provided `delegationIndex` input is optimistically used to find the delegation contract quickly in the list. If the specified
* index is incorrect, then we revert to a brute-force search.
*/
function _removeDelegationFromInvestorDelegations(address depositor, uint256 delegationIndex, IDelegationShare delegationShare) internal {
// if the delegation contract matches with the delegation contract index provided
if (investorDelegations[depositor][delegationIndex] == delegationShare) {
// replace the delegation contract with the last delegation contract in the list
investorDelegations[depositor][delegationIndex] =
investorDelegations[depositor][investorDelegations[depositor].length - 1];
} else {
//loop through all of the strategies, find the right one, then replace
uint256 delegationLength = investorDelegations[depositor].length;
for (uint256 j = 0; j < delegationLength;) {
if (investorDelegations[depositor][j] == delegationShare) {
//replace the delegation contract with the last delegation contract in the list
investorDelegations[depositor][j] = investorDelegations[depositor][investorDelegations[depositor].length - 1];
break;
}
unchecked {
++j;
}
}
}
// pop off the last entry in the list of strategies
investorDelegations[depositor].pop();
}
/**
* @notice If the `depositor` has no existing shares, then they can `undelegate` themselves.
* This allows people a "hard reset" in their relationship after withdrawing all of their stake.
*/
function _undelegate(address depositor) internal {
require(investorDelegations[depositor].length == 0, "InvestmentManager._undelegate: depositor has active deposits");
delegation.undelegate(depositor);
}
function max(uint32 x, uint32 y) internal pure returns (uint32) {
return x > y ? x : y;
}
// VIEW FUNCTIONS
/**
* @notice Used to check if a queued withdrawal can be completed. Returns 'true' if the withdrawal can be immediately
* completed, and 'false' otherwise.
* @dev This function will revert if the specified `queuedWithdrawal` does not exist
*/
function canCompleteQueuedWithdrawal(QueuedWithdrawal calldata queuedWithdrawal) external view returns (bool) {
// find the withdrawalRoot
bytes32 withdrawalRoot = calculateWithdrawalRoot(queuedWithdrawal);
// verify that the queued withdrawal actually exists
require(
queuedWithdrawals[withdrawalRoot].unlockTimestamp != 0,
"DelegationManager.canCompleteQueuedWithdrawal: withdrawal does not exist"
);
if (delegationSlasher.isFrozen(queuedWithdrawal.delegatedAddress)) {
return false;
}
return (
uint32(block.timestamp) >= queuedWithdrawals[withdrawalRoot].unlockTimestamp
|| (queuedWithdrawal.delegatedAddress == address(0))
);
}
/**
* @notice Get all details on the depositor's investments and corresponding shares
* @return (depositor's strategies, shares in these strategies)
*/
function getDeposits(address depositor) external view returns (IDelegationShare[] memory, uint256[] memory) {
uint256 delegationLength = investorDelegations[depositor].length;
uint256[] memory shares = new uint256[](delegationLength);
for (uint256 i = 0; i < delegationLength;) {
shares[i] = investorDelegationShares[depositor][investorDelegations[depositor][i]];
unchecked {
++i;
}
}
return (investorDelegations[depositor], shares);
}
/// @notice Simple getter function that returns `investorStrats[staker].length`.
function investorDelegationLength(address staker) external view returns (uint256) {
return investorDelegations[staker].length;
}
/// @notice Returns the keccak256 hash of `queuedWithdrawal`.
function calculateWithdrawalRoot(QueuedWithdrawal memory queuedWithdrawal) public pure returns (bytes32) {
return (
keccak256(
abi.encode(
queuedWithdrawal.delegations,
queuedWithdrawal.tokens,
queuedWithdrawal.shares,
queuedWithdrawal.depositor,
queuedWithdrawal.withdrawerAndNonce,
queuedWithdrawal.delegatedAddress
)
)
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "./interfaces/IDelegationManager.sol";
import "./interfaces/IDelegationShare.sol";
import "./interfaces/IDelegation.sol";
import "./interfaces/IDelegationSlasher.sol";
/**
* @title Storage variables for the `InvestmentManager` contract.
* @author Layr Labs, Inc.
* @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 deposit struct used by the contract
bytes32 public constant DEPOSIT_TYPEHASH =
keccak256("Deposit(address strategy,address token,uint256 amount,uint256 nonce,uint256 expiry)");
/// @notice EIP-712 Domain separator
bytes32 public DOMAIN_SEPARATOR;
// staker => number of signed deposit nonce (used in depositIntoStrategyOnBehalfOf)
mapping(address => uint256) public nonces;
/**
* @notice When a staker undelegates or an operator deregisters, their stake can still be slashed based on tasks/services created
* within `REASONABLE_STAKES_UPDATE_PERIOD` of the present moment. In other words, this is the lag between undelegation/deregistration
* and the staker's/operator's funds no longer being slashable due to misbehavior *on a new task*.
*/
uint256 public constant REASONABLE_STAKES_UPDATE_PERIOD = 30 seconds;
// fixed waiting period for withdrawals
// TODO: set this to a proper interval for production
uint32 public constant WITHDRAWAL_WAITING_PERIOD = 10 seconds;
// maximum length of dynamic arrays in `investorStrats` mapping, for sanity's sake
uint8 internal constant MAX_INVESTOR_DELEGATION_LENGTH = 32;
// delegation system contracts
IDelegation public immutable delegation;
IDelegationSlasher public immutable delegationSlasher;
// staker => IDelegationShare => number of shares which they currently hold
mapping(address => mapping(IDelegationShare => uint256)) public investorDelegationShares;
// staker => array of DelegationShare in which they have nonzero shares
mapping(address => IDelegationShare[]) public investorDelegations;
// hash of withdrawal inputs, aka 'withdrawalRoot' => timestamps & address related to the withdrawal
mapping(bytes32 => WithdrawalStorage) public queuedWithdrawals;
// staker => cumulative number of queued withdrawals they have ever initiated. only increments (doesn't decrement)
mapping(address => uint256) public numWithdrawalsQueued;
constructor(IDelegation _delegation, IDelegationSlasher _delegationSlasher) {
delegation = _delegation;
delegationSlasher = _delegationSlasher;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
} else if (error == RecoverError.InvalidSignatureV) {
revert("ECDSA: invalid signature 'v' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
// Check the signature length
// - case 65: r,s,v signature (standard)
// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else if (signature.length == 64) {
bytes32 r;
bytes32 vs;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
assembly {
r := mload(add(signature, 0x20))
vs := mload(add(signature, 0x40))
}
return tryRecover(hash, r, vs);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address, RecoverError) {
bytes32 s;
uint8 v;
assembly {
s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
v := add(shr(255, vs), 27)
}
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(
bytes32 hash,
bytes32 r,
bytes32 vs
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
if (v != 27 && v != 28) {
return (address(0), RecoverError.InvalidSignatureV);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC1155 compliant contract, as defined in the
* https://eips.ethereum.org/EIPS/eip-1155[EIP].
*
* _Available since v3.1._
*/
interface IERC1155 is IERC165 {
/**
* @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`.
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
* @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all
* transfers.
*/
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/**
* @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to
* `approved`.
*/
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
/**
* @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI.
*
* If an {URI} event was emitted for `id`, the standard
* https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value
* returned by {IERC1155MetadataURI-uri}.
*/
event URI(string value, uint256 indexed id);
/**
* @dev Returns the amount of tokens of token type `id` owned by `account`.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}.
*
* Requirements:
*
* - `accounts` and `ids` must have the same length.
*/
function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
external
view
returns (uint256[] memory);
/**
* @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`,
*
* Emits an {ApprovalForAll} event.
*
* Requirements:
*
* - `operator` cannot be the caller.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns true if `operator` is approved to transfer ``account``'s tokens.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
* @dev Transfers `amount` tokens of token type `id` from `from` to `to`.
*
* Emits a {TransferSingle} event.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}.
* - `from` must have a balance of tokens of type `id` of at least `amount`.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the
* acceptance magic value.
*/
function safeTransferFrom(
address from,
address to,
uint256 id,
uint256 amount,
bytes calldata data
) external;
/**
* @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}.
*
* Emits a {TransferBatch} event.
*
* Requirements:
*
* - `ids` and `amounts` must have the same length.
* - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the
* acceptance magic value.
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytes calldata data
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "./DelegationStorage.sol";
import "./DelegationSlasher.sol";
import "./WhiteListBase.sol";
/**
* @title The primary delegation contract.
* @notice This is the contract for delegation. The main functionalities of this contract are
* - for enabling any staker to register as a delegate and specify the delegation terms it has agreed to
* - for enabling anyone to register as an operator
* - for a registered staker to delegate its stake to the operator of its agreed upon delegation terms contract
* - for a staker to undelegate its assets
* - for anyone to challenge a staker's claim to have fulfilled all its obligation before undelegation
*/
abstract contract Delegation is Initializable, OwnableUpgradeable, PausableUpgradeable, WhiteList, DelegationStorage {
/// @notice Simple permission for functions that are only callable by the InvestmentManager contract.
modifier onlyDelegationManager() {
require(msg.sender == address(delegationManager), "onlyDelegationManager");
_;
}
// INITIALIZING FUNCTIONS
constructor(IDelegationManager _delegationManager)
DelegationStorage(_delegationManager)
{
_disableInitializers();
}
/// @dev Emitted when a low-level call to `delegationTerms.onDelegationReceived` fails, returning `returnData`
event OnDelegationReceivedCallFailure(IDelegationCallback indexed delegationTerms, bytes32 returnData);
/// @dev Emitted when a low-level call to `delegationTerms.onDelegationWithdrawn` fails, returning `returnData`
event OnDelegationWithdrawnCallFailure(IDelegationCallback indexed delegationTerms, bytes32 returnData);
event RegisterOperator(address delegationCallback, address register);
event DelegateTo(address delegatior, address operator);
event DecreaseDelegatedShares(address delegatedShare, address operator, uint256 share);
event IncreaseDelegatedShares(address delegatedShare, address operator, uint256 share);
function initialize(address initialOwner)
external
initializer
{
DOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, bytes("Mantle"), block.chainid, address(this)));
_transferOwnership(initialOwner);
}
// PERMISSION FUNCTIONS
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
// EXTERNAL FUNCTIONS
/**
* @notice This will be called by an operator to register itself as an operator that stakers can choose to delegate to.
* @param dt is the `DelegationTerms` contract that the operator has for those who delegate to them.
* @dev An operator can set `dt` equal to their own address (or another EOA address), in the event that they want to split payments
* in a more 'trustful' manner.
* @dev In the present design, once set, there is no way for an operator to ever modify the address of their DelegationTerms contract.
*/
function registerAsOperator(IDelegationCallback dt) external whitelistOnly(msg.sender) {
require(
address(delegationCallback[msg.sender]) == address(0),
"Delegation.registerAsOperator: Delegate has already registered"
);
// store the address of the delegation contract that the operator is providing.
delegationCallback[msg.sender] = dt;
_delegate(msg.sender, msg.sender);
emit RegisterOperator(address(dt),msg.sender);
}
/**
* @notice This will be called by a staker to delegate its assets to some operator.
* @param operator is the operator to whom staker (msg.sender) is delegating its assets
*/
function delegateTo(address operator) external whenNotPaused {
_delegate(msg.sender, operator);
}
/**
* @notice Delegates from `staker` to `operator`.
* @dev requires that r, vs are a valid ECSDA signature from `staker` indicating their intention for this action
*/
function delegateToSignature(address staker, address operator, uint256 expiry, bytes32 r, bytes32 vs)
external
whenNotPaused
{
require(expiry == 0 || expiry >= block.timestamp, "delegation signature expired");
// calculate struct hash, then increment `staker`'s nonce
// EIP-712 standard
bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, staker, operator, nonces[staker]++, expiry));
bytes32 digestHash = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, structHash));
//check validity of signature
address recoveredAddress = ECDSA.recover(digestHash, r, vs);
require(recoveredAddress == staker, "Delegation.delegateToBySignature: sig not from staker");
_delegate(staker, operator);
}
/**
* @notice Undelegates `staker` from the operator who they are delegated to.
* @notice Callable only by the InvestmentManager
* @dev Should only ever be called in the event that the `staker` has no active deposits.
*/
function undelegate(address staker) external onlyDelegationManager {
delegationStatus[staker] = DelegationStatus.UNDELEGATED;
delegatedTo[staker] = address(0);
}
/**
* @notice Increases the `staker`'s delegated shares in `strategy` by `shares, typically called when the staker has further deposits
* @dev Callable only by the InvestmentManager
*/
function increaseDelegatedShares(address staker, IDelegationShare delegationShare, uint256 shares)
external
onlyDelegationManager
{
//if the staker is delegated to an operator
if (isDelegated(staker)) {
address operator = delegatedTo[staker];
// add strategy shares to delegate's shares
operatorShares[operator][delegationShare] += shares;
//Calls into operator's delegationTerms contract to update weights of individual staker
IDelegationShare[] memory investorDelegations = new IDelegationShare[](1);
uint256[] memory investorShares = new uint[](1);
investorDelegations[0] = delegationShare;
investorShares[0] = shares;
// call into hook in delegationCallback contract
IDelegationCallback dt = delegationCallback[operator];
_delegationReceivedHook(dt, staker, operator, investorDelegations, investorShares);
emit IncreaseDelegatedShares(address(delegationShare), operator, shares);
}
}
/**
* @notice Decreases the `staker`'s delegated shares in `strategy` by `shares, typically called when the staker withdraws
* @dev Callable only by the InvestmentManager
*/
function decreaseDelegatedShares(address staker, IDelegationShare delegationShare, uint256 shares)
external
onlyDelegationManager
{
//if the staker is delegated to an operator
if (isDelegated(staker)) {
address operator = delegatedTo[staker];
// subtract strategy shares from delegate's shares
operatorShares[operator][delegationShare] -= shares;
//Calls into operator's delegationCallback contract to update weights of individual staker
IDelegationShare[] memory investorDelegationShares = new IDelegationShare[](1);
uint256[] memory investorShares = new uint[](1);
investorDelegationShares[0] = delegationShare;
investorShares[0] = shares;
// call into hook in delegationCallback contract
IDelegationCallback dt = delegationCallback[operator];
_delegationWithdrawnHook(dt, staker, operator, investorDelegationShares, investorShares);
emit DecreaseDelegatedShares(address(delegationShare), operator, shares);
}
}
/// @notice Version of `decreaseDelegatedShares` that accepts an array of inputs.
function decreaseDelegatedShares(
address staker,
IDelegationShare[] calldata strategies,
uint256[] calldata shares
)
external
onlyDelegationManager
{
if (isDelegated(staker)) {
address operator = delegatedTo[staker];
// subtract strategy shares from delegate's shares
uint256 stratsLength = strategies.length;
for (uint256 i = 0; i < stratsLength;) {
operatorShares[operator][strategies[i]] -= shares[i];
emit DecreaseDelegatedShares(address(strategies[i]), operator, shares[i]);
unchecked {
++i;
}
}
// call into hook in delegationCallback contract
IDelegationCallback dt = delegationCallback[operator];
_delegationWithdrawnHook(dt, staker, operator, strategies, shares);
}
}
// INTERNAL FUNCTIONS
/**
* @notice Makes a low-level call to `dt.onDelegationReceived(staker, strategies, shares)`, ignoring reverts and with a gas budget
* equal to `LOW_LEVEL_GAS_BUDGET` (a constant defined in this contract).
* @dev *If* the low-level call fails, then this function emits the event `OnDelegationReceivedCallFailure(dt, returnData)`, where
* `returnData` is *only the first 32 bytes* returned by the call to `dt`.
*/
function _delegationReceivedHook(
IDelegationCallback dt,
address staker,
address operator,
IDelegationShare[] memory delegationShares,
uint256[] memory shares
)
internal
{
/**
* We use low-level call functionality here to ensure that an operator cannot maliciously make this function fail in order to prevent undelegation.
* In particular, in-line assembly is also used to prevent the copying of uncapped return data which is also a potential DoS vector.
*/
// format calldata
(bool success, bytes memory returnData) = address(dt).call{gas: LOW_LEVEL_GAS_BUDGET}(
abi.encodeWithSelector(IDelegationCallback.onDelegationReceived.selector, staker, operator, delegationShares, shares)
);
// if the call fails, we emit a special event rather than reverting
if (!success) {
emit OnDelegationReceivedCallFailure(dt, returnData[0]);
}
}
/**
* @notice Makes a low-level call to `dt.onDelegationWithdrawn(staker, strategies, shares)`, ignoring reverts and with a gas budget
* equal to `LOW_LEVEL_GAS_BUDGET` (a constant defined in this contract).
* @dev *If* the low-level call fails, then this function emits the event `OnDelegationReceivedCallFailure(dt, returnData)`, where
* `returnData` is *only the first 32 bytes* returned by the call to `dt`.
*/
function _delegationWithdrawnHook(
IDelegationCallback dt,
address staker,
address operator,
IDelegationShare[] memory delegationShares,
uint256[] memory shares
)
internal
{
/**
* We use low-level call functionality here to ensure that an operator cannot maliciously make this function fail in order to prevent undelegation.
* In particular, in-line assembly is also used to prevent the copying of uncapped return data which is also a potential DoS vector.
*/
(bool success, bytes memory returnData) = address(dt).call{gas: LOW_LEVEL_GAS_BUDGET}(
abi.encodeWithSelector(IDelegationCallback.onDelegationWithdrawn.selector, staker, operator, delegationShares, shares)
);
// if the call fails, we emit a special event rather than reverting
if (!success) {
emit OnDelegationWithdrawnCallFailure(dt, returnData[0]);
}
}
/**
* @notice Internal function implementing the delegation *from* `staker` *to* `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
* @dev Ensures that the operator has registered as a delegate (`address(dt) != address(0)`), verifies that `staker` is not already
* delegated, and records the new delegation.
*/
function _delegate(address staker, address operator) internal {
IDelegationCallback dt = delegationCallback[operator];
require(
address(dt) != address(0), "Delegation._delegate: operator has not yet registered as a delegate"
);
require(isNotDelegated(staker), "Delegation._delegate: staker has existing delegation");
// checks that operator has not been frozen
IDelegationSlasher slasher = delegationManager.delegationSlasher();
require(!slasher.isFrozen(operator), "Delegation._delegate: cannot delegate to a frozen operator");
// record delegation relation between the staker and operator
delegatedTo[staker] = operator;
// record that the staker is delegated
delegationStatus[staker] = DelegationStatus.DELEGATED;
// retrieve list of strategies and their shares from investment manager
(IDelegationShare[] memory delegationShares, uint256[] memory shares) = delegationManager.getDeposits(staker);
// add strategy shares to delegate's shares
uint256 delegationLength = delegationShares.length;
for (uint256 i = 0; i < delegationLength;) {
// update the share amounts for each of the operator's strategies
operatorShares[operator][delegationShares[i]] += shares[i];
unchecked {
++i;
}
}
// call into hook in delegationCallback contract
_delegationReceivedHook(dt, staker, operator, delegationShares, shares);
emit DelegateTo(staker, operator);
}
// VIEW FUNCTIONS
/// @notice Returns 'true' if `staker` *is* actively delegated, and 'false' otherwise.
function isDelegated(address staker) public view returns (bool) {
return (delegationStatus[staker] == DelegationStatus.DELEGATED);
}
/// @notice Returns 'true' if `staker` is *not* actively delegated, and 'false' otherwise.
function isNotDelegated(address staker) public view returns (bool) {
return (delegationStatus[staker] == DelegationStatus.UNDELEGATED);
}
/// @notice Returns if an operator can be delegated to, i.e. it has called `registerAsOperator`.
function isOperator(address operator) external view returns (bool) {
return (address(delegationCallback[operator]) != address(0));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "./interfaces/IDelegationSlasher.sol";
import "./interfaces/IDelegation.sol";
import "./interfaces/IDelegationManager.sol";
/**
* @title The primary 'slashing' contract.
* @notice This contract specifies details on slashing. The functionalities are:
* - adding contracts who have permission to perform slashing,
* - revoking permission for slashing from specified contracts,
* - calling investManager to do actual slashing.
*/
abstract contract DelegationSlasher is Initializable, OwnableUpgradeable, PausableUpgradeable, IDelegationSlasher {
// ,DSTest
/// @notice The central InvestmentManager contract
IDelegationManager public immutable investmentManager;
/// @notice The Delegation contract
IDelegation public immutable delegation;
// contract address => whether or not the contract is allowed to slash any staker (or operator)
mapping(address => bool) public globallyPermissionedContracts;
// user => contract => the time before which the contract is allowed to slash the user
mapping(address => mapping(address => uint32)) public bondedUntil;
// staker => if their funds are 'frozen' and potentially subject to slashing or not
mapping(address => bool) internal frozenStatus;
uint32 internal constant MAX_BONDED_UNTIL = type(uint32).max;
event GloballyPermissionedContractAdded(address indexed contractAdded);
event GloballyPermissionedContractRemoved(address indexed contractRemoved);
event OptedIntoSlashing(address indexed operator, address indexed contractAddress);
event SlashingAbilityRevoked(address indexed operator, address indexed contractAddress, uint32 unbondedAfter);
event OperatorSlashed(address indexed slashedOperator, address indexed slashingContract);
event FrozenStatusReset(address indexed previouslySlashedAddress);
constructor(IDelegationManager _investmentManager, IDelegation _delegation) {
investmentManager = _investmentManager;
delegation = _delegation;
_disableInitializers();
}
// EXTERNAL FUNCTIONS
function initialize(
address initialOwner
) external initializer {
_transferOwnership(initialOwner);
// add InvestmentManager & Delegation to list of permissioned contracts
_addGloballyPermissionedContract(address(investmentManager));
_addGloballyPermissionedContract(address(delegation));
}
/**
* @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 allowToSlash(address contractAddress) external {
_optIntoSlashing(msg.sender, contractAddress);
}
/*
TODO: we still need to figure out how/when to appropriately call this function
perhaps a registry can safely call this function after an operator has been deregistered for a very safe amount of time (like a month)
*/
/// @notice Called by a contract to revoke its ability to slash `operator`, once `unbondedAfter` is reached.
function revokeSlashingAbility(address operator, uint32 unbondedAfter) external {
_revokeSlashingAbility(operator, msg.sender, unbondedAfter);
}
/**
* @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 `allowToSlash`.
*/
function freezeOperator(address toBeFrozen) external whenNotPaused {
require(
canSlash(toBeFrozen, msg.sender),
"Slasher.freezeOperator: msg.sender does not have permission to slash this operator"
);
_freezeOperator(toBeFrozen, msg.sender);
}
/**
* @notice Used to give global slashing permission to `contracts`.
* @dev Callable only by the contract owner (i.e. governance).
*/
function addGloballyPermissionedContracts(address[] calldata contracts) external onlyOwner {
for (uint256 i = 0; i < contracts.length;) {
_addGloballyPermissionedContract(contracts[i]);
unchecked {
++i;
}
}
}
/**
* @notice Used to revoke global slashing permission from `contracts`.
* @dev Callable only by the contract owner (i.e. governance).
*/
function removeGloballyPermissionedContracts(address[] calldata contracts) external onlyOwner {
for (uint256 i = 0; i < contracts.length;) {
_removeGloballyPermissionedContract(contracts[i]);
unchecked {
++i;
}
}
}
/**
* @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 onlyOwner {
for (uint256 i = 0; i < frozenAddresses.length;) {
_resetFrozenStatus(frozenAddresses[i]);
unchecked {
++i;
}
}
}
// INTERNAL FUNCTIONS
function _optIntoSlashing(address operator, address contractAddress) internal {
//allow the contract to slash anytime before a time VERY far in the future
bondedUntil[operator][contractAddress] = MAX_BONDED_UNTIL;
emit OptedIntoSlashing(operator, contractAddress);
}
function _revokeSlashingAbility(address operator, address contractAddress, uint32 unbondedAfter) internal {
if (bondedUntil[operator][contractAddress] == MAX_BONDED_UNTIL) {
//contractAddress can now only slash operator before unbondedAfter
bondedUntil[operator][contractAddress] = unbondedAfter;
emit SlashingAbilityRevoked(operator, contractAddress, unbondedAfter);
}
}
function _addGloballyPermissionedContract(address contractToAdd) internal {
if (!globallyPermissionedContracts[contractToAdd]) {
globallyPermissionedContracts[contractToAdd] = true;
emit GloballyPermissionedContractAdded(contractToAdd);
}
}
function _removeGloballyPermissionedContract(address contractToRemove) internal {
if (globallyPermissionedContracts[contractToRemove]) {
globallyPermissionedContracts[contractToRemove] = false;
emit GloballyPermissionedContractRemoved(contractToRemove);
}
}
function _freezeOperator(address toBeFrozen, address slashingContract) internal {
if (!frozenStatus[toBeFrozen]) {
frozenStatus[toBeFrozen] = true;
emit OperatorSlashed(toBeFrozen, slashingContract);
}
}
function _resetFrozenStatus(address previouslySlashedAddress) internal {
if (frozenStatus[previouslySlashedAddress]) {
frozenStatus[previouslySlashedAddress] = false;
emit FrozenStatusReset(previouslySlashedAddress);
}
}
// VIEW FUNCTIONS
/**
* @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 investmentManager until the slashing process is completed
* and the staker's status is reset (to 'unfrozen').
* @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) {
if (frozenStatus[staker]) {
return true;
} else if (delegation.isDelegated(staker)) {
address operatorAddress = delegation.delegatedTo(staker);
return (frozenStatus[operatorAddress]);
} else {
return false;
}
}
/// @notice Returns true if `slashingContract` is currently allowed to slash `toBeSlashed`.
function canSlash(address toBeSlashed, address slashingContract) public view returns (bool) {
if (globallyPermissionedContracts[slashingContract]) {
return true;
} else if (block.timestamp < bondedUntil[toBeSlashed][slashingContract]) {
return true;
} else {
return false;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "./interfaces/IDelegationManager.sol";
import "./interfaces/IDelegationCallback.sol";
import "./interfaces/IDelegation.sol";
/**
* @title Storage variables for the `Delegation` contract.
* @author Layr Labs, Inc.
* @notice This storage contract is separate from the logic to simplify the upgrade process.
*/
abstract contract DelegationStorage is IDelegation {
/// @notice Gas budget provided in calls to DelegationTerms contracts
uint256 internal constant LOW_LEVEL_GAS_BUDGET = 1e5;
/// @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 delegation struct used by the contract
bytes32 public constant DELEGATION_TYPEHASH =
keccak256("Delegation(address delegator,address operator,uint256 nonce,uint256 expiry)");
/// @notice EIP-712 Domain separator
bytes32 public DOMAIN_SEPARATOR;
/// @notice The InvestmentManager contract
IDelegationManager public immutable delegationManager;
// operator => investment strategy => total number of shares delegated to them
mapping(address => mapping(IDelegationShare => uint256)) public operatorShares;
// operator => delegation terms contract
mapping(address => IDelegationCallback) public delegationCallback;
// staker => operator
mapping(address => address) public delegatedTo;
// staker => whether they are delegated or not
mapping(address => IDelegation.DelegationStatus) public delegationStatus;
// delegator => number of signed delegation nonce (used in delegateToBySignature)
mapping(address => uint256) public nonces;
constructor(IDelegationManager _investmentManager) {
delegationManager = _investmentManager;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../utils/Context.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 Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_setOwner(_msgSender());
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_setOwner(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");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"","type":"address"},{"indexed":false,"internalType":"address","name":"","type":"address"},{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"Defund","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"depositor","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","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":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"","type":"address"},{"indexed":false,"internalType":"enum TssStakingSlashing.SlashType","name":"","type":"uint8"}],"name":"Slashing","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"depositor","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"","type":"address"},{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"WithdrawQueue","type":"event"},{"inputs":[],"name":"DEFUND_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canCompleteQueuedWithdrawal","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimerOperators","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"clearQuitRequestList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"completeWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defund","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"}],"name":"delegateTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"delegation","outputs":[{"internalType":"contract IDelegation","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delegationManager","outputs":[{"internalType":"contract IDelegationManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"delegators","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"depositor","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"newShares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"explanation","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getQuitRequestList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"batchIndex","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"getSlashRecord","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSlashingParams","outputs":[{"internalType":"uint256[2]","name":"","type":"uint256[2]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_mantleToken","type":"address"},{"internalType":"address","name":"_tssGroupContract","type":"address"},{"internalType":"address","name":"_delegationManager","type":"address"},{"internalType":"address","name":"_delegation","type":"address"},{"internalType":"address","name":"_l1messenger","type":"address"},{"internalType":"address","name":"_regulatoryAccount","type":"address"},{"internalType":"address","name":"_tssManager","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"name":"isCanOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"isJailed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isSetParam","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"messenger","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"contract IDelegationShare[]","name":"investorDelegationShares","type":"address[]"},{"internalType":"uint256[]","name":"investorShares","type":"uint256[]"}],"name":"onDelegationReceived","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"contract IDelegationShare[]","name":"delegationShares","type":"address[]"},{"internalType":"uint256[]","name":"investorShares","type":"uint256[]"}],"name":"onDelegationReceived","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"contract IDelegationShare[]","name":"delegationShares","type":"address[]"},{"internalType":"uint256[]","name":"investorShares","type":"uint256[]"}],"name":"onDelegationWithdrawn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegator","type":"address"},{"internalType":"contract IDelegationShare[]","name":"investorDelegationShares","type":"address[]"},{"internalType":"uint256[]","name":"investorShares","type":"uint256[]"}],"name":"onDelegationWithdrawn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"operatorClaimers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"operators","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"payForService","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"quitRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"quitRequestList","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_pubKey","type":"bytes"}],"name":"registerAsOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"regulatoryAccount","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"address","name":"_claimer","type":"address"}],"name":"setClaimer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"setRegulatoryAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[2]","name":"_slashAmount","type":"uint256[2]"}],"name":"setSlashingParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"setTokenAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tssGroup","type":"address"}],"name":"setTssGroupAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tssManager","type":"address"}],"name":"setTssManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"shares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountShares","type":"uint256"}],"name":"sharesToUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountShares","type":"uint256"}],"name":"sharesToUnderlyingView","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"slashAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_messageBytes","type":"bytes"},{"internalType":"bytes","name":"_sig","type":"bytes"}],"name":"slashing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalShares","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":[],"name":"tssDelegationContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tssDelegationManagerContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tssGroupContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tssManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unJail","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountUnderlying","type":"uint256"}],"name":"underlyingToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountUnderlying","type":"uint256"}],"name":"underlyingToSharesView","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"underlyingToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"userUnderlying","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"userUnderlyingView","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"depositor","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amountShares","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawalRoots","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"withdrawals","outputs":[{"internalType":"address","name":"depositor","type":"address"},{"components":[{"internalType":"address","name":"withdrawer","type":"address"},{"internalType":"uint96","name":"nonce","type":"uint96"}],"internalType":"struct IDelegationManager.WithdrawerAndNonce","name":"withdrawerAndNonce","type":"tuple"},{"internalType":"address","name":"delegatedAddress","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60806040523480156200001157600080fd5b5060cd80546001600160a01b03191690556200002c62000032565b620000f4565b600054610100900460ff16156200009f5760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b606482015260840160405180910390fd5b60005460ff9081161015620000f2576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b61586c80620001046000396000f3fe6080604052600436106103c35760003560e01c80638f6a6240116101f2578063ce749c291161010d578063ea4d3c9b116100a0578063f47c83c51161006f578063f47c83c514610598578063f5cf673b14610bfe578063f756fa2114610c1e578063fee17dd514610c3357600080fd5b8063ea4d3c9b14610b7e578063f2bd740014610b9e578063f2fde38b14610bbe578063f3e7387514610bde57600080fd5b8063d9caed12116100dc578063d9caed1214610ae8578063df5cf72314610b08578063e3dae51c14610b28578063ea0c8c2f14610b4857600080fd5b8063ce749c2914610a5d578063ce7c2ac214610a72578063d323041d14610a92578063d58ecaaa14610ab257600080fd5b8063ac637c7a11610185578063bbb26c6311610154578063bbb26c63146109de578063c3a82e11146109f0578063c665c35a14610a1d578063c8b6cbf714610a3d57600080fd5b8063ac637c7a1461095c578063b1bbdc411461097c578063b3fc1cb21461099c578063b6b55f25146109be57600080fd5b80639c211730116101c15780639c211730146108e2578063a458413a14610907578063a5424a5514610927578063ab5921e11461094757600080fd5b80638f6a62401461086d57806390ed6bf41461088d57806395cf5bea146108a2578063965a303f146108c257600080fd5b80635716d17c116102e2578063740efec3116102755780638340f549116102445780638340f549146107d95780638c871019146107f95780638d23fc61146108195780638da5cb5b1461084f57600080fd5b8063740efec3146106dc5780637a8b2637146106f15780637a9262a214610711578063829673ef1461079357600080fd5b806360ac5166116102b157806360ac5166146106725780636dd300b5146106925780636eae5b11146106b2578063715018a6146106c757600080fd5b80635716d17c146105fa57806357b0f05f1461061a5780635887c33c1461063a5780635c975abb1461065a57600080fd5b8063358764761161035a5780633ccfd60b116103295780633ccfd60b14610583578063412cdd2c14610598578063490ab9d6146105b8578063553ca5f8146105da57600080fd5b806335876476146104ff5780633a98ef391461051f5780633c86a42f146105435780633cb747bf1461056357600080fd5b806326a4e8d21161039657806326a4e8d21461047d578063289087ba1461049d57806329f13431146104b757806332a9efc4146104d757600080fd5b8063111b8c02146103c857806313e7c9d8146103df57806314bfb527146104155780632495a59914610445575b600080fd5b3480156103d457600080fd5b506103dd610c48565b005b3480156103eb57600080fd5b506103ff6103fa36600461482f565b610c5e565b60405161040c91906148a8565b60405180910390f35b34801561042157600080fd5b5061043561043036600461482f565b610cf8565b604051901515815260200161040c565b34801561045157600080fd5b5060ca54610465906001600160a01b031681565b6040516001600160a01b03909116815260200161040c565b34801561048957600080fd5b506103dd61049836600461482f565b610e00565b3480156104a957600080fd5b5060dd546104359060ff1681565b3480156104c357600080fd5b506103dd6104d23660046148bb565b610e50565b3480156104e357600080fd5b50610465732f44bd2a54ac3fb20cd7783cf94334069641dac981565b34801561050b57600080fd5b506103dd61051a3660046148dd565b610f72565b34801561052b57600080fd5b5061053560cb5481565b60405190815260200161040c565b34801561054f57600080fd5b506103dd61055e36600461482f565b61112d565b34801561056f57600080fd5b5060cd54610465906001600160a01b031681565b34801561058f57600080fd5b506103dd61117d565b3480156105a457600080fd5b506103dd6105b3366004614af9565b505050565b3480156105c457600080fd5b506105cd611713565b60405161040c9190614b6f565b3480156105e657600080fd5b506105356105f536600461482f565b611775565b34801561060657600080fd5b5060cf54610465906001600160a01b031681565b34801561062657600080fd5b50610465610635366004614bbc565b611789565b34801561064657600080fd5b506103dd610655366004614c1e565b6117b3565b34801561066657600080fd5b5060975460ff16610435565b34801561067e57600080fd5b506103dd61068d36600461482f565b611ac5565b34801561069e57600080fd5b506103dd6106ad366004614c8a565b611b15565b3480156106be57600080fd5b506103dd611cca565b3480156106d357600080fd5b506103dd611ee7565b3480156106e857600080fd5b506103dd611ef9565b3480156106fd57600080fd5b5061053561070c366004614bbc565b612283565b34801561071d57600080fd5b5061078461072c36600461482f565b60d8602090815260009182526040918290206003810154835180850190945260048201546001600160a01b038082168652600160a01b9091046001600160601b03169385019390935260059091015490821692911683565b60405161040c93929190614d34565b34801561079f57600080fd5b506104356107ae366004614d64565b600091825260d5602090815260408084206001600160a01b0393909316845291905290205460ff1690565b3480156107e557600080fd5b506105356107f4366004614d94565b6122b9565b34801561080557600080fd5b50610535610814366004614bbc565b612581565b34801561082557600080fd5b5061046561083436600461482f565b60da602052600090815260409020546001600160a01b031681565b34801561085b57600080fd5b506033546001600160a01b0316610465565b34801561087957600080fd5b5061053561088836600461482f565b61258c565b34801561089957600080fd5b506103dd61259a565b3480156108ae57600080fd5b506103dd6108bd366004614dd5565b612658565b3480156108ce57600080fd5b5060d054610465906001600160a01b031681565b3480156108ee57600080fd5b5060dd546104659061010090046001600160a01b031681565b34801561091357600080fd5b5061043561092236600461482f565b61276d565b34801561093357600080fd5b506103dd61094236600461482f565b6127f2565b34801561095357600080fd5b506103ff612848565b34801561096857600080fd5b506103dd61097736600461482f565b612868565b34801561098857600080fd5b506103dd610997366004614c8a565b61289a565b3480156109a857600080fd5b506109b1612c6f565b60405161040c9190614e17565b3480156109ca57600080fd5b506105356109d9366004614bbc565b612ca9565b6103dd6109ec366004614e48565b5050565b3480156109fc57600080fd5b50610535610a0b36600461482f565b60d76020526000908152604090205481565b348015610a2957600080fd5b5060d654610465906001600160a01b031681565b348015610a4957600080fd5b50610465610a58366004614e48565b612d41565b348015610a6957600080fd5b50610535612d79565b348015610a7e57600080fd5b50610535610a8d36600461482f565b612f40565b348015610a9e57600080fd5b5060ce54610465906001600160a01b031681565b348015610abe57600080fd5b50610465610acd36600461482f565b60db602052600090815260409020546001600160a01b031681565b348015610af457600080fd5b506103dd610b03366004614d94565b612fc3565b348015610b1457600080fd5b5060cc54610465906001600160a01b031681565b348015610b3457600080fd5b50610535610b43366004614bbc565b6131c5565b348015610b5457600080fd5b50610465610b6336600461482f565b60dc602052600090815260409020546001600160a01b031681565b348015610b8a57600080fd5b5060c954610465906001600160a01b031681565b348015610baa57600080fd5b50610535610bb9366004614bbc565b61320a565b348015610bca57600080fd5b506103dd610bd936600461482f565b613221565b348015610bea57600080fd5b50610535610bf9366004614bbc565b61329a565b348015610c0a57600080fd5b506103dd610c19366004614e74565b6132a5565b348015610c2a57600080fd5b506103dd61354b565b348015610c3f57600080fd5b506104356137cd565b610c50613a00565b610c5c60d260006146b9565b565b60d16020526000908152604090208054610c7790614ea2565b80601f0160208091040260200160405190810160405280929190818152602001828054610ca390614ea2565b8015610cf05780601f10610cc557610100808354040283529160200191610cf0565b820191906000526020600020905b815481529060010190602001808311610cd357829003601f168201915b505050505081565b60ce546001600160a01b03828116600090815260d16020526040808220905163152eee4560e21b81529193849316916354bbb91491610d3991600401614ed7565b600060405180830381600087803b158015610d5357600080fd5b505af1158015610d67573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610d8f9190810190614fa2565b805151909150604014610de05760405162461bcd60e51b81526020600482015260146024820152731d1cdcc81b595b58995c881b9bdd08195e1a5cdd60621b60448201526064015b60405180910390fd5b600181604001516001811115610df857610df8615081565b149392505050565b610e08613a00565b6001600160a01b038116610e2e5760405162461bcd60e51b8152600401610dd790615097565b60ca80546001600160a01b0319166001600160a01b0392909216919091179055565b610e58613a00565b8035602082013511610ec05760405162461bcd60e51b815260206004820152602b60248201527f696e76616c696420706172616d20736c617368416d6f756e742c20616e696d7560448201526a73203c3d20757074696d6560a81b6064820152608401610dd7565b60005b6002811015610f61576000828260028110610ee057610ee06150c0565b602002013511610f235760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a5908185b5bdd5b9d60921b6044820152606401610dd7565b818160028110610f3557610f356150c0565b602002013560d38260028110610f4d57610f4d6150c0565b015580610f59816150ec565b915050610ec3565b505060dd805460ff19166001179055565b600054610100900460ff1615808015610f925750600054600160ff909116105b80610fac5750303b158015610fac575060005460ff166001145b61100f5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610dd7565b6000805460ff191660011790558015611032576000805461ff0019166101001790555b61103a613a5a565b611042613a89565b60ca80546001600160a01b03808b166001600160a01b03199283161790925560ce80548a841690831617905560cf8054898416908316811790915560d08054898516908416811790915560c98054841690921790915560cc80548316909117905560cd805487841690831617905560d68054868416921691909117905560dd805491841661010002610100600160a81b03199092169190911790558015611123576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050505050505050565b611135613a00565b6001600160a01b03811661115b5760405162461bcd60e51b8152600401610dd790615097565b60d680546001600160a01b0319166001600160a01b0392909216919091179055565b60cc54604051633e28391d60e01b81523360048201526001600160a01b0390911690633e28391d9060240160206040518083038186803b1580156111c057600080fd5b505afa1580156111d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f89190615107565b6112345760405162461bcd60e51b815260206004820152600d60248201526c3737ba103232b632b3b0ba37b960991b6044820152606401610dd7565b33600090815260d760205260409020541561129d5760405162461bcd60e51b8152602060048201526024808201527f6d73672073656e64657220616c726561647920726571756573742077697468646044820152637261777360e01b6064820152608401610dd7565b604080516001808252818301909252600091602080830190803683370190505090506000816000815181106112d4576112d46150c0565b60209081029190910101526040805160018082528183019092526000918160200160208202803683370190505090503081600081518110611317576113176150c0565b6001600160a01b039290921660209283029190910190910152604080516001808252818301909252600091816020016020820280368337505060ca5482519293506001600160a01b031691839150600090611374576113746150c0565b6001600160a01b0392909216602092830291909101909101526040805160018082528183019092526000918160200160208202803683370190505090506113ba33612f40565b816000815181106113cd576113cd6150c0565b602090810291909101015260cf546040516329f0dcef60e21b81523360048201526000916001600160a01b03169063a7c373bc9060240160206040518083038186803b15801561141c57600080fd5b505afa158015611430573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114549190615129565b905060006040518060400160405280336001600160a01b0316815260200161147b84613ab8565b6001600160601b0316905260cc54604051631976849960e21b81523360048201529192506000916001600160a01b03909116906365da12649060240160206040518083038186803b1580156114cf57600080fd5b505afa1580156114e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115079190615142565b6040805160c08101825288815260208082018990528183018890523360608301819052608083018790526001600160a01b03851660a0840152600090815260d8825292909220815180519495509193849391926115689284929101906146d7565b50602082810151805161158192600185019201906146d7565b506040820151805161159d916002840191602090910190614738565b5060608201516003820180546001600160a01b039283166001600160a01b031991821617909155608084015180516020909101516001600160601b0316600160a01b029083161760048085019190915560a09094015160059093018054938316939091169290921790915560cf5460405163529075a560e01b8152600093919092169163529075a59161163c9133918e918e918e918e918d91016151d3565b602060405180830381600087803b15801561165657600080fd5b505af115801561166a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061168e9190615129565b33600081815260d76020526040812083905588519293507f784199bc7528da846b1da87aeba29604de8f22a93e73379d5d1c7282fa3c727f928991906116d6576116d66150c0565b60200260200101516040516117009291906001600160a01b03929092168252602082015260400190565b60405180910390a1505050505050505050565b606060d280548060200260200160405190810160405280929190818152602001828054801561176b57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161174d575b5050505050905090565b600061178361070c83612f40565b92915050565b60d2818154811061179957600080fd5b6000918252602090912001546001600160a01b0316905081565b600260655414156118065760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610dd7565b600260655560dd5461010090046001600160a01b031633146118835760405162461bcd60e51b815260206004820152603060248201527f5473735374616b696e67536c617368696e673a206d73672e73656e646572206960448201526f39903737ba103a39b9a6b0b730b3b2b960811b6064820152608401610dd7565b60006118918486018661524c565b90506118a08160200151610cf8565b156118ed5760405162461bcd60e51b815260206004820152601760248201527f746865206e6f646520616c7265616479206a61696c65640000000000000000006044820152606401610dd7565b8051600090815260d560209081526040808320828501516001600160a01b0316845290915290205460ff16156119575760405162461bcd60e51b815260206004820152600f60248201526e185b1c9958591e481cdb185cda1959608a1b6044820152606401610dd7565b8051600090815260d560209081526040808320828501516001600160a01b039081168552925291829020805460ff1916600117905560ce549151911690633231a7f0906119a79088908890615344565b6040519081900381206001600160e01b031960e084901b1682526119d19187908790600401615354565b602060405180830381600087803b1580156119eb57600080fd5b505af11580156119ff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a239190615107565b611a6f5760405162461bcd60e51b815260206004820152601c60248201527f7369676e6572206e6f74207473732067726f757020707562206b6579000000006044820152606401610dd7565b611a7881613b24565b7f9453459a6e9fa2069f1490c99cec15646afa157300f218a7f7840b23d09dbd3e81602001518260600151604051611ab192919061538a565b60405180910390a150506001606555505050565b611acd613a00565b6001600160a01b038116611af35760405162461bcd60e51b8152600401610dd790615097565b60ce80546001600160a01b0319166001600160a01b0392909216919091179055565b60cc546001600160a01b03163314611b3f5760405162461bcd60e51b8152600401610dd7906153c5565b815160018114611b8b5760405162461bcd60e51b815260206004820152601760248201527664656c65676174696f6e206f6e6c7920666f722074737360481b6044820152606401610dd7565b8151600114611bdc5760405162461bcd60e51b815260206004820152601d60248201527f64656c65676174696f6e207368617265206f6e6c7920666f72207473730000006044820152606401610dd7565b306001600160a01b031683600081518110611bf957611bf96150c0565b60200260200101516001600160a01b031614611c535760405162461bcd60e51b81526020600482015260196024820152781b5d5cdd081d5cd94818dd5c9c995b9d0818dbdb9d1c9858dd603a1b6044820152606401610dd7565b6001600160a01b03858116600090815260da602052604090205416611cc3576001600160a01b03808616600081815260da602090815260408083208054958a166001600160a01b03199687168117909155835260d982528220805460018101825590835291200180549092161790555b5050505050565b60dd5460ff16611d1c5760405162461bcd60e51b815260206004820152601d60248201527f68617665206e6f74207365742074686520736c61736820616d6f756e740000006044820152606401610dd7565b611d2533610cf8565b611d8b5760405162461bcd60e51b815260206004820152603160248201527f416e20756e6a61696c6564207573657220646f65736e2774206e65656420746f6044820152700818d85b1b081d1a1a5cc81b595d1a1bd9607a1b6064820152608401610dd7565b6000611d95613d84565b905060d36001015460cb5460cc5460405163778e55f360e01b815284916001600160a01b03169063778e55f390611dd2903390309060040161540e565b60206040518083038186803b158015611dea57600080fd5b505afa158015611dfe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e229190615129565b611e2c9190615428565b611e369190615447565b1015611e7b5760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606401610dd7565b60ce5433600090815260d160205260409081902090516310a7b12760e21b81526001600160a01b039092169163429ec49c91611eb991600401614ed7565b600060405180830381600087803b158015611ed357600080fd5b505af1158015611cc3573d6000803e3d6000fd5b611eef613a00565b610c5c6000613e05565b60026065541415611f4c5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610dd7565b600260655560cc5460405163778e55f360e01b81526000916001600160a01b03169063778e55f390611f84903390309060040161540e565b60206040518083038186803b158015611f9c57600080fd5b505afa158015611fb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fd49190615129565b116120175760405162461bcd60e51b8152602060048201526013602482015272191bc81b9bdd081a185d994819195c1bdcda5d606a1b6044820152606401610dd7565b60ce5433600090815260d16020526040908190209051630243371760e51b81526001600160a01b0390921691634866e2e09161205591600401614ed7565b602060405180830381600087803b15801561206f57600080fd5b505af1158015612083573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120a79190615107565b8061213d575060ce5433600090815260d16020526040908190209051635e5e2fe360e11b81526001600160a01b039092169163bcbc5fc6916120eb91600401614ed7565b602060405180830381600087803b15801561210557600080fd5b505af1158015612119573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061213d9190615107565b61219b5760405162461bcd60e51b815260206004820152602960248201527f6e6f742061742074686520696e6163746976652067726f7570206f722061637460448201526806976652067726f75760bc1b6064820152608401610dd7565b60005b60d25481101561223a57336001600160a01b031660d282815481106121c5576121c56150c0565b6000918252602090912001546001600160a01b031614156122285760405162461bcd60e51b815260206004820152601a60248201527f616c726561647920696e2071756974526571756573744c6973740000000000006044820152606401610dd7565b80612232816150ec565b91505061219e565b5060d28054600181810183556000929092527ff2192e1030363415d7b4fb0406540a0060e8e2fc8982f3f32289379e11fa65460180546001600160a01b03191633179055606555565b600060cb5460001415612294575090565b60cb54826122a0613d84565b6122aa9190615428565b6117839190615447565b919050565b60006122c3613e57565b60c9546001600160a01b031633146122ed5760405162461bcd60e51b8152600401610dd7906153c5565b60ca546001600160a01b038481169116146123705760405162461bcd60e51b815260206004820152603d60248201527f44656c65676174696f6e5368617265426173652e6465706f7369743a2043616e60448201527f206f6e6c79206465706f73697420756e6465726c79696e67546f6b656e0000006064820152608401610dd7565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b179052905160009182916001600160a01b038716916123b391615469565b6000604051808303816000865af19150503d80600081146123f0576040519150601f19603f3d011682016040523d82523d6000602084013e6123f5565b606091505b50915091508161245c5760405162461bcd60e51b815260206004820152602c60248201527f756e6465726c79696e67546f6b656e2068617665206e6f206d6574686f64207760448201526b69746820646563696d616c7360a01b6064820152608401610dd7565b600061246782615485565b905061247481600a61558d565b61247f906001615428565b8510156124c65760405162461bcd60e51b8152602060048201526015602482015274185b5bdd5b9d081b5d5cdd0819dd080c481d5b9a5d605a1b6044820152606401610dd7565b6000856124d1613d84565b6124db9190615599565b90508015806124ea575060cb54155b156124f757859450612513565b8060cb54876125069190615428565b6125109190615447565b94505b8460cb600082825461252591906155b0565b9091555050604080516001600160a01b03808b168252891660208201529081018790527f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f629060600160405180910390a1505050505b9392505050565b6000611783826131c5565b6000611783610bf983612f40565b33600090815260d760205260409020546126055760405162461bcd60e51b815260206004820152602660248201527f6d73672073656e646572206d757374207265717565737420776974686472617760448201526508199a5c9cdd60d21b6064820152608401610dd7565b33600081815260d760205260408082205460cf5491516331f8ee6760e11b8152600481018290526024810194909452604484019290925290916001600160a01b03909116906363f1dcce90606401611eb9565b61269782828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613e9d92505050565b6001600160a01b0316336001600160a01b0316146126ee5760405162461bcd60e51b81526020600482015260146024820152730e0eac4d8d2c640d6caf240dcdee840dac2e8c6d60631b6044820152606401610dd7565b60d0546040516311a38c8560e01b81526001600160a01b03909116906311a38c8590612720903090339060040161540e565b600060405180830381600087803b15801561273a57600080fd5b505af115801561274e573d6000803e3d6000fd5b505033600090815260d1602052604090206105b3925090508383614773565b60cf546040516358775aaf60e11b81526000916001600160a01b03169063b0eeb55e906127a0908590309060040161540e565b602060405180830381600087803b1580156127ba57600080fd5b505af11580156127ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117839190615107565b6127fa613a00565b6001600160a01b0381166128205760405162461bcd60e51b8152600401610dd790615097565b60dd80546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b60606040518060800160405280604881526020016157ef60489139905090565b60d05460405163438bb7e560e01b81526001600160a01b039091169063438bb7e590611eb9908490339060040161540e565b60cc546001600160a01b031633146128c45760405162461bcd60e51b8152600401610dd7906153c5565b8151600181146129105760405162461bcd60e51b815260206004820152601760248201527664656c65676174696f6e206f6e6c7920666f722074737360481b6044820152606401610dd7565b81516001146129615760405162461bcd60e51b815260206004820152601d60248201527f64656c65676174696f6e207368617265206f6e6c7920666f72207473730000006044820152606401610dd7565b306001600160a01b03168360008151811061297e5761297e6150c0565b60200260200101516001600160a01b0316146129d85760405162461bcd60e51b81526020600482015260196024820152781b5d5cdd081d5cd94818dd5c9c995b9d0818dbdb9d1c9858dd603a1b6044820152606401610dd7565b816000815181106129eb576129eb6150c0565b602002602001015160cf60009054906101000a90046001600160a01b03166001600160a01b03166305579ccc8786600081518110612a2b57612a2b6150c0565b60200260200101516040518363ffffffff1660e01b8152600401612a5092919061540e565b60206040518083038186803b158015612a6857600080fd5b505afa158015612a7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aa09190615129565b1415611cc3576001600160a01b038416600090815260d96020908152604080832080548251818502810185019093528083529192909190830182828015612b1057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612af2575b5050505050905060005b8151811015612c6657866001600160a01b0316828281518110612b3f57612b3f6150c0565b60200260200101516001600160a01b03161415612c54576001600160a01b038616600090815260d9602052604090208251612b7c90600190615599565b81548110612b8c57612b8c6150c0565b60009182526020808320909101546001600160a01b03898116845260d99092526040909220805491909216919083908110612bc957612bc96150c0565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918816815260d990915260409020805480612c0e57612c0e6155c8565b60008281526020808220830160001990810180546001600160a01b031990811690915593019093556001600160a01b038a16815260da9092526040909120805490911690555b80612c5e816150ec565b915050612b1a565b50505050505050565b612c776147e7565b60408051808201918290529060d39060029082845b815481526020019060010190808311612c8c575050505050905090565b60cf5460ca54604051631abeb0d960e01b81523060048201526001600160a01b0391821660248201526044810184905233606482015260009283921690631abeb0d990608401602060405180830381600087803b158015612d0957600080fd5b505af1158015612d1d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257a9190615129565b60d96020528160005260406000208181548110612d5d57600080fd5b6000918252602090912001546001600160a01b03169150829050565b60ca546040516370a0823160e01b815230600482015260009182916001600160a01b03909116906370a082319060240160206040518083038186803b158015612dc157600080fd5b505afa158015612dd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612df99190615129565b905060008111612e425760405162461bcd60e51b81526020600482015260146024820152734e6f742073756666696369656e742066756e647360601b6044820152606401610dd7565b60ca5460405163a9059cbb60e01b8152732f44bd2a54ac3fb20cd7783cf94334069641dac96004820152602481018390526001600160a01b039091169063a9059cbb90604401602060405180830381600087803b158015612ea257600080fd5b505af1158015612eb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eda9190615107565b5060ca54604080516001600160a01b039092168252732f44bd2a54ac3fb20cd7783cf94334069641dac9602083015281018290527f3bb082152a6aeca4accd7a549583361cf3387c060a33681f9bc6f22fa6333f8c9060600160405180910390a1919050565b60c954604051634e5b005d60e11b81526000916001600160a01b031690639cb600ba90612f73908590309060040161540e565b60206040518083038186803b158015612f8b57600080fd5b505afa158015612f9f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117839190615129565b612fcb613e57565b60c9546001600160a01b03163314612ff55760405162461bcd60e51b8152600401610dd7906153c5565b60ca546001600160a01b038381169116146130835760405162461bcd60e51b815260206004820152604260248201527f44656c65676174696f6e5368617265426173652e77697468647261773a20436160448201527f6e206f6e6c792077697468647261772074686520737472617465677920746f6b60648201526132b760f11b608482015260a401610dd7565b60cb548111156131185760405162461bcd60e51b815260206004820152605460248201527f44656c65676174696f6e5368617265426173652e77697468647261773a20616d60448201527f6f756e74536861726573206d757374206265206c657373207468616e206f7220606482015273657175616c20746f20746f74616c53686172657360601b608482015260a401610dd7565b60cb805482810390915560008282141561313b57613134613d84565b905061315c565b8183613145613d84565b61314f9190615428565b6131599190615447565b90505b60ca54613173906001600160a01b03168683613efc565b604080516001600160a01b038088168252861660208201529081018290527f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb9060600160405180910390a15050505050565b6000806131d0613d84565b90508015806131df575060cb54155b156131eb575090919050565b8060cb54846131fa9190615428565b61257a9190615447565b50919050565b60d3816002811061321a57600080fd5b0154905081565b613229613a00565b6001600160a01b03811661328e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610dd7565b61329781613e05565b50565b600061178382612283565b336001600160a01b0383161461330e5760405162461bcd60e51b815260206004820152602860248201527f6d73672073656e64657220697320646966662077697468206f70657261746f72604482015267206164647265737360c01b6064820152608401610dd7565b60cc546040516336b87bd760e11b81523360048201526001600160a01b0390911690636d70f7ae9060240160206040518083038186803b15801561335157600080fd5b505afa158015613365573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133899190615107565b6133e35760405162461bcd60e51b815260206004820152602560248201527f6d73672073656e646572206973206e6f742072656769737465726564206f70656044820152643930ba37b960d91b6064820152608401610dd7565b6001600160a01b03818116600090815260dc6020526040902054161561344b5760405162461bcd60e51b815260206004820152601960248201527f74686520636c61696d657220686173206265656e2075736564000000000000006044820152606401610dd7565b6001600160a01b03828116600090815260db602052604090205416156134a1576001600160a01b03808316600090815260db6020908152604080832054909316825260dc90522080546001600160a01b03191690555b6001600160a01b03808316600081815260db6020908152604080832080549587166001600160a01b03199687168117909155835260dc90915280822080549094169092179092555163f5cf673b60e01b90613502908590859060240161540e565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915290506105b36020602160991b01621e848083613f4e565b33600090815260d760205260409020546135775760405162461bcd60e51b8152600401610dd7906155de565b33600090815260d8602090815260408083208151815460e09481028201850190935260c081018381529093919284928491908401828280156135e257602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116135c4575b505050505081526020016001820180548060200260200160405190810160405280929190818152602001828054801561364457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613626575b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561369c57602002820191906000526020600020905b815481526020019060010190808311613688575b505050918352505060038201546001600160a01b039081166020808401919091526040805180820182526004808701548086168352600160a01b90046001600160601b03169382019390935281850152600590940154821660609093019290925260cf54925163218c446b60e21b8152939450919091169163863111ac9161372b9133918691600191016156ad565b600060405180830381600087803b15801561374557600080fd5b505af1158015613759573d6000803e3d6000fd5b505033600090815260d76020908152604080832083905560d890915281209250905061378582826146b9565b6137936001830160006146b9565b6137a16002830160006146b9565b506003810180546001600160a01b03199081169091556000600483015560059091018054909116905550565b33600090815260d760205260408120546137f95760405162461bcd60e51b8152600401610dd7906155de565b33600090815260d8602090815260408083208151815460e09481028201850190935260c0810183815290939192849284919084018282801561386457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613846575b50505050508152602001600182018054806020026020016040519081016040528092919081815260200182805480156138c657602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116138a8575b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561391e57602002820191906000526020600020905b81548152602001906001019080831161390a575b505050918352505060038201546001600160a01b039081166020808401919091526040805180820182526004808701548086168352600160a01b90046001600160601b03169382019390935281850152600590940154821660609093019290925260c95492516329a8131760e11b81529394509190911691635350262e916139a8918591016156e3565b602060405180830381600087803b1580156139c257600080fd5b505af11580156139d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139fa9190615107565b91505090565b6033546001600160a01b03163314610c5c5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610dd7565b600054610100900460ff16613a815760405162461bcd60e51b8152600401610dd7906156f6565b610c5c613fb0565b600054610100900460ff16613ab05760405162461bcd60e51b8152600401610dd7906156f6565b610c5c613fe0565b60006001600160601b03821115613b205760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201526536206269747360d01b6064820152608401610dd7565b5090565b60dd5460ff16613b765760405162461bcd60e51b815260206004820152601d60248201527f68617665206e6f74207365742074686520736c61736820616d6f756e740000006044820152606401610dd7565b6020808201516001600160a01b0316600090815260d1909152604081208054613b9e90614ea2565b80601f0160208091040260200160405190810160405280929190818152602001828054613bca90614ea2565b8015613c175780601f10613bec57610100808354040283529160200191613c17565b820191906000526020600020905b815481529060010190602001808311613bfa57829003601f168201915b5093945060009350613c2892505050565b82606001516001811115613c3e57613c3e615081565b1415613cb55760ce5460405163254ff98160e01b81526001600160a01b039091169063254ff98190613c749084906004016148a8565b600060405180830381600087803b158015613c8e57600080fd5b505af1158015613ca2573d6000803e3d6000fd5b505050506109ec8260200151600061400e565b600182606001516001811115613ccd57613ccd615081565b1415613d445760ce5460405163254ff98160e01b81526001600160a01b039091169063254ff98190613d039084906004016148a8565b600060405180830381600087803b158015613d1d57600080fd5b505af1158015613d31573d6000803e3d6000fd5b505050506109ec8260200151600161400e565b60405162461bcd60e51b8152602060048201526015602482015274657272207479706520666f7220736c617368696e6760581b6044820152606401610dd7565b60ca546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b158015613dc857600080fd5b505afa158015613ddc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e009190615129565b905090565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60975460ff1615610c5c5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610dd7565b60008151604014613ef05760405162461bcd60e51b815260206004820152601f60248201527f7075626c6963206b6579206c656e677468206d757374203634206279746573006044820152606401610dd7565b50805160209091012090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526105b390849061446f565b60cd54604051633dbb202b60e01b81526001600160a01b0390911690633dbb202b90613f8290869085908790600401615741565b600060405180830381600087803b158015613f9c57600080fd5b505af1158015612c66573d6000803e3d6000fd5b600054610100900460ff16613fd75760405162461bcd60e51b8152600401610dd7906156f6565b610c5c33613e05565b600054610100900460ff166140075760405162461bcd60e51b8152600401610dd7906156f6565b6001606555565b600080614019613d84565b905060d3836002811061402e5761402e6150c0565b015460cb5460cc5460405163778e55f360e01b815284916001600160a01b03169063778e55f390614065908a90309060040161540e565b60206040518083038186803b15801561407d57600080fd5b505afa158015614091573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140b59190615129565b6140bf9190615428565b6140c99190615447565b10156141175760405162461bcd60e51b815260206004820152601a60248201527f646f206e6f74206861766520656e6f75676874207368617265730000000000006044820152606401610dd7565b8060cb5460d3856002811061412e5761412e6150c0565b015461413a9190615428565b6141449190615447565b60cc5460405163778e55f360e01b81529193506000916001600160a01b039091169063778e55f39061417c908890309060040161540e565b60206040518083038186803b15801561419457600080fd5b505afa1580156141a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141cc9190615129565b60408051600180825281830190925291925060009190602080830190803683370190505090503081600081518110614206576142066150c0565b6001600160a01b039290921660209283029190910190910152604080516001808252818301909252600091816020016020820280368337019050509050600081600081518110614258576142586150c0565b6020908102919091010152604080516001808252818301909252600091816020016020820280368337505060ca5482519293506001600160a01b0316918391506000906142a7576142a76150c0565b6001600160a01b03928316602091820292909201810191909152908916600090815260d982526040808220805482518186028101860190935280835292939192909183018282801561432257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311614304575b5050505050905060005b815181101561446357600061435983838151811061434c5761434c6150c0565b6020026020010151612f40565b60408051600180825281830190925291925060009190602080830190803683370190505090508761438a838c615428565b6143949190615447565b816000815181106143a7576143a76150c0565b602090810291909101015260cf5484516001600160a01b03909116906306f1f684908690869081106143db576143db6150c0565b602090810291909101015160d6546040516001600160e01b031960e085901b16815261441c92916001600160a01b0316908c908b908d90899060040161577b565b600060405180830381600087803b15801561443657600080fd5b505af115801561444a573d6000803e3d6000fd5b505050505050808061445b906150ec565b91505061432c565b50505050505050505050565b60006144c4826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166145419092919063ffffffff16565b8051909150156105b357808060200190518101906144e29190615107565b6105b35760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610dd7565b60606145508484600085614558565b949350505050565b6060824710156145b95760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610dd7565b843b6146075760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610dd7565b600080866001600160a01b031685876040516146239190615469565b60006040518083038185875af1925050503d8060008114614660576040519150601f19603f3d011682016040523d82523d6000602084013e614665565b606091505b5091509150614675828286614680565b979650505050505050565b6060831561468f57508161257a565b82511561469f5782518084602001fd5b8160405162461bcd60e51b8152600401610dd791906148a8565b50805460008255906000526020600020908101906132979190614805565b82805482825590600052602060002090810192821561472c579160200282015b8281111561472c57825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906146f7565b50613b20929150614805565b82805482825590600052602060002090810192821561472c579160200282015b8281111561472c578251825591602001919060010190614758565b82805461477f90614ea2565b90600052602060002090601f0160209004810192826147a1576000855561472c565b82601f106147ba5782800160ff1982351617855561472c565b8280016001018555821561472c579182015b8281111561472c5782358255916020019190600101906147cc565b60405180604001604052806002906020820280368337509192915050565b5b80821115613b205760008155600101614806565b6001600160a01b038116811461329757600080fd5b60006020828403121561484157600080fd5b813561257a8161481a565b60005b8381101561486757818101518382015260200161484f565b83811115614876576000848401525b50505050565b6000815180845261489481602086016020860161484c565b601f01601f19169290920160200192915050565b60208152600061257a602083018461487c565b6000604082840312156148cd57600080fd5b8260408301111561320457600080fd5b600080600080600080600060e0888a0312156148f857600080fd5b87356149038161481a565b965060208801356149138161481a565b955060408801356149238161481a565b945060608801356149338161481a565b935060808801356149438161481a565b925060a08801356149538161481a565b915060c08801356149638161481a565b8091505092959891949750929550565b634e487b7160e01b600052604160045260246000fd5b6040516060810167ffffffffffffffff811182821017156149ac576149ac614973565b60405290565b6040516080810167ffffffffffffffff811182821017156149ac576149ac614973565b604051601f8201601f1916810167ffffffffffffffff811182821017156149fe576149fe614973565b604052919050565b600067ffffffffffffffff821115614a2057614a20614973565b5060051b60200190565b600082601f830112614a3b57600080fd5b81356020614a50614a4b83614a06565b6149d5565b82815260059290921b84018101918181019086841115614a6f57600080fd5b8286015b84811015614a93578035614a868161481a565b8352918301918301614a73565b509695505050505050565b600082601f830112614aaf57600080fd5b81356020614abf614a4b83614a06565b82815260059290921b84018101918181019086841115614ade57600080fd5b8286015b84811015614a935780358352918301918301614ae2565b600080600060608486031215614b0e57600080fd5b8335614b198161481a565b9250602084013567ffffffffffffffff80821115614b3657600080fd5b614b4287838801614a2a565b93506040860135915080821115614b5857600080fd5b50614b6586828701614a9e565b9150509250925092565b6020808252825182820181905260009190848201906040850190845b81811015614bb05783516001600160a01b031683529284019291840191600101614b8b565b50909695505050505050565b600060208284031215614bce57600080fd5b5035919050565b60008083601f840112614be757600080fd5b50813567ffffffffffffffff811115614bff57600080fd5b602083019150836020828501011115614c1757600080fd5b9250929050565b60008060008060408587031215614c3457600080fd5b843567ffffffffffffffff80821115614c4c57600080fd5b614c5888838901614bd5565b90965094506020870135915080821115614c7157600080fd5b50614c7e87828801614bd5565b95989497509550505050565b60008060008060808587031215614ca057600080fd5b8435614cab8161481a565b93506020850135614cbb8161481a565b9250604085013567ffffffffffffffff80821115614cd857600080fd5b614ce488838901614a2a565b93506060870135915080821115614cfa57600080fd5b50614d0787828801614a9e565b91505092959194509250565b80516001600160a01b031682526020908101516001600160601b0316910152565b6001600160a01b0384811682526080820190614d536020840186614d13565b808416606084015250949350505050565b60008060408385031215614d7757600080fd5b823591506020830135614d898161481a565b809150509250929050565b600080600060608486031215614da957600080fd5b8335614db48161481a565b92506020840135614dc48161481a565b929592945050506040919091013590565b60008060208385031215614de857600080fd5b823567ffffffffffffffff811115614dff57600080fd5b614e0b85828601614bd5565b90969095509350505050565b60408101818360005b6002811015614e3f578151835260209283019290910190600101614e20565b50505092915050565b60008060408385031215614e5b57600080fd5b8235614e668161481a565b946020939093013593505050565b60008060408385031215614e8757600080fd5b8235614e928161481a565b91506020830135614d898161481a565b600181811c90821680614eb657607f821691505b6020821081141561320457634e487b7160e01b600052602260045260246000fd5b600060208083526000845481600182811c915080831680614ef957607f831692505b858310811415614f1757634e487b7160e01b85526022600452602485fd5b878601838152602001818015614f345760018114614f4557614f70565b60ff19861682528782019650614f70565b60008b81526020902060005b86811015614f6a57815484820152908501908901614f51565b83019750505b50949998505050505050505050565b80516122b48161481a565b6002811061329757600080fd5b80516122b481614f8a565b60006020808385031215614fb557600080fd5b825167ffffffffffffffff80821115614fcd57600080fd5b9084019060608287031215614fe157600080fd5b614fe9614989565b825182811115614ff857600080fd5b8301601f8101881361500957600080fd5b80518381111561501b5761501b614973565b61502d601f8201601f191687016149d5565b9350808452888682840101111561504357600080fd5b6150528187860188850161484c565b5050818152615062848401614f7f565b8482015261507260408401614f97565b60408201529695505050505050565b634e487b7160e01b600052602160045260246000fd5b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415615100576151006150d6565b5060010190565b60006020828403121561511957600080fd5b8151801515811461257a57600080fd5b60006020828403121561513b57600080fd5b5051919050565b60006020828403121561515457600080fd5b815161257a8161481a565b600081518084526020808501945080840160005b8381101561518f57815187529582019590820190600101615173565b509495945050505050565b600081518084526020808501945080840160005b8381101561518f5781516001600160a01b0316875295820195908201906001016151ae565b6001600160a01b038716815260e0602082018190526000906151f79083018861515f565b8281036040840152615209818861519a565b9050828103606084015261521d818761519a565b90508281036080840152615231818661515f565b91505061467560a0830184614d13565b80356122b481614f8a565b6000602080838503121561525f57600080fd5b823567ffffffffffffffff8082111561527757600080fd5b908401906080828703121561528b57600080fd5b6152936149b2565b82358152838301356152a48161481a565b818501526040830135828111156152ba57600080fd5b83019150601f820187136152cd57600080fd5b81356152db614a4b82614a06565b81815260059190911b830185019085810190898311156152fa57600080fd5b938601935b828510156153215784356153128161481a565b825293860193908601906152ff565b604084015250615335905060608401615241565b60608201529695505050505050565b8183823760009101908152919050565b83815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b6001600160a01b038316815260408101600283106153b857634e487b7160e01b600052602160045260246000fd5b8260208301529392505050565b60208082526029908201527f44656c65676174696f6e5368617265426173652e6f6e6c7944656c656761746960408201526837b726b0b730b3b2b960b91b606082015260800190565b6001600160a01b0392831681529116602082015260400190565b6000816000190483118215151615615442576154426150d6565b500290565b60008261546457634e487b7160e01b600052601260045260246000fd5b500490565b6000825161547b81846020870161484c565b9190910192915050565b805160208083015191908110156132045760001960209190910360031b1b16919050565b600181815b808511156154e45781600019048211156154ca576154ca6150d6565b808516156154d757918102915b93841c93908002906154ae565b509250929050565b6000826154fb57506001611783565b8161550857506000611783565b816001811461551e576002811461552857615544565b6001915050611783565b60ff841115615539576155396150d6565b50506001821b611783565b5060208310610133831016604e8410600b8410161715615567575081810a611783565b61557183836154a9565b8060001904821115615585576155856150d6565b029392505050565b600061257a83836154ec565b6000828210156155ab576155ab6150d6565b500390565b600082198211156155c3576155c36150d6565b500190565b634e487b7160e01b600052603160045260246000fd5b60208082526024908201527f6d73672073656e64657220646964206e6f7420726571756573742077697468646040820152637261777360e01b606082015260800190565b6000815160e0845261563760e085018261519a565b905060208301518482036020860152615650828261519a565b9150506040830151848203604086015261566a828261515f565b915050606083015160018060a01b038082166060870152608085015191506156956080870183614d13565b8060a08601511660c087015250508091505092915050565b6001600160a01b03841681526060602082018190526000906156d190830185615622565b90508215156040830152949350505050565b60208152600061257a6020830184615622565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6001600160a01b03841681526060602082018190526000906157659083018561487c565b905063ffffffff83166040830152949350505050565b6001600160a01b0387811682528616602082015260c0604082018190526000906157a79083018761519a565b82810360608401526157b9818761519a565b905082810360808401526157cd818661515f565b905082810360a08401526157e1818561515f565b999850505050505050505056fe4d616e746c6520746f6b656e2044656c65676174696f6e536861726520696d706c656d656e746174696f6e20666f72207375626d6f64756c657320617320616e206578616d706c65a2646970667358221220e57271eade17a5b142954064cbb2d1593ec6948af13ee3ae82a9524376f3306b64736f6c63430008090033
Deployed Bytecode
0x6080604052600436106103c35760003560e01c80638f6a6240116101f2578063ce749c291161010d578063ea4d3c9b116100a0578063f47c83c51161006f578063f47c83c514610598578063f5cf673b14610bfe578063f756fa2114610c1e578063fee17dd514610c3357600080fd5b8063ea4d3c9b14610b7e578063f2bd740014610b9e578063f2fde38b14610bbe578063f3e7387514610bde57600080fd5b8063d9caed12116100dc578063d9caed1214610ae8578063df5cf72314610b08578063e3dae51c14610b28578063ea0c8c2f14610b4857600080fd5b8063ce749c2914610a5d578063ce7c2ac214610a72578063d323041d14610a92578063d58ecaaa14610ab257600080fd5b8063ac637c7a11610185578063bbb26c6311610154578063bbb26c63146109de578063c3a82e11146109f0578063c665c35a14610a1d578063c8b6cbf714610a3d57600080fd5b8063ac637c7a1461095c578063b1bbdc411461097c578063b3fc1cb21461099c578063b6b55f25146109be57600080fd5b80639c211730116101c15780639c211730146108e2578063a458413a14610907578063a5424a5514610927578063ab5921e11461094757600080fd5b80638f6a62401461086d57806390ed6bf41461088d57806395cf5bea146108a2578063965a303f146108c257600080fd5b80635716d17c116102e2578063740efec3116102755780638340f549116102445780638340f549146107d95780638c871019146107f95780638d23fc61146108195780638da5cb5b1461084f57600080fd5b8063740efec3146106dc5780637a8b2637146106f15780637a9262a214610711578063829673ef1461079357600080fd5b806360ac5166116102b157806360ac5166146106725780636dd300b5146106925780636eae5b11146106b2578063715018a6146106c757600080fd5b80635716d17c146105fa57806357b0f05f1461061a5780635887c33c1461063a5780635c975abb1461065a57600080fd5b8063358764761161035a5780633ccfd60b116103295780633ccfd60b14610583578063412cdd2c14610598578063490ab9d6146105b8578063553ca5f8146105da57600080fd5b806335876476146104ff5780633a98ef391461051f5780633c86a42f146105435780633cb747bf1461056357600080fd5b806326a4e8d21161039657806326a4e8d21461047d578063289087ba1461049d57806329f13431146104b757806332a9efc4146104d757600080fd5b8063111b8c02146103c857806313e7c9d8146103df57806314bfb527146104155780632495a59914610445575b600080fd5b3480156103d457600080fd5b506103dd610c48565b005b3480156103eb57600080fd5b506103ff6103fa36600461482f565b610c5e565b60405161040c91906148a8565b60405180910390f35b34801561042157600080fd5b5061043561043036600461482f565b610cf8565b604051901515815260200161040c565b34801561045157600080fd5b5060ca54610465906001600160a01b031681565b6040516001600160a01b03909116815260200161040c565b34801561048957600080fd5b506103dd61049836600461482f565b610e00565b3480156104a957600080fd5b5060dd546104359060ff1681565b3480156104c357600080fd5b506103dd6104d23660046148bb565b610e50565b3480156104e357600080fd5b50610465732f44bd2a54ac3fb20cd7783cf94334069641dac981565b34801561050b57600080fd5b506103dd61051a3660046148dd565b610f72565b34801561052b57600080fd5b5061053560cb5481565b60405190815260200161040c565b34801561054f57600080fd5b506103dd61055e36600461482f565b61112d565b34801561056f57600080fd5b5060cd54610465906001600160a01b031681565b34801561058f57600080fd5b506103dd61117d565b3480156105a457600080fd5b506103dd6105b3366004614af9565b505050565b3480156105c457600080fd5b506105cd611713565b60405161040c9190614b6f565b3480156105e657600080fd5b506105356105f536600461482f565b611775565b34801561060657600080fd5b5060cf54610465906001600160a01b031681565b34801561062657600080fd5b50610465610635366004614bbc565b611789565b34801561064657600080fd5b506103dd610655366004614c1e565b6117b3565b34801561066657600080fd5b5060975460ff16610435565b34801561067e57600080fd5b506103dd61068d36600461482f565b611ac5565b34801561069e57600080fd5b506103dd6106ad366004614c8a565b611b15565b3480156106be57600080fd5b506103dd611cca565b3480156106d357600080fd5b506103dd611ee7565b3480156106e857600080fd5b506103dd611ef9565b3480156106fd57600080fd5b5061053561070c366004614bbc565b612283565b34801561071d57600080fd5b5061078461072c36600461482f565b60d8602090815260009182526040918290206003810154835180850190945260048201546001600160a01b038082168652600160a01b9091046001600160601b03169385019390935260059091015490821692911683565b60405161040c93929190614d34565b34801561079f57600080fd5b506104356107ae366004614d64565b600091825260d5602090815260408084206001600160a01b0393909316845291905290205460ff1690565b3480156107e557600080fd5b506105356107f4366004614d94565b6122b9565b34801561080557600080fd5b50610535610814366004614bbc565b612581565b34801561082557600080fd5b5061046561083436600461482f565b60da602052600090815260409020546001600160a01b031681565b34801561085b57600080fd5b506033546001600160a01b0316610465565b34801561087957600080fd5b5061053561088836600461482f565b61258c565b34801561089957600080fd5b506103dd61259a565b3480156108ae57600080fd5b506103dd6108bd366004614dd5565b612658565b3480156108ce57600080fd5b5060d054610465906001600160a01b031681565b3480156108ee57600080fd5b5060dd546104659061010090046001600160a01b031681565b34801561091357600080fd5b5061043561092236600461482f565b61276d565b34801561093357600080fd5b506103dd61094236600461482f565b6127f2565b34801561095357600080fd5b506103ff612848565b34801561096857600080fd5b506103dd61097736600461482f565b612868565b34801561098857600080fd5b506103dd610997366004614c8a565b61289a565b3480156109a857600080fd5b506109b1612c6f565b60405161040c9190614e17565b3480156109ca57600080fd5b506105356109d9366004614bbc565b612ca9565b6103dd6109ec366004614e48565b5050565b3480156109fc57600080fd5b50610535610a0b36600461482f565b60d76020526000908152604090205481565b348015610a2957600080fd5b5060d654610465906001600160a01b031681565b348015610a4957600080fd5b50610465610a58366004614e48565b612d41565b348015610a6957600080fd5b50610535612d79565b348015610a7e57600080fd5b50610535610a8d36600461482f565b612f40565b348015610a9e57600080fd5b5060ce54610465906001600160a01b031681565b348015610abe57600080fd5b50610465610acd36600461482f565b60db602052600090815260409020546001600160a01b031681565b348015610af457600080fd5b506103dd610b03366004614d94565b612fc3565b348015610b1457600080fd5b5060cc54610465906001600160a01b031681565b348015610b3457600080fd5b50610535610b43366004614bbc565b6131c5565b348015610b5457600080fd5b50610465610b6336600461482f565b60dc602052600090815260409020546001600160a01b031681565b348015610b8a57600080fd5b5060c954610465906001600160a01b031681565b348015610baa57600080fd5b50610535610bb9366004614bbc565b61320a565b348015610bca57600080fd5b506103dd610bd936600461482f565b613221565b348015610bea57600080fd5b50610535610bf9366004614bbc565b61329a565b348015610c0a57600080fd5b506103dd610c19366004614e74565b6132a5565b348015610c2a57600080fd5b506103dd61354b565b348015610c3f57600080fd5b506104356137cd565b610c50613a00565b610c5c60d260006146b9565b565b60d16020526000908152604090208054610c7790614ea2565b80601f0160208091040260200160405190810160405280929190818152602001828054610ca390614ea2565b8015610cf05780601f10610cc557610100808354040283529160200191610cf0565b820191906000526020600020905b815481529060010190602001808311610cd357829003601f168201915b505050505081565b60ce546001600160a01b03828116600090815260d16020526040808220905163152eee4560e21b81529193849316916354bbb91491610d3991600401614ed7565b600060405180830381600087803b158015610d5357600080fd5b505af1158015610d67573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610d8f9190810190614fa2565b805151909150604014610de05760405162461bcd60e51b81526020600482015260146024820152731d1cdcc81b595b58995c881b9bdd08195e1a5cdd60621b60448201526064015b60405180910390fd5b600181604001516001811115610df857610df8615081565b149392505050565b610e08613a00565b6001600160a01b038116610e2e5760405162461bcd60e51b8152600401610dd790615097565b60ca80546001600160a01b0319166001600160a01b0392909216919091179055565b610e58613a00565b8035602082013511610ec05760405162461bcd60e51b815260206004820152602b60248201527f696e76616c696420706172616d20736c617368416d6f756e742c20616e696d7560448201526a73203c3d20757074696d6560a81b6064820152608401610dd7565b60005b6002811015610f61576000828260028110610ee057610ee06150c0565b602002013511610f235760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a5908185b5bdd5b9d60921b6044820152606401610dd7565b818160028110610f3557610f356150c0565b602002013560d38260028110610f4d57610f4d6150c0565b015580610f59816150ec565b915050610ec3565b505060dd805460ff19166001179055565b600054610100900460ff1615808015610f925750600054600160ff909116105b80610fac5750303b158015610fac575060005460ff166001145b61100f5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610dd7565b6000805460ff191660011790558015611032576000805461ff0019166101001790555b61103a613a5a565b611042613a89565b60ca80546001600160a01b03808b166001600160a01b03199283161790925560ce80548a841690831617905560cf8054898416908316811790915560d08054898516908416811790915560c98054841690921790915560cc80548316909117905560cd805487841690831617905560d68054868416921691909117905560dd805491841661010002610100600160a81b03199092169190911790558015611123576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5050505050505050565b611135613a00565b6001600160a01b03811661115b5760405162461bcd60e51b8152600401610dd790615097565b60d680546001600160a01b0319166001600160a01b0392909216919091179055565b60cc54604051633e28391d60e01b81523360048201526001600160a01b0390911690633e28391d9060240160206040518083038186803b1580156111c057600080fd5b505afa1580156111d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111f89190615107565b6112345760405162461bcd60e51b815260206004820152600d60248201526c3737ba103232b632b3b0ba37b960991b6044820152606401610dd7565b33600090815260d760205260409020541561129d5760405162461bcd60e51b8152602060048201526024808201527f6d73672073656e64657220616c726561647920726571756573742077697468646044820152637261777360e01b6064820152608401610dd7565b604080516001808252818301909252600091602080830190803683370190505090506000816000815181106112d4576112d46150c0565b60209081029190910101526040805160018082528183019092526000918160200160208202803683370190505090503081600081518110611317576113176150c0565b6001600160a01b039290921660209283029190910190910152604080516001808252818301909252600091816020016020820280368337505060ca5482519293506001600160a01b031691839150600090611374576113746150c0565b6001600160a01b0392909216602092830291909101909101526040805160018082528183019092526000918160200160208202803683370190505090506113ba33612f40565b816000815181106113cd576113cd6150c0565b602090810291909101015260cf546040516329f0dcef60e21b81523360048201526000916001600160a01b03169063a7c373bc9060240160206040518083038186803b15801561141c57600080fd5b505afa158015611430573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114549190615129565b905060006040518060400160405280336001600160a01b0316815260200161147b84613ab8565b6001600160601b0316905260cc54604051631976849960e21b81523360048201529192506000916001600160a01b03909116906365da12649060240160206040518083038186803b1580156114cf57600080fd5b505afa1580156114e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115079190615142565b6040805160c08101825288815260208082018990528183018890523360608301819052608083018790526001600160a01b03851660a0840152600090815260d8825292909220815180519495509193849391926115689284929101906146d7565b50602082810151805161158192600185019201906146d7565b506040820151805161159d916002840191602090910190614738565b5060608201516003820180546001600160a01b039283166001600160a01b031991821617909155608084015180516020909101516001600160601b0316600160a01b029083161760048085019190915560a09094015160059093018054938316939091169290921790915560cf5460405163529075a560e01b8152600093919092169163529075a59161163c9133918e918e918e918e918d91016151d3565b602060405180830381600087803b15801561165657600080fd5b505af115801561166a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061168e9190615129565b33600081815260d76020526040812083905588519293507f784199bc7528da846b1da87aeba29604de8f22a93e73379d5d1c7282fa3c727f928991906116d6576116d66150c0565b60200260200101516040516117009291906001600160a01b03929092168252602082015260400190565b60405180910390a1505050505050505050565b606060d280548060200260200160405190810160405280929190818152602001828054801561176b57602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161174d575b5050505050905090565b600061178361070c83612f40565b92915050565b60d2818154811061179957600080fd5b6000918252602090912001546001600160a01b0316905081565b600260655414156118065760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610dd7565b600260655560dd5461010090046001600160a01b031633146118835760405162461bcd60e51b815260206004820152603060248201527f5473735374616b696e67536c617368696e673a206d73672e73656e646572206960448201526f39903737ba103a39b9a6b0b730b3b2b960811b6064820152608401610dd7565b60006118918486018661524c565b90506118a08160200151610cf8565b156118ed5760405162461bcd60e51b815260206004820152601760248201527f746865206e6f646520616c7265616479206a61696c65640000000000000000006044820152606401610dd7565b8051600090815260d560209081526040808320828501516001600160a01b0316845290915290205460ff16156119575760405162461bcd60e51b815260206004820152600f60248201526e185b1c9958591e481cdb185cda1959608a1b6044820152606401610dd7565b8051600090815260d560209081526040808320828501516001600160a01b039081168552925291829020805460ff1916600117905560ce549151911690633231a7f0906119a79088908890615344565b6040519081900381206001600160e01b031960e084901b1682526119d19187908790600401615354565b602060405180830381600087803b1580156119eb57600080fd5b505af11580156119ff573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a239190615107565b611a6f5760405162461bcd60e51b815260206004820152601c60248201527f7369676e6572206e6f74207473732067726f757020707562206b6579000000006044820152606401610dd7565b611a7881613b24565b7f9453459a6e9fa2069f1490c99cec15646afa157300f218a7f7840b23d09dbd3e81602001518260600151604051611ab192919061538a565b60405180910390a150506001606555505050565b611acd613a00565b6001600160a01b038116611af35760405162461bcd60e51b8152600401610dd790615097565b60ce80546001600160a01b0319166001600160a01b0392909216919091179055565b60cc546001600160a01b03163314611b3f5760405162461bcd60e51b8152600401610dd7906153c5565b815160018114611b8b5760405162461bcd60e51b815260206004820152601760248201527664656c65676174696f6e206f6e6c7920666f722074737360481b6044820152606401610dd7565b8151600114611bdc5760405162461bcd60e51b815260206004820152601d60248201527f64656c65676174696f6e207368617265206f6e6c7920666f72207473730000006044820152606401610dd7565b306001600160a01b031683600081518110611bf957611bf96150c0565b60200260200101516001600160a01b031614611c535760405162461bcd60e51b81526020600482015260196024820152781b5d5cdd081d5cd94818dd5c9c995b9d0818dbdb9d1c9858dd603a1b6044820152606401610dd7565b6001600160a01b03858116600090815260da602052604090205416611cc3576001600160a01b03808616600081815260da602090815260408083208054958a166001600160a01b03199687168117909155835260d982528220805460018101825590835291200180549092161790555b5050505050565b60dd5460ff16611d1c5760405162461bcd60e51b815260206004820152601d60248201527f68617665206e6f74207365742074686520736c61736820616d6f756e740000006044820152606401610dd7565b611d2533610cf8565b611d8b5760405162461bcd60e51b815260206004820152603160248201527f416e20756e6a61696c6564207573657220646f65736e2774206e65656420746f6044820152700818d85b1b081d1a1a5cc81b595d1a1bd9607a1b6064820152608401610dd7565b6000611d95613d84565b905060d36001015460cb5460cc5460405163778e55f360e01b815284916001600160a01b03169063778e55f390611dd2903390309060040161540e565b60206040518083038186803b158015611dea57600080fd5b505afa158015611dfe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e229190615129565b611e2c9190615428565b611e369190615447565b1015611e7b5760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e742062616c616e636560601b6044820152606401610dd7565b60ce5433600090815260d160205260409081902090516310a7b12760e21b81526001600160a01b039092169163429ec49c91611eb991600401614ed7565b600060405180830381600087803b158015611ed357600080fd5b505af1158015611cc3573d6000803e3d6000fd5b611eef613a00565b610c5c6000613e05565b60026065541415611f4c5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610dd7565b600260655560cc5460405163778e55f360e01b81526000916001600160a01b03169063778e55f390611f84903390309060040161540e565b60206040518083038186803b158015611f9c57600080fd5b505afa158015611fb0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fd49190615129565b116120175760405162461bcd60e51b8152602060048201526013602482015272191bc81b9bdd081a185d994819195c1bdcda5d606a1b6044820152606401610dd7565b60ce5433600090815260d16020526040908190209051630243371760e51b81526001600160a01b0390921691634866e2e09161205591600401614ed7565b602060405180830381600087803b15801561206f57600080fd5b505af1158015612083573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120a79190615107565b8061213d575060ce5433600090815260d16020526040908190209051635e5e2fe360e11b81526001600160a01b039092169163bcbc5fc6916120eb91600401614ed7565b602060405180830381600087803b15801561210557600080fd5b505af1158015612119573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061213d9190615107565b61219b5760405162461bcd60e51b815260206004820152602960248201527f6e6f742061742074686520696e6163746976652067726f7570206f722061637460448201526806976652067726f75760bc1b6064820152608401610dd7565b60005b60d25481101561223a57336001600160a01b031660d282815481106121c5576121c56150c0565b6000918252602090912001546001600160a01b031614156122285760405162461bcd60e51b815260206004820152601a60248201527f616c726561647920696e2071756974526571756573744c6973740000000000006044820152606401610dd7565b80612232816150ec565b91505061219e565b5060d28054600181810183556000929092527ff2192e1030363415d7b4fb0406540a0060e8e2fc8982f3f32289379e11fa65460180546001600160a01b03191633179055606555565b600060cb5460001415612294575090565b60cb54826122a0613d84565b6122aa9190615428565b6117839190615447565b919050565b60006122c3613e57565b60c9546001600160a01b031633146122ed5760405162461bcd60e51b8152600401610dd7906153c5565b60ca546001600160a01b038481169116146123705760405162461bcd60e51b815260206004820152603d60248201527f44656c65676174696f6e5368617265426173652e6465706f7369743a2043616e60448201527f206f6e6c79206465706f73697420756e6465726c79696e67546f6b656e0000006064820152608401610dd7565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b179052905160009182916001600160a01b038716916123b391615469565b6000604051808303816000865af19150503d80600081146123f0576040519150601f19603f3d011682016040523d82523d6000602084013e6123f5565b606091505b50915091508161245c5760405162461bcd60e51b815260206004820152602c60248201527f756e6465726c79696e67546f6b656e2068617665206e6f206d6574686f64207760448201526b69746820646563696d616c7360a01b6064820152608401610dd7565b600061246782615485565b905061247481600a61558d565b61247f906001615428565b8510156124c65760405162461bcd60e51b8152602060048201526015602482015274185b5bdd5b9d081b5d5cdd0819dd080c481d5b9a5d605a1b6044820152606401610dd7565b6000856124d1613d84565b6124db9190615599565b90508015806124ea575060cb54155b156124f757859450612513565b8060cb54876125069190615428565b6125109190615447565b94505b8460cb600082825461252591906155b0565b9091555050604080516001600160a01b03808b168252891660208201529081018790527f5548c837ab068cf56a2c2479df0882a4922fd203edb7517321831d95078c5f629060600160405180910390a1505050505b9392505050565b6000611783826131c5565b6000611783610bf983612f40565b33600090815260d760205260409020546126055760405162461bcd60e51b815260206004820152602660248201527f6d73672073656e646572206d757374207265717565737420776974686472617760448201526508199a5c9cdd60d21b6064820152608401610dd7565b33600081815260d760205260408082205460cf5491516331f8ee6760e11b8152600481018290526024810194909452604484019290925290916001600160a01b03909116906363f1dcce90606401611eb9565b61269782828080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250613e9d92505050565b6001600160a01b0316336001600160a01b0316146126ee5760405162461bcd60e51b81526020600482015260146024820152730e0eac4d8d2c640d6caf240dcdee840dac2e8c6d60631b6044820152606401610dd7565b60d0546040516311a38c8560e01b81526001600160a01b03909116906311a38c8590612720903090339060040161540e565b600060405180830381600087803b15801561273a57600080fd5b505af115801561274e573d6000803e3d6000fd5b505033600090815260d1602052604090206105b3925090508383614773565b60cf546040516358775aaf60e11b81526000916001600160a01b03169063b0eeb55e906127a0908590309060040161540e565b602060405180830381600087803b1580156127ba57600080fd5b505af11580156127ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117839190615107565b6127fa613a00565b6001600160a01b0381166128205760405162461bcd60e51b8152600401610dd790615097565b60dd80546001600160a01b0390921661010002610100600160a81b0319909216919091179055565b60606040518060800160405280604881526020016157ef60489139905090565b60d05460405163438bb7e560e01b81526001600160a01b039091169063438bb7e590611eb9908490339060040161540e565b60cc546001600160a01b031633146128c45760405162461bcd60e51b8152600401610dd7906153c5565b8151600181146129105760405162461bcd60e51b815260206004820152601760248201527664656c65676174696f6e206f6e6c7920666f722074737360481b6044820152606401610dd7565b81516001146129615760405162461bcd60e51b815260206004820152601d60248201527f64656c65676174696f6e207368617265206f6e6c7920666f72207473730000006044820152606401610dd7565b306001600160a01b03168360008151811061297e5761297e6150c0565b60200260200101516001600160a01b0316146129d85760405162461bcd60e51b81526020600482015260196024820152781b5d5cdd081d5cd94818dd5c9c995b9d0818dbdb9d1c9858dd603a1b6044820152606401610dd7565b816000815181106129eb576129eb6150c0565b602002602001015160cf60009054906101000a90046001600160a01b03166001600160a01b03166305579ccc8786600081518110612a2b57612a2b6150c0565b60200260200101516040518363ffffffff1660e01b8152600401612a5092919061540e565b60206040518083038186803b158015612a6857600080fd5b505afa158015612a7c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aa09190615129565b1415611cc3576001600160a01b038416600090815260d96020908152604080832080548251818502810185019093528083529192909190830182828015612b1057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311612af2575b5050505050905060005b8151811015612c6657866001600160a01b0316828281518110612b3f57612b3f6150c0565b60200260200101516001600160a01b03161415612c54576001600160a01b038616600090815260d9602052604090208251612b7c90600190615599565b81548110612b8c57612b8c6150c0565b60009182526020808320909101546001600160a01b03898116845260d99092526040909220805491909216919083908110612bc957612bc96150c0565b600091825260208083209190910180546001600160a01b0319166001600160a01b03948516179055918816815260d990915260409020805480612c0e57612c0e6155c8565b60008281526020808220830160001990810180546001600160a01b031990811690915593019093556001600160a01b038a16815260da9092526040909120805490911690555b80612c5e816150ec565b915050612b1a565b50505050505050565b612c776147e7565b60408051808201918290529060d39060029082845b815481526020019060010190808311612c8c575050505050905090565b60cf5460ca54604051631abeb0d960e01b81523060048201526001600160a01b0391821660248201526044810184905233606482015260009283921690631abeb0d990608401602060405180830381600087803b158015612d0957600080fd5b505af1158015612d1d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061257a9190615129565b60d96020528160005260406000208181548110612d5d57600080fd5b6000918252602090912001546001600160a01b03169150829050565b60ca546040516370a0823160e01b815230600482015260009182916001600160a01b03909116906370a082319060240160206040518083038186803b158015612dc157600080fd5b505afa158015612dd5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612df99190615129565b905060008111612e425760405162461bcd60e51b81526020600482015260146024820152734e6f742073756666696369656e742066756e647360601b6044820152606401610dd7565b60ca5460405163a9059cbb60e01b8152732f44bd2a54ac3fb20cd7783cf94334069641dac96004820152602481018390526001600160a01b039091169063a9059cbb90604401602060405180830381600087803b158015612ea257600080fd5b505af1158015612eb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612eda9190615107565b5060ca54604080516001600160a01b039092168252732f44bd2a54ac3fb20cd7783cf94334069641dac9602083015281018290527f3bb082152a6aeca4accd7a549583361cf3387c060a33681f9bc6f22fa6333f8c9060600160405180910390a1919050565b60c954604051634e5b005d60e11b81526000916001600160a01b031690639cb600ba90612f73908590309060040161540e565b60206040518083038186803b158015612f8b57600080fd5b505afa158015612f9f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117839190615129565b612fcb613e57565b60c9546001600160a01b03163314612ff55760405162461bcd60e51b8152600401610dd7906153c5565b60ca546001600160a01b038381169116146130835760405162461bcd60e51b815260206004820152604260248201527f44656c65676174696f6e5368617265426173652e77697468647261773a20436160448201527f6e206f6e6c792077697468647261772074686520737472617465677920746f6b60648201526132b760f11b608482015260a401610dd7565b60cb548111156131185760405162461bcd60e51b815260206004820152605460248201527f44656c65676174696f6e5368617265426173652e77697468647261773a20616d60448201527f6f756e74536861726573206d757374206265206c657373207468616e206f7220606482015273657175616c20746f20746f74616c53686172657360601b608482015260a401610dd7565b60cb805482810390915560008282141561313b57613134613d84565b905061315c565b8183613145613d84565b61314f9190615428565b6131599190615447565b90505b60ca54613173906001600160a01b03168683613efc565b604080516001600160a01b038088168252861660208201529081018290527f9b1bfa7fa9ee420a16e124f794c35ac9f90472acc99140eb2f6447c714cad8eb9060600160405180910390a15050505050565b6000806131d0613d84565b90508015806131df575060cb54155b156131eb575090919050565b8060cb54846131fa9190615428565b61257a9190615447565b50919050565b60d3816002811061321a57600080fd5b0154905081565b613229613a00565b6001600160a01b03811661328e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610dd7565b61329781613e05565b50565b600061178382612283565b336001600160a01b0383161461330e5760405162461bcd60e51b815260206004820152602860248201527f6d73672073656e64657220697320646966662077697468206f70657261746f72604482015267206164647265737360c01b6064820152608401610dd7565b60cc546040516336b87bd760e11b81523360048201526001600160a01b0390911690636d70f7ae9060240160206040518083038186803b15801561335157600080fd5b505afa158015613365573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133899190615107565b6133e35760405162461bcd60e51b815260206004820152602560248201527f6d73672073656e646572206973206e6f742072656769737465726564206f70656044820152643930ba37b960d91b6064820152608401610dd7565b6001600160a01b03818116600090815260dc6020526040902054161561344b5760405162461bcd60e51b815260206004820152601960248201527f74686520636c61696d657220686173206265656e2075736564000000000000006044820152606401610dd7565b6001600160a01b03828116600090815260db602052604090205416156134a1576001600160a01b03808316600090815260db6020908152604080832054909316825260dc90522080546001600160a01b03191690555b6001600160a01b03808316600081815260db6020908152604080832080549587166001600160a01b03199687168117909155835260dc90915280822080549094169092179092555163f5cf673b60e01b90613502908590859060240161540e565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915290506105b36020602160991b01621e848083613f4e565b33600090815260d760205260409020546135775760405162461bcd60e51b8152600401610dd7906155de565b33600090815260d8602090815260408083208151815460e09481028201850190935260c081018381529093919284928491908401828280156135e257602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116135c4575b505050505081526020016001820180548060200260200160405190810160405280929190818152602001828054801561364457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613626575b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561369c57602002820191906000526020600020905b815481526020019060010190808311613688575b505050918352505060038201546001600160a01b039081166020808401919091526040805180820182526004808701548086168352600160a01b90046001600160601b03169382019390935281850152600590940154821660609093019290925260cf54925163218c446b60e21b8152939450919091169163863111ac9161372b9133918691600191016156ad565b600060405180830381600087803b15801561374557600080fd5b505af1158015613759573d6000803e3d6000fd5b505033600090815260d76020908152604080832083905560d890915281209250905061378582826146b9565b6137936001830160006146b9565b6137a16002830160006146b9565b506003810180546001600160a01b03199081169091556000600483015560059091018054909116905550565b33600090815260d760205260408120546137f95760405162461bcd60e51b8152600401610dd7906155de565b33600090815260d8602090815260408083208151815460e09481028201850190935260c0810183815290939192849284919084018282801561386457602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311613846575b50505050508152602001600182018054806020026020016040519081016040528092919081815260200182805480156138c657602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116138a8575b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561391e57602002820191906000526020600020905b81548152602001906001019080831161390a575b505050918352505060038201546001600160a01b039081166020808401919091526040805180820182526004808701548086168352600160a01b90046001600160601b03169382019390935281850152600590940154821660609093019290925260c95492516329a8131760e11b81529394509190911691635350262e916139a8918591016156e3565b602060405180830381600087803b1580156139c257600080fd5b505af11580156139d6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139fa9190615107565b91505090565b6033546001600160a01b03163314610c5c5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610dd7565b600054610100900460ff16613a815760405162461bcd60e51b8152600401610dd7906156f6565b610c5c613fb0565b600054610100900460ff16613ab05760405162461bcd60e51b8152600401610dd7906156f6565b610c5c613fe0565b60006001600160601b03821115613b205760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203960448201526536206269747360d01b6064820152608401610dd7565b5090565b60dd5460ff16613b765760405162461bcd60e51b815260206004820152601d60248201527f68617665206e6f74207365742074686520736c61736820616d6f756e740000006044820152606401610dd7565b6020808201516001600160a01b0316600090815260d1909152604081208054613b9e90614ea2565b80601f0160208091040260200160405190810160405280929190818152602001828054613bca90614ea2565b8015613c175780601f10613bec57610100808354040283529160200191613c17565b820191906000526020600020905b815481529060010190602001808311613bfa57829003601f168201915b5093945060009350613c2892505050565b82606001516001811115613c3e57613c3e615081565b1415613cb55760ce5460405163254ff98160e01b81526001600160a01b039091169063254ff98190613c749084906004016148a8565b600060405180830381600087803b158015613c8e57600080fd5b505af1158015613ca2573d6000803e3d6000fd5b505050506109ec8260200151600061400e565b600182606001516001811115613ccd57613ccd615081565b1415613d445760ce5460405163254ff98160e01b81526001600160a01b039091169063254ff98190613d039084906004016148a8565b600060405180830381600087803b158015613d1d57600080fd5b505af1158015613d31573d6000803e3d6000fd5b505050506109ec8260200151600161400e565b60405162461bcd60e51b8152602060048201526015602482015274657272207479706520666f7220736c617368696e6760581b6044820152606401610dd7565b60ca546040516370a0823160e01b81523060048201526000916001600160a01b0316906370a082319060240160206040518083038186803b158015613dc857600080fd5b505afa158015613ddc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e009190615129565b905090565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60975460ff1615610c5c5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610dd7565b60008151604014613ef05760405162461bcd60e51b815260206004820152601f60248201527f7075626c6963206b6579206c656e677468206d757374203634206279746573006044820152606401610dd7565b50805160209091012090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526105b390849061446f565b60cd54604051633dbb202b60e01b81526001600160a01b0390911690633dbb202b90613f8290869085908790600401615741565b600060405180830381600087803b158015613f9c57600080fd5b505af1158015612c66573d6000803e3d6000fd5b600054610100900460ff16613fd75760405162461bcd60e51b8152600401610dd7906156f6565b610c5c33613e05565b600054610100900460ff166140075760405162461bcd60e51b8152600401610dd7906156f6565b6001606555565b600080614019613d84565b905060d3836002811061402e5761402e6150c0565b015460cb5460cc5460405163778e55f360e01b815284916001600160a01b03169063778e55f390614065908a90309060040161540e565b60206040518083038186803b15801561407d57600080fd5b505afa158015614091573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906140b59190615129565b6140bf9190615428565b6140c99190615447565b10156141175760405162461bcd60e51b815260206004820152601a60248201527f646f206e6f74206861766520656e6f75676874207368617265730000000000006044820152606401610dd7565b8060cb5460d3856002811061412e5761412e6150c0565b015461413a9190615428565b6141449190615447565b60cc5460405163778e55f360e01b81529193506000916001600160a01b039091169063778e55f39061417c908890309060040161540e565b60206040518083038186803b15801561419457600080fd5b505afa1580156141a8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141cc9190615129565b60408051600180825281830190925291925060009190602080830190803683370190505090503081600081518110614206576142066150c0565b6001600160a01b039290921660209283029190910190910152604080516001808252818301909252600091816020016020820280368337019050509050600081600081518110614258576142586150c0565b6020908102919091010152604080516001808252818301909252600091816020016020820280368337505060ca5482519293506001600160a01b0316918391506000906142a7576142a76150c0565b6001600160a01b03928316602091820292909201810191909152908916600090815260d982526040808220805482518186028101860190935280835292939192909183018282801561432257602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311614304575b5050505050905060005b815181101561446357600061435983838151811061434c5761434c6150c0565b6020026020010151612f40565b60408051600180825281830190925291925060009190602080830190803683370190505090508761438a838c615428565b6143949190615447565b816000815181106143a7576143a76150c0565b602090810291909101015260cf5484516001600160a01b03909116906306f1f684908690869081106143db576143db6150c0565b602090810291909101015160d6546040516001600160e01b031960e085901b16815261441c92916001600160a01b0316908c908b908d90899060040161577b565b600060405180830381600087803b15801561443657600080fd5b505af115801561444a573d6000803e3d6000fd5b505050505050808061445b906150ec565b91505061432c565b50505050505050505050565b60006144c4826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166145419092919063ffffffff16565b8051909150156105b357808060200190518101906144e29190615107565b6105b35760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610dd7565b60606145508484600085614558565b949350505050565b6060824710156145b95760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610dd7565b843b6146075760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610dd7565b600080866001600160a01b031685876040516146239190615469565b60006040518083038185875af1925050503d8060008114614660576040519150601f19603f3d011682016040523d82523d6000602084013e614665565b606091505b5091509150614675828286614680565b979650505050505050565b6060831561468f57508161257a565b82511561469f5782518084602001fd5b8160405162461bcd60e51b8152600401610dd791906148a8565b50805460008255906000526020600020908101906132979190614805565b82805482825590600052602060002090810192821561472c579160200282015b8281111561472c57825182546001600160a01b0319166001600160a01b039091161782556020909201916001909101906146f7565b50613b20929150614805565b82805482825590600052602060002090810192821561472c579160200282015b8281111561472c578251825591602001919060010190614758565b82805461477f90614ea2565b90600052602060002090601f0160209004810192826147a1576000855561472c565b82601f106147ba5782800160ff1982351617855561472c565b8280016001018555821561472c579182015b8281111561472c5782358255916020019190600101906147cc565b60405180604001604052806002906020820280368337509192915050565b5b80821115613b205760008155600101614806565b6001600160a01b038116811461329757600080fd5b60006020828403121561484157600080fd5b813561257a8161481a565b60005b8381101561486757818101518382015260200161484f565b83811115614876576000848401525b50505050565b6000815180845261489481602086016020860161484c565b601f01601f19169290920160200192915050565b60208152600061257a602083018461487c565b6000604082840312156148cd57600080fd5b8260408301111561320457600080fd5b600080600080600080600060e0888a0312156148f857600080fd5b87356149038161481a565b965060208801356149138161481a565b955060408801356149238161481a565b945060608801356149338161481a565b935060808801356149438161481a565b925060a08801356149538161481a565b915060c08801356149638161481a565b8091505092959891949750929550565b634e487b7160e01b600052604160045260246000fd5b6040516060810167ffffffffffffffff811182821017156149ac576149ac614973565b60405290565b6040516080810167ffffffffffffffff811182821017156149ac576149ac614973565b604051601f8201601f1916810167ffffffffffffffff811182821017156149fe576149fe614973565b604052919050565b600067ffffffffffffffff821115614a2057614a20614973565b5060051b60200190565b600082601f830112614a3b57600080fd5b81356020614a50614a4b83614a06565b6149d5565b82815260059290921b84018101918181019086841115614a6f57600080fd5b8286015b84811015614a93578035614a868161481a565b8352918301918301614a73565b509695505050505050565b600082601f830112614aaf57600080fd5b81356020614abf614a4b83614a06565b82815260059290921b84018101918181019086841115614ade57600080fd5b8286015b84811015614a935780358352918301918301614ae2565b600080600060608486031215614b0e57600080fd5b8335614b198161481a565b9250602084013567ffffffffffffffff80821115614b3657600080fd5b614b4287838801614a2a565b93506040860135915080821115614b5857600080fd5b50614b6586828701614a9e565b9150509250925092565b6020808252825182820181905260009190848201906040850190845b81811015614bb05783516001600160a01b031683529284019291840191600101614b8b565b50909695505050505050565b600060208284031215614bce57600080fd5b5035919050565b60008083601f840112614be757600080fd5b50813567ffffffffffffffff811115614bff57600080fd5b602083019150836020828501011115614c1757600080fd5b9250929050565b60008060008060408587031215614c3457600080fd5b843567ffffffffffffffff80821115614c4c57600080fd5b614c5888838901614bd5565b90965094506020870135915080821115614c7157600080fd5b50614c7e87828801614bd5565b95989497509550505050565b60008060008060808587031215614ca057600080fd5b8435614cab8161481a565b93506020850135614cbb8161481a565b9250604085013567ffffffffffffffff80821115614cd857600080fd5b614ce488838901614a2a565b93506060870135915080821115614cfa57600080fd5b50614d0787828801614a9e565b91505092959194509250565b80516001600160a01b031682526020908101516001600160601b0316910152565b6001600160a01b0384811682526080820190614d536020840186614d13565b808416606084015250949350505050565b60008060408385031215614d7757600080fd5b823591506020830135614d898161481a565b809150509250929050565b600080600060608486031215614da957600080fd5b8335614db48161481a565b92506020840135614dc48161481a565b929592945050506040919091013590565b60008060208385031215614de857600080fd5b823567ffffffffffffffff811115614dff57600080fd5b614e0b85828601614bd5565b90969095509350505050565b60408101818360005b6002811015614e3f578151835260209283019290910190600101614e20565b50505092915050565b60008060408385031215614e5b57600080fd5b8235614e668161481a565b946020939093013593505050565b60008060408385031215614e8757600080fd5b8235614e928161481a565b91506020830135614d898161481a565b600181811c90821680614eb657607f821691505b6020821081141561320457634e487b7160e01b600052602260045260246000fd5b600060208083526000845481600182811c915080831680614ef957607f831692505b858310811415614f1757634e487b7160e01b85526022600452602485fd5b878601838152602001818015614f345760018114614f4557614f70565b60ff19861682528782019650614f70565b60008b81526020902060005b86811015614f6a57815484820152908501908901614f51565b83019750505b50949998505050505050505050565b80516122b48161481a565b6002811061329757600080fd5b80516122b481614f8a565b60006020808385031215614fb557600080fd5b825167ffffffffffffffff80821115614fcd57600080fd5b9084019060608287031215614fe157600080fd5b614fe9614989565b825182811115614ff857600080fd5b8301601f8101881361500957600080fd5b80518381111561501b5761501b614973565b61502d601f8201601f191687016149d5565b9350808452888682840101111561504357600080fd5b6150528187860188850161484c565b5050818152615062848401614f7f565b8482015261507260408401614f97565b60408201529695505050505050565b634e487b7160e01b600052602160045260246000fd5b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b6000600019821415615100576151006150d6565b5060010190565b60006020828403121561511957600080fd5b8151801515811461257a57600080fd5b60006020828403121561513b57600080fd5b5051919050565b60006020828403121561515457600080fd5b815161257a8161481a565b600081518084526020808501945080840160005b8381101561518f57815187529582019590820190600101615173565b509495945050505050565b600081518084526020808501945080840160005b8381101561518f5781516001600160a01b0316875295820195908201906001016151ae565b6001600160a01b038716815260e0602082018190526000906151f79083018861515f565b8281036040840152615209818861519a565b9050828103606084015261521d818761519a565b90508281036080840152615231818661515f565b91505061467560a0830184614d13565b80356122b481614f8a565b6000602080838503121561525f57600080fd5b823567ffffffffffffffff8082111561527757600080fd5b908401906080828703121561528b57600080fd5b6152936149b2565b82358152838301356152a48161481a565b818501526040830135828111156152ba57600080fd5b83019150601f820187136152cd57600080fd5b81356152db614a4b82614a06565b81815260059190911b830185019085810190898311156152fa57600080fd5b938601935b828510156153215784356153128161481a565b825293860193908601906152ff565b604084015250615335905060608401615241565b60608201529695505050505050565b8183823760009101908152919050565b83815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b6001600160a01b038316815260408101600283106153b857634e487b7160e01b600052602160045260246000fd5b8260208301529392505050565b60208082526029908201527f44656c65676174696f6e5368617265426173652e6f6e6c7944656c656761746960408201526837b726b0b730b3b2b960b91b606082015260800190565b6001600160a01b0392831681529116602082015260400190565b6000816000190483118215151615615442576154426150d6565b500290565b60008261546457634e487b7160e01b600052601260045260246000fd5b500490565b6000825161547b81846020870161484c565b9190910192915050565b805160208083015191908110156132045760001960209190910360031b1b16919050565b600181815b808511156154e45781600019048211156154ca576154ca6150d6565b808516156154d757918102915b93841c93908002906154ae565b509250929050565b6000826154fb57506001611783565b8161550857506000611783565b816001811461551e576002811461552857615544565b6001915050611783565b60ff841115615539576155396150d6565b50506001821b611783565b5060208310610133831016604e8410600b8410161715615567575081810a611783565b61557183836154a9565b8060001904821115615585576155856150d6565b029392505050565b600061257a83836154ec565b6000828210156155ab576155ab6150d6565b500390565b600082198211156155c3576155c36150d6565b500190565b634e487b7160e01b600052603160045260246000fd5b60208082526024908201527f6d73672073656e64657220646964206e6f7420726571756573742077697468646040820152637261777360e01b606082015260800190565b6000815160e0845261563760e085018261519a565b905060208301518482036020860152615650828261519a565b9150506040830151848203604086015261566a828261515f565b915050606083015160018060a01b038082166060870152608085015191506156956080870183614d13565b8060a08601511660c087015250508091505092915050565b6001600160a01b03841681526060602082018190526000906156d190830185615622565b90508215156040830152949350505050565b60208152600061257a6020830184615622565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6001600160a01b03841681526060602082018190526000906157659083018561487c565b905063ffffffff83166040830152949350505050565b6001600160a01b0387811682528616602082015260c0604082018190526000906157a79083018761519a565b82810360608401526157b9818761519a565b905082810360808401526157cd818661515f565b905082810360a08401526157e1818561515f565b999850505050505050505056fe4d616e746c6520746f6b656e2044656c65676174696f6e536861726520696d706c656d656e746174696f6e20666f72207375626d6f64756c657320617320616e206578616d706c65a2646970667358221220e57271eade17a5b142954064cbb2d1593ec6948af13ee3ae82a9524376f3306b64736f6c63430008090033
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.