Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
StakeManagerExtension
Compiler Version
v0.5.17+commit.d19bba13
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity)
/**
*Submitted for verification at Etherscan.io on 2023-08-07
*/
pragma solidity ^0.5.2;
/**
* @title ERC20 interface
* @dev see https://eips.ethereum.org/EIPS/eip-20
*/
interface IERC20 {
function transfer(address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
/**
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
interface IGovernance {
function update(address target, bytes calldata data) external;
}
contract Governable {
IGovernance public governance;
constructor(address _governance) public {
governance = IGovernance(_governance);
}
modifier onlyGovernance() {
_assertGovernance();
_;
}
function _assertGovernance() private view {
require(
msg.sender == address(governance),
"Only governance contract is authorized"
);
}
}
contract IWithdrawManager {
function createExitQueue(address token) external;
function verifyInclusion(
bytes calldata data,
uint8 offset,
bool verifyTxInclusion
) external view returns (uint256 age);
function addExitToQueue(
address exitor,
address childToken,
address rootToken,
uint256 exitAmountOrTokenId,
bytes32 txHash,
bool isRegularExit,
uint256 priority
) external;
function addInput(
uint256 exitId,
uint256 age,
address utxoOwner,
address token
) external;
function challengeExit(
uint256 exitId,
uint256 inputId,
bytes calldata challengeData,
address adjudicatorPredicate
) external;
}
contract Registry is Governable {
// @todo hardcode constants
bytes32 private constant WETH_TOKEN = keccak256("wethToken");
bytes32 private constant DEPOSIT_MANAGER = keccak256("depositManager");
bytes32 private constant STAKE_MANAGER = keccak256("stakeManager");
bytes32 private constant VALIDATOR_SHARE = keccak256("validatorShare");
bytes32 private constant WITHDRAW_MANAGER = keccak256("withdrawManager");
bytes32 private constant CHILD_CHAIN = keccak256("childChain");
bytes32 private constant STATE_SENDER = keccak256("stateSender");
bytes32 private constant SLASHING_MANAGER = keccak256("slashingManager");
address public erc20Predicate;
address public erc721Predicate;
mapping(bytes32 => address) public contractMap;
mapping(address => address) public rootToChildToken;
mapping(address => address) public childToRootToken;
mapping(address => bool) public proofValidatorContracts;
mapping(address => bool) public isERC721;
enum Type {Invalid, ERC20, ERC721, Custom}
struct Predicate {
Type _type;
}
mapping(address => Predicate) public predicates;
event TokenMapped(address indexed rootToken, address indexed childToken);
event ProofValidatorAdded(address indexed validator, address indexed from);
event ProofValidatorRemoved(address indexed validator, address indexed from);
event PredicateAdded(address indexed predicate, address indexed from);
event PredicateRemoved(address indexed predicate, address indexed from);
event ContractMapUpdated(bytes32 indexed key, address indexed previousContract, address indexed newContract);
constructor(address _governance) public Governable(_governance) {}
function updateContractMap(bytes32 _key, address _address) external onlyGovernance {
emit ContractMapUpdated(_key, contractMap[_key], _address);
contractMap[_key] = _address;
}
/**
* @dev Map root token to child token
* @param _rootToken Token address on the root chain
* @param _childToken Token address on the child chain
* @param _isERC721 Is the token being mapped ERC721
*/
function mapToken(
address _rootToken,
address _childToken,
bool _isERC721
) external onlyGovernance {
require(_rootToken != address(0x0) && _childToken != address(0x0), "INVALID_TOKEN_ADDRESS");
rootToChildToken[_rootToken] = _childToken;
childToRootToken[_childToken] = _rootToken;
isERC721[_rootToken] = _isERC721;
IWithdrawManager(contractMap[WITHDRAW_MANAGER]).createExitQueue(_rootToken);
emit TokenMapped(_rootToken, _childToken);
}
function addErc20Predicate(address predicate) public onlyGovernance {
require(predicate != address(0x0), "Can not add null address as predicate");
erc20Predicate = predicate;
addPredicate(predicate, Type.ERC20);
}
function addErc721Predicate(address predicate) public onlyGovernance {
erc721Predicate = predicate;
addPredicate(predicate, Type.ERC721);
}
function addPredicate(address predicate, Type _type) public onlyGovernance {
require(predicates[predicate]._type == Type.Invalid, "Predicate already added");
predicates[predicate]._type = _type;
emit PredicateAdded(predicate, msg.sender);
}
function removePredicate(address predicate) public onlyGovernance {
require(predicates[predicate]._type != Type.Invalid, "Predicate does not exist");
delete predicates[predicate];
emit PredicateRemoved(predicate, msg.sender);
}
function getValidatorShareAddress() public view returns (address) {
return contractMap[VALIDATOR_SHARE];
}
function getWethTokenAddress() public view returns (address) {
return contractMap[WETH_TOKEN];
}
function getDepositManagerAddress() public view returns (address) {
return contractMap[DEPOSIT_MANAGER];
}
function getStakeManagerAddress() public view returns (address) {
return contractMap[STAKE_MANAGER];
}
function getSlashingManagerAddress() public view returns (address) {
return contractMap[SLASHING_MANAGER];
}
function getWithdrawManagerAddress() public view returns (address) {
return contractMap[WITHDRAW_MANAGER];
}
function getChildChainAndStateSender() public view returns (address, address) {
return (contractMap[CHILD_CHAIN], contractMap[STATE_SENDER]);
}
function isTokenMapped(address _token) public view returns (bool) {
return rootToChildToken[_token] != address(0x0);
}
function isTokenMappedAndIsErc721(address _token) public view returns (bool) {
require(isTokenMapped(_token), "TOKEN_NOT_MAPPED");
return isERC721[_token];
}
function isTokenMappedAndGetPredicate(address _token) public view returns (address) {
if (isTokenMappedAndIsErc721(_token)) {
return erc721Predicate;
}
return erc20Predicate;
}
function isChildTokenErc721(address childToken) public view returns (bool) {
address rootToken = childToRootToken[childToken];
require(rootToken != address(0x0), "Child token is not mapped");
return isERC721[rootToken];
}
}
contract Lockable {
bool public locked;
modifier onlyWhenUnlocked() {
_assertUnlocked();
_;
}
function _assertUnlocked() private view {
require(!locked, "locked");
}
function lock() public {
locked = true;
}
function unlock() public {
locked = false;
}
}
contract GovernanceLockable is Lockable, Governable {
constructor(address governance) public Governable(governance) {}
function lock() public onlyGovernance {
super.lock();
}
function unlock() public onlyGovernance {
super.unlock();
}
}
contract IStakeManager {
// validator replacement
function startAuction(
uint256 validatorId,
uint256 amount,
bool acceptDelegation,
bytes calldata signerPubkey
) external;
function confirmAuctionBid(uint256 validatorId, uint256 heimdallFee) external;
function transferFunds(
uint256 validatorId,
uint256 amount,
address delegator
) external returns (bool);
function delegationDeposit(
uint256 validatorId,
uint256 amount,
address delegator
) external returns (bool);
function unstake(uint256 validatorId) external;
function totalStakedFor(address addr) external view returns (uint256);
function stakeFor(
address user,
uint256 amount,
uint256 heimdallFee,
bool acceptDelegation,
bytes memory signerPubkey
) public;
function checkSignatures(
uint256 blockInterval,
bytes32 voteHash,
bytes32 stateRoot,
address proposer,
uint[3][] calldata sigs
) external returns (uint256);
function updateValidatorState(uint256 validatorId, int256 amount) public;
function ownerOf(uint256 tokenId) public view returns (address);
function slash(bytes calldata slashingInfoList) external returns (uint256);
function validatorStake(uint256 validatorId) public view returns (uint256);
function epoch() public view returns (uint256);
function getRegistry() public view returns (address);
function withdrawalDelay() public view returns (uint256);
function delegatedAmount(uint256 validatorId) public view returns(uint256);
function decreaseValidatorDelegatedAmount(uint256 validatorId, uint256 amount) public;
function withdrawDelegatorsReward(uint256 validatorId) public returns(uint256);
function delegatorsReward(uint256 validatorId) public view returns(uint256);
function dethroneAndStake(
address auctionUser,
uint256 heimdallFee,
uint256 validatorId,
uint256 auctionAmount,
bool acceptDelegation,
bytes calldata signerPubkey
) external;
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
* @notice Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
/**
* @title RootChainable
*/
contract RootChainable is Ownable {
address public rootChain;
// Rootchain changed
event RootChainChanged(
address indexed previousRootChain,
address indexed newRootChain
);
// only root chain
modifier onlyRootChain() {
require(msg.sender == rootChain);
_;
}
/**
* @dev Allows the current owner to change root chain address.
* @param newRootChain The address to new rootchain.
*/
function changeRootChain(address newRootChain) public onlyOwner {
require(newRootChain != address(0));
emit RootChainChanged(rootChain, newRootChain);
rootChain = newRootChain;
}
}
// dummy interface to avoid cyclic dependency
contract IStakeManagerLocal {
enum Status {Inactive, Active, Locked, Unstaked}
struct Validator {
uint256 amount;
uint256 reward;
uint256 activationEpoch;
uint256 deactivationEpoch;
uint256 jailTime;
address signer;
address contractAddress;
Status status;
}
mapping(uint256 => Validator) public validators;
bytes32 public accountStateRoot;
uint256 public activeAmount; // delegation amount from validator contract
uint256 public validatorRewards;
function currentValidatorSetTotalStake() public view returns (uint256);
// signer to Validator mapping
function signerToValidator(address validatorAddress)
public
view
returns (uint256);
function isValidator(uint256 validatorId) public view returns (bool);
}
contract StakingInfo is Ownable {
using SafeMath for uint256;
mapping(uint256 => uint256) public validatorNonce;
/// @dev Emitted when validator stakes in '_stakeFor()' in StakeManager.
/// @param signer validator address.
/// @param validatorId unique integer to identify a validator.
/// @param nonce to synchronize the events in heimdal.
/// @param activationEpoch validator's first epoch as proposer.
/// @param amount staking amount.
/// @param total total staking amount.
/// @param signerPubkey public key of the validator
event Staked(
address indexed signer,
uint256 indexed validatorId,
uint256 nonce,
uint256 indexed activationEpoch,
uint256 amount,
uint256 total,
bytes signerPubkey
);
/// @dev Emitted when validator unstakes in 'unstakeClaim()'
/// @param user address of the validator.
/// @param validatorId unique integer to identify a validator.
/// @param amount staking amount.
/// @param total total staking amount.
event Unstaked(
address indexed user,
uint256 indexed validatorId,
uint256 amount,
uint256 total
);
/// @dev Emitted when validator unstakes in '_unstake()'.
/// @param user address of the validator.
/// @param validatorId unique integer to identify a validator.
/// @param nonce to synchronize the events in heimdal.
/// @param deactivationEpoch last epoch for validator.
/// @param amount staking amount.
event UnstakeInit(
address indexed user,
uint256 indexed validatorId,
uint256 nonce,
uint256 deactivationEpoch,
uint256 indexed amount
);
/// @dev Emitted when the validator public key is updated in 'updateSigner()'.
/// @param validatorId unique integer to identify a validator.
/// @param nonce to synchronize the events in heimdal.
/// @param oldSigner old address of the validator.
/// @param newSigner new address of the validator.
/// @param signerPubkey public key of the validator.
event SignerChange(
uint256 indexed validatorId,
uint256 nonce,
address indexed oldSigner,
address indexed newSigner,
bytes signerPubkey
);
event Restaked(uint256 indexed validatorId, uint256 amount, uint256 total);
event Jailed(
uint256 indexed validatorId,
uint256 indexed exitEpoch,
address indexed signer
);
event UnJailed(uint256 indexed validatorId, address indexed signer);
event Slashed(uint256 indexed nonce, uint256 indexed amount);
event ThresholdChange(uint256 newThreshold, uint256 oldThreshold);
event DynastyValueChange(uint256 newDynasty, uint256 oldDynasty);
event ProposerBonusChange(
uint256 newProposerBonus,
uint256 oldProposerBonus
);
event RewardUpdate(uint256 newReward, uint256 oldReward);
/// @dev Emitted when validator confirms the auction bid and at the time of restaking in confirmAuctionBid() and restake().
/// @param validatorId unique integer to identify a validator.
/// @param nonce to synchronize the events in heimdal.
/// @param newAmount the updated stake amount.
event StakeUpdate(
uint256 indexed validatorId,
uint256 indexed nonce,
uint256 indexed newAmount
);
event ClaimRewards(
uint256 indexed validatorId,
uint256 indexed amount,
uint256 indexed totalAmount
);
event StartAuction(
uint256 indexed validatorId,
uint256 indexed amount,
uint256 indexed auctionAmount
);
event ConfirmAuction(
uint256 indexed newValidatorId,
uint256 indexed oldValidatorId,
uint256 indexed amount
);
event TopUpFee(address indexed user, uint256 indexed fee);
event ClaimFee(address indexed user, uint256 indexed fee);
// Delegator events
event ShareMinted(
uint256 indexed validatorId,
address indexed user,
uint256 indexed amount,
uint256 tokens
);
event ShareBurned(
uint256 indexed validatorId,
address indexed user,
uint256 indexed amount,
uint256 tokens
);
event DelegatorClaimedRewards(
uint256 indexed validatorId,
address indexed user,
uint256 indexed rewards
);
event DelegatorRestaked(
uint256 indexed validatorId,
address indexed user,
uint256 indexed totalStaked
);
event DelegatorUnstaked(
uint256 indexed validatorId,
address indexed user,
uint256 amount
);
event UpdateCommissionRate(
uint256 indexed validatorId,
uint256 indexed newCommissionRate,
uint256 indexed oldCommissionRate
);
Registry public registry;
modifier onlyValidatorContract(uint256 validatorId) {
address _contract;
(, , , , , , _contract, ) = IStakeManagerLocal(
registry.getStakeManagerAddress()
)
.validators(validatorId);
require(_contract == msg.sender,
"Invalid sender, not validator");
_;
}
modifier StakeManagerOrValidatorContract(uint256 validatorId) {
address _contract;
address _stakeManager = registry.getStakeManagerAddress();
(, , , , , , _contract, ) = IStakeManagerLocal(_stakeManager).validators(
validatorId
);
require(_contract == msg.sender || _stakeManager == msg.sender,
"Invalid sender, not stake manager or validator contract");
_;
}
modifier onlyStakeManager() {
require(registry.getStakeManagerAddress() == msg.sender,
"Invalid sender, not stake manager");
_;
}
modifier onlySlashingManager() {
require(registry.getSlashingManagerAddress() == msg.sender,
"Invalid sender, not slashing manager");
_;
}
constructor(address _registry) public {
registry = Registry(_registry);
}
function updateNonce(
uint256[] calldata validatorIds,
uint256[] calldata nonces
) external onlyOwner {
require(validatorIds.length == nonces.length, "args length mismatch");
for (uint256 i = 0; i < validatorIds.length; ++i) {
validatorNonce[validatorIds[i]] = nonces[i];
}
}
function logStaked(
address signer,
bytes memory signerPubkey,
uint256 validatorId,
uint256 activationEpoch,
uint256 amount,
uint256 total
) public onlyStakeManager {
validatorNonce[validatorId] = validatorNonce[validatorId].add(1);
emit Staked(
signer,
validatorId,
validatorNonce[validatorId],
activationEpoch,
amount,
total,
signerPubkey
);
}
function logUnstaked(
address user,
uint256 validatorId,
uint256 amount,
uint256 total
) public onlyStakeManager {
emit Unstaked(user, validatorId, amount, total);
}
function logUnstakeInit(
address user,
uint256 validatorId,
uint256 deactivationEpoch,
uint256 amount
) public onlyStakeManager {
validatorNonce[validatorId] = validatorNonce[validatorId].add(1);
emit UnstakeInit(
user,
validatorId,
validatorNonce[validatorId],
deactivationEpoch,
amount
);
}
function logSignerChange(
uint256 validatorId,
address oldSigner,
address newSigner,
bytes memory signerPubkey
) public onlyStakeManager {
validatorNonce[validatorId] = validatorNonce[validatorId].add(1);
emit SignerChange(
validatorId,
validatorNonce[validatorId],
oldSigner,
newSigner,
signerPubkey
);
}
function logRestaked(uint256 validatorId, uint256 amount, uint256 total)
public
onlyStakeManager
{
emit Restaked(validatorId, amount, total);
}
function logJailed(uint256 validatorId, uint256 exitEpoch, address signer)
public
onlyStakeManager
{
emit Jailed(validatorId, exitEpoch, signer);
}
function logUnjailed(uint256 validatorId, address signer)
public
onlyStakeManager
{
emit UnJailed(validatorId, signer);
}
function logSlashed(uint256 nonce, uint256 amount)
public
onlySlashingManager
{
emit Slashed(nonce, amount);
}
function logThresholdChange(uint256 newThreshold, uint256 oldThreshold)
public
onlyStakeManager
{
emit ThresholdChange(newThreshold, oldThreshold);
}
function logDynastyValueChange(uint256 newDynasty, uint256 oldDynasty)
public
onlyStakeManager
{
emit DynastyValueChange(newDynasty, oldDynasty);
}
function logProposerBonusChange(
uint256 newProposerBonus,
uint256 oldProposerBonus
) public onlyStakeManager {
emit ProposerBonusChange(newProposerBonus, oldProposerBonus);
}
function logRewardUpdate(uint256 newReward, uint256 oldReward)
public
onlyStakeManager
{
emit RewardUpdate(newReward, oldReward);
}
function logStakeUpdate(uint256 validatorId)
public
StakeManagerOrValidatorContract(validatorId)
{
validatorNonce[validatorId] = validatorNonce[validatorId].add(1);
emit StakeUpdate(
validatorId,
validatorNonce[validatorId],
totalValidatorStake(validatorId)
);
}
function logClaimRewards(
uint256 validatorId,
uint256 amount,
uint256 totalAmount
) public onlyStakeManager {
emit ClaimRewards(validatorId, amount, totalAmount);
}
function logStartAuction(
uint256 validatorId,
uint256 amount,
uint256 auctionAmount
) public onlyStakeManager {
emit StartAuction(validatorId, amount, auctionAmount);
}
function logConfirmAuction(
uint256 newValidatorId,
uint256 oldValidatorId,
uint256 amount
) public onlyStakeManager {
emit ConfirmAuction(newValidatorId, oldValidatorId, amount);
}
function logTopUpFee(address user, uint256 fee) public onlyStakeManager {
emit TopUpFee(user, fee);
}
function logClaimFee(address user, uint256 fee) public onlyStakeManager {
emit ClaimFee(user, fee);
}
function getStakerDetails(uint256 validatorId)
public
view
returns (
uint256 amount,
uint256 reward,
uint256 activationEpoch,
uint256 deactivationEpoch,
address signer,
uint256 _status
)
{
IStakeManagerLocal stakeManager = IStakeManagerLocal(
registry.getStakeManagerAddress()
);
address _contract;
IStakeManagerLocal.Status status;
(
amount,
reward,
activationEpoch,
deactivationEpoch,
,
signer,
_contract,
status
) = stakeManager.validators(validatorId);
_status = uint256(status);
if (_contract != address(0x0)) {
reward += IStakeManagerLocal(_contract).validatorRewards();
}
}
function totalValidatorStake(uint256 validatorId)
public
view
returns (uint256 validatorStake)
{
address contractAddress;
(validatorStake, , , , , , contractAddress, ) = IStakeManagerLocal(
registry.getStakeManagerAddress()
)
.validators(validatorId);
if (contractAddress != address(0x0)) {
validatorStake += IStakeManagerLocal(contractAddress).activeAmount();
}
}
function getAccountStateRoot()
public
view
returns (bytes32 accountStateRoot)
{
accountStateRoot = IStakeManagerLocal(registry.getStakeManagerAddress())
.accountStateRoot();
}
function getValidatorContractAddress(uint256 validatorId)
public
view
returns (address ValidatorContract)
{
(, , , , , , ValidatorContract, ) = IStakeManagerLocal(
registry.getStakeManagerAddress()
)
.validators(validatorId);
}
// validator Share contract logging func
function logShareMinted(
uint256 validatorId,
address user,
uint256 amount,
uint256 tokens
) public onlyValidatorContract(validatorId) {
emit ShareMinted(validatorId, user, amount, tokens);
}
function logShareBurned(
uint256 validatorId,
address user,
uint256 amount,
uint256 tokens
) public onlyValidatorContract(validatorId) {
emit ShareBurned(validatorId, user, amount, tokens);
}
function logDelegatorClaimRewards(
uint256 validatorId,
address user,
uint256 rewards
) public onlyValidatorContract(validatorId) {
emit DelegatorClaimedRewards(validatorId, user, rewards);
}
function logDelegatorRestaked(
uint256 validatorId,
address user,
uint256 totalStaked
) public onlyValidatorContract(validatorId) {
emit DelegatorRestaked(validatorId, user, totalStaked);
}
function logDelegatorUnstaked(uint256 validatorId, address user, uint256 amount)
public
onlyValidatorContract(validatorId)
{
emit DelegatorUnstaked(validatorId, user, amount);
}
// deprecated
function logUpdateCommissionRate(
uint256 validatorId,
uint256 newCommissionRate,
uint256 oldCommissionRate
) public onlyValidatorContract(validatorId) {
emit UpdateCommissionRate(
validatorId,
newCommissionRate,
oldCommissionRate
);
}
}
/**
* @title IERC165
* @dev https://eips.ethereum.org/EIPS/eip-165
*/
interface IERC165 {
/**
* @notice Query if a contract implements an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @dev Interface identification is specified in ERC-165. This function
* uses less than 30,000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
/**
* @title ERC721 Non-Fungible Token Standard basic interface
* @dev see https://eips.ethereum.org/EIPS/eip-721
*/
contract IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) public view returns (uint256 balance);
function ownerOf(uint256 tokenId) public view returns (address owner);
function approve(address to, uint256 tokenId) public;
function getApproved(uint256 tokenId) public view returns (address operator);
function setApprovalForAll(address operator, bool _approved) public;
function isApprovedForAll(address owner, address operator) public view returns (bool);
function transferFrom(address from, address to, uint256 tokenId) public;
function safeTransferFrom(address from, address to, uint256 tokenId) public;
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public;
}
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
contract IERC721Receiver {
/**
* @notice Handle the receipt of an NFT
* @dev The ERC721 smart contract calls this function on the recipient
* after a `safeTransfer`. This function MUST return the function selector,
* otherwise the caller will revert the transaction. The selector to be
* returned can be obtained as `this.onERC721Received.selector`. This
* function MAY throw to revert and reject the transfer.
* Note: the ERC721 contract address is always the message sender.
* @param operator The address which called `safeTransferFrom` function
* @param from The address which previously owned the token
* @param tokenId The NFT identifier which is being transferred
* @param data Additional data with no specified format
* @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
*/
function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data)
public returns (bytes4);
}
/**
* Utility library of inline functions on addresses
*/
library Address {
/**
* Returns whether the target address is a contract
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param account address of the account to check
* @return whether the target address is a contract
*/
function isContract(address account) internal view returns (bool) {
uint256 size;
// XXX Currently there is no better way to check if there is a contract in an address
// than to check the size of the code at that address.
// See https://ethereum.stackexchange.com/a/14016/36603
// for more details about how this works.
// TODO Check this again before the Serenity release, because all addresses will be
// contracts then.
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}
/**
* @title Counters
* @author Matt Condon (@shrugs)
* @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number
* of elements in a mapping, issuing ERC721 ids, or counting request ids
*
* Include with `using Counters for Counters.Counter;`
* Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the SafeMath
* overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never
* directly accessed.
*/
library Counters {
using SafeMath for uint256;
struct Counter {
// This variable should never be directly accessed by users of the library: interactions must be restricted to
// the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add
// this feature: see https://github.com/ethereum/solidity/issues/4637
uint256 _value; // default: 0
}
function current(Counter storage counter) internal view returns (uint256) {
return counter._value;
}
function increment(Counter storage counter) internal {
counter._value += 1;
}
function decrement(Counter storage counter) internal {
counter._value = counter._value.sub(1);
}
}
/**
* @title ERC165
* @author Matt Condon (@shrugs)
* @dev Implements ERC165 using a lookup table.
*/
contract ERC165 is IERC165 {
bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;
/*
* 0x01ffc9a7 ===
* bytes4(keccak256('supportsInterface(bytes4)'))
*/
/**
* @dev a mapping of interface id to whether or not it's supported
*/
mapping(bytes4 => bool) private _supportedInterfaces;
/**
* @dev A contract implementing SupportsInterfaceWithLookup
* implement ERC165 itself
*/
constructor () internal {
_registerInterface(_INTERFACE_ID_ERC165);
}
/**
* @dev implement supportsInterface(bytes4) using a lookup table
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool) {
return _supportedInterfaces[interfaceId];
}
/**
* @dev internal method for registering an interface
*/
function _registerInterface(bytes4 interfaceId) internal {
require(interfaceId != 0xffffffff);
_supportedInterfaces[interfaceId] = true;
}
}
/**
* @title ERC721 Non-Fungible Token Standard basic implementation
* @dev see https://eips.ethereum.org/EIPS/eip-721
*/
contract ERC721 is ERC165, IERC721 {
using SafeMath for uint256;
using Address for address;
using Counters for Counters.Counter;
// Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
// which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`
bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;
// Mapping from token ID to owner
mapping (uint256 => address) private _tokenOwner;
// Mapping from token ID to approved address
mapping (uint256 => address) private _tokenApprovals;
// Mapping from owner to number of owned token
mapping (address => Counters.Counter) private _ownedTokensCount;
// Mapping from owner to operator approvals
mapping (address => mapping (address => bool)) private _operatorApprovals;
bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;
/*
* 0x80ac58cd ===
* bytes4(keccak256('balanceOf(address)')) ^
* bytes4(keccak256('ownerOf(uint256)')) ^
* bytes4(keccak256('approve(address,uint256)')) ^
* bytes4(keccak256('getApproved(uint256)')) ^
* bytes4(keccak256('setApprovalForAll(address,bool)')) ^
* bytes4(keccak256('isApprovedForAll(address,address)')) ^
* bytes4(keccak256('transferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^
* bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)'))
*/
constructor () public {
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721);
}
/**
* @dev Gets the balance of the specified address
* @param owner address to query the balance of
* @return uint256 representing the amount owned by the passed address
*/
function balanceOf(address owner) public view returns (uint256) {
require(owner != address(0));
return _ownedTokensCount[owner].current();
}
/**
* @dev Gets the owner of the specified token ID
* @param tokenId uint256 ID of the token to query the owner of
* @return address currently marked as the owner of the given token ID
*/
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _tokenOwner[tokenId];
require(owner != address(0));
return owner;
}
/**
* @dev Approves another address to transfer the given token ID
* The zero address indicates there is no approved address.
* There can only be one approved address per token at a given time.
* Can only be called by the token owner or an approved operator.
* @param to address to be approved for the given token ID
* @param tokenId uint256 ID of the token to be approved
*/
function approve(address to, uint256 tokenId) public {
address owner = ownerOf(tokenId);
require(to != owner);
require(msg.sender == owner || isApprovedForAll(owner, msg.sender));
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
}
/**
* @dev Gets the approved address for a token ID, or zero if no address set
* Reverts if the token ID does not exist.
* @param tokenId uint256 ID of the token to query the approval of
* @return address currently approved for the given token ID
*/
function getApproved(uint256 tokenId) public view returns (address) {
require(_exists(tokenId));
return _tokenApprovals[tokenId];
}
/**
* @dev Sets or unsets the approval of a given operator
* An operator is allowed to transfer all tokens of the sender on their behalf
* @param to operator address to set the approval
* @param approved representing the status of the approval to be set
*/
function setApprovalForAll(address to, bool approved) public {
require(to != msg.sender);
_operatorApprovals[msg.sender][to] = approved;
emit ApprovalForAll(msg.sender, to, approved);
}
/**
* @dev Tells whether an operator is approved by a given owner
* @param owner owner address which you want to query the approval of
* @param operator operator address which you want to query the approval of
* @return bool whether the given operator is approved by the given owner
*/
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev Transfers the ownership of a given token ID to another address
* Usage of this method is discouraged, use `safeTransferFrom` whenever possible
* Requires the msg.sender to be the owner, approved, or operator
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function transferFrom(address from, address to, uint256 tokenId) public {
require(_isApprovedOrOwner(msg.sender, tokenId));
_transferFrom(from, to, tokenId);
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement `onERC721Received`,
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
* the transfer is reverted.
* Requires the msg.sender to be the owner, approved, or operator
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev Safely transfers the ownership of a given token ID to another address
* If the target address is a contract, it must implement `onERC721Received`,
* which is called upon a safe transfer, and return the magic value
* `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise,
* the transfer is reverted.
* Requires the msg.sender to be the owner, approved, or operator
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes data to send along with a safe transfer check
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
transferFrom(from, to, tokenId);
require(_checkOnERC721Received(from, to, tokenId, _data));
}
/**
* @dev Returns whether the specified token exists
* @param tokenId uint256 ID of the token to query the existence of
* @return bool whether the token exists
*/
function _exists(uint256 tokenId) internal view returns (bool) {
address owner = _tokenOwner[tokenId];
return owner != address(0);
}
/**
* @dev Returns whether the given spender can transfer a given token ID
* @param spender address of the spender to query
* @param tokenId uint256 ID of the token to be transferred
* @return bool whether the msg.sender is approved for the given token ID,
* is an operator of the owner, or is the owner of the token
*/
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
address owner = ownerOf(tokenId);
return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
}
/**
* @dev Internal function to mint a new token
* Reverts if the given token ID already exists
* @param to The address that will own the minted token
* @param tokenId uint256 ID of the token to be minted
*/
function _mint(address to, uint256 tokenId) internal {
require(to != address(0));
require(!_exists(tokenId));
_tokenOwner[tokenId] = to;
_ownedTokensCount[to].increment();
emit Transfer(address(0), to, tokenId);
}
/**
* @dev Internal function to burn a specific token
* Reverts if the token does not exist
* Deprecated, use _burn(uint256) instead.
* @param owner owner of the token to burn
* @param tokenId uint256 ID of the token being burned
*/
function _burn(address owner, uint256 tokenId) internal {
require(ownerOf(tokenId) == owner);
_clearApproval(tokenId);
_ownedTokensCount[owner].decrement();
_tokenOwner[tokenId] = address(0);
emit Transfer(owner, address(0), tokenId);
}
/**
* @dev Internal function to burn a specific token
* Reverts if the token does not exist
* @param tokenId uint256 ID of the token being burned
*/
function _burn(uint256 tokenId) internal {
_burn(ownerOf(tokenId), tokenId);
}
/**
* @dev Internal function to transfer ownership of a given token ID to another address.
* As opposed to transferFrom, this imposes no restrictions on msg.sender.
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function _transferFrom(address from, address to, uint256 tokenId) internal {
require(ownerOf(tokenId) == from);
require(to != address(0));
_clearApproval(tokenId);
_ownedTokensCount[from].decrement();
_ownedTokensCount[to].increment();
_tokenOwner[tokenId] = to;
emit Transfer(from, to, tokenId);
}
/**
* @dev Internal function to invoke `onERC721Received` on a target address
* The call is not executed if the target address is not a contract
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes optional data to send along with the call
* @return bool whether the call correctly returned the expected magic value
*/
function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
internal returns (bool)
{
if (!to.isContract()) {
return true;
}
bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data);
return (retval == _ERC721_RECEIVED);
}
/**
* @dev Private function to clear current approval of a given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function _clearApproval(uint256 tokenId) private {
if (_tokenApprovals[tokenId] != address(0)) {
_tokenApprovals[tokenId] = address(0);
}
}
}
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
contract IERC721Enumerable is IERC721 {
function totalSupply() public view returns (uint256);
function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256 tokenId);
function tokenByIndex(uint256 index) public view returns (uint256);
}
/**
* @title ERC-721 Non-Fungible Token with optional enumeration extension logic
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
contract ERC721Enumerable is ERC165, ERC721, IERC721Enumerable {
// Mapping from owner to list of owned token IDs
mapping(address => uint256[]) private _ownedTokens;
// Mapping from token ID to index of the owner tokens list
mapping(uint256 => uint256) private _ownedTokensIndex;
// Array with all token ids, used for enumeration
uint256[] private _allTokens;
// Mapping from token id to position in the allTokens array
mapping(uint256 => uint256) private _allTokensIndex;
bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;
/*
* 0x780e9d63 ===
* bytes4(keccak256('totalSupply()')) ^
* bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) ^
* bytes4(keccak256('tokenByIndex(uint256)'))
*/
/**
* @dev Constructor function
*/
constructor () public {
// register the supported interface to conform to ERC721Enumerable via ERC165
_registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
}
/**
* @dev Gets the token ID at a given index of the tokens list of the requested owner
* @param owner address owning the tokens list to be accessed
* @param index uint256 representing the index to be accessed of the requested tokens list
* @return uint256 token ID at the given index of the tokens list owned by the requested address
*/
function tokenOfOwnerByIndex(address owner, uint256 index) public view returns (uint256) {
require(index < balanceOf(owner));
return _ownedTokens[owner][index];
}
/**
* @dev Gets the total amount of tokens stored by the contract
* @return uint256 representing the total amount of tokens
*/
function totalSupply() public view returns (uint256) {
return _allTokens.length;
}
/**
* @dev Gets the token ID at a given index of all the tokens in this contract
* Reverts if the index is greater or equal to the total number of tokens
* @param index uint256 representing the index to be accessed of the tokens list
* @return uint256 token ID at the given index of the tokens list
*/
function tokenByIndex(uint256 index) public view returns (uint256) {
require(index < totalSupply());
return _allTokens[index];
}
/**
* @dev Internal function to transfer ownership of a given token ID to another address.
* As opposed to transferFrom, this imposes no restrictions on msg.sender.
* @param from current owner of the token
* @param to address to receive the ownership of the given token ID
* @param tokenId uint256 ID of the token to be transferred
*/
function _transferFrom(address from, address to, uint256 tokenId) internal {
super._transferFrom(from, to, tokenId);
_removeTokenFromOwnerEnumeration(from, tokenId);
_addTokenToOwnerEnumeration(to, tokenId);
}
/**
* @dev Internal function to mint a new token
* Reverts if the given token ID already exists
* @param to address the beneficiary that will own the minted token
* @param tokenId uint256 ID of the token to be minted
*/
function _mint(address to, uint256 tokenId) internal {
super._mint(to, tokenId);
_addTokenToOwnerEnumeration(to, tokenId);
_addTokenToAllTokensEnumeration(tokenId);
}
/**
* @dev Internal function to burn a specific token
* Reverts if the token does not exist
* Deprecated, use _burn(uint256) instead
* @param owner owner of the token to burn
* @param tokenId uint256 ID of the token being burned
*/
function _burn(address owner, uint256 tokenId) internal {
super._burn(owner, tokenId);
_removeTokenFromOwnerEnumeration(owner, tokenId);
// Since tokenId will be deleted, we can clear its slot in _ownedTokensIndex to trigger a gas refund
_ownedTokensIndex[tokenId] = 0;
_removeTokenFromAllTokensEnumeration(tokenId);
}
/**
* @dev Gets the list of token IDs of the requested owner
* @param owner address owning the tokens
* @return uint256[] List of token IDs owned by the requested address
*/
function _tokensOfOwner(address owner) internal view returns (uint256[] storage) {
return _ownedTokens[owner];
}
/**
* @dev Private function to add a token to this extension's ownership-tracking data structures.
* @param to address representing the new owner of the given token ID
* @param tokenId uint256 ID of the token to be added to the tokens list of the given address
*/
function _addTokenToOwnerEnumeration(address to, uint256 tokenId) private {
_ownedTokensIndex[tokenId] = _ownedTokens[to].length;
_ownedTokens[to].push(tokenId);
}
/**
* @dev Private function to add a token to this extension's token tracking data structures.
* @param tokenId uint256 ID of the token to be added to the tokens list
*/
function _addTokenToAllTokensEnumeration(uint256 tokenId) private {
_allTokensIndex[tokenId] = _allTokens.length;
_allTokens.push(tokenId);
}
/**
* @dev Private function to remove a token from this extension's ownership-tracking data structures. Note that
* while the token is not assigned a new owner, the _ownedTokensIndex mapping is _not_ updated: this allows for
* gas optimizations e.g. when performing a transfer operation (avoiding double writes).
* This has O(1) time complexity, but alters the order of the _ownedTokens array.
* @param from address representing the previous owner of the given token ID
* @param tokenId uint256 ID of the token to be removed from the tokens list of the given address
*/
function _removeTokenFromOwnerEnumeration(address from, uint256 tokenId) private {
// To prevent a gap in from's tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = _ownedTokens[from].length.sub(1);
uint256 tokenIndex = _ownedTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary
if (tokenIndex != lastTokenIndex) {
uint256 lastTokenId = _ownedTokens[from][lastTokenIndex];
_ownedTokens[from][tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_ownedTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
}
// This also deletes the contents at the last position of the array
_ownedTokens[from].length--;
// Note that _ownedTokensIndex[tokenId] hasn't been cleared: it still points to the old slot (now occupied by
// lastTokenId, or just over the end of the array if the token was the last one).
}
/**
* @dev Private function to remove a token from this extension's token tracking data structures.
* This has O(1) time complexity, but alters the order of the _allTokens array.
* @param tokenId uint256 ID of the token to be removed from the tokens list
*/
function _removeTokenFromAllTokensEnumeration(uint256 tokenId) private {
// To prevent a gap in the tokens array, we store the last token in the index of the token to delete, and
// then delete the last slot (swap and pop).
uint256 lastTokenIndex = _allTokens.length.sub(1);
uint256 tokenIndex = _allTokensIndex[tokenId];
// When the token to delete is the last token, the swap operation is unnecessary. However, since this occurs so
// rarely (when the last minted token is burnt) that we still do the swap here to avoid the gas cost of adding
// an 'if' statement (like in _removeTokenFromOwnerEnumeration)
uint256 lastTokenId = _allTokens[lastTokenIndex];
_allTokens[tokenIndex] = lastTokenId; // Move the last token to the slot of the to-delete token
_allTokensIndex[lastTokenId] = tokenIndex; // Update the moved token's index
// This also deletes the contents at the last position of the array
_allTokens.length--;
_allTokensIndex[tokenId] = 0;
}
}
/**
* @title ERC-721 Non-Fungible Token Standard, optional metadata extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
contract IERC721Metadata is IERC721 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function tokenURI(uint256 tokenId) external view returns (string memory);
}
contract ERC721Metadata is ERC165, ERC721, IERC721Metadata {
// Token name
string private _name;
// Token symbol
string private _symbol;
// Optional mapping for token URIs
mapping(uint256 => string) private _tokenURIs;
bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;
/*
* 0x5b5e139f ===
* bytes4(keccak256('name()')) ^
* bytes4(keccak256('symbol()')) ^
* bytes4(keccak256('tokenURI(uint256)'))
*/
/**
* @dev Constructor function
*/
constructor (string memory name, string memory symbol) public {
_name = name;
_symbol = symbol;
// register the supported interfaces to conform to ERC721 via ERC165
_registerInterface(_INTERFACE_ID_ERC721_METADATA);
}
/**
* @dev Gets the token name
* @return string representing the token name
*/
function name() external view returns (string memory) {
return _name;
}
/**
* @dev Gets the token symbol
* @return string representing the token symbol
*/
function symbol() external view returns (string memory) {
return _symbol;
}
/**
* @dev Returns an URI for a given token ID
* Throws if the token ID does not exist. May return an empty string.
* @param tokenId uint256 ID of the token to query
*/
function tokenURI(uint256 tokenId) external view returns (string memory) {
require(_exists(tokenId));
return _tokenURIs[tokenId];
}
/**
* @dev Internal function to set the token URI for a given token
* Reverts if the token ID does not exist
* @param tokenId uint256 ID of the token to set its URI
* @param uri string URI to assign
*/
function _setTokenURI(uint256 tokenId, string memory uri) internal {
require(_exists(tokenId));
_tokenURIs[tokenId] = uri;
}
/**
* @dev Internal function to burn a specific token
* Reverts if the token does not exist
* Deprecated, use _burn(uint256) instead
* @param owner owner of the token to burn
* @param tokenId uint256 ID of the token being burned by the msg.sender
*/
function _burn(address owner, uint256 tokenId) internal {
super._burn(owner, tokenId);
// Clear metadata (if any)
if (bytes(_tokenURIs[tokenId]).length != 0) {
delete _tokenURIs[tokenId];
}
}
}
/**
* @title Full ERC721 Token
* This implementation includes all the required and some optional functionality of the ERC721 standard
* Moreover, it includes approve all functionality using operator terminology
* @dev see https://eips.ethereum.org/EIPS/eip-721
*/
contract ERC721Full is ERC721, ERC721Enumerable, ERC721Metadata {
constructor (string memory name, string memory symbol) public ERC721Metadata(name, symbol) {
// solhint-disable-previous-line no-empty-blocks
}
}
contract StakingNFT is ERC721Full, Ownable {
constructor(string memory name, string memory symbol)
public
ERC721Full(name, symbol)
{
// solhint-disable-previous-line no-empty-blocks
}
function mint(address to, uint256 tokenId) public onlyOwner {
require(
balanceOf(to) == 0,
"Validators MUST NOT own multiple stake position"
);
_mint(to, tokenId);
}
function burn(uint256 tokenId) public onlyOwner {
_burn(tokenId);
}
function _transferFrom(address from, address to, uint256 tokenId) internal {
require(
balanceOf(to) == 0,
"Validators MUST NOT own multiple stake position"
);
super._transferFrom(from, to, tokenId);
}
}
/*
* SPDX-License-Identitifer: MIT
*/
// See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-897.md
interface ERCProxy {
function proxyType() external pure returns (uint256 proxyTypeId);
function implementation() external view returns (address codeAddr);
}
contract DelegateProxyForwarder {
function delegatedFwd(address _dst, bytes memory _calldata) internal {
// solium-disable-next-line security/no-inline-assembly
assembly {
let result := delegatecall(
sub(gas, 10000),
_dst,
add(_calldata, 0x20),
mload(_calldata),
0,
0
)
let size := returndatasize
let ptr := mload(0x40)
returndatacopy(ptr, 0, size)
// revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas.
// if the call returned error data, forward it
switch result
case 0 {
revert(ptr, size)
}
default {
return(ptr, size)
}
}
}
function isContract(address _target) internal view returns (bool) {
if (_target == address(0)) {
return false;
}
uint256 size;
assembly {
size := extcodesize(_target)
}
return size > 0;
}
}
contract DelegateProxy is ERCProxy, DelegateProxyForwarder {
function proxyType() external pure returns (uint256 proxyTypeId) {
// Upgradeable proxy
proxyTypeId = 2;
}
function implementation() external view returns (address);
}
contract UpgradableProxy is DelegateProxy {
event ProxyUpdated(address indexed _new, address indexed _old);
event OwnerUpdate(address _new, address _old);
bytes32 constant IMPLEMENTATION_SLOT = keccak256("matic.network.proxy.implementation");
bytes32 constant OWNER_SLOT = keccak256("matic.network.proxy.owner");
constructor(address _proxyTo) public {
setOwner(msg.sender);
setImplementation(_proxyTo);
}
function() external payable {
// require(currentContract != 0, "If app code has not been set yet, do not call");
// Todo: filter out some calls or handle in the end fallback
delegatedFwd(loadImplementation(), msg.data);
}
modifier onlyProxyOwner() {
require(loadOwner() == msg.sender, "NOT_OWNER");
_;
}
function owner() external view returns(address) {
return loadOwner();
}
function loadOwner() internal view returns(address) {
address _owner;
bytes32 position = OWNER_SLOT;
assembly {
_owner := sload(position)
}
return _owner;
}
function implementation() external view returns (address) {
return loadImplementation();
}
function loadImplementation() internal view returns(address) {
address _impl;
bytes32 position = IMPLEMENTATION_SLOT;
assembly {
_impl := sload(position)
}
return _impl;
}
function transferOwnership(address newOwner) public onlyProxyOwner {
require(newOwner != address(0), "ZERO_ADDRESS");
emit OwnerUpdate(newOwner, loadOwner());
setOwner(newOwner);
}
function setOwner(address newOwner) private {
bytes32 position = OWNER_SLOT;
assembly {
sstore(position, newOwner)
}
}
function updateImplementation(address _newProxyTo) public onlyProxyOwner {
require(_newProxyTo != address(0x0), "INVALID_PROXY_ADDRESS");
require(isContract(_newProxyTo), "DESTINATION_ADDRESS_IS_NOT_A_CONTRACT");
emit ProxyUpdated(_newProxyTo, loadImplementation());
setImplementation(_newProxyTo);
}
function updateAndCall(address _newProxyTo, bytes memory data) payable public onlyProxyOwner {
updateImplementation(_newProxyTo);
(bool success, bytes memory returnData) = address(this).call.value(msg.value)(data);
require(success, string(returnData));
}
function setImplementation(address _newProxyTo) private {
bytes32 position = IMPLEMENTATION_SLOT;
assembly {
sstore(position, _newProxyTo)
}
}
}
contract ValidatorShareProxy is UpgradableProxy {
constructor(address _registry) public UpgradableProxy(_registry) {}
function loadImplementation() internal view returns (address) {
return Registry(super.loadImplementation()).getValidatorShareAddress();
}
}
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* https://eips.ethereum.org/EIPS/eip-20
* Originally based on code by FirstBlood:
* https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*
* This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
* all accounts just by listening to said events. Note that this isn't required by the specification, and other
* compliant implementations may not do it.
*/
contract ERC20 is IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowed;
uint256 private _totalSupply;
/**
* @dev Total number of tokens in existence
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev Gets the balance of the specified address.
* @param owner The address to query the balance of.
* @return A uint256 representing the amount owned by the passed address.
*/
function balanceOf(address owner) public view returns (uint256) {
return _balances[owner];
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param owner address The address which owns the funds.
* @param spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _allowed[owner][spender];
}
/**
* @dev Transfer token to a specified address
* @param to The address to transfer to.
* @param value The amount to be transferred.
*/
function transfer(address to, uint256 value) public returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
* 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
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
*/
function approve(address spender, uint256 value) public returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
/**
* @dev Transfer tokens from one address to another.
* Note that while this function emits an Approval event, this is not required as per the specification,
* and other compliant implementations may not emit the event.
* @param from address The address which you want to send tokens from
* @param to address The address which you want to transfer to
* @param value uint256 the amount of tokens to be transferred
*/
function transferFrom(address from, address to, uint256 value) public returns (bool) {
_transfer(from, to, value);
_approve(from, msg.sender, _allowed[from][msg.sender].sub(value));
return true;
}
/**
* @dev Increase the amount of tokens that an owner allowed to a spender.
* approve should be called when _allowed[msg.sender][spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* Emits an Approval event.
* @param spender The address which will spend the funds.
* @param addedValue The amount of tokens to increase the allowance by.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(msg.sender, spender, _allowed[msg.sender][spender].add(addedValue));
return true;
}
/**
* @dev Decrease the amount of tokens that an owner allowed to a spender.
* approve should be called when _allowed[msg.sender][spender] == 0. To decrement
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* Emits an Approval event.
* @param spender The address which will spend the funds.
* @param subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(msg.sender, spender, _allowed[msg.sender][spender].sub(subtractedValue));
return true;
}
/**
* @dev Transfer token for a specified addresses
* @param from The address to transfer from.
* @param to The address to transfer to.
* @param value The amount to be transferred.
*/
function _transfer(address from, address to, uint256 value) internal {
require(to != address(0));
_balances[from] = _balances[from].sub(value);
_balances[to] = _balances[to].add(value);
emit Transfer(from, to, value);
}
/**
* @dev Internal function that mints an amount of the token and assigns it to
* an account. This encapsulates the modification of balances such that the
* proper events are emitted.
* @param account The account that will receive the created tokens.
* @param value The amount that will be created.
*/
function _mint(address account, uint256 value) internal {
require(account != address(0));
_totalSupply = _totalSupply.add(value);
_balances[account] = _balances[account].add(value);
emit Transfer(address(0), account, value);
}
/**
* @dev Internal function that burns an amount of the token of a given
* account.
* @param account The account whose tokens will be burnt.
* @param value The amount that will be burnt.
*/
function _burn(address account, uint256 value) internal {
require(account != address(0));
_totalSupply = _totalSupply.sub(value);
_balances[account] = _balances[account].sub(value);
emit Transfer(account, address(0), value);
}
/**
* @dev Approve an address to spend another addresses' tokens.
* @param owner The address that owns the tokens.
* @param spender The address that will spend the tokens.
* @param value The number of tokens that can be spent.
*/
function _approve(address owner, address spender, uint256 value) internal {
require(spender != address(0));
require(owner != address(0));
_allowed[owner][spender] = value;
emit Approval(owner, spender, value);
}
/**
* @dev Internal function that burns an amount of the token of a given
* account, deducting from the sender's allowance for said account. Uses the
* internal burn function.
* Emits an Approval event (reflecting the reduced allowance).
* @param account The account whose tokens will be burnt.
* @param value The amount that will be burnt.
*/
function _burnFrom(address account, uint256 value) internal {
_burn(account, value);
_approve(account, msg.sender, _allowed[account][msg.sender].sub(value));
}
}
contract ERC20NonTradable is ERC20 {
function _approve(
address owner,
address spender,
uint256 value
) internal {
revert("disabled");
}
}
contract Initializable {
bool inited = false;
modifier initializer() {
require(!inited, "already inited");
inited = true;
_;
}
}
contract IStakeManagerEventsHub {
struct Validator {
uint256 amount;
uint256 reward;
uint256 activationEpoch;
uint256 deactivationEpoch;
uint256 jailTime;
address signer;
address contractAddress;
}
mapping(uint256 => Validator) public validators;
}
contract EventsHub is Initializable {
Registry public registry;
modifier onlyValidatorContract(uint256 validatorId) {
address _contract;
(, , , , , , _contract) = IStakeManagerEventsHub(registry.getStakeManagerAddress()).validators(validatorId);
require(_contract == msg.sender, "not validator");
_;
}
modifier onlyStakeManager() {
require(registry.getStakeManagerAddress() == msg.sender,
"Invalid sender, not stake manager");
_;
}
function initialize(Registry _registry) external initializer {
registry = _registry;
}
event ShareBurnedWithId(
uint256 indexed validatorId,
address indexed user,
uint256 indexed amount,
uint256 tokens,
uint256 nonce
);
function logShareBurnedWithId(
uint256 validatorId,
address user,
uint256 amount,
uint256 tokens,
uint256 nonce
) public onlyValidatorContract(validatorId) {
emit ShareBurnedWithId(validatorId, user, amount, tokens, nonce);
}
event DelegatorUnstakeWithId(
uint256 indexed validatorId,
address indexed user,
uint256 amount,
uint256 nonce
);
function logDelegatorUnstakedWithId(
uint256 validatorId,
address user,
uint256 amount,
uint256 nonce
) public onlyValidatorContract(validatorId) {
emit DelegatorUnstakeWithId(validatorId, user, amount, nonce);
}
event RewardParams(
uint256 rewardDecreasePerCheckpoint,
uint256 maxRewardedCheckpoints,
uint256 checkpointRewardDelta
);
function logRewardParams(
uint256 rewardDecreasePerCheckpoint,
uint256 maxRewardedCheckpoints,
uint256 checkpointRewardDelta
) public onlyStakeManager {
emit RewardParams(rewardDecreasePerCheckpoint, maxRewardedCheckpoints, checkpointRewardDelta);
}
event UpdateCommissionRate(
uint256 indexed validatorId,
uint256 indexed newCommissionRate,
uint256 indexed oldCommissionRate
);
function logUpdateCommissionRate(
uint256 validatorId,
uint256 newCommissionRate,
uint256 oldCommissionRate
) public onlyStakeManager {
emit UpdateCommissionRate(
validatorId,
newCommissionRate,
oldCommissionRate
);
}
event SharesTransfer(
uint256 indexed validatorId,
address indexed from,
address indexed to,
uint256 value
);
function logSharesTransfer(
uint256 validatorId,
address from,
address to,
uint256 value
) public onlyValidatorContract(validatorId) {
emit SharesTransfer(validatorId, from, to, value);
}
}
contract OwnableLockable is Lockable, Ownable {
function lock() public onlyOwner {
super.lock();
}
function unlock() public onlyOwner {
super.unlock();
}
}
// note this contract interface is only for stakeManager use
contract IValidatorShare {
function withdrawRewards() public;
function unstakeClaimTokens() public;
function getLiquidRewards(address user) public view returns (uint256);
function owner() public view returns (address);
function restake() public returns(uint256, uint256);
function unlock() external;
function lock() external;
function drain(
address token,
address payable destination,
uint256 amount
) external;
function slash(uint256 valPow, uint256 delegatedAmount, uint256 totalAmountToSlash) external returns (uint256);
function updateDelegation(bool delegation) external;
function migrateOut(address user, uint256 amount) external;
function migrateIn(address user, uint256 amount) external;
}
contract ValidatorShare is IValidatorShare, ERC20NonTradable, OwnableLockable, Initializable {
struct DelegatorUnbond {
uint256 shares;
uint256 withdrawEpoch;
}
uint256 constant EXCHANGE_RATE_PRECISION = 100;
// maximum matic possible, even if rate will be 1 and all matic will be staken in one go, it will result in 10 ^ 58 shares
uint256 constant EXCHANGE_RATE_HIGH_PRECISION = 10**29;
uint256 constant MAX_COMMISION_RATE = 100;
uint256 constant REWARD_PRECISION = 10**25;
StakingInfo public stakingLogger;
IStakeManager public stakeManager;
uint256 public validatorId;
uint256 public validatorRewards_deprecated;
uint256 public commissionRate_deprecated;
uint256 public lastCommissionUpdate_deprecated;
uint256 public minAmount;
uint256 public totalStake_deprecated;
uint256 public rewardPerShare;
uint256 public activeAmount;
bool public delegation;
uint256 public withdrawPool;
uint256 public withdrawShares;
mapping(address => uint256) amountStaked_deprecated; // deprecated, keep for foundation delegators
mapping(address => DelegatorUnbond) public unbonds;
mapping(address => uint256) public initalRewardPerShare;
mapping(address => uint256) public unbondNonces;
mapping(address => mapping(uint256 => DelegatorUnbond)) public unbonds_new;
EventsHub public eventsHub;
// onlyOwner will prevent this contract from initializing, since it's owner is going to be 0x0 address
function initialize(
uint256 _validatorId,
address _stakingLogger,
address _stakeManager
) external initializer {
validatorId = _validatorId;
stakingLogger = StakingInfo(_stakingLogger);
stakeManager = IStakeManager(_stakeManager);
_transferOwnership(_stakeManager);
_getOrCacheEventsHub();
minAmount = 10**18;
delegation = true;
}
/**
Public View Methods
*/
function exchangeRate() public view returns (uint256) {
uint256 totalShares = totalSupply();
uint256 precision = _getRatePrecision();
return totalShares == 0 ? precision : stakeManager.delegatedAmount(validatorId).mul(precision).div(totalShares);
}
function getTotalStake(address user) public view returns (uint256, uint256) {
uint256 shares = balanceOf(user);
uint256 rate = exchangeRate();
if (shares == 0) {
return (0, rate);
}
return (rate.mul(shares).div(_getRatePrecision()), rate);
}
function withdrawExchangeRate() public view returns (uint256) {
uint256 precision = _getRatePrecision();
if (validatorId < 8) {
// fix of potentially broken withdrawals for future unbonding
// foundation validators have no slashing enabled and thus we can return default exchange rate
// because without slashing rate will stay constant
return precision;
}
uint256 _withdrawShares = withdrawShares;
return _withdrawShares == 0 ? precision : withdrawPool.mul(precision).div(_withdrawShares);
}
function getLiquidRewards(address user) public view returns (uint256) {
return _calculateReward(user, getRewardPerShare());
}
function getRewardPerShare() public view returns (uint256) {
return _calculateRewardPerShareWithRewards(stakeManager.delegatorsReward(validatorId));
}
/**
Public Methods
*/
function buyVoucher(uint256 _amount, uint256 _minSharesToMint) public returns(uint256 amountToDeposit) {
_withdrawAndTransferReward(msg.sender);
amountToDeposit = _buyShares(_amount, _minSharesToMint, msg.sender);
require(stakeManager.delegationDeposit(validatorId, amountToDeposit, msg.sender), "deposit failed");
return amountToDeposit;
}
function restake() public returns(uint256, uint256) {
address user = msg.sender;
uint256 liquidReward = _withdrawReward(user);
uint256 amountRestaked;
require(liquidReward >= minAmount, "Too small rewards to restake");
if (liquidReward != 0) {
amountRestaked = _buyShares(liquidReward, 0, user);
if (liquidReward > amountRestaked) {
// return change to the user
require(
stakeManager.transferFunds(validatorId, liquidReward - amountRestaked, user),
"Insufficent rewards"
);
stakingLogger.logDelegatorClaimRewards(validatorId, user, liquidReward - amountRestaked);
}
(uint256 totalStaked, ) = getTotalStake(user);
stakingLogger.logDelegatorRestaked(validatorId, user, totalStaked);
}
return (amountRestaked, liquidReward);
}
function sellVoucher(uint256 claimAmount, uint256 maximumSharesToBurn) public {
(uint256 shares, uint256 _withdrawPoolShare) = _sellVoucher(claimAmount, maximumSharesToBurn);
DelegatorUnbond memory unbond = unbonds[msg.sender];
unbond.shares = unbond.shares.add(_withdrawPoolShare);
// refresh undond period
unbond.withdrawEpoch = stakeManager.epoch();
unbonds[msg.sender] = unbond;
StakingInfo logger = stakingLogger;
logger.logShareBurned(validatorId, msg.sender, claimAmount, shares);
logger.logStakeUpdate(validatorId);
}
function withdrawRewards() public {
uint256 rewards = _withdrawAndTransferReward(msg.sender);
require(rewards >= minAmount, "Too small rewards amount");
}
function migrateOut(address user, uint256 amount) external onlyOwner {
_withdrawAndTransferReward(user);
(uint256 totalStaked, uint256 rate) = getTotalStake(user);
require(totalStaked >= amount, "Migrating too much");
uint256 precision = _getRatePrecision();
uint256 shares = amount.mul(precision).div(rate);
_burn(user, shares);
stakeManager.updateValidatorState(validatorId, -int256(amount));
activeAmount = activeAmount.sub(amount);
stakingLogger.logShareBurned(validatorId, user, amount, shares);
stakingLogger.logStakeUpdate(validatorId);
stakingLogger.logDelegatorUnstaked(validatorId, user, amount);
}
function migrateIn(address user, uint256 amount) external onlyOwner {
_withdrawAndTransferReward(user);
_buyShares(amount, 0, user);
}
function unstakeClaimTokens() public {
DelegatorUnbond memory unbond = unbonds[msg.sender];
uint256 amount = _unstakeClaimTokens(unbond);
delete unbonds[msg.sender];
stakingLogger.logDelegatorUnstaked(validatorId, msg.sender, amount);
}
function slash(
uint256 validatorStake,
uint256 delegatedAmount,
uint256 totalAmountToSlash
) external onlyOwner returns (uint256) {
uint256 _withdrawPool = withdrawPool;
uint256 delegationAmount = delegatedAmount.add(_withdrawPool);
if (delegationAmount == 0) {
return 0;
}
// total amount to be slashed from delegation pool (active + inactive)
uint256 _amountToSlash = delegationAmount.mul(totalAmountToSlash).div(validatorStake.add(delegationAmount));
uint256 _amountToSlashWithdrawalPool = _withdrawPool.mul(_amountToSlash).div(delegationAmount);
// slash inactive pool
uint256 stakeSlashed = _amountToSlash.sub(_amountToSlashWithdrawalPool);
stakeManager.decreaseValidatorDelegatedAmount(validatorId, stakeSlashed);
activeAmount = activeAmount.sub(stakeSlashed);
withdrawPool = withdrawPool.sub(_amountToSlashWithdrawalPool);
return _amountToSlash;
}
function updateDelegation(bool _delegation) external onlyOwner {
delegation = _delegation;
}
function drain(
address token,
address payable destination,
uint256 amount
) external onlyOwner {
if (token == address(0x0)) {
destination.transfer(amount);
} else {
require(ERC20(token).transfer(destination, amount), "Drain failed");
}
}
/**
New shares exit API
*/
function sellVoucher_new(uint256 claimAmount, uint256 maximumSharesToBurn) public {
(uint256 shares, uint256 _withdrawPoolShare) = _sellVoucher(claimAmount, maximumSharesToBurn);
uint256 unbondNonce = unbondNonces[msg.sender].add(1);
DelegatorUnbond memory unbond = DelegatorUnbond({
shares: _withdrawPoolShare,
withdrawEpoch: stakeManager.epoch()
});
unbonds_new[msg.sender][unbondNonce] = unbond;
unbondNonces[msg.sender] = unbondNonce;
_getOrCacheEventsHub().logShareBurnedWithId(validatorId, msg.sender, claimAmount, shares, unbondNonce);
stakingLogger.logStakeUpdate(validatorId);
}
function unstakeClaimTokens_new(uint256 unbondNonce) public {
DelegatorUnbond memory unbond = unbonds_new[msg.sender][unbondNonce];
uint256 amount = _unstakeClaimTokens(unbond);
delete unbonds_new[msg.sender][unbondNonce];
_getOrCacheEventsHub().logDelegatorUnstakedWithId(validatorId, msg.sender, amount, unbondNonce);
}
/**
Private Methods
*/
function _getOrCacheEventsHub() private returns(EventsHub) {
EventsHub _eventsHub = eventsHub;
if (_eventsHub == EventsHub(0x0)) {
_eventsHub = EventsHub(Registry(stakeManager.getRegistry()).contractMap(keccak256("eventsHub")));
eventsHub = _eventsHub;
}
return _eventsHub;
}
function _sellVoucher(uint256 claimAmount, uint256 maximumSharesToBurn) private returns(uint256, uint256) {
// first get how much staked in total and compare to target unstake amount
(uint256 totalStaked, uint256 rate) = getTotalStake(msg.sender);
require(totalStaked != 0 && totalStaked >= claimAmount, "Too much requested");
// convert requested amount back to shares
uint256 precision = _getRatePrecision();
uint256 shares = claimAmount.mul(precision).div(rate);
require(shares <= maximumSharesToBurn, "too much slippage");
_withdrawAndTransferReward(msg.sender);
_burn(msg.sender, shares);
stakeManager.updateValidatorState(validatorId, -int256(claimAmount));
activeAmount = activeAmount.sub(claimAmount);
uint256 _withdrawPoolShare = claimAmount.mul(precision).div(withdrawExchangeRate());
withdrawPool = withdrawPool.add(claimAmount);
withdrawShares = withdrawShares.add(_withdrawPoolShare);
return (shares, _withdrawPoolShare);
}
function _unstakeClaimTokens(DelegatorUnbond memory unbond) private returns(uint256) {
uint256 shares = unbond.shares;
require(
unbond.withdrawEpoch.add(stakeManager.withdrawalDelay()) <= stakeManager.epoch() && shares > 0,
"Incomplete withdrawal period"
);
uint256 _amount = withdrawExchangeRate().mul(shares).div(_getRatePrecision());
withdrawShares = withdrawShares.sub(shares);
withdrawPool = withdrawPool.sub(_amount);
require(stakeManager.transferFunds(validatorId, _amount, msg.sender), "Insufficent rewards");
return _amount;
}
function _getRatePrecision() private view returns (uint256) {
// if foundation validator, use old precision
if (validatorId < 8) {
return EXCHANGE_RATE_PRECISION;
}
return EXCHANGE_RATE_HIGH_PRECISION;
}
function _calculateRewardPerShareWithRewards(uint256 accumulatedReward) private view returns (uint256) {
uint256 _rewardPerShare = rewardPerShare;
if (accumulatedReward != 0) {
uint256 totalShares = totalSupply();
if (totalShares != 0) {
_rewardPerShare = _rewardPerShare.add(accumulatedReward.mul(REWARD_PRECISION).div(totalShares));
}
}
return _rewardPerShare;
}
function _calculateReward(address user, uint256 _rewardPerShare) private view returns (uint256) {
uint256 shares = balanceOf(user);
if (shares == 0) {
return 0;
}
uint256 _initialRewardPerShare = initalRewardPerShare[user];
if (_initialRewardPerShare == _rewardPerShare) {
return 0;
}
return _rewardPerShare.sub(_initialRewardPerShare).mul(shares).div(REWARD_PRECISION);
}
function _withdrawReward(address user) private returns (uint256) {
uint256 _rewardPerShare = _calculateRewardPerShareWithRewards(
stakeManager.withdrawDelegatorsReward(validatorId)
);
uint256 liquidRewards = _calculateReward(user, _rewardPerShare);
rewardPerShare = _rewardPerShare;
initalRewardPerShare[user] = _rewardPerShare;
return liquidRewards;
}
function _withdrawAndTransferReward(address user) private returns (uint256) {
uint256 liquidRewards = _withdrawReward(user);
if (liquidRewards != 0) {
require(stakeManager.transferFunds(validatorId, liquidRewards, user), "Insufficent rewards");
stakingLogger.logDelegatorClaimRewards(validatorId, user, liquidRewards);
}
return liquidRewards;
}
function _buyShares(
uint256 _amount,
uint256 _minSharesToMint,
address user
) private onlyWhenUnlocked returns (uint256) {
require(delegation, "Delegation is disabled");
uint256 rate = exchangeRate();
uint256 precision = _getRatePrecision();
uint256 shares = _amount.mul(precision).div(rate);
require(shares >= _minSharesToMint, "Too much slippage");
require(unbonds[user].shares == 0, "Ongoing exit");
_mint(user, shares);
// clamp amount of tokens in case resulted shares requires less tokens than anticipated
_amount = rate.mul(shares).div(precision);
stakeManager.updateValidatorState(validatorId, int256(_amount));
activeAmount = activeAmount.add(_amount);
StakingInfo logger = stakingLogger;
logger.logShareMinted(validatorId, user, _amount, shares);
logger.logStakeUpdate(validatorId);
return _amount;
}
function _transfer(
address from,
address to,
uint256 value
) internal {
// get rewards for recipient
_withdrawAndTransferReward(to);
// convert rewards to shares
_withdrawAndTransferReward(from);
// move shares to recipient
super._transfer(from, to, value);
_getOrCacheEventsHub().logSharesTransfer(validatorId, from, to, value);
}
}
contract ValidatorShareFactory {
/**
- factory to create new validatorShare contracts
*/
function create(uint256 validatorId, address loggerAddress, address registry) public returns (address) {
ValidatorShareProxy proxy = new ValidatorShareProxy(registry);
proxy.transferOwnership(msg.sender);
address proxyAddr = address(proxy);
(bool success, bytes memory data) = proxyAddr.call.gas(gasleft())(
abi.encodeWithSelector(
ValidatorShare(proxyAddr).initialize.selector,
validatorId,
loggerAddress,
msg.sender
)
);
require(success, string(data));
return proxyAddr;
}
}
contract StakeManagerStorage is GovernanceLockable, RootChainable {
enum Status {Inactive, Active, Locked, Unstaked}
struct Auction {
uint256 amount;
uint256 startEpoch;
address user;
bool acceptDelegation;
bytes signerPubkey;
}
struct State {
uint256 amount;
uint256 stakerCount;
}
struct StateChange {
int256 amount;
int256 stakerCount;
}
struct Validator {
uint256 amount;
uint256 reward;
uint256 activationEpoch;
uint256 deactivationEpoch;
uint256 jailTime;
address signer;
address contractAddress;
Status status;
uint256 commissionRate;
uint256 lastCommissionUpdate;
uint256 delegatorsReward;
uint256 delegatedAmount;
uint256 initialRewardPerStake;
}
uint256 constant MAX_COMMISION_RATE = 100;
uint256 constant MAX_PROPOSER_BONUS = 100;
uint256 constant REWARD_PRECISION = 10**25;
uint256 internal constant INCORRECT_VALIDATOR_ID = 2**256 - 1;
uint256 internal constant INITIALIZED_AMOUNT = 1;
IERC20 public token;
address public registry;
StakingInfo public logger;
StakingNFT public NFTContract;
ValidatorShareFactory public validatorShareFactory;
uint256 public WITHDRAWAL_DELAY; // unit: epoch
uint256 public currentEpoch;
// genesis/governance variables
uint256 public dynasty; // unit: epoch 50 days
uint256 public CHECKPOINT_REWARD; // update via governance
uint256 public minDeposit; // in ERC20 token
uint256 public minHeimdallFee; // in ERC20 token
uint256 public checkPointBlockInterval;
uint256 public signerUpdateLimit;
uint256 public validatorThreshold; //128
uint256 public totalStaked;
uint256 public NFTCounter;
uint256 public totalRewards;
uint256 public totalRewardsLiquidated;
uint256 public auctionPeriod; // 1 week in epochs
uint256 public proposerBonus; // 10 % of total rewards
bytes32 public accountStateRoot;
// Stop validator auction for some time when updating dynasty value
uint256 public replacementCoolDown;
bool public delegationEnabled;
mapping(uint256 => Validator) public validators;
mapping(address => uint256) public signerToValidator;
// current epoch stake power and stakers count
State public validatorState;
mapping(uint256 => StateChange) public validatorStateChanges;
mapping(address => uint256) public userFeeExit;
//Ongoing auctions for validatorId
mapping(uint256 => Auction) public validatorAuction;
// validatorId to last signer update epoch
mapping(uint256 => uint256) public latestSignerUpdateEpoch;
uint256 public totalHeimdallFee;
}
contract StakeManagerStorageExtension {
address public eventsHub;
uint256 public rewardPerStake;
address public extensionCode;
address[] public signers;
uint256 constant CHK_REWARD_PRECISION = 100;
uint256 public prevBlockInterval;
// how much less reward per skipped checkpoint, 0 - 100%
uint256 public rewardDecreasePerCheckpoint;
// how many checkpoints to reward
uint256 public maxRewardedCheckpoints;
// increase / decrease value for faster or slower checkpoints, 0 - 100%
uint256 public checkpointRewardDelta;
}
/**
* @title Math
* @dev Assorted math operations
*/
library Math {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Calculates the average of two numbers. Since these are integers,
* averages of an even and odd number cannot be represented, and will be
* rounded down.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow, so we distribute
return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
}
}
interface IValidatorRegistry {
function validators(address _validator) external view returns (bool);
function validatorWhitelistingEnable() external view returns (bool);
function updateValidatorsPermission(address _validator, bool _isWhitelisted) external;
function toggleWhitelisting(bool _validatorWhitelistingEnable) external;
}
contract StakeManagerExtension is StakeManagerStorage, Initializable, StakeManagerStorageExtension {
using SafeMath for uint256;
constructor() public GovernanceLockable(address(0x0)) {}
IValidatorRegistry public validatorRegistry;
function updateValidatorRegistry(address _newContract) public onlyOwner {
require(_newContract != address(0), "address cannot be zero");
validatorRegistry = IValidatorRegistry(_newContract);
}
//checks only when validator registry enabled
function checkValidatorWhitelisting(address validator) public view returns(bool){
if(validatorRegistry.validatorWhitelistingEnable()){
return validatorRegistry.validators(validator);
}
else {
return true;
}
}
function startAuction(
uint256 validatorId,
uint256 amount,
bool _acceptDelegation,
bytes calldata _signerPubkey
) external {
uint256 currentValidatorAmount = validators[validatorId].amount;
require(
validators[validatorId].deactivationEpoch == 0 && currentValidatorAmount != 0,
"Invalid validator for an auction"
);
uint256 senderValidatorId = signerToValidator[msg.sender];
// make sure that signer wasn't used already
require(
NFTContract.balanceOf(msg.sender) == 0 && // existing validators can't bid
senderValidatorId != INCORRECT_VALIDATOR_ID,
"Already used address"
);
uint256 _currentEpoch = currentEpoch;
uint256 _replacementCoolDown = replacementCoolDown;
// when dynasty period is updated validators are in cooldown period
require(_replacementCoolDown == 0 || _replacementCoolDown <= _currentEpoch, "Cooldown period");
// (auctionPeriod--dynasty)--(auctionPeriod--dynasty)--(auctionPeriod--dynasty)
// if it's auctionPeriod then will get residue smaller then auctionPeriod
// from (CurrentPeriod of validator )%(auctionPeriod--dynasty)
// make sure that its `auctionPeriod` window
// dynasty = 30, auctionPeriod = 7, activationEpoch = 1, currentEpoch = 39
// residue 1 = (39-1)% (7+30), if residue <= auctionPeriod it's `auctionPeriod`
require(
(_currentEpoch.sub(validators[validatorId].activationEpoch) % dynasty.add(auctionPeriod)) < auctionPeriod,
"Invalid auction period"
);
uint256 perceivedStake = currentValidatorAmount;
perceivedStake = perceivedStake.add(validators[validatorId].delegatedAmount);
Auction storage auction = validatorAuction[validatorId];
uint256 currentAuctionAmount = auction.amount;
perceivedStake = Math.max(perceivedStake, currentAuctionAmount);
require(perceivedStake < amount, "Must bid higher");
require(token.transferFrom(msg.sender, address(this), amount), "Transfer failed");
//replace prev auction
if (currentAuctionAmount != 0) {
require(token.transfer(auction.user, currentAuctionAmount), "Bid return failed");
}
// create new auction
auction.amount = amount;
auction.user = msg.sender;
auction.acceptDelegation = _acceptDelegation;
auction.signerPubkey = _signerPubkey;
logger.logStartAuction(validatorId, currentValidatorAmount, amount);
}
function confirmAuctionBid(
uint256 validatorId,
uint256 heimdallFee, /** for new validator */
IStakeManager stakeManager
) external {
Auction storage auction = validatorAuction[validatorId];
address auctionUser = auction.user;
require(
msg.sender == auctionUser || NFTContract.tokenOfOwnerByIndex(msg.sender, 0) == validatorId,
"Only bidder can confirm"
);
uint256 _currentEpoch = currentEpoch;
require(
_currentEpoch.sub(auction.startEpoch) % auctionPeriod.add(dynasty) >= auctionPeriod,
"Not allowed before auctionPeriod"
);
require(auction.user != address(0x0), "Invalid auction");
uint256 validatorAmount = validators[validatorId].amount;
uint256 perceivedStake = validatorAmount;
uint256 auctionAmount = auction.amount;
perceivedStake = perceivedStake.add(validators[validatorId].delegatedAmount);
// validator is last auctioner
if (perceivedStake >= auctionAmount && validators[validatorId].deactivationEpoch == 0) {
require(token.transfer(auctionUser, auctionAmount), "Bid return failed");
//cleanup auction data
auction.startEpoch = _currentEpoch;
logger.logConfirmAuction(validatorId, validatorId, validatorAmount);
} else {
stakeManager.dethroneAndStake(
auctionUser,
heimdallFee,
validatorId,
auctionAmount,
auction.acceptDelegation,
auction.signerPubkey
);
}
uint256 startEpoch = auction.startEpoch;
delete validatorAuction[validatorId];
validatorAuction[validatorId].startEpoch = startEpoch;
}
function migrateValidatorsData(uint256 validatorIdFrom, uint256 validatorIdTo) external {
for (uint256 i = validatorIdFrom; i < validatorIdTo; ++i) {
ValidatorShare contractAddress = ValidatorShare(validators[i].contractAddress);
if (contractAddress != ValidatorShare(0)) {
// move validator rewards out from ValidatorShare contract
validators[i].reward = contractAddress.validatorRewards_deprecated().add(INITIALIZED_AMOUNT);
validators[i].delegatedAmount = contractAddress.activeAmount();
validators[i].commissionRate = contractAddress.commissionRate_deprecated();
} else {
validators[i].reward = validators[i].reward.add(INITIALIZED_AMOUNT);
}
validators[i].delegatorsReward = INITIALIZED_AMOUNT;
}
}
function updateCheckpointRewardParams(
uint256 _rewardDecreasePerCheckpoint,
uint256 _maxRewardedCheckpoints,
uint256 _checkpointRewardDelta
) external {
require(_maxRewardedCheckpoints.mul(_rewardDecreasePerCheckpoint) <= CHK_REWARD_PRECISION);
require(_checkpointRewardDelta <= CHK_REWARD_PRECISION);
rewardDecreasePerCheckpoint = _rewardDecreasePerCheckpoint;
maxRewardedCheckpoints = _maxRewardedCheckpoints;
checkpointRewardDelta = _checkpointRewardDelta;
_getOrCacheEventsHub().logRewardParams(_rewardDecreasePerCheckpoint, _maxRewardedCheckpoints, _checkpointRewardDelta);
}
function updateCommissionRate(uint256 validatorId, uint256 newCommissionRate) external {
uint256 _epoch = currentEpoch;
uint256 _lastCommissionUpdate = validators[validatorId].lastCommissionUpdate;
require( // withdrawalDelay == dynasty
(_lastCommissionUpdate.add(WITHDRAWAL_DELAY) <= _epoch) || _lastCommissionUpdate == 0, // For initial setting of commission rate
"Cooldown"
);
require(newCommissionRate <= MAX_COMMISION_RATE, "Incorrect value");
_getOrCacheEventsHub().logUpdateCommissionRate(validatorId, newCommissionRate, validators[validatorId].commissionRate);
validators[validatorId].commissionRate = newCommissionRate;
validators[validatorId].lastCommissionUpdate = _epoch;
}
function _getOrCacheEventsHub() private returns(EventsHub) {
EventsHub _eventsHub = EventsHub(eventsHub);
if (_eventsHub == EventsHub(0x0)) {
_eventsHub = EventsHub(Registry(registry).contractMap(keccak256("eventsHub")));
eventsHub = address(_eventsHub);
}
return _eventsHub;
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousRootChain","type":"address"},{"indexed":true,"internalType":"address","name":"newRootChain","type":"address"}],"name":"RootChainChanged","type":"event"},{"constant":true,"inputs":[],"name":"CHECKPOINT_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"NFTContract","outputs":[{"internalType":"contract StakingNFT","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"NFTCounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"WITHDRAWAL_DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"accountStateRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"auctionPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newRootChain","type":"address"}],"name":"changeRootChain","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"checkPointBlockInterval","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"validator","type":"address"}],"name":"checkValidatorWhitelisting","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"checkpointRewardDelta","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"validatorId","type":"uint256"},{"internalType":"uint256","name":"heimdallFee","type":"uint256"},{"internalType":"contract IStakeManager","name":"stakeManager","type":"address"}],"name":"confirmAuctionBid","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"currentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"delegationEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dynasty","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"eventsHub","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"extensionCode","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"governance","outputs":[{"internalType":"contract IGovernance","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"latestSignerUpdateEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"lock","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"locked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"logger","outputs":[{"internalType":"contract StakingInfo","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"maxRewardedCheckpoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"validatorIdFrom","type":"uint256"},{"internalType":"uint256","name":"validatorIdTo","type":"uint256"}],"name":"migrateValidatorsData","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"minDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"minHeimdallFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"prevBlockInterval","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proposerBonus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"registry","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"replacementCoolDown","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rewardDecreasePerCheckpoint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rewardPerStake","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rootChain","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"signerToValidator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"signerUpdateLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"signers","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"validatorId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"_acceptDelegation","type":"bool"},{"internalType":"bytes","name":"_signerPubkey","type":"bytes"}],"name":"startAuction","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalHeimdallFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalRewardsLiquidated","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalStaked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"unlock","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"_rewardDecreasePerCheckpoint","type":"uint256"},{"internalType":"uint256","name":"_maxRewardedCheckpoints","type":"uint256"},{"internalType":"uint256","name":"_checkpointRewardDelta","type":"uint256"}],"name":"updateCheckpointRewardParams","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"validatorId","type":"uint256"},{"internalType":"uint256","name":"newCommissionRate","type":"uint256"}],"name":"updateCommissionRate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"_newContract","type":"address"}],"name":"updateValidatorRegistry","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userFeeExit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"validatorAuction","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"startEpoch","type":"uint256"},{"internalType":"address","name":"user","type":"address"},{"internalType":"bool","name":"acceptDelegation","type":"bool"},{"internalType":"bytes","name":"signerPubkey","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"validatorRegistry","outputs":[{"internalType":"contract IValidatorRegistry","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"validatorShareFactory","outputs":[{"internalType":"contract ValidatorShareFactory","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"validatorState","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"stakerCount","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"validatorStateChanges","outputs":[{"internalType":"int256","name":"amount","type":"int256"},{"internalType":"int256","name":"stakerCount","type":"int256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"validatorThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"validators","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"activationEpoch","type":"uint256"},{"internalType":"uint256","name":"deactivationEpoch","type":"uint256"},{"internalType":"uint256","name":"jailTime","type":"uint256"},{"internalType":"address","name":"signer","type":"address"},{"internalType":"address","name":"contractAddress","type":"address"},{"internalType":"enum StakeManagerStorage.Status","name":"status","type":"uint8"},{"internalType":"uint256","name":"commissionRate","type":"uint256"},{"internalType":"uint256","name":"lastCommissionUpdate","type":"uint256"},{"internalType":"uint256","name":"delegatorsReward","type":"uint256"},{"internalType":"uint256","name":"delegatedAmount","type":"uint256"},{"internalType":"uint256","name":"initialRewardPerStake","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]Contract Creation Code
60806040526023805460ff1916905534801561001a57600080fd5b5060008054610100600160a81b0319168155600180546001600160a01b0319163317908190556040516001600160a01b039190911691907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a3612043806100856000396000f3fe608060405234801561001057600080fd5b50600436106103835760003560e01c80637c7eaf1a116101de578063ac35ab921161010f578063e8afa8e8116100ad578063f83d08ba1161007c578063f83d08ba146108ee578063f8a3176c146108f6578063fba58f34146108fe578063fc0c546a1461090657610383565b8063e8afa8e814610892578063f24ccbfe146108b8578063f2fde38b146108c0578063f376ebbb146108e657610383565b8063d7f5549d116100e9578063d7f5549d14610842578063dcd962b21461085f578063e568959a14610882578063e59ee0c61461088a57610383565b8063ac35ab9214610800578063cd6b838814610832578063cf3090121461083a57610383565b806391f1a3a51161017c5780639ddbbf85116101565780639ddbbf8514610748578063a68548771461076b578063a69df4b5146107f0578063a8dc889b146107f857610383565b806391f1a3a514610730578063987ab9db146107385780639a8a62431461074057610383565b8063817b1cd2116101b8578063817b1cd214610710578063883b455f146107185780638da5cb5b146107205780638f32d59b1461072857610383565b80637c7eaf1a146106da5780637d669752146106e257806380d5c523146106ea57610383565b80634c0f6a8e116102b857806360c8d122116102565780637667180811610230578063766718081461069c57806377939d10146106a457806378f84a44146106ac5780637b103999146106d257610383565b806360c8d122146106615780637060054d1461068c578063715018a61461069457610383565b80635325e144116102925780635325e1441461058257806354b8c601146106495780635508d8e1146106515780635aa6e6751461065957610383565b80634c0f6a8e146105385780634e3c83f1146105725780634fd101d71461057a57610383565b806325726df21161032557806335aa2e44116102ff57806335aa2e44146104495780633862da0b1461050257806341b3d18514610528578063451b59851461053057610383565b806325726df21461040357806331c2273b14610439578063342745861461044157610383565b806317c2b9101161036157806317c2b910146103b25780631ae4818f146103ba5780632079fb9a146103de57806325316411146103fb57610383565b80630cccfc58146103885780630e15561a146103a25780630ebb172a146103aa575b600080fd5b61039061090e565b60408051918252519081900360200190f35b610390610914565b61039061091a565b610390610920565b6103c2610926565b604080516001600160a01b039092168252519081900360200190f35b6103c2600480360360208110156103f457600080fd5b5035610935565b61039061095c565b6104206004803603602081101561041957600080fd5b5035610962565b6040805192835260208301919091528051918290030190f35b6103c261097b565b61039061098a565b6104666004803603602081101561045f57600080fd5b5035610990565b604051808e81526020018d81526020018c81526020018b81526020018a8152602001896001600160a01b03166001600160a01b03168152602001886001600160a01b03166001600160a01b031681526020018760038111156104c457fe5b60ff1681526020018681526020018581526020018481526020018381526020018281526020019d505050505050505050505050505060405180910390f35b6103906004803603602081101561051857600080fd5b50356001600160a01b0316610a01565b610390610a13565b610390610a19565b61055e6004803603602081101561054e57600080fd5b50356001600160a01b0316610a1f565b604080519115158252519081900360200190f35b610390610b1e565b610390610b24565b61059f6004803603602081101561059857600080fd5b5035610b2a565b60405180868152602001858152602001846001600160a01b03166001600160a01b031681526020018315151515815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561060a5781810151838201526020016105f2565b50505050905090810190601f1680156106375780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390f35b61055e610bf0565b610390610bf9565b6103c2610bff565b61068a6004803603606081101561067757600080fd5b5080359060208101359060400135610c13565b005b610390610cc7565b61068a610ccd565b610390610d28565b610390610d2e565b610390600480360360208110156106c257600080fd5b50356001600160a01b0316610d34565b6103c2610d46565b610390610d55565b610390610d5b565b61068a6004803603602081101561070057600080fd5b50356001600160a01b0316610d61565b610390610de8565b6103c2610dee565b6103c2610e02565b61055e610e12565b610390610e23565b6103c2610e29565b610390610e38565b61068a6004803603604081101561075e57600080fd5b5080359060200135610e3e565b61068a6004803603608081101561078157600080fd5b8135916020810135916040820135151591908101906080810160608201356401000000008111156107b157600080fd5b8201836020820111156107c357600080fd5b803590602001918460018302840111640100000000831117156107e557600080fd5b50909250905061104e565b61068a611562565b610390611574565b61068a6004803603606081101561081657600080fd5b50803590602081013590604001356001600160a01b031661157a565b610390611a75565b61055e611a7b565b6103906004803603602081101561085857600080fd5b5035611a84565b61068a6004803603604081101561087557600080fd5b5080359060200135611a96565b610390611bfa565b610420611c00565b61068a600480360360208110156108a857600080fd5b50356001600160a01b0316611c09565b6103c2611c89565b61068a600480360360208110156108d657600080fd5b50356001600160a01b0316611c98565b6103c2611cb5565b61068a611cc4565b6103c2611cd4565b610390611ce3565b6103c2611ce9565b60155481565b60135481565b60085481565b60175481565b6007546001600160a01b031681565b6026818154811061094257fe5b6000918252602090912001546001600160a01b0316905081565b600e5481565b601e602052600090815260409020805460019091015482565b6006546001600160a01b031681565b60165481565b601a60205260009081526040902080546001820154600283015460038401546004850154600586015460068701546007880154600889015460098a0154600a8b0154600b909b0154999a98999798969795966001600160a01b039586169695851695600160a01b90950460ff16948d565b601b6020526000908152604090205481565b600c5481565b60295481565b602b546040805163c3dc504f60e01b815290516000926001600160a01b03169163c3dc504f916004808301926020929190829003018186803b158015610a6457600080fd5b505afa158015610a78573d6000803e3d6000fd5b505050506040513d6020811015610a8e57600080fd5b505115610b1557602b5460408051631f4a58fb60e31b81526001600160a01b0385811660048301529151919092169163fa52c7d8916024808301926020929190829003018186803b158015610ae257600080fd5b505afa158015610af6573d6000803e3d6000fd5b505050506040513d6020811015610b0c57600080fd5b50519050610b19565b5060015b919050565b600f5481565b60105481565b60208080526000918252604091829020805460018083015460028085015460038601805489516101009682161596909602600019011692909204601f8101889004880285018801909852878452939691956001600160a01b03851695600160a01b90950460ff1694939290830182828015610be65780601f10610bbb57610100808354040283529160200191610be6565b820191906000526020600020905b815481529060010190602001808311610bc957829003601f168201915b5050505050905085565b60195460ff1681565b60125481565b60005461010090046001600160a01b031681565b6064610c25838563ffffffff611cf816565b1115610c3057600080fd5b6064811115610c3e57600080fd5b60288390556029829055602a819055610c55611d28565b6001600160a01b031663abf158d58484846040518463ffffffff1660e01b8152600401808481526020018381526020018281526020019350505050600060405180830381600087803b158015610caa57600080fd5b505af1158015610cbe573d6000803e3d6000fd5b50505050505050565b600a5481565b610cd5610e12565b610cde57600080fd5b6001546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b60095481565b60185481565b601f6020526000908152604090205481565b6004546001600160a01b031681565b602a5481565b600b5481565b610d69610e12565b610d7257600080fd5b6001600160a01b038116610dc6576040805162461bcd60e51b8152602060048201526016602482015275616464726573732063616e6e6f74206265207a65726f60501b604482015290519081900360640190fd5b602b80546001600160a01b0319166001600160a01b0392909216919091179055565b60115481565b60235461010090046001600160a01b031681565b6001546001600160a01b03165b90565b6001546001600160a01b0316331490565b60275481565b6002546001600160a01b031681565b60225481565b815b81811015611049576000818152601a60205260409020600601546001600160a01b03168015610ff457610edf6001826001600160a01b03166339c31e936040518163ffffffff1660e01b815260040160206040518083038186803b158015610ea757600080fd5b505afa158015610ebb573d6000803e3d6000fd5b505050506040513d6020811015610ed157600080fd5b50519063ffffffff611dfa16565b601a600084815260200190815260200160002060010181905550806001600160a01b0316633a09bf446040518163ffffffff1660e01b815260040160206040518083038186803b158015610f3257600080fd5b505afa158015610f46573d6000803e3d6000fd5b505050506040513d6020811015610f5c57600080fd5b50516000838152601a6020908152604091829020600a01929092558051638ccdd28960e01b815290516001600160a01b03841692638ccdd2899260048082019391829003018186803b158015610fb157600080fd5b505afa158015610fc5573d6000803e3d6000fd5b505050506040513d6020811015610fdb57600080fd5b50516000838152601a602052604090206007015561102a565b6000828152601a602052604090206001908101546110179163ffffffff611dfa16565b6000838152601a60205260409020600101555b506000818152601a602052604090206001600990910181905501610e40565b505050565b6000858152601a60205260409020805460039091015415801561107057508015155b6110c1576040805162461bcd60e51b815260206004820181905260248201527f496e76616c69642076616c696461746f7220666f7220616e2061756374696f6e604482015290519081900360640190fd5b336000818152601b60209081526040918290205460065483516370a0823160e01b81526004810195909552925190936001600160a01b03909316926370a08231926024808301939192829003018186803b15801561111e57600080fd5b505afa158015611132573d6000803e3d6000fd5b505050506040513d602081101561114857600080fd5b505115801561115957506000198114155b6111a1576040805162461bcd60e51b8152602060048201526014602482015273416c72656164792075736564206164647265737360601b604482015290519081900360640190fd5b6009546018548015806111b45750818111155b6111f7576040805162461bcd60e51b815260206004820152600f60248201526e10dbdbdb191bdddb881c195c9a5bd9608a1b604482015290519081900360640190fd5b601554600a5461120d908263ffffffff611dfa16565b60008b8152601a602052604090206002015461123090859063ffffffff611e0c16565b8161123757fe5b0610611283576040805162461bcd60e51b8152602060048201526016602482015275125b9d985b1a5908185d58dd1a5bdb881c195c9a5bd960521b604482015290519081900360640190fd5b6000898152601a60205260409020600a015484906112a890829063ffffffff611dfa16565b60008b815260208052604090208054919250906112c58382611e21565b92508a831061130d576040805162461bcd60e51b815260206004820152600f60248201526e26bab9ba103134b2103434b3b432b960891b604482015290519081900360640190fd5b600354604080516323b872dd60e01b8152336004820152306024820152604481018e905290516001600160a01b03909216916323b872dd916064808201926020929091908290030181600087803b15801561136757600080fd5b505af115801561137b573d6000803e3d6000fd5b505050506040513d602081101561139157600080fd5b50516113d6576040805162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b604482015290519081900360640190fd5b80156114a85760035460028301546040805163a9059cbb60e01b81526001600160a01b039283166004820152602481018590529051919092169163a9059cbb9160448083019260209291908290030181600087803b15801561143757600080fd5b505af115801561144b573d6000803e3d6000fd5b505050506040513d602081101561146157600080fd5b50516114a8576040805162461bcd60e51b8152602060048201526011602482015270109a59081c995d1d5c9b8819985a5b1959607a1b604482015290519081900360640190fd5b8a8255600282018054336001600160a01b03199091161760ff60a01b1916600160a01b8c1515021790556114e0600383018a8a611f10565b5060055460408051630934a6df60e01b8152600481018f9052602481018a9052604481018e905290516001600160a01b0390921691630934a6df9160648082019260009290919082900301818387803b15801561153c57600080fd5b505af1158015611550573d6000803e3d6000fd5b50505050505050505050505050505050565b61156a611e38565b611572611e86565b565b60245481565b6000838152602080526040902060028101546001600160a01b03163381148061161d575060065460408051632f745c5960e01b815233600482015260006024820152905187926001600160a01b031691632f745c59916044808301926020929190829003018186803b1580156115ef57600080fd5b505afa158015611603573d6000803e3d6000fd5b505050506040513d602081101561161957600080fd5b5051145b61166e576040805162461bcd60e51b815260206004820152601760248201527f4f6e6c79206269646465722063616e20636f6e6669726d000000000000000000604482015290519081900360640190fd5b600954601554600a5461168890829063ffffffff611dfa16565b600185015461169e90849063ffffffff611e0c16565b816116a557fe5b0610156116f9576040805162461bcd60e51b815260206004820181905260248201527f4e6f7420616c6c6f776564206265666f72652061756374696f6e506572696f64604482015290519081900360640190fd5b60028301546001600160a01b031661174a576040805162461bcd60e51b815260206004820152600f60248201526e24b73b30b634b21030bab1ba34b7b760891b604482015290519081900360640190fd5b6000868152601a6020526040902080548454600a909201549091829161177790839063ffffffff611dfa16565b915080821015801561179857506000898152601a6020526040902060030154155b156118e3576003546040805163a9059cbb60e01b81526001600160a01b038881166004830152602482018590529151919092169163a9059cbb9160448083019260209291908290030181600087803b1580156117f357600080fd5b505af1158015611807573d6000803e3d6000fd5b505050506040513d602081101561181d57600080fd5b5051611864576040805162461bcd60e51b8152602060048201526011602482015270109a59081c995d1d5c9b8819985a5b1959607a1b604482015290519081900360640190fd5b600186018490556005546040805163e12ab1af60e01b8152600481018c9052602481018c90526044810186905290516001600160a01b039092169163e12ab1af9160648082019260009290919082900301818387803b1580156118c657600080fd5b505af11580156118da573d6000803e3d6000fd5b50505050611a16565b866001600160a01b03166352b8115d868a8c858b60020160149054906101000a900460ff168c6003016040518763ffffffff1660e01b815260040180876001600160a01b03166001600160a01b0316815260200186815260200185815260200184815260200183151515158152602001806020018281038252838181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156119d85780601f106119ad576101008083540402835291602001916119d8565b820191906000526020600020905b8154815290600101906020018083116119bb57829003601f168201915b5050975050505050505050600060405180830381600087803b1580156119fd57600080fd5b505af1158015611a11573d6000803e3d6000fd5b505050505b60018087015460008b815260208052604081208181559283018190556002830180546001600160a81b0319169055909190611a546003830182611f8e565b50506000998a52602080526040909920600101989098555050505050505050565b60145481565b60005460ff1681565b60216020526000908152604090205481565b6009546000838152601a6020526040902060089081015490548290611ac290839063ffffffff611dfa16565b111580611acd575080155b611b09576040805162461bcd60e51b815260206004820152600860248201526721b7b7b63237bbb760c11b604482015290519081900360640190fd5b6064831115611b51576040805162461bcd60e51b815260206004820152600f60248201526e496e636f72726563742076616c756560881b604482015290519081900360640190fd5b611b59611d28565b6001600160a01b031663c98cc0028585601a6000898152602001908152602001600020600701546040518463ffffffff1660e01b8152600401808481526020018381526020018281526020019350505050600060405180830381600087803b158015611bc457600080fd5b505af1158015611bd8573d6000803e3d6000fd5b50505060009485525050601a6020526040909220600781019190915560080155565b60285481565b601c54601d5482565b611c11610e12565b611c1a57600080fd5b6001600160a01b038116611c2d57600080fd5b6002546040516001600160a01b038084169216907f211c9015fc81c0dbd45bd99f0f29fc1c143bfd53442d5ffd722bbbef7a887fe990600090a3600280546001600160a01b0319166001600160a01b0392909216919091179055565b6005546001600160a01b031681565b611ca0610e12565b611ca957600080fd5b611cb281611e92565b50565b602b546001600160a01b031681565b611ccc611e38565b611572611f01565b6025546001600160a01b031681565b600d5481565b6003546001600160a01b031681565b600082611d0757506000611d22565b82820282848281611d1457fe5b0414611d1f57600080fd5b90505b92915050565b60235460009061010090046001600160a01b031680611df55760048054604080516832bb32b73a39a43ab160b91b8152815190819003600901812063cac39a0560e01b825293810193909352516001600160a01b039091169163cac39a05916024808301926020929190829003018186803b158015611da657600080fd5b505afa158015611dba573d6000803e3d6000fd5b505050506040513d6020811015611dd057600080fd5b505160238054610100600160a81b0319166101006001600160a01b0384160217905590505b905090565b600082820183811015611d1f57600080fd5b600082821115611e1b57600080fd5b50900390565b600081831015611e315781611d1f565b5090919050565b60005461010090046001600160a01b031633146115725760405162461bcd60e51b8152600401808060200182810382526026815260200180611fe96026913960400191505060405180910390fd5b6000805460ff19169055565b6001600160a01b038116611ea557600080fd5b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000805460ff19166001179055565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611f515782800160ff19823516178555611f7e565b82800160010185558215611f7e579182015b82811115611f7e578235825591602001919060010190611f63565b50611f8a929150611fce565b5090565b50805460018160011615610100020316600290046000825580601f10611fb45750611cb2565b601f016020900490600052602060002090810190611cb291905b610e0f91905b80821115611f8a5760008155600101611fd456fe4f6e6c7920676f7665726e616e636520636f6e747261637420697320617574686f72697a6564a265627a7a72315820ca57d7c83e42dc265592db7dda4e542accb09f0818adbeeb01d5a2c32e2acb2164736f6c63430005110032
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103835760003560e01c80637c7eaf1a116101de578063ac35ab921161010f578063e8afa8e8116100ad578063f83d08ba1161007c578063f83d08ba146108ee578063f8a3176c146108f6578063fba58f34146108fe578063fc0c546a1461090657610383565b8063e8afa8e814610892578063f24ccbfe146108b8578063f2fde38b146108c0578063f376ebbb146108e657610383565b8063d7f5549d116100e9578063d7f5549d14610842578063dcd962b21461085f578063e568959a14610882578063e59ee0c61461088a57610383565b8063ac35ab9214610800578063cd6b838814610832578063cf3090121461083a57610383565b806391f1a3a51161017c5780639ddbbf85116101565780639ddbbf8514610748578063a68548771461076b578063a69df4b5146107f0578063a8dc889b146107f857610383565b806391f1a3a514610730578063987ab9db146107385780639a8a62431461074057610383565b8063817b1cd2116101b8578063817b1cd214610710578063883b455f146107185780638da5cb5b146107205780638f32d59b1461072857610383565b80637c7eaf1a146106da5780637d669752146106e257806380d5c523146106ea57610383565b80634c0f6a8e116102b857806360c8d122116102565780637667180811610230578063766718081461069c57806377939d10146106a457806378f84a44146106ac5780637b103999146106d257610383565b806360c8d122146106615780637060054d1461068c578063715018a61461069457610383565b80635325e144116102925780635325e1441461058257806354b8c601146106495780635508d8e1146106515780635aa6e6751461065957610383565b80634c0f6a8e146105385780634e3c83f1146105725780634fd101d71461057a57610383565b806325726df21161032557806335aa2e44116102ff57806335aa2e44146104495780633862da0b1461050257806341b3d18514610528578063451b59851461053057610383565b806325726df21461040357806331c2273b14610439578063342745861461044157610383565b806317c2b9101161036157806317c2b910146103b25780631ae4818f146103ba5780632079fb9a146103de57806325316411146103fb57610383565b80630cccfc58146103885780630e15561a146103a25780630ebb172a146103aa575b600080fd5b61039061090e565b60408051918252519081900360200190f35b610390610914565b61039061091a565b610390610920565b6103c2610926565b604080516001600160a01b039092168252519081900360200190f35b6103c2600480360360208110156103f457600080fd5b5035610935565b61039061095c565b6104206004803603602081101561041957600080fd5b5035610962565b6040805192835260208301919091528051918290030190f35b6103c261097b565b61039061098a565b6104666004803603602081101561045f57600080fd5b5035610990565b604051808e81526020018d81526020018c81526020018b81526020018a8152602001896001600160a01b03166001600160a01b03168152602001886001600160a01b03166001600160a01b031681526020018760038111156104c457fe5b60ff1681526020018681526020018581526020018481526020018381526020018281526020019d505050505050505050505050505060405180910390f35b6103906004803603602081101561051857600080fd5b50356001600160a01b0316610a01565b610390610a13565b610390610a19565b61055e6004803603602081101561054e57600080fd5b50356001600160a01b0316610a1f565b604080519115158252519081900360200190f35b610390610b1e565b610390610b24565b61059f6004803603602081101561059857600080fd5b5035610b2a565b60405180868152602001858152602001846001600160a01b03166001600160a01b031681526020018315151515815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561060a5781810151838201526020016105f2565b50505050905090810190601f1680156106375780820380516001836020036101000a031916815260200191505b50965050505050505060405180910390f35b61055e610bf0565b610390610bf9565b6103c2610bff565b61068a6004803603606081101561067757600080fd5b5080359060208101359060400135610c13565b005b610390610cc7565b61068a610ccd565b610390610d28565b610390610d2e565b610390600480360360208110156106c257600080fd5b50356001600160a01b0316610d34565b6103c2610d46565b610390610d55565b610390610d5b565b61068a6004803603602081101561070057600080fd5b50356001600160a01b0316610d61565b610390610de8565b6103c2610dee565b6103c2610e02565b61055e610e12565b610390610e23565b6103c2610e29565b610390610e38565b61068a6004803603604081101561075e57600080fd5b5080359060200135610e3e565b61068a6004803603608081101561078157600080fd5b8135916020810135916040820135151591908101906080810160608201356401000000008111156107b157600080fd5b8201836020820111156107c357600080fd5b803590602001918460018302840111640100000000831117156107e557600080fd5b50909250905061104e565b61068a611562565b610390611574565b61068a6004803603606081101561081657600080fd5b50803590602081013590604001356001600160a01b031661157a565b610390611a75565b61055e611a7b565b6103906004803603602081101561085857600080fd5b5035611a84565b61068a6004803603604081101561087557600080fd5b5080359060200135611a96565b610390611bfa565b610420611c00565b61068a600480360360208110156108a857600080fd5b50356001600160a01b0316611c09565b6103c2611c89565b61068a600480360360208110156108d657600080fd5b50356001600160a01b0316611c98565b6103c2611cb5565b61068a611cc4565b6103c2611cd4565b610390611ce3565b6103c2611ce9565b60155481565b60135481565b60085481565b60175481565b6007546001600160a01b031681565b6026818154811061094257fe5b6000918252602090912001546001600160a01b0316905081565b600e5481565b601e602052600090815260409020805460019091015482565b6006546001600160a01b031681565b60165481565b601a60205260009081526040902080546001820154600283015460038401546004850154600586015460068701546007880154600889015460098a0154600a8b0154600b909b0154999a98999798969795966001600160a01b039586169695851695600160a01b90950460ff16948d565b601b6020526000908152604090205481565b600c5481565b60295481565b602b546040805163c3dc504f60e01b815290516000926001600160a01b03169163c3dc504f916004808301926020929190829003018186803b158015610a6457600080fd5b505afa158015610a78573d6000803e3d6000fd5b505050506040513d6020811015610a8e57600080fd5b505115610b1557602b5460408051631f4a58fb60e31b81526001600160a01b0385811660048301529151919092169163fa52c7d8916024808301926020929190829003018186803b158015610ae257600080fd5b505afa158015610af6573d6000803e3d6000fd5b505050506040513d6020811015610b0c57600080fd5b50519050610b19565b5060015b919050565b600f5481565b60105481565b60208080526000918252604091829020805460018083015460028085015460038601805489516101009682161596909602600019011692909204601f8101889004880285018801909852878452939691956001600160a01b03851695600160a01b90950460ff1694939290830182828015610be65780601f10610bbb57610100808354040283529160200191610be6565b820191906000526020600020905b815481529060010190602001808311610bc957829003601f168201915b5050505050905085565b60195460ff1681565b60125481565b60005461010090046001600160a01b031681565b6064610c25838563ffffffff611cf816565b1115610c3057600080fd5b6064811115610c3e57600080fd5b60288390556029829055602a819055610c55611d28565b6001600160a01b031663abf158d58484846040518463ffffffff1660e01b8152600401808481526020018381526020018281526020019350505050600060405180830381600087803b158015610caa57600080fd5b505af1158015610cbe573d6000803e3d6000fd5b50505050505050565b600a5481565b610cd5610e12565b610cde57600080fd5b6001546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600180546001600160a01b0319169055565b60095481565b60185481565b601f6020526000908152604090205481565b6004546001600160a01b031681565b602a5481565b600b5481565b610d69610e12565b610d7257600080fd5b6001600160a01b038116610dc6576040805162461bcd60e51b8152602060048201526016602482015275616464726573732063616e6e6f74206265207a65726f60501b604482015290519081900360640190fd5b602b80546001600160a01b0319166001600160a01b0392909216919091179055565b60115481565b60235461010090046001600160a01b031681565b6001546001600160a01b03165b90565b6001546001600160a01b0316331490565b60275481565b6002546001600160a01b031681565b60225481565b815b81811015611049576000818152601a60205260409020600601546001600160a01b03168015610ff457610edf6001826001600160a01b03166339c31e936040518163ffffffff1660e01b815260040160206040518083038186803b158015610ea757600080fd5b505afa158015610ebb573d6000803e3d6000fd5b505050506040513d6020811015610ed157600080fd5b50519063ffffffff611dfa16565b601a600084815260200190815260200160002060010181905550806001600160a01b0316633a09bf446040518163ffffffff1660e01b815260040160206040518083038186803b158015610f3257600080fd5b505afa158015610f46573d6000803e3d6000fd5b505050506040513d6020811015610f5c57600080fd5b50516000838152601a6020908152604091829020600a01929092558051638ccdd28960e01b815290516001600160a01b03841692638ccdd2899260048082019391829003018186803b158015610fb157600080fd5b505afa158015610fc5573d6000803e3d6000fd5b505050506040513d6020811015610fdb57600080fd5b50516000838152601a602052604090206007015561102a565b6000828152601a602052604090206001908101546110179163ffffffff611dfa16565b6000838152601a60205260409020600101555b506000818152601a602052604090206001600990910181905501610e40565b505050565b6000858152601a60205260409020805460039091015415801561107057508015155b6110c1576040805162461bcd60e51b815260206004820181905260248201527f496e76616c69642076616c696461746f7220666f7220616e2061756374696f6e604482015290519081900360640190fd5b336000818152601b60209081526040918290205460065483516370a0823160e01b81526004810195909552925190936001600160a01b03909316926370a08231926024808301939192829003018186803b15801561111e57600080fd5b505afa158015611132573d6000803e3d6000fd5b505050506040513d602081101561114857600080fd5b505115801561115957506000198114155b6111a1576040805162461bcd60e51b8152602060048201526014602482015273416c72656164792075736564206164647265737360601b604482015290519081900360640190fd5b6009546018548015806111b45750818111155b6111f7576040805162461bcd60e51b815260206004820152600f60248201526e10dbdbdb191bdddb881c195c9a5bd9608a1b604482015290519081900360640190fd5b601554600a5461120d908263ffffffff611dfa16565b60008b8152601a602052604090206002015461123090859063ffffffff611e0c16565b8161123757fe5b0610611283576040805162461bcd60e51b8152602060048201526016602482015275125b9d985b1a5908185d58dd1a5bdb881c195c9a5bd960521b604482015290519081900360640190fd5b6000898152601a60205260409020600a015484906112a890829063ffffffff611dfa16565b60008b815260208052604090208054919250906112c58382611e21565b92508a831061130d576040805162461bcd60e51b815260206004820152600f60248201526e26bab9ba103134b2103434b3b432b960891b604482015290519081900360640190fd5b600354604080516323b872dd60e01b8152336004820152306024820152604481018e905290516001600160a01b03909216916323b872dd916064808201926020929091908290030181600087803b15801561136757600080fd5b505af115801561137b573d6000803e3d6000fd5b505050506040513d602081101561139157600080fd5b50516113d6576040805162461bcd60e51b815260206004820152600f60248201526e151c985b9cd9995c8819985a5b1959608a1b604482015290519081900360640190fd5b80156114a85760035460028301546040805163a9059cbb60e01b81526001600160a01b039283166004820152602481018590529051919092169163a9059cbb9160448083019260209291908290030181600087803b15801561143757600080fd5b505af115801561144b573d6000803e3d6000fd5b505050506040513d602081101561146157600080fd5b50516114a8576040805162461bcd60e51b8152602060048201526011602482015270109a59081c995d1d5c9b8819985a5b1959607a1b604482015290519081900360640190fd5b8a8255600282018054336001600160a01b03199091161760ff60a01b1916600160a01b8c1515021790556114e0600383018a8a611f10565b5060055460408051630934a6df60e01b8152600481018f9052602481018a9052604481018e905290516001600160a01b0390921691630934a6df9160648082019260009290919082900301818387803b15801561153c57600080fd5b505af1158015611550573d6000803e3d6000fd5b50505050505050505050505050505050565b61156a611e38565b611572611e86565b565b60245481565b6000838152602080526040902060028101546001600160a01b03163381148061161d575060065460408051632f745c5960e01b815233600482015260006024820152905187926001600160a01b031691632f745c59916044808301926020929190829003018186803b1580156115ef57600080fd5b505afa158015611603573d6000803e3d6000fd5b505050506040513d602081101561161957600080fd5b5051145b61166e576040805162461bcd60e51b815260206004820152601760248201527f4f6e6c79206269646465722063616e20636f6e6669726d000000000000000000604482015290519081900360640190fd5b600954601554600a5461168890829063ffffffff611dfa16565b600185015461169e90849063ffffffff611e0c16565b816116a557fe5b0610156116f9576040805162461bcd60e51b815260206004820181905260248201527f4e6f7420616c6c6f776564206265666f72652061756374696f6e506572696f64604482015290519081900360640190fd5b60028301546001600160a01b031661174a576040805162461bcd60e51b815260206004820152600f60248201526e24b73b30b634b21030bab1ba34b7b760891b604482015290519081900360640190fd5b6000868152601a6020526040902080548454600a909201549091829161177790839063ffffffff611dfa16565b915080821015801561179857506000898152601a6020526040902060030154155b156118e3576003546040805163a9059cbb60e01b81526001600160a01b038881166004830152602482018590529151919092169163a9059cbb9160448083019260209291908290030181600087803b1580156117f357600080fd5b505af1158015611807573d6000803e3d6000fd5b505050506040513d602081101561181d57600080fd5b5051611864576040805162461bcd60e51b8152602060048201526011602482015270109a59081c995d1d5c9b8819985a5b1959607a1b604482015290519081900360640190fd5b600186018490556005546040805163e12ab1af60e01b8152600481018c9052602481018c90526044810186905290516001600160a01b039092169163e12ab1af9160648082019260009290919082900301818387803b1580156118c657600080fd5b505af11580156118da573d6000803e3d6000fd5b50505050611a16565b866001600160a01b03166352b8115d868a8c858b60020160149054906101000a900460ff168c6003016040518763ffffffff1660e01b815260040180876001600160a01b03166001600160a01b0316815260200186815260200185815260200184815260200183151515158152602001806020018281038252838181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156119d85780601f106119ad576101008083540402835291602001916119d8565b820191906000526020600020905b8154815290600101906020018083116119bb57829003601f168201915b5050975050505050505050600060405180830381600087803b1580156119fd57600080fd5b505af1158015611a11573d6000803e3d6000fd5b505050505b60018087015460008b815260208052604081208181559283018190556002830180546001600160a81b0319169055909190611a546003830182611f8e565b50506000998a52602080526040909920600101989098555050505050505050565b60145481565b60005460ff1681565b60216020526000908152604090205481565b6009546000838152601a6020526040902060089081015490548290611ac290839063ffffffff611dfa16565b111580611acd575080155b611b09576040805162461bcd60e51b815260206004820152600860248201526721b7b7b63237bbb760c11b604482015290519081900360640190fd5b6064831115611b51576040805162461bcd60e51b815260206004820152600f60248201526e496e636f72726563742076616c756560881b604482015290519081900360640190fd5b611b59611d28565b6001600160a01b031663c98cc0028585601a6000898152602001908152602001600020600701546040518463ffffffff1660e01b8152600401808481526020018381526020018281526020019350505050600060405180830381600087803b158015611bc457600080fd5b505af1158015611bd8573d6000803e3d6000fd5b50505060009485525050601a6020526040909220600781019190915560080155565b60285481565b601c54601d5482565b611c11610e12565b611c1a57600080fd5b6001600160a01b038116611c2d57600080fd5b6002546040516001600160a01b038084169216907f211c9015fc81c0dbd45bd99f0f29fc1c143bfd53442d5ffd722bbbef7a887fe990600090a3600280546001600160a01b0319166001600160a01b0392909216919091179055565b6005546001600160a01b031681565b611ca0610e12565b611ca957600080fd5b611cb281611e92565b50565b602b546001600160a01b031681565b611ccc611e38565b611572611f01565b6025546001600160a01b031681565b600d5481565b6003546001600160a01b031681565b600082611d0757506000611d22565b82820282848281611d1457fe5b0414611d1f57600080fd5b90505b92915050565b60235460009061010090046001600160a01b031680611df55760048054604080516832bb32b73a39a43ab160b91b8152815190819003600901812063cac39a0560e01b825293810193909352516001600160a01b039091169163cac39a05916024808301926020929190829003018186803b158015611da657600080fd5b505afa158015611dba573d6000803e3d6000fd5b505050506040513d6020811015611dd057600080fd5b505160238054610100600160a81b0319166101006001600160a01b0384160217905590505b905090565b600082820183811015611d1f57600080fd5b600082821115611e1b57600080fd5b50900390565b600081831015611e315781611d1f565b5090919050565b60005461010090046001600160a01b031633146115725760405162461bcd60e51b8152600401808060200182810382526026815260200180611fe96026913960400191505060405180910390fd5b6000805460ff19169055565b6001600160a01b038116611ea557600080fd5b6001546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000805460ff19166001179055565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611f515782800160ff19823516178555611f7e565b82800160010185558215611f7e579182015b82811115611f7e578235825591602001919060010190611f63565b50611f8a929150611fce565b5090565b50805460018160011615610100020316600290046000825580601f10611fb45750611cb2565b601f016020900490600052602060002090810190611cb291905b610e0f91905b80821115611f8a5760008155600101611fd456fe4f6e6c7920676f7665726e616e636520636f6e747261637420697320617574686f72697a6564a265627a7a72315820ca57d7c83e42dc265592db7dda4e542accb09f0818adbeeb01d5a2c32e2acb2164736f6c63430005110032
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
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.