More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 233 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Stake | 20642768 | 3 days ago | IN | 0 ETH | 0.00063369 | ||||
Claim Reward | 20636635 | 4 days ago | IN | 0 ETH | 0.00020333 | ||||
Claim Reward | 20592203 | 10 days ago | IN | 0 ETH | 0.0003077 | ||||
Stake | 20583201 | 11 days ago | IN | 0 ETH | 0.00031793 | ||||
Claim Reward | 20578770 | 12 days ago | IN | 0 ETH | 0.0003109 | ||||
Unpause | 20576128 | 12 days ago | IN | 0 ETH | 0.00005658 | ||||
Start Reward Per... | 20575325 | 12 days ago | IN | 0 ETH | 0.0000584 | ||||
Deposit Reward | 20575298 | 12 days ago | IN | 0 ETH | 0.00008393 | ||||
Unstake | 20571926 | 13 days ago | IN | 0 ETH | 0.00022402 | ||||
Claim Reward | 20571923 | 13 days ago | IN | 0 ETH | 0.00012886 | ||||
Unstake | 20571896 | 13 days ago | IN | 0 ETH | 0.00020284 | ||||
Claim Reward | 20571889 | 13 days ago | IN | 0 ETH | 0.0000945 | ||||
Unstake | 20571869 | 13 days ago | IN | 0 ETH | 0.00016692 | ||||
Claim Reward | 20571865 | 13 days ago | IN | 0 ETH | 0.00009561 | ||||
Unstake | 20571842 | 13 days ago | IN | 0 ETH | 0.00017457 | ||||
Claim Reward | 20571836 | 13 days ago | IN | 0 ETH | 0.00009784 | ||||
Unstake | 20571820 | 13 days ago | IN | 0 ETH | 0.00018424 | ||||
Claim Reward | 20571813 | 13 days ago | IN | 0 ETH | 0.00010639 | ||||
Unstake | 20571781 | 13 days ago | IN | 0 ETH | 0.00020163 | ||||
Claim Reward | 20571768 | 13 days ago | IN | 0 ETH | 0.00011508 | ||||
Unstake | 20571746 | 13 days ago | IN | 0 ETH | 0.00021526 | ||||
Claim Reward | 20571742 | 13 days ago | IN | 0 ETH | 0.00012013 | ||||
Unstake | 20571715 | 13 days ago | IN | 0 ETH | 0.0002059 | ||||
Claim Reward | 20571709 | 13 days ago | IN | 0 ETH | 0.00011388 | ||||
Claim Reward | 20571620 | 13 days ago | IN | 0 ETH | 0.00013328 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
VestedStaking
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import "./BaseStaking.sol"; /** * @title Vested Staking Contract * @dev Extends BaseStaking to support various vesting schedules for different types of stakeholders, * including pre-seed funding, private presale, team and advisors, and public participants. * It enables staking with time-locked rewards based on predefined vesting periods and distributions. */ contract VestedStaking is BaseStaking{ mapping(address => VestingSchedule) private vestingSchedules; //Pre Seed funding vesting type uint256[] public vestingPeriodsPreSeedFunding = [ 7 days, // 7 days from start 1 * 30 days, // 1 month from start 2 * 30 days, // 2 months from start 3 * 30 days, // 3 months from start 6 * 30 days, // 6 months from start 9 * 30 days, // 9 months from start 12 * 30 days, // 12 months from start 15 * 30 days // 15 months from start ]; uint8[] public vestingDistributionPreSeedFunding = [10, 3, 3, 4, 20, 20, 20, 20]; // Percentages // Private presale vesting type uint256[] public vestingPeriodsPrivatePresale = [ 7 days, // 7 days from start 1 * 30 days, // 1 month from start 2 * 30 days, // 2 months from start 3 * 30 days // 3 months from start ]; uint8[] public vestingDistributionPrivatePresale = [10, 30, 30, 30]; // Percentages // Team and advisors vesting type uint256[] public vestingPeriodsTeamAndAdvisors = [ 6 * 30 days, // 6 months from start 12 * 30 days, // 12 months from start 15 * 30 days, // 15 months from start 24 * 30 days // 24 months from start ]; uint8[] public vestingDistributionTeamAndAdvisors = [20, 20, 20, 40]; // Percentages // Team and advisors vesting type uint256[] public vestingPeriodsPublic = [ 3 * 30 days // 3 months from start ]; uint8[] public vestingDistributionPublic = [100]; // Percentages enum VestingType { None, // 0 PreSeedFunding, // 1 PrivatePresale, // 2 TeamAndAdvisors, // 3 Public // 4 } struct VestingSchedule { uint256[] periods; // timestamps when each vesting period ends uint256[] amounts; // amounts that can be withdrawn at each period uint256 withdrawn; // total amount already withdrawn VestingType vestingType; } /** * @dev Initializes the VestedStaking contract by setting up the admin, rewards token, and staking token. * It calls the BaseStaking constructor to inherit base staking functionalities. * @param _multisigAdmin Address of the multisig admin for role management. * @param _rewardsToken Address of the ERC20 token used as rewards. * @param _stakingToken Address of the ERC20 token accepted for staking. */ constructor( address _multisigAdmin, address _rewardsToken, address _stakingToken ) BaseStaking(_multisigAdmin,_rewardsToken,_stakingToken,0) { } /** * @notice Allows users to stake tokens under the public vesting schedule. * @dev Stakes tokens with a predefined public vesting schedule. Ensures that either no previous * vesting schedule is set for the staker or only allows continuation under the public vesting type. * @param amount The amount of tokens to be staked. */ function stake(uint256 amount) external override nonReentrant whenNotPaused { require(vestingSchedules[msg.sender].vestingType== VestingType.None || vestingSchedules[msg.sender].vestingType == VestingType.Public,"Can not previously have a vesting schedule or must be Public"); _stakeFor(amount,msg.sender,msg.sender,VestingType.Public); } /** * @notice Allows a manager to stake tokens for a given investor with a designated vesting schedule. * @dev Stakes tokens for a specified investor address under a given vesting type. Only callable * by accounts with manager role, and enforces that the chosen vesting schedule is valid and not public. * @param amount The amount of tokens to be staked by the investor. * @param investor Address of the investor for whom tokens are being staked. * @param vestingType The type of vesting schedule to be applied. */ function stakeFor(uint256 amount, address investor, VestingType vestingType) external onlyManager nonReentrant whenNotPaused { require(investor != address(0), "Invalid address"); require(amount > 0, "Invalid amount"); require(vestingType != VestingType.None, "Invalid vesting type"); require(vestingType != VestingType.Public, "Public vesting is not allowed in stakeFor"); require(vestingSchedules[investor].vestingType== VestingType.None || vestingSchedules[investor].vestingType == vestingType,"Can not previously have a vesting schedule or use the same vesting schedule"); _stakeFor(amount,investor,msg.sender,vestingType); } /** * @notice Allows users to unstake tokens according to their applicable vesting schedule. * @dev Unstakes a specified amount of tokens if the amount does not exceed the available vested amount. * Checks and applies the vesting schedule to determine the withdrawable amount before proceeding with the unstake. * @param amount The amount of tokens to be unstaked. */ function unstake(uint amount) public override nonReentrant{ require(vestingSchedules[msg.sender].vestingType!= VestingType.None,"No vesting schedule found"); require(amount > 0, "Invalid amount"); VestingSchedule storage schedule = vestingSchedules[msg.sender]; //Check if there is a vesting schedule and then check if its allowed to withdraw if(schedule.amounts.length > 0){ uint256 available = 0; uint256 totalVested = 0; for (uint256 i = 0; i < schedule.periods.length; i++) { if (block.timestamp >= schedule.periods[i]) { available += schedule.amounts[i]; } totalVested += schedule.amounts[i]; } require(available >= schedule.withdrawn + amount, "You are trying to withdraw more than available"); schedule.withdrawn += amount; // Check if all vested amounts are withdrawn and all periods are outdated if (schedule.withdrawn == totalVested && block.timestamp >= schedule.periods[schedule.periods.length - 1]) { // Clear the vesting schedule delete vestingSchedules[msg.sender]; } } _unstake(amount,msg.sender); } /** * @notice Returns the available amount of tokens that can be unstaked for a specific account at the current time. * @dev Calculates the available amount based on the account's vesting schedule and already withdrawn amount. * @param account The address of the account to check the available unstake amount for. * @return The amount of tokens available for unstaking. */ function getAvailableAmount(address account) public view returns (uint256) { VestingSchedule memory schedule = vestingSchedules[account]; uint256 available = 0; if(schedule.amounts.length > 0){ for (uint256 i = 0; i < schedule.periods.length; i++) { if (block.timestamp >= schedule.periods[i]) { available = available + schedule.amounts[i]; } } return available - schedule.withdrawn; } else{ return balanceOf(account); } } /** * @notice Retrieves the vesting schedule for a specific account. * @dev Returns the details of the vesting schedule, including periods, amounts, withdrawn amount, and vesting type. * @param account The address of the account whose vesting schedule is being queried. * @return periods Array of timestamps when each vesting period ends. * @return amounts Array of amounts that can be withdrawn at each period. * @return withdrawn Total amount already withdrawn under the vesting schedule. * @return vestingType The type of vesting applied to the account. */ function getVestingSchedule(address account) public view returns (uint256[] memory, uint256[] memory, uint256, uint8) { VestingSchedule memory schedule = vestingSchedules[account]; return (schedule.periods, schedule.amounts, schedule.withdrawn, uint8(schedule.vestingType)); } /** * @notice Determines the next timestamp when tokens become available for unstaking. * @dev Calculates the next unlock timestamp based on the account's vesting schedule. * @param account The address of the account to check the next unlock timestamp for. * @return The timestamp of the next vesting period when tokens become available for unstaking. */ function getNextUnlockTimestamp(address account) public view returns (uint256) { if(vestingSchedules[account].amounts.length > 0){ VestingSchedule memory schedule = vestingSchedules[account]; for (uint256 i = 0; i < schedule.periods.length; i++) { if (block.timestamp < schedule.periods[i]) { return schedule.periods[i]; } } } return 0; } /** * @dev Internal function to stake tokens for an investor with a specified vesting type. * Calculates the distribution of staked tokens across the vesting periods based on the vesting type's schedule. * @param amount The amount of tokens to be staked. * @param investor The address of the investor the topens are staked for. * @param payer The address of the account paying for the staked tokens * @param vestingType The type of vesting schedule to apply to the staked tokens. */ function _stakeFor(uint256 amount, address investor, address payer, VestingType vestingType) internal { uint256[] memory vestedAmounts; uint256[] memory vestedPeriods; uint8[] memory distribution; uint256[] memory periods; if (vestingType == VestingType.PreSeedFunding) { vestedAmounts = new uint256[](vestingDistributionPreSeedFunding.length); vestedPeriods = new uint256[](vestingPeriodsPreSeedFunding.length); distribution = vestingDistributionPreSeedFunding; periods = vestingPeriodsPreSeedFunding; } else if (vestingType == VestingType.PrivatePresale) { vestedAmounts = new uint256[](vestingDistributionPrivatePresale.length); vestedPeriods = new uint256[](vestingPeriodsPrivatePresale.length); distribution = vestingDistributionPrivatePresale; periods = vestingPeriodsPrivatePresale; } else if (vestingType == VestingType.TeamAndAdvisors) { vestedAmounts = new uint256[](vestingDistributionTeamAndAdvisors.length); vestedPeriods = new uint256[](vestingPeriodsTeamAndAdvisors.length); distribution = vestingDistributionTeamAndAdvisors; periods = vestingPeriodsTeamAndAdvisors; } else if (vestingType == VestingType.Public) { vestedAmounts = new uint256[](vestingDistributionPublic.length); vestedPeriods = new uint256[](vestingPeriodsPublic.length); distribution = vestingDistributionPublic; periods = vestingPeriodsPublic; } else { revert("Invalid vesting type"); } //If staking more than once, add the previous balance uint256 total = balanceOf(investor) + amount; uint256 roundingError = 0; uint256 sumVestedAmounts = 0; for (uint8 i = 0; i < distribution.length; i++) { uint256 vestedAmount = (total * distribution[i]) / 100; roundingError += (total * distribution[i]) % 100; if (roundingError >= 100) { vestedAmount += 1; roundingError -= 100; } vestedAmounts[i] = vestedAmount; vestedPeriods[i] = block.timestamp + periods[i]; sumVestedAmounts += vestedAmount; } // Adjust the last element to account for any residual rounding error if (distribution.length > 0) { vestedAmounts[distribution.length - 1] += (total - sumVestedAmounts); } vestingSchedules[investor] = VestingSchedule(vestedPeriods, vestedAmounts, 0,vestingType); _stake(amount,investor,payer); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol) pragma solidity ^0.8.20; import {IAccessControl} from "./IAccessControl.sol"; import {Context} from "../utils/Context.sol"; import {ERC165} from "../utils/introspection/ERC165.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address account => bool) hasRole; bytes32 adminRole; } mapping(bytes32 role => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with an {AccessControlUnauthorizedAccount} error including the required role. */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual returns (bool) { return _roles[role].hasRole[account]; } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()` * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier. */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account` * is missing `role`. */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert AccessControlUnauthorizedAccount(account, role); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleGranted} event. */ function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `callerConfirmation`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address callerConfirmation) public virtual { if (callerConfirmation != _msgSender()) { revert AccessControlBadConfirmation(); } _revokeRole(role, callerConfirmation); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual returns (bool) { if (!hasRole(role, account)) { _roles[role].hasRole[account] = true; emit RoleGranted(role, account, _msgSender()); return true; } else { return false; } } /** * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual returns (bool) { if (hasRole(role, account)) { _roles[role].hasRole[account] = false; emit RoleRevoked(role, account, _msgSender()); return true; } else { return false; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol) pragma solidity ^0.8.20; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControl { /** * @dev The `account` is missing a role. */ error AccessControlUnauthorizedAccount(address account, bytes32 neededRole); /** * @dev The caller of a function is not the expected one. * * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}. */ error AccessControlBadConfirmation(); /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `callerConfirmation`. */ function renounceRole(bytes32 role, address callerConfirmation) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol) pragma solidity ^0.8.20; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol) pragma solidity ^0.8.20; import {Context} from "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { bool private _paused; /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); /** * @dev The operation failed because the contract is paused. */ error EnforcedPause(); /** * @dev The operation failed because the contract is not paused. */ error ExpectedPause(); /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { if (paused()) { revert EnforcedPause(); } } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { if (!paused()) { revert ExpectedPause(); } } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = 2; uint256 private _status; /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); constructor() { _status = NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be NOT_ENTERED if (_status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail _status = ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == ENTERED; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import "./IStaking.sol"; /** @title Base contract for staking * @author Challenge.GG * @dev Implements the mechanisms for staking tokens and earning rewards over time. * This abstract contract provides a foundation for different staking strategies through * derived contracts. It integrates with OpenZeppelin's security modules, including * ReentrancyGuard for preventing reentrancy attacks, AccessControl for role-based permissions, * and Pausable for emergency stop mechanisms. Stakers can stake and unstake tokens, * claim rewards based on their staked amount, and the contract's state can be paused or unpaused * by accounts with the manager role. The contract also allows for the recovery of erroneous * token transfers and the adjustment of key parameters by authorized roles. * * The staking logic includes an unstake fee mechanism to incentivize longer-term staking, * and the reward calculation is designed to distribute rewards fairly among all stakers * based on the duration of their stake and the amount staked. It supports updating reward * rates and duration, offering flexibility in how rewards are distributed. * Roles: * - Default Admin: Can manage other roles, set unstake fees, and pause/unpause the contract. * - Manager: Can notify new reward amounts and pause/unpause the contract. * * Key Features: * - Flexible and updatable reward distribution * - Secure staking and unstaking with fee option * - Role-based function access for enhanced security * - OpenZeppelin's ReentrancyGuard, AccessControl, and Pausable integrations * * @notice Designed to be extended for specific staking strategies and token types. * It does not implement the `stake`,`unstake`and `setUnstakeFee` functions, leaving the specific * staking logic to be defined in derived contracts. * Based on https://docs.synthetix.io/contracts/source/contracts/stakingrewards */ abstract contract BaseStaking is IStaking, ReentrancyGuard, AccessControl, Pausable { using SafeERC20 for IERC20; // Define roles bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE"); IERC20 public rewardsToken; //Token used for rewards IERC20 public stakingToken; //Token used for staking uint256 public periodFinish = 0; //Timestamp when rewards duration finishes uint256 public rewardRate = 0; // Rate at which rewards are distributed per second to stakers. uint256 public rewardsDuration = 90 days; //The duration rewards will be paid out for, default 90 days uint256 public lastUpdateTime; // Timestamp of the last update to rewardPerTokenStored or reward distribution. uint256 public rewardPerTokenStored; // Accumulated reward per token, updated at each stake, unstake, or claim action. uint256 public unstakeFeePercentage = 0; //In percent, 0=0%, 5=5% uint256 public undistributedRewards = 0; // Variable to track undistributed rewards bool public pendingReward = false; // Flag to track if there's a pending reward to be distributed uint256 private _totalFees; //Total fees collected uint256 private _totalSupply; // Total amount of tokens staked in the contract. uint256 private _newReward; // New reward amount uint256 public _totalRewards; // Total deposited rewards mapping(address => uint256) public userRewardPerTokenPaid; // Tracks the amount of reward per token each user has been paid for, to calculate the pending rewards accurately. mapping(address => uint256) public rewards; // Stores the amount of rewards that each staker is entitled to claim. mapping(address => uint256) private _balances; // Records the amount of staking tokens each user has staked in the contract. mapping(address => uint256) private _stakerIndex; // Mapping from staker to their index in the stakers array // Array of all stakers. We maintain an array of active stakers which doesn't contain any addresses with 0. //So to get number of stakes we can just check length of this array address[] public stakers; //Events /// @notice Emitted when a new reward is added. /// @param reward The amount of the reward added. event RewardAdded(uint256 reward); /// @notice Emitted when the rewards period is started. /// @param reward The amount of the reward for the period. /// @param periodFinish The timestamp when the period will end. event RewardPeriodStarted(uint256 reward, uint256 periodFinish); /// @notice Emitted when a user stakes tokens. /// @param user The address of the user who staked tokens. /// @param payer The address from which the tokens are being transferred. /// @param amount The amount of tokens staked. event Staked(address indexed user, address indexed payer, uint256 amount); /// @notice Emitted when a user unstakes tokens. /// @param user The address of the user who unstaked tokens. /// @param amount The amount of tokens unstaked. /// @param fee The fee deducted from the unstaked amount, if any. event Unstaked(address indexed user, uint256 amount, uint256 fee); /// @notice Emitted when rewards are paid to a user. /// @param user The address of the user who received the reward. /// @param reward The amount of the reward paid. event RewardPaid(address indexed user, uint256 reward); /// @notice Emitted when the rewards duration is updated. /// @param newDuration The new duration for rewards distribution. event RewardsDurationUpdated(uint256 newDuration); /// @notice Emitted when ERC20 tokens are recovered from the contract. /// @param token The address of the token recovered. /// @param amount The amount of the token recovered. event Recovered(address token, uint256 amount); /// @notice Emitted when fees are withdrawn from the contract. /// @param amount The amount of fees withdrawn. event FeesWithdrawn(uint256 amount); //Modifiers /// @notice Restricted to members of the admin role. modifier onlyAdmin() { require(isAdmin(msg.sender), "Restricted to admins"); _; } /// @notice Restricted to members of the Manager role. modifier onlyManager() { require(isManager(msg.sender), "Restricted to managers"); _; } /// @notice Updates the reward for a given account before executing the modified function. /// This modifier ensures that any function it is applied to will first update the account's reward calculation /// based on the latest state of the contract. This is crucial for functions that affect the rewards distribution, /// such as staking, unstaking, and claiming rewards, to ensure that the rewards are calculated accurately and fairly. /// @param account The address of the account for which rewards are to be updated. modifier updateReward(address account) { _updateReward(account); _; } /** * @dev Initializes the contract with specific addresses for the rewards and staking tokens, * an admin address for role management, and the unstake fee percentage. It sets up the necessary roles * and ensures that the provided addresses and fee percentage are valid. * The constructor performs checks to prevent common configuration errors, such as invalid addresses * or an excessive unstake fee percentage. * * Requirements: * - `_rewardsToken` and `_stakingToken` must be valid ERC20 token addresses. * - `_unstakeFeePercentage` must not exceed 5%, ensuring fees are kept within reasonable limits. * * @param _multisigAdmin Address that will be granted the default admin role, capable of managing other roles. * @param _rewardsToken Address of the ERC20 token used for rewards. * @param _stakingToken Address of the ERC20 token that will be staked. * @param _unstakeFeePercentage The fee percentage charged when unstaking tokens. Expressed as a percentage of the unstaked amount. */ constructor( address _multisigAdmin, address _rewardsToken, address _stakingToken, uint256 _unstakeFeePercentage ) { require(_rewardsToken != address(0), "Invalid rewards token"); require(_stakingToken != address(0), "Invalid staking token"); require(_unstakeFeePercentage <= 5, "Unstake fee percentage cannot be more than 5%"); _grantRole(DEFAULT_ADMIN_ROLE, _multisigAdmin); _setRoleAdmin(MANAGER_ROLE, DEFAULT_ADMIN_ROLE); rewardsToken = IERC20(_rewardsToken); stakingToken = IERC20(_stakingToken); unstakeFeePercentage = _unstakeFeePercentage; } /// @notice Allows the contract to receive Ether directly without calling a function. receive() external payable {} //Update functions /// @dev Add an account to the Manager role. Restricted to admins. /// @param account Address to add function addManager(address account) external onlyAdmin { grantRole(MANAGER_ROLE, account); } /// @dev Remove an account from the Manager role. Restricted to admins. /// @param account Address to remove function removeManager(address account) external onlyAdmin { revokeRole(MANAGER_ROLE, account); } /// @dev Recovers ERC20 tokens sent to the contract by mistake. /// @param tokenAddress Address of the token to recover /// @param tokenAmount Amount of tokens to recover /// @param to Address to send the recovered tokens to function recoverERC20(address tokenAddress, uint256 tokenAmount, address to) external onlyAdmin { require(to != address(0), "Invalid address"); require(tokenAddress != address(stakingToken), "Cannot withdraw the staking token"); require(tokenAddress != address(rewardsToken), "Cannot withdraw the rewards token"); IERC20(tokenAddress).safeTransfer(to, tokenAmount); emit Recovered(tokenAddress, tokenAmount); } /// @dev WIthdraws any fees collected by the contract. /// @param to Address to send the recovered fees to function withdrawFees(address to) external onlyAdmin { require(to != address(0), "Invalid address"); require(_totalFees > 0, "No fees to recover"); uint256 fees = _totalFees; _totalFees = 0; stakingToken.safeTransfer(to, fees); emit FeesWithdrawn(fees); } /// @dev Withdraws ETH sent to contract by mistake /// @param to Address to send ETH to /// @param amount Amount to send function withdrawETH(address payable to, uint256 amount) external nonReentrant onlyAdmin { require(to != address(0), "To wallet is the zero address"); require(address(this).balance >= amount, "Insufficient ETH balance"); Address.sendValue(to, amount); } /// @dev Pauses the contract, preventing staking. function pause() external onlyManager { _pause(); } /// @dev Unpauses the contract, allowing staking to resume. function unpause() external onlyManager { _unpause(); } function depositReward(uint256 amount) external onlyManager { require(amount > 0, "Amount must be greater than 0"); // Transfer the deposited rewards from the manager's account to the contract. rewardsToken.safeTransferFrom(msg.sender, address(this), amount); // Update the total deposited rewards. _totalRewards += amount; // Update the new reward amount. _newReward += amount; emit RewardAdded(amount); } /// @dev Notifies the contract that a new reward amount is available. /// It updates the reward rate and the period finish time based on the new reward amount and the duration. function startRewardPeriod() external onlyManager updateReward(address(0)) { require(block.timestamp > periodFinish, "Previous period must end before adding new rewards"); require(_newReward > 0, "New reward must be greater than 0"); uint256 newReward = _newReward + undistributedRewards; // Add any undistributed rewards from previous periods to the new reward require(newReward > rewardsDuration, "Reward must be greater than duration to avoid 0 reward rate"); _newReward = 0; // Reset the new reward amount if (_totalSupply == 0) { // If there's no supply, simply update undistributedRewards without changing the rewardRate undistributedRewards = newReward; pendingReward = true; return; // Exit the function early } pendingReward = false; // Reset the pending reward flag as we're distributing rewards now rewardRate = newReward / rewardsDuration; // Recalculate the exact amount of rewards to be distributed based on the new reward rate uint256 exactRewardAmountDistributed = rewardRate * rewardsDuration; // Update undistributedRewards with the difference between the newReward and what will be distributed undistributedRewards = newReward - exactRewardAmountDistributed; lastUpdateTime = block.timestamp; periodFinish = block.timestamp + rewardsDuration; emit RewardPeriodStarted(exactRewardAmountDistributed, periodFinish); } /// @dev Updates the duration of the rewards distribution period. /// @param _rewardsDuration The new duration for rewards distribution function setRewardsDuration(uint256 _rewardsDuration) external onlyManager { require( block.timestamp > periodFinish, "Previous rewards period must be complete before changing the duration for the new period" ); rewardsDuration = _rewardsDuration; emit RewardsDurationUpdated(rewardsDuration); } /// @dev Stake function, should we implemented in derived contract /// @param amount Amount to stake function stake(uint256 amount) external virtual nonReentrant whenNotPaused { } /// @dev Unstake function, should we implemented in derived contract /// @param amount Amount to unstake function unstake(uint amount) external virtual nonReentrant{ } /** * @notice Claims accrued rewards for the caller. * This function calculates the current reward for the caller, updates their reward balance to zero, * and transfers the calculated reward to their address. It ensures that rewards are only claimed * for the actual amount earned based on the staked tokens and the duration of the stake. * The function is non-reentrant to prevent re-entrancy attacks and ensures the contract is not paused. * * Requirements: * - The caller must have a positive reward balance. * - There must be sufficient rewards in the contract to cover the claim. * * Emits a {RewardPaid} event indicating the caller and the amount of rewards claimed. */ function claimReward() external nonReentrant updateReward(msg.sender) { uint256 reward = rewards[msg.sender]; if (reward > 0) { require(_totalRewards >= reward, "Insufficient rewards"); rewards[msg.sender] = 0; _totalRewards -= reward; rewardsToken.safeTransfer(msg.sender, reward); emit RewardPaid(msg.sender, reward); } } //View /// @notice Returns the total supply of staked tokens. function totalSupply() public view returns (uint256) { return _totalSupply; } /// @notice Returns the total number of unique stakers in the contract. function totalStakers() public view returns (uint256) { return stakers.length; } /// @notice Returns the balance of the rewards token held by the contract. function rewardTokenBalance() public view returns (uint256) { return _totalRewards; } /// @notice Returns the total rewards available for coming reward period, including any undistributed rewards. function upcomingRewards() public view returns (uint256) { return _newReward + undistributedRewards; } /// @notice Returns the staking balance of a specified address. /// @param account The address of the staker whose balance is being queried. function balanceOf(address account) public view returns (uint256) { return _balances[account]; } /// @notice Returns the latest timestamp for applicable rewards, either now or the end of the rewards period. function lastTimeRewardApplicable() public view returns (uint256) { return block.timestamp < periodFinish ? block.timestamp : periodFinish; } /// @notice Calculates and returns the current reward per token staked, accounting for the time since last update. function rewardPerToken() public view returns (uint256) { if (_totalSupply == 0) { return rewardPerTokenStored; } return rewardPerTokenStored + ((lastTimeRewardApplicable()-lastUpdateTime) * rewardRate * 1e18 / _totalSupply); } /// @notice Calculates the amount of rewards an account has earned but not yet claimed. /// This function computes the earned rewards by taking into account the current reward per token, /// subtracting the amount already paid to the account, and adding any rewards previously accrued. /// @param account The address of the staker for whom to calculate the earned rewards. /// @return uint256 The total rewards earned by the account up to the current moment, ready to be claimed. function earned(address account) public view returns (uint256) { return (_balances[account] * (rewardPerToken() - userRewardPerTokenPaid[account]) / 1e18) + rewards[account]; } /// @notice Calculates the total rewards distributed by the contract over the entire rewards duration. function getRewardForDuration() public view returns (uint256) { return rewardRate * rewardsDuration; } /// @dev Checks if the account belongs to the admin role. /// @param account Address to check /// @return True if account belongs to 'ADMIN' role function isAdmin(address account) public view returns (bool) { return hasRole(DEFAULT_ADMIN_ROLE, account); } /// @dev Checks if the account belongs to the manager role. /// @param account Address to check /// @return True if account belongs to 'MANAGER' role function isManager(address account) public view returns (bool) { return hasRole(MANAGER_ROLE, account); } /// @notice Returns the total fees collected by the contract. function totalFees() external view returns (uint256) { return _totalFees; } /// @notice Returns staking information for a given account. /// @param account The address of the account for which to retrieve staking information. /// @return totalStaked Total supply of staked tokens in the contract. /// @return stakerCount Total number of unique stakers. /// @return currentRewardsDuration The duration rewards will be paid out for. /// @return currentPeriodFinish Timestamp when rewards duration finishes. /// @return currentRewardPerToken The current reward per token staked. /// @return rewardForDuration The total rewards distributed by the contract over the entire rewards duration. /// @return unstakeFee The current unstake fee percentage. /// @return accountBalance The staking balance of the specified account. /// @return accountEarned The total rewards earned by the specified account up to the current moment. function getAccountStakingInfo(address account) external view returns ( uint256 totalStaked, uint256 stakerCount, uint256 currentRewardsDuration, uint256 currentPeriodFinish, uint256 currentRewardPerToken, uint256 rewardForDuration, uint256 unstakeFee, uint256 accountBalance, uint256 accountEarned ) { totalStaked = totalSupply(); stakerCount = totalStakers(); currentRewardsDuration = rewardsDuration; currentPeriodFinish = periodFinish; currentRewardPerToken = rewardPerToken(); rewardForDuration = getRewardForDuration(); unstakeFee = unstakeFeePercentage; accountBalance = balanceOf(account); accountEarned = earned(account); } /// @notice Returns staking information for a given account. /// @return totalStaked Total supply of staked tokens in the contract. /// @return stakerCount Total number of unique stakers. /// @return currentRewardsDuration The duration rewards will be paid out for. /// @return currentPeriodFinish Timestamp when rewards duration finishes. /// @return currentRewardPerToken The current reward per token staked. /// @return rewardForDuration The total rewards distributed by the contract over the entire rewards duration. /// @return unstakeFee The current unstake fee percentage. function getGeneralStakingInfo() external view returns ( uint256 totalStaked, uint256 stakerCount, uint256 currentRewardsDuration, uint256 currentPeriodFinish, uint256 currentRewardPerToken, uint256 rewardForDuration, uint256 unstakeFee ) { totalStaked = totalSupply(); stakerCount = totalStakers(); currentRewardsDuration = rewardsDuration; currentPeriodFinish = periodFinish; currentRewardPerToken = rewardPerToken(); rewardForDuration = getRewardForDuration(); unstakeFee = unstakeFeePercentage; } //Internal /// @dev Internal function to handle the logic of staking tokens for a specified address. /// This function updates the total supply of staked tokens, the balance of the staker, and ensures /// the transfer of tokens into the contract. It is called by the public-facing stake function or /// other internal mechanisms that implement staking logic. This function also updates the reward /// calculation for the staker to accurately reflect their new staking balance. /// Emits a {Staked} event upon successful staking. /// @param amount The amount of tokens to be staked by the staker. /// @param stakerFor The address of the account performing the stake. /// @param payer The address from which the tokens are being transferred. function _stake(uint amount, address stakerFor, address payer) internal updateReward(stakerFor) { require(amount > 0, "Cannot stake 0"); _totalSupply = _totalSupply + amount; if(_balances[stakerFor] == 0){ stakers.push(stakerFor); _stakerIndex[stakerFor] = stakers.length - 1; } _balances[stakerFor] = _balances[stakerFor] + amount; stakingToken.safeTransferFrom(payer, address(this), amount); // Check if there is a pending reward to be distributed if (pendingReward) { _redistributeUndistributedRewards(); } emit Staked(stakerFor, payer, amount); } /// @dev Internal function to handle the logic of unstaking tokens for a specified address. /// This function updates the total supply of staked tokens and the balance of the unstaker, /// ensuring the transfer of tokens back to the unstaker's address, minus any applicable fees. /// It is called by the public-facing unstake function or other internal mechanisms that implement /// unstaking logic. This function also updates the reward calculation for the unstaker to accurately /// reflect their new staking balance. In the case of unstaking, a fee may be applied, which is /// deducted from the unstaked amount as specified by the contract's unstake fee percentage. /// Emits an {Unstaked} event upon successful unstaking, including the amount unstaked and the fee applied. /// @param amount The amount of tokens to be unstaked by the unstaker. /// @param unstaker The address of the account performing the unstake. function _unstake(uint256 amount, address unstaker) internal updateReward(unstaker) { require(amount > 0, "Cannot unstake 0"); require(_balances[unstaker] >= amount, "Cannot unstake more than the current stake"); //sub will check this but this is a better error message _totalSupply = _totalSupply - amount; _balances[unstaker] = _balances[unstaker] - amount; if(_balances[unstaker] == 0){ // Unstaker has no more staked tokens, remove them from stakers array uint256 index = _stakerIndex[unstaker]; uint256 lastIndex = stakers.length - 1; address lastStaker = stakers[lastIndex]; // Swap unstaker with last staker in array stakers[index] = lastStaker; _stakerIndex[lastStaker] = index; // Remove last staker in array stakers.pop(); delete _stakerIndex[unstaker]; } uint256 fee = amount * unstakeFeePercentage / 100; uint256 amountAfterFee = amount - fee; _totalFees = _totalFees + fee; stakingToken.safeTransfer(unstaker, amountAfterFee); emit Unstaked(unstaker, amount,fee); } /// @dev Internal function to set the unstake fee percentage. /// This function updates the unstake fee percentage, ensuring that it adheres to the contract's /// policy on unstaking fees. It is designed to be called by privileged functions that manage /// contract parameters, providing a controlled way to adjust the fee that applies when stakers /// choose to unstake their tokens. This adjustment mechanism allows for dynamic modification /// of the fee structure based on governance decisions or contract management strategies. /// Emits a {UnstakeFeeUpdated} event upon successful update of the fee percentage. /// @param _unstakeFeePercentage The new unstake fee percentage to be set, expressed as a whole number /// (e.g., 5 for a 5% fee). function _setUnstakeFee(uint256 _unstakeFeePercentage) internal { require(_unstakeFeePercentage <= 5, "Unstake fee percentage cannot be more than 5%"); unstakeFeePercentage = _unstakeFeePercentage; } /// @dev Internal function to update the reward for a specific account. /// This function recalculates and updates the accumulated reward for an account based on the latest /// changes in the total staked amount, the reward rate, and the time elapsed since the last update. /// It ensures that each staker's rewards are accurately tracked and updated before any operation that /// could affect their rewards, such as staking, unstaking, or claiming rewards. /// @param account The address of the staker whose rewards need updating. If the address is zero, /// it signifies a global update applicable to the entire system rather than an individual account. function _updateReward(address account) internal { rewardPerTokenStored = rewardPerToken(); lastUpdateTime = lastTimeRewardApplicable(); if (account != address(0)) { rewards[account] = earned(account); userRewardPerTokenPaid[account] = rewardPerTokenStored; } } /// @dev Internal function triggered on first stake if there is a pending rewards period. /// It updates the reward rate and the period finish time based on the new reward amount and the duration. function _redistributeUndistributedRewards() internal updateReward(address(0)) { pendingReward = false; // Reset the pending reward flag as we're distributing rewards now rewardRate = undistributedRewards / rewardsDuration; // Recalculate the exact amount that will be distributed uint256 exactRewardAmountDistributed = rewardRate * rewardsDuration; // Update undistributedRewards to account for any rounding discrepancies undistributedRewards = undistributedRewards-exactRewardAmountDistributed; lastUpdateTime = block.timestamp; periodFinish = block.timestamp + rewardsDuration; emit RewardPeriodStarted(exactRewardAmountDistributed, periodFinish); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; // Modified version of https://docs.synthetix.io/contracts/source/interfaces/istakingrewards interface IStaking { // Write function stake(uint256 amount) external; function unstake(uint256 amount) external; function claimReward() external; // Views function balanceOf(address account) external view returns (uint256); function earned(address account) external view returns (uint256); function getRewardForDuration() external view returns (uint256); function lastTimeRewardApplicable() external view returns (uint256); function rewardPerToken() external view returns (uint256); function totalSupply() external view returns (uint256); }
{ "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_multisigAdmin","type":"address"},{"internalType":"address","name":"_rewardsToken","type":"address"},{"internalType":"address","name":"_stakingToken","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeesWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"periodFinish","type":"uint256"}],"name":"RewardPeriodStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newDuration","type":"uint256"}],"name":"RewardsDurationUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"payer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"Unstaked","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_totalRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"addManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositReward","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"earned","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAccountStakingInfo","outputs":[{"internalType":"uint256","name":"totalStaked","type":"uint256"},{"internalType":"uint256","name":"stakerCount","type":"uint256"},{"internalType":"uint256","name":"currentRewardsDuration","type":"uint256"},{"internalType":"uint256","name":"currentPeriodFinish","type":"uint256"},{"internalType":"uint256","name":"currentRewardPerToken","type":"uint256"},{"internalType":"uint256","name":"rewardForDuration","type":"uint256"},{"internalType":"uint256","name":"unstakeFee","type":"uint256"},{"internalType":"uint256","name":"accountBalance","type":"uint256"},{"internalType":"uint256","name":"accountEarned","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getAvailableAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGeneralStakingInfo","outputs":[{"internalType":"uint256","name":"totalStaked","type":"uint256"},{"internalType":"uint256","name":"stakerCount","type":"uint256"},{"internalType":"uint256","name":"currentRewardsDuration","type":"uint256"},{"internalType":"uint256","name":"currentPeriodFinish","type":"uint256"},{"internalType":"uint256","name":"currentRewardPerToken","type":"uint256"},{"internalType":"uint256","name":"rewardForDuration","type":"uint256"},{"internalType":"uint256","name":"unstakeFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getNextUnlockTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardForDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVestingSchedule","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastTimeRewardApplicable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastUpdateTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingReward","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"periodFinish","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"removeManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardPerToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardPerTokenStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardTokenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardsToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_rewardsDuration","type":"uint256"}],"name":"setRewardsDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"investor","type":"address"},{"internalType":"enum VestedStaking.VestingType","name":"vestingType","type":"uint8"}],"name":"stakeFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"stakers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startRewardPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"undistributedRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unstakeFeePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"upcomingRewards","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userRewardPerTokenPaid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vestingDistributionPreSeedFunding","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vestingDistributionPrivatePresale","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vestingDistributionPublic","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vestingDistributionTeamAndAdvisors","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vestingPeriodsPreSeedFunding","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vestingPeriodsPrivatePresale","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vestingPeriodsPublic","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vestingPeriodsTeamAndAdvisors","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address payable","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"withdrawFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
6000600481905560058190556276a70060068190556009829055600a91909155600b805460ff1916905561018060405262093a80608090815262278d0060a052624f1a0060c05260e09190915262ed4e0061010052630163f500610120526301da9c00610140526302514300610160526200007f906016906008620004ca565b506040805161010081018252600a8152600360208201819052918101919091526004606082015260146080820181905260a0820181905260c0820181905260e0820152620000d290601790600862000522565b506040805160808101825262093a80815262278d006020820152624f1a00918101919091526276a700606082015262000110906018906004620005ca565b5060408051608081018252600a8152601e6020820181905291810182905260608101919091526200014690601990600462000522565b506040805160808101825262ed4e0081526301da9c0060208201526302514300918101919091526303b5380060608201526200018790601a906004620004ca565b50604080516080810182526014808252602082018190529181019190915260286060820152620001bc90601b90600462000522565b5060408051602081019091526276a7008152620001de90601c906001620005ca565b50604080516020810190915260648152620001fe90601d90600162000522565b503480156200020c57600080fd5b506040516200414d3803806200414d8339810160408190526200022f9162000643565b600160009081556002805460ff191690558390839083906001600160a01b038316620002a25760405162461bcd60e51b815260206004820152601560248201527f496e76616c6964207265776172647320746f6b656e000000000000000000000060448201526064015b60405180910390fd5b6001600160a01b038216620002fa5760405162461bcd60e51b815260206004820152601560248201527f496e76616c6964207374616b696e6720746f6b656e0000000000000000000000604482015260640162000299565b6005811115620003635760405162461bcd60e51b815260206004820152602d60248201527f556e7374616b65206665652070657263656e746167652063616e6e6f7420626560448201526c206d6f7265207468616e20352560981b606482015260840162000299565b62000370600085620003e6565b506200039e7f241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b0860006200047d565b60028054610100600160a81b0319166101006001600160a01b0395861602179055600380546001600160a01b0319169290931691909117909155600955506200068d92505050565b60008281526001602090815260408083206001600160a01b038516845290915281205460ff16620004735760008381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a450600162000477565b5060005b92915050565b6000828152600160208190526040808320909101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b82805482825590600052602060002090810192821562000510579160200282015b8281111562000510578251829063ffffffff16905591602001919060010190620004eb565b506200051e9291506200060f565b5090565b82805482825590600052602060002090601f01602090048101928215620005105791602002820160005b838211156200058c57835183826101000a81548160ff021916908360ff16021790555092602001926001016020816000010492830192600103026200054c565b8015620005bb5782816101000a81549060ff02191690556001016020816000010492830192600103026200058c565b50506200051e9291506200060f565b82805482825590600052602060002090810192821562000510579160200282015b8281111562000510578251829062ffffff16905591602001919060010190620005eb565b5b808211156200051e576000815560010162000610565b80516001600160a01b03811681146200063e57600080fd5b919050565b6000806000606084860312156200065957600080fd5b620006648462000626565b9250620006746020850162000626565b9150620006846040850162000626565b90509250925092565b613ab0806200069d6000396000f3fe6080604052600436106103b05760003560e01c806378e81d07116101e7578063b88a802f1161010d578063d547741f116100a0578063ebe2b12b1161006f578063ebe2b12b14610b2c578063ec87621c14610b42578063f3ae241514610b64578063fd5e6dd114610b8457600080fd5b8063d547741f14610acb578063df136d6514610aeb578063e6a1d27d14610b01578063e7d6007e14610b1757600080fd5b8063c9122507116100dc578063c912250714610a51578063cc1a378f14610a71578063cd3daf9d14610a91578063d1af0c7d14610aa657600080fd5b8063b88a802f146109e6578063b9af515e146109fb578063c299cadb14610a1b578063c8f33c9114610a3b57600080fd5b80639583027911610185578063ac18de4311610154578063ac18de4314610922578063b2ed51e914610942578063b51609b4146109a6578063b5b654b3146109c657600080fd5b8063958302791461089d5780639f829063146108bd578063a217fddf146108ed578063a694fc3a1461090257600080fd5b80638456cb59116101c15780638456cb5914610826578063869890381461083b5780638b8763471461085057806391d148541461087d57600080fd5b806378e81d07146107db5780637b0a47ee146107fb57806380faa57d1461081157600080fd5b8063248a9ca3116102d7578063386a95251161026a5780635c975abb116102395780635c975abb1461074057806370a082311461075857806372f702f31461078e57806375824f23146107c657600080fd5b8063386a9525146106ab5780633f4ba83a146106c15780634782f779146106d65780634b62bfb0146106f657600080fd5b80632e17de78116102a65780632e17de78146106355780632f2ff15d14610655578063319ce2bb1461067557806336568abe1461068b57600080fd5b8063248a9ca3146105ae57806324d7806c146105df57806328af352b146105ff5780632d06177a1461061557600080fd5b80631664a2701161034f5780631c1f78eb1161031e5780631c1f78eb146105395780631e2720ff1461054e5780631f3a71ba1461056e57806323f965e11461058e57600080fd5b80631664a270146104b25780631758f63e146104e457806318160ddd146105045780631b44718a1461051957600080fd5b806313114a9d1161038b57806313114a9d1461044c578063131a033814610461578063137ee36e14610478578063164e68de1461049257600080fd5b80628cc262146103bc57806301ffc9a7146103ef5780630700037d1461041f57600080fd5b366103b757005b600080fd5b3480156103c857600080fd5b506103dc6103d73660046136ad565b610ba4565b6040519081526020015b60405180910390f35b3480156103fb57600080fd5b5061040f61040a3660046136ca565b610c21565b60405190151581526020016103e6565b34801561042b57600080fd5b506103dc61043a3660046136ad565b60116020526000908152604090205481565b34801561045857600080fd5b50600c546103dc565b34801561046d57600080fd5b50610476610c56565b005b34801561048457600080fd5b50600b5461040f9060ff1681565b34801561049e57600080fd5b506104766104ad3660046136ad565b610e98565b3480156104be57600080fd5b506104d26104cd3660046136f4565b610f82565b60405160ff90911681526020016103e6565b3480156104f057600080fd5b506104766104ff36600461370d565b610fb6565b34801561051057600080fd5b50600d546103dc565b34801561052557600080fd5b506104d26105343660046136f4565b611239565b34801561054557600080fd5b506103dc611249565b34801561055a57600080fd5b506104766105693660046136f4565b611260565b34801561057a57600080fd5b506103dc6105893660046136ad565b611359565b34801561059a57600080fd5b506104d26105a93660046136f4565b611514565b3480156105ba57600080fd5b506103dc6105c93660046136f4565b6000908152600160208190526040909120015490565b3480156105eb57600080fd5b5061040f6105fa3660046136ad565b611524565b34801561060b57600080fd5b506103dc600f5481565b34801561062157600080fd5b506104766106303660046136ad565b611530565b34801561064157600080fd5b506104766106503660046136f4565b61156d565b34801561066157600080fd5b50610476610670366004613753565b61180b565b34801561068157600080fd5b506103dc600a5481565b34801561069757600080fd5b506104766106a6366004613753565b611837565b3480156106b757600080fd5b506103dc60065481565b3480156106cd57600080fd5b5061047661186a565b3480156106e257600080fd5b506104766106f1366004613783565b611899565b34801561070257600080fd5b5061070b611984565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e0016103e6565b34801561074c57600080fd5b5060025460ff1661040f565b34801561076457600080fd5b506103dc6107733660046136ad565b6001600160a01b031660009081526012602052604090205490565b34801561079a57600080fd5b506003546107ae906001600160a01b031681565b6040516001600160a01b0390911681526020016103e6565b3480156107d257600080fd5b50600f546103dc565b3480156107e757600080fd5b506104d26107f63660046136f4565b6119d1565b34801561080757600080fd5b506103dc60055481565b34801561081d57600080fd5b506103dc6119e1565b34801561083257600080fd5b506104766119f8565b34801561084757600080fd5b506014546103dc565b34801561085c57600080fd5b506103dc61086b3660046136ad565b60106020526000908152604090205481565b34801561088957600080fd5b5061040f610898366004613753565b611a25565b3480156108a957600080fd5b506103dc6108b83660046136f4565b611a50565b3480156108c957600080fd5b506108dd6108d83660046136ad565b611a71565b6040516103e694939291906137ea565b3480156108f957600080fd5b506103dc600081565b34801561090e57600080fd5b5061047661091d3660046136f4565b611bb9565b34801561092e57600080fd5b5061047661093d3660046136ad565b611cab565b34801561094e57600080fd5b5061096261095d3660046136ad565b611ce8565b60408051998a5260208a0198909852968801959095526060870193909352608086019190915260a085015260c084015260e0830152610100820152610120016103e6565b3480156109b257600080fd5b506104766109c136600461382a565b611d68565b3480156109d257600080fd5b506103dc6109e13660046136ad565b611edb565b3480156109f257600080fd5b50610476612077565b348015610a0757600080fd5b506103dc610a163660046136f4565b61216f565b348015610a2757600080fd5b506103dc610a363660046136f4565b61217f565b348015610a4757600080fd5b506103dc60075481565b348015610a5d57600080fd5b506103dc610a6c3660046136f4565b61218f565b348015610a7d57600080fd5b50610476610a8c3660046136f4565b61219f565b348015610a9d57600080fd5b506103dc612296565b348015610ab257600080fd5b506002546107ae9061010090046001600160a01b031681565b348015610ad757600080fd5b50610476610ae6366004613753565b6122f7565b348015610af757600080fd5b506103dc60085481565b348015610b0d57600080fd5b506103dc60095481565b348015610b2357600080fd5b506103dc61231d565b348015610b3857600080fd5b506103dc60045481565b348015610b4e57600080fd5b506103dc600080516020613a5b83398151915281565b348015610b7057600080fd5b5061040f610b7f3660046136ad565b61232f565b348015610b9057600080fd5b506107ae610b9f3660046136f4565b612349565b6001600160a01b0381166000908152601160209081526040808320546010909252822054670de0b6b3a764000090610bda612296565b610be49190613877565b6001600160a01b038516600090815260126020526040902054610c07919061388a565b610c1191906138b7565b610c1b91906138cb565b92915050565b60006001600160e01b03198216637965db0b60e01b1480610c1b57506301ffc9a760e01b6001600160e01b0319831614610c1b565b610c5f3361232f565b610c845760405162461bcd60e51b8152600401610c7b906138de565b60405180910390fd5b6000610c8f81612373565b6004544211610cfb5760405162461bcd60e51b815260206004820152603260248201527f50726576696f757320706572696f64206d75737420656e64206265666f726520604482015271616464696e67206e6577207265776172647360701b6064820152608401610c7b565b6000600e5411610d575760405162461bcd60e51b815260206004820152602160248201527f4e657720726577617264206d7573742062652067726561746572207468616e206044820152600360fc1b6064820152608401610c7b565b6000600a54600e54610d6991906138cb565b90506006548111610de25760405162461bcd60e51b815260206004820152603b60248201527f526577617264206d7573742062652067726561746572207468616e206475726160448201527f74696f6e20746f2061766f6964203020726577617264207261746500000000006064820152608401610c7b565b6000600e819055600d549003610e0557600a55600b805460ff1916600117905550565b600b805460ff19169055600654610e1c90826138b7565b6005819055600654600091610e31919061388a565b9050610e3d8183613877565b600a55426007819055600654610e52916138cb565b60048190556040805183815260208101929092527f867b60a9d1d7c706c0754dcb3bc7047022d07de0f3e55a79d1b74c69653ff89b91015b60405180910390a150505b50565b610ea133611524565b610ebd5760405162461bcd60e51b8152600401610c7b9061390e565b6001600160a01b038116610ee35760405162461bcd60e51b8152600401610c7b9061393c565b6000600c5411610f2a5760405162461bcd60e51b81526020600482015260126024820152712737903332b2b9903a37903932b1b7bb32b960711b6044820152606401610c7b565b600c80546000909155600354610f4a906001600160a01b031683836123cf565b6040518181527f9800e6f57aeb4360eaa72295a820a4293e1e66fbfcabcd8874ae141304a76deb906020015b60405180910390a15050565b601b8181548110610f9257600080fd5b9060005260206000209060209182820401919006915054906101000a900460ff1681565b610fbf3361232f565b610fdb5760405162461bcd60e51b8152600401610c7b906138de565b610fe361242e565b610feb612458565b6001600160a01b0382166110115760405162461bcd60e51b8152600401610c7b9061393c565b600083116110525760405162461bcd60e51b815260206004820152600e60248201526d125b9d985b1a5908185b5bdd5b9d60921b6044820152606401610c7b565b600081600481111561106657611066613965565b036110aa5760405162461bcd60e51b8152602060048201526014602482015273496e76616c69642076657374696e67207479706560601b6044820152606401610c7b565b60048160048111156110be576110be613965565b0361111d5760405162461bcd60e51b815260206004820152602960248201527f5075626c69632076657374696e67206973206e6f7420616c6c6f77656420696e6044820152681039ba30b5b2a337b960b91b6064820152608401610c7b565b6001600160a01b03821660009081526015602052604081206003015460ff16600481111561114d5761114d613965565b1480611198575080600481111561116657611166613965565b6001600160a01b03831660009081526015602052604090206003015460ff16600481111561119657611196613965565b145b61121e5760405162461bcd60e51b815260206004820152604b60248201527f43616e206e6f742070726576696f75736c79206861766520612076657374696e60448201527f67207363686564756c65206f7220757365207468652073616d6520766573746960648201526a6e67207363686564756c6560a81b608482015260a401610c7b565b61122a8383338461247c565b6112346001600055565b505050565b60178181548110610f9257600080fd5b600060065460055461125b919061388a565b905090565b6112693361232f565b6112855760405162461bcd60e51b8152600401610c7b906138de565b600081116112d55760405162461bcd60e51b815260206004820152601d60248201527f416d6f756e74206d7573742062652067726561746572207468616e20300000006044820152606401610c7b565b6002546112f29061010090046001600160a01b0316333084612cdf565b80600f600082825461130491906138cb565b9250508190555080600e600082825461131d91906138cb565b90915550506040518181527fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d906020015b60405180910390a150565b6001600160a01b03811660009081526015602090815260408083208151815460a094810282018501909352608081018381528594919384928491908401828280156113c357602002820191906000526020600020905b8154815260200190600101908083116113af575b505050505081526020016001820180548060200260200160405190810160405280929190818152602001828054801561141b57602002820191906000526020600020905b815481526020019060010190808311611407575b505050918352505060028201546020820152600382015460409091019060ff16600481111561144c5761144c613965565b600481111561145d5761145d613965565b905250602081015151909150600090156114f65760005b8251518110156114de5782518051829081106114925761149261397b565b602002602001015142106114cc57826020015181815181106114b6576114b661397b565b6020026020010151826114c991906138cb565b91505b806114d681613991565b915050611474565b5060408201516114ee9082613877565b949350505050565b6001600160a01b0384166000908152601260205260409020546114ee565b601d8181548110610f9257600080fd5b6000610c1b8183611a25565b61153933611524565b6115555760405162461bcd60e51b8152600401610c7b9061390e565b610e95600080516020613a5b8339815191528261180b565b61157561242e565b3360009081526015602052604081206003015460ff16600481111561159c5761159c613965565b036115e95760405162461bcd60e51b815260206004820152601960248201527f4e6f2076657374696e67207363686564756c6520666f756e64000000000000006044820152606401610c7b565b6000811161162a5760405162461bcd60e51b815260206004820152600e60248201526d125b9d985b1a5908185b5bdd5b9d60921b6044820152606401610c7b565b3360009081526015602052604090206001810154156117f65760008060005b83548110156116e5578360000181815481106116675761166761397b565b906000526020600020015442106116a65783600101818154811061168d5761168d61397b565b9060005260206000200154836116a391906138cb565b92505b8360010181815481106116bb576116bb61397b565b9060005260206000200154826116d191906138cb565b9150806116dd81613991565b915050611649565b508383600201546116f691906138cb565b82101561175c5760405162461bcd60e51b815260206004820152602e60248201527f596f752061726520747279696e6720746f207769746864726177206d6f72652060448201526d7468616e20617661696c61626c6560901b6064820152608401610c7b565b8383600201600082825461177091906138cb565b90915550506002830154811480156117b257508254839061179390600190613877565b815481106117a3576117a361397b565b90600052602060002001544210155b156117f357336000908152601560205260408120906117d1828261361a565b6117df60018301600061361a565b5060006002820155600301805460ff191690555b50505b6118008233612d18565b50610e956001600055565b6000828152600160208190526040909120015461182781612fce565b6118318383612fd8565b50505050565b6001600160a01b03811633146118605760405163334bd91960e11b815260040160405180910390fd5b6112348282613051565b6118733361232f565b61188f5760405162461bcd60e51b8152600401610c7b906138de565b6118976130be565b565b6118a161242e565b6118aa33611524565b6118c65760405162461bcd60e51b8152600401610c7b9061390e565b6001600160a01b03821661191c5760405162461bcd60e51b815260206004820152601d60248201527f546f2077616c6c657420697320746865207a65726f20616464726573730000006044820152606401610c7b565b8047101561196c5760405162461bcd60e51b815260206004820152601860248201527f496e73756666696369656e74204554482062616c616e636500000000000000006044820152606401610c7b565b6119768282613110565b6119806001600055565b5050565b6000806000806000806000611998600d5490565b96506119a360145490565b9550600654945060045493506119b7612296565b92506119c1611249565b9150600954905090919293949596565b60198181548110610f9257600080fd5b600060045442106119f3575060045490565b504290565b611a013361232f565b611a1d5760405162461bcd60e51b8152600401610c7b906138de565b6118976131a7565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b601a8181548110611a6057600080fd5b600091825260209091200154905081565b6001600160a01b03811660009081526015602090815260408083208151815460a09481028201850190935260808101838152606095869590948594859493909284928491840182828015611ae457602002820191906000526020600020905b815481526020019060010190808311611ad0575b5050505050815260200160018201805480602002602001604051908101604052809291908181526020018280548015611b3c57602002820191906000526020600020905b815481526020019060010190808311611b28575b505050918352505060028201546020820152600382015460409091019060ff166004811115611b6d57611b6d613965565b6004811115611b7e57611b7e613965565b81525050905080600001518160200151826040015183606001516004811115611ba957611ba9613965565b9450945094509450509193509193565b611bc161242e565b611bc9612458565b3360009081526015602052604081206003015460ff166004811115611bf057611bf0613965565b1480611c22575060043360009081526015602052604090206003015460ff166004811115611c2057611c20613965565b145b611c945760405162461bcd60e51b815260206004820152603c60248201527f43616e206e6f742070726576696f75736c79206861766520612076657374696e60448201527f67207363686564756c65206f72206d757374206265205075626c6963000000006064820152608401610c7b565b611ca1813333600461247c565b610e956001600055565b611cb433611524565b611cd05760405162461bcd60e51b8152600401610c7b9061390e565b610e95600080516020613a5b833981519152826122f7565b6000806000806000806000806000611cff600d5490565b9850611d0a60145490565b975060065496506004549550611d1e612296565b9450611d28611249565b93506009549250611d4e8a6001600160a01b031660009081526012602052604090205490565b9150611d598a610ba4565b90509193959799909294969850565b611d7133611524565b611d8d5760405162461bcd60e51b8152600401610c7b9061390e565b6001600160a01b038116611db35760405162461bcd60e51b8152600401610c7b9061393c565b6003546001600160a01b0390811690841603611e1b5760405162461bcd60e51b815260206004820152602160248201527f43616e6e6f7420776974686472617720746865207374616b696e6720746f6b656044820152603760f91b6064820152608401610c7b565b6002546001600160a01b03610100909104811690841603611e885760405162461bcd60e51b815260206004820152602160248201527f43616e6e6f7420776974686472617720746865207265776172647320746f6b656044820152603760f91b6064820152608401610c7b565b611e9c6001600160a01b03841682846123cf565b604080516001600160a01b0385168152602081018490527f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa289101610e8a565b6001600160a01b0381166000908152601560205260408120600101541561206f576001600160a01b03821660009081526015602090815260408083208151815460a09481028201850190935260808101838152909391928492849190840182828015611f6657602002820191906000526020600020905b815481526020019060010190808311611f52575b5050505050815260200160018201805480602002602001604051908101604052809291908181526020018280548015611fbe57602002820191906000526020600020905b815481526020019060010190808311611faa575b505050918352505060028201546020820152600382015460409091019060ff166004811115611fef57611fef613965565b600481111561200057612000613965565b905250905060005b81515181101561206c5781518051829081106120265761202661397b565b602002602001015142101561205a5781518051829081106120495761204961397b565b602002602001015192505050919050565b8061206481613991565b915050612008565b50505b506000919050565b61207f61242e565b3361208981612373565b3360009081526011602052604090205480156121635780600f5410156120e85760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e74207265776172647360601b6044820152606401610c7b565b336000908152601160205260408120819055600f805483929061210c908490613877565b909155505060025461212d9061010090046001600160a01b031633836123cf565b60405181815233907fe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e04869060200160405180910390a25b50506118976001600055565b60168181548110611a6057600080fd5b601c8181548110611a6057600080fd5b60188181548110611a6057600080fd5b6121a83361232f565b6121c45760405162461bcd60e51b8152600401610c7b906138de565b60045442116122615760405162461bcd60e51b815260206004820152605860248201527f50726576696f7573207265776172647320706572696f64206d7573742062652060448201527f636f6d706c657465206265666f7265206368616e67696e67207468652064757260648201527f6174696f6e20666f7220746865206e657720706572696f640000000000000000608482015260a401610c7b565b60068190556040518181527ffb46ca5a5e06d4540d6387b930a7c978bce0db5f449ec6b3f5d07c6e1d44f2d39060200161134e565b6000600d546000036122a9575060085490565b600d546005546007546122ba6119e1565b6122c49190613877565b6122ce919061388a565b6122e090670de0b6b3a764000061388a565b6122ea91906138b7565b60085461125b91906138cb565b6000828152600160208190526040909120015461231381612fce565b6118318383613051565b6000600a54600e5461125b91906138cb565b6000610c1b600080516020613a5b83398151915283611a25565b6014818154811061235957600080fd5b6000918252602090912001546001600160a01b0316905081565b61237b612296565b6008556123866119e1565b6007556001600160a01b03811615610e95576123a181610ba4565b6001600160a01b03821660009081526011602090815260408083209390935560085460109091529190205550565b6040516001600160a01b0383811660248301526044820183905261123491859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506131e4565b60026000540361245157604051633ee5aeb560e01b815260040160405180910390fd5b6002600055565b60025460ff16156118975760405163d93c066560e01b815260040160405180910390fd5b6060808080600185600481111561249557612495613965565b036125ed5760175467ffffffffffffffff8111156124b5576124b56139aa565b6040519080825280602002602001820160405280156124de578160200160208202803683370190505b5060165490945067ffffffffffffffff8111156124fd576124fd6139aa565b604051908082528060200260200182016040528015612526578160200160208202803683370190505b50601780546040805160208084028201810190925282815293965083018282801561258e57602002820191906000526020600020906000905b825461010083900a900460ff1681526020600192830181810494850194909303909202910180841161255f5790505b5050505050915060168054806020026020016040519081016040528092919081815260200182805480156125e157602002820191906000526020600020905b8154815260200190600101908083116125cd575b50505050509050612a6a565b600285600481111561260157612601613965565b036127575760195467ffffffffffffffff811115612621576126216139aa565b60405190808252806020026020018201604052801561264a578160200160208202803683370190505b5060185490945067ffffffffffffffff811115612669576126696139aa565b604051908082528060200260200182016040528015612692578160200160208202803683370190505b5060198054604080516020808402820181019092528281529396508301828280156126fa57602002820191906000526020600020906000905b825461010083900a900460ff168152602060019283018181049485019490930390920291018084116126cb5790505b5050505050915060188054806020026020016040519081016040528092919081815260200182805480156125e157602002820191906000526020600020908154815260200190600101908083116125cd5750505050509050612a6a565b600385600481111561276b5761276b613965565b036128c157601b5467ffffffffffffffff81111561278b5761278b6139aa565b6040519080825280602002602001820160405280156127b4578160200160208202803683370190505b50601a5490945067ffffffffffffffff8111156127d3576127d36139aa565b6040519080825280602002602001820160405280156127fc578160200160208202803683370190505b50601b80546040805160208084028201810190925282815293965083018282801561286457602002820191906000526020600020906000905b825461010083900a900460ff168152602060019283018181049485019490930390920291018084116128355790505b50505050509150601a8054806020026020016040519081016040528092919081815260200182805480156125e157602002820191906000526020600020908154815260200190600101908083116125cd5750505050509050612a6a565b60048560048111156128d5576128d5613965565b03612a2b57601d5467ffffffffffffffff8111156128f5576128f56139aa565b60405190808252806020026020018201604052801561291e578160200160208202803683370190505b50601c5490945067ffffffffffffffff81111561293d5761293d6139aa565b604051908082528060200260200182016040528015612966578160200160208202803683370190505b50601d8054604080516020808402820181019092528281529396508301828280156129ce57602002820191906000526020600020906000905b825461010083900a900460ff1681526020600192830181810494850194909303909202910180841161299f5790505b50505050509150601c8054806020026020016040519081016040528092919081815260200182805480156125e157602002820191906000526020600020908154815260200190600101908083116125cd5750505050509050612a6a565b60405162461bcd60e51b8152602060048201526014602482015273496e76616c69642076657374696e67207479706560601b6044820152606401610c7b565b6001600160a01b038716600090815260126020526040812054612a8e908a906138cb565b905060008060005b85518160ff161015612bcd5760006064878360ff1681518110612abb57612abb61397b565b602002602001015160ff1686612ad1919061388a565b612adb91906138b7565b90506064878360ff1681518110612af457612af461397b565b602002602001015160ff1686612b0a919061388a565b612b1491906139c0565b612b1e90856138cb565b935060648410612b4357612b336001826138cb565b9050612b40606485613877565b93505b80898360ff1681518110612b5957612b5961397b565b602002602001018181525050858260ff1681518110612b7a57612b7a61397b565b602002602001015142612b8d91906138cb565b888360ff1681518110612ba257612ba261397b565b6020908102919091010152612bb781846138cb565b9250508080612bc5906139d4565b915050612a96565b50845115612c1657612bdf8184613877565b8760018751612bee9190613877565b81518110612bfe57612bfe61397b565b60200260200101818151612c1291906138cb565b9052505b604051806080016040528087815260200188815260200160008152602001896004811115612c4657612c46613965565b90526001600160a01b038b166000908152601560209081526040909120825180519192612c7892849290910190613638565b506020828101518051612c919260018501920190613638565b5060408201516002820155606082015160038201805460ff19166001836004811115612cbf57612cbf613965565b0217905550905050612cd28b8b8b613247565b5050505050505050505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526118319186918216906323b872dd906084016123fc565b80612d2281612373565b60008311612d655760405162461bcd60e51b815260206004820152601060248201526f043616e6e6f7420756e7374616b6520360841b6044820152606401610c7b565b6001600160a01b038216600090815260126020526040902054831115612de05760405162461bcd60e51b815260206004820152602a60248201527f43616e6e6f7420756e7374616b65206d6f7265207468616e207468652063757260448201526972656e74207374616b6560b01b6064820152608401610c7b565b82600d54612dee9190613877565b600d556001600160a01b038216600090815260126020526040902054612e15908490613877565b6001600160a01b038316600090815260126020526040812082905503612f2f576001600160a01b038216600090815260136020526040812054601454909190612e6090600190613877565b9050600060148281548110612e7757612e7761397b565b600091825260209091200154601480546001600160a01b039092169250829185908110612ea657612ea661397b565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905591831681526013909152604090208390556014805480612ef057612ef06139f3565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b03871682526013905260408120555050505b6000606460095485612f41919061388a565b612f4b91906138b7565b90506000612f598286613877565b905081600c54612f6991906138cb565b600c55600354612f83906001600160a01b031685836123cf565b60408051868152602081018490526001600160a01b038616917f7fc4727e062e336010f2c282598ef5f14facb3de68cf8195c2f23e1454b2b74e910160405180910390a25050505050565b610e9581336133ec565b6000612fe48383611a25565b6130495760008381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a4506001610c1b565b506000610c1b565b600061305d8383611a25565b156130495760008381526001602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610c1b565b6130c6613425565b6002805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b804710156131335760405163cd78605960e01b8152306004820152602401610c7b565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613180576040519150601f19603f3d011682016040523d82523d6000602084013e613185565b606091505b505090508061123457604051630a12f52160e11b815260040160405180910390fd5b6131af612458565b6002805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586130f33390565b60006131f96001600160a01b03841683613448565b9050805160001415801561321e57508080602001905181019061321c9190613a09565b155b1561123457604051635274afe760e01b81526001600160a01b0384166004820152602401610c7b565b8161325181612373565b600084116132925760405162461bcd60e51b815260206004820152600e60248201526d043616e6e6f74207374616b6520360941b6044820152606401610c7b565b83600d546132a091906138cb565b600d556001600160a01b038316600090815260126020526040812054900361333557601480546001808201835560008390527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec90910180546001600160a01b0319166001600160a01b038716179055905461331b9190613877565b6001600160a01b0384166000908152601360205260409020555b6001600160a01b0383166000908152601260205260409020546133599085906138cb565b6001600160a01b038085166000908152601260205260409020919091556003546133869116833087612cdf565b600b5460ff16156133995761339961345d565b816001600160a01b0316836001600160a01b03167f5dac0c1b1112564a045ba943c9d50270893e8e826c49be8e7073adc713ab7bd7866040516133de91815260200190565b60405180910390a350505050565b6133f68282611a25565b6119805760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610c7b565b60025460ff1661189757604051638dfc202b60e01b815260040160405180910390fd5b6060613456838360006134f8565b9392505050565b600061346881612373565b600b805460ff19169055600654600a5461348291906138b7565b6005819055600654600091613497919061388a565b905080600a546134a79190613877565b600a554260078190556006546134bc916138cb565b60048190556040805183815260208101929092527f867b60a9d1d7c706c0754dcb3bc7047022d07de0f3e55a79d1b74c69653ff89b9101610f76565b60608147101561351d5760405163cd78605960e01b8152306004820152602401610c7b565b600080856001600160a01b031684866040516135399190613a2b565b60006040518083038185875af1925050503d8060008114613576576040519150601f19603f3d011682016040523d82523d6000602084013e61357b565b606091505b509150915061358b868383613595565b9695505050505050565b6060826135aa576135a5826135f1565b613456565b81511580156135c157506001600160a01b0384163b155b156135ea57604051639996b31560e01b81526001600160a01b0385166004820152602401610c7b565b5080613456565b8051156136015780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b5080546000825590600052602060002090810190610e959190613683565b828054828255906000526020600020908101928215613673579160200282015b82811115613673578251825591602001919060010190613658565b5061367f929150613683565b5090565b5b8082111561367f5760008155600101613684565b6001600160a01b0381168114610e9557600080fd5b6000602082840312156136bf57600080fd5b813561345681613698565b6000602082840312156136dc57600080fd5b81356001600160e01b03198116811461345657600080fd5b60006020828403121561370657600080fd5b5035919050565b60008060006060848603121561372257600080fd5b83359250602084013561373481613698565b915060408401356005811061374857600080fd5b809150509250925092565b6000806040838503121561376657600080fd5b82359150602083013561377881613698565b809150509250929050565b6000806040838503121561379657600080fd5b82356137a181613698565b946020939093013593505050565b600081518084526020808501945080840160005b838110156137df578151875295820195908201906001016137c3565b509495945050505050565b6080815260006137fd60808301876137af565b828103602084015261380f81876137af565b91505083604083015260ff8316606083015295945050505050565b60008060006060848603121561383f57600080fd5b833561384a81613698565b925060208401359150604084013561374881613698565b634e487b7160e01b600052601160045260246000fd5b81810381811115610c1b57610c1b613861565b8082028115828204841417610c1b57610c1b613861565b634e487b7160e01b600052601260045260246000fd5b6000826138c6576138c66138a1565b500490565b80820180821115610c1b57610c1b613861565b6020808252601690820152755265737472696374656420746f206d616e616765727360501b604082015260600190565b6020808252601490820152735265737472696374656420746f2061646d696e7360601b604082015260600190565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000600182016139a3576139a3613861565b5060010190565b634e487b7160e01b600052604160045260246000fd5b6000826139cf576139cf6138a1565b500690565b600060ff821660ff81036139ea576139ea613861565b60010192915050565b634e487b7160e01b600052603160045260246000fd5b600060208284031215613a1b57600080fd5b8151801515811461345657600080fd5b6000825160005b81811015613a4c5760208186018101518583015201613a32565b50600092019182525091905056fe241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b08a26469706673582212203e023a27399f784e4585cdf9ad3a5f3d598aecc3b8c1717e957b38266bc93e2f64736f6c63430008150033000000000000000000000000312406d71a6f88e78e0ab1b9edb81e85e4e45e0a0000000000000000000000002ac29781547eb7501d3cfd9733ce11e38df5463a0000000000000000000000002ac29781547eb7501d3cfd9733ce11e38df5463a
Deployed Bytecode
0x6080604052600436106103b05760003560e01c806378e81d07116101e7578063b88a802f1161010d578063d547741f116100a0578063ebe2b12b1161006f578063ebe2b12b14610b2c578063ec87621c14610b42578063f3ae241514610b64578063fd5e6dd114610b8457600080fd5b8063d547741f14610acb578063df136d6514610aeb578063e6a1d27d14610b01578063e7d6007e14610b1757600080fd5b8063c9122507116100dc578063c912250714610a51578063cc1a378f14610a71578063cd3daf9d14610a91578063d1af0c7d14610aa657600080fd5b8063b88a802f146109e6578063b9af515e146109fb578063c299cadb14610a1b578063c8f33c9114610a3b57600080fd5b80639583027911610185578063ac18de4311610154578063ac18de4314610922578063b2ed51e914610942578063b51609b4146109a6578063b5b654b3146109c657600080fd5b8063958302791461089d5780639f829063146108bd578063a217fddf146108ed578063a694fc3a1461090257600080fd5b80638456cb59116101c15780638456cb5914610826578063869890381461083b5780638b8763471461085057806391d148541461087d57600080fd5b806378e81d07146107db5780637b0a47ee146107fb57806380faa57d1461081157600080fd5b8063248a9ca3116102d7578063386a95251161026a5780635c975abb116102395780635c975abb1461074057806370a082311461075857806372f702f31461078e57806375824f23146107c657600080fd5b8063386a9525146106ab5780633f4ba83a146106c15780634782f779146106d65780634b62bfb0146106f657600080fd5b80632e17de78116102a65780632e17de78146106355780632f2ff15d14610655578063319ce2bb1461067557806336568abe1461068b57600080fd5b8063248a9ca3146105ae57806324d7806c146105df57806328af352b146105ff5780632d06177a1461061557600080fd5b80631664a2701161034f5780631c1f78eb1161031e5780631c1f78eb146105395780631e2720ff1461054e5780631f3a71ba1461056e57806323f965e11461058e57600080fd5b80631664a270146104b25780631758f63e146104e457806318160ddd146105045780631b44718a1461051957600080fd5b806313114a9d1161038b57806313114a9d1461044c578063131a033814610461578063137ee36e14610478578063164e68de1461049257600080fd5b80628cc262146103bc57806301ffc9a7146103ef5780630700037d1461041f57600080fd5b366103b757005b600080fd5b3480156103c857600080fd5b506103dc6103d73660046136ad565b610ba4565b6040519081526020015b60405180910390f35b3480156103fb57600080fd5b5061040f61040a3660046136ca565b610c21565b60405190151581526020016103e6565b34801561042b57600080fd5b506103dc61043a3660046136ad565b60116020526000908152604090205481565b34801561045857600080fd5b50600c546103dc565b34801561046d57600080fd5b50610476610c56565b005b34801561048457600080fd5b50600b5461040f9060ff1681565b34801561049e57600080fd5b506104766104ad3660046136ad565b610e98565b3480156104be57600080fd5b506104d26104cd3660046136f4565b610f82565b60405160ff90911681526020016103e6565b3480156104f057600080fd5b506104766104ff36600461370d565b610fb6565b34801561051057600080fd5b50600d546103dc565b34801561052557600080fd5b506104d26105343660046136f4565b611239565b34801561054557600080fd5b506103dc611249565b34801561055a57600080fd5b506104766105693660046136f4565b611260565b34801561057a57600080fd5b506103dc6105893660046136ad565b611359565b34801561059a57600080fd5b506104d26105a93660046136f4565b611514565b3480156105ba57600080fd5b506103dc6105c93660046136f4565b6000908152600160208190526040909120015490565b3480156105eb57600080fd5b5061040f6105fa3660046136ad565b611524565b34801561060b57600080fd5b506103dc600f5481565b34801561062157600080fd5b506104766106303660046136ad565b611530565b34801561064157600080fd5b506104766106503660046136f4565b61156d565b34801561066157600080fd5b50610476610670366004613753565b61180b565b34801561068157600080fd5b506103dc600a5481565b34801561069757600080fd5b506104766106a6366004613753565b611837565b3480156106b757600080fd5b506103dc60065481565b3480156106cd57600080fd5b5061047661186a565b3480156106e257600080fd5b506104766106f1366004613783565b611899565b34801561070257600080fd5b5061070b611984565b604080519788526020880196909652948601939093526060850191909152608084015260a083015260c082015260e0016103e6565b34801561074c57600080fd5b5060025460ff1661040f565b34801561076457600080fd5b506103dc6107733660046136ad565b6001600160a01b031660009081526012602052604090205490565b34801561079a57600080fd5b506003546107ae906001600160a01b031681565b6040516001600160a01b0390911681526020016103e6565b3480156107d257600080fd5b50600f546103dc565b3480156107e757600080fd5b506104d26107f63660046136f4565b6119d1565b34801561080757600080fd5b506103dc60055481565b34801561081d57600080fd5b506103dc6119e1565b34801561083257600080fd5b506104766119f8565b34801561084757600080fd5b506014546103dc565b34801561085c57600080fd5b506103dc61086b3660046136ad565b60106020526000908152604090205481565b34801561088957600080fd5b5061040f610898366004613753565b611a25565b3480156108a957600080fd5b506103dc6108b83660046136f4565b611a50565b3480156108c957600080fd5b506108dd6108d83660046136ad565b611a71565b6040516103e694939291906137ea565b3480156108f957600080fd5b506103dc600081565b34801561090e57600080fd5b5061047661091d3660046136f4565b611bb9565b34801561092e57600080fd5b5061047661093d3660046136ad565b611cab565b34801561094e57600080fd5b5061096261095d3660046136ad565b611ce8565b60408051998a5260208a0198909852968801959095526060870193909352608086019190915260a085015260c084015260e0830152610100820152610120016103e6565b3480156109b257600080fd5b506104766109c136600461382a565b611d68565b3480156109d257600080fd5b506103dc6109e13660046136ad565b611edb565b3480156109f257600080fd5b50610476612077565b348015610a0757600080fd5b506103dc610a163660046136f4565b61216f565b348015610a2757600080fd5b506103dc610a363660046136f4565b61217f565b348015610a4757600080fd5b506103dc60075481565b348015610a5d57600080fd5b506103dc610a6c3660046136f4565b61218f565b348015610a7d57600080fd5b50610476610a8c3660046136f4565b61219f565b348015610a9d57600080fd5b506103dc612296565b348015610ab257600080fd5b506002546107ae9061010090046001600160a01b031681565b348015610ad757600080fd5b50610476610ae6366004613753565b6122f7565b348015610af757600080fd5b506103dc60085481565b348015610b0d57600080fd5b506103dc60095481565b348015610b2357600080fd5b506103dc61231d565b348015610b3857600080fd5b506103dc60045481565b348015610b4e57600080fd5b506103dc600080516020613a5b83398151915281565b348015610b7057600080fd5b5061040f610b7f3660046136ad565b61232f565b348015610b9057600080fd5b506107ae610b9f3660046136f4565b612349565b6001600160a01b0381166000908152601160209081526040808320546010909252822054670de0b6b3a764000090610bda612296565b610be49190613877565b6001600160a01b038516600090815260126020526040902054610c07919061388a565b610c1191906138b7565b610c1b91906138cb565b92915050565b60006001600160e01b03198216637965db0b60e01b1480610c1b57506301ffc9a760e01b6001600160e01b0319831614610c1b565b610c5f3361232f565b610c845760405162461bcd60e51b8152600401610c7b906138de565b60405180910390fd5b6000610c8f81612373565b6004544211610cfb5760405162461bcd60e51b815260206004820152603260248201527f50726576696f757320706572696f64206d75737420656e64206265666f726520604482015271616464696e67206e6577207265776172647360701b6064820152608401610c7b565b6000600e5411610d575760405162461bcd60e51b815260206004820152602160248201527f4e657720726577617264206d7573742062652067726561746572207468616e206044820152600360fc1b6064820152608401610c7b565b6000600a54600e54610d6991906138cb565b90506006548111610de25760405162461bcd60e51b815260206004820152603b60248201527f526577617264206d7573742062652067726561746572207468616e206475726160448201527f74696f6e20746f2061766f6964203020726577617264207261746500000000006064820152608401610c7b565b6000600e819055600d549003610e0557600a55600b805460ff1916600117905550565b600b805460ff19169055600654610e1c90826138b7565b6005819055600654600091610e31919061388a565b9050610e3d8183613877565b600a55426007819055600654610e52916138cb565b60048190556040805183815260208101929092527f867b60a9d1d7c706c0754dcb3bc7047022d07de0f3e55a79d1b74c69653ff89b91015b60405180910390a150505b50565b610ea133611524565b610ebd5760405162461bcd60e51b8152600401610c7b9061390e565b6001600160a01b038116610ee35760405162461bcd60e51b8152600401610c7b9061393c565b6000600c5411610f2a5760405162461bcd60e51b81526020600482015260126024820152712737903332b2b9903a37903932b1b7bb32b960711b6044820152606401610c7b565b600c80546000909155600354610f4a906001600160a01b031683836123cf565b6040518181527f9800e6f57aeb4360eaa72295a820a4293e1e66fbfcabcd8874ae141304a76deb906020015b60405180910390a15050565b601b8181548110610f9257600080fd5b9060005260206000209060209182820401919006915054906101000a900460ff1681565b610fbf3361232f565b610fdb5760405162461bcd60e51b8152600401610c7b906138de565b610fe361242e565b610feb612458565b6001600160a01b0382166110115760405162461bcd60e51b8152600401610c7b9061393c565b600083116110525760405162461bcd60e51b815260206004820152600e60248201526d125b9d985b1a5908185b5bdd5b9d60921b6044820152606401610c7b565b600081600481111561106657611066613965565b036110aa5760405162461bcd60e51b8152602060048201526014602482015273496e76616c69642076657374696e67207479706560601b6044820152606401610c7b565b60048160048111156110be576110be613965565b0361111d5760405162461bcd60e51b815260206004820152602960248201527f5075626c69632076657374696e67206973206e6f7420616c6c6f77656420696e6044820152681039ba30b5b2a337b960b91b6064820152608401610c7b565b6001600160a01b03821660009081526015602052604081206003015460ff16600481111561114d5761114d613965565b1480611198575080600481111561116657611166613965565b6001600160a01b03831660009081526015602052604090206003015460ff16600481111561119657611196613965565b145b61121e5760405162461bcd60e51b815260206004820152604b60248201527f43616e206e6f742070726576696f75736c79206861766520612076657374696e60448201527f67207363686564756c65206f7220757365207468652073616d6520766573746960648201526a6e67207363686564756c6560a81b608482015260a401610c7b565b61122a8383338461247c565b6112346001600055565b505050565b60178181548110610f9257600080fd5b600060065460055461125b919061388a565b905090565b6112693361232f565b6112855760405162461bcd60e51b8152600401610c7b906138de565b600081116112d55760405162461bcd60e51b815260206004820152601d60248201527f416d6f756e74206d7573742062652067726561746572207468616e20300000006044820152606401610c7b565b6002546112f29061010090046001600160a01b0316333084612cdf565b80600f600082825461130491906138cb565b9250508190555080600e600082825461131d91906138cb565b90915550506040518181527fde88a922e0d3b88b24e9623efeb464919c6bf9f66857a65e2bfcf2ce87a9433d906020015b60405180910390a150565b6001600160a01b03811660009081526015602090815260408083208151815460a094810282018501909352608081018381528594919384928491908401828280156113c357602002820191906000526020600020905b8154815260200190600101908083116113af575b505050505081526020016001820180548060200260200160405190810160405280929190818152602001828054801561141b57602002820191906000526020600020905b815481526020019060010190808311611407575b505050918352505060028201546020820152600382015460409091019060ff16600481111561144c5761144c613965565b600481111561145d5761145d613965565b905250602081015151909150600090156114f65760005b8251518110156114de5782518051829081106114925761149261397b565b602002602001015142106114cc57826020015181815181106114b6576114b661397b565b6020026020010151826114c991906138cb565b91505b806114d681613991565b915050611474565b5060408201516114ee9082613877565b949350505050565b6001600160a01b0384166000908152601260205260409020546114ee565b601d8181548110610f9257600080fd5b6000610c1b8183611a25565b61153933611524565b6115555760405162461bcd60e51b8152600401610c7b9061390e565b610e95600080516020613a5b8339815191528261180b565b61157561242e565b3360009081526015602052604081206003015460ff16600481111561159c5761159c613965565b036115e95760405162461bcd60e51b815260206004820152601960248201527f4e6f2076657374696e67207363686564756c6520666f756e64000000000000006044820152606401610c7b565b6000811161162a5760405162461bcd60e51b815260206004820152600e60248201526d125b9d985b1a5908185b5bdd5b9d60921b6044820152606401610c7b565b3360009081526015602052604090206001810154156117f65760008060005b83548110156116e5578360000181815481106116675761166761397b565b906000526020600020015442106116a65783600101818154811061168d5761168d61397b565b9060005260206000200154836116a391906138cb565b92505b8360010181815481106116bb576116bb61397b565b9060005260206000200154826116d191906138cb565b9150806116dd81613991565b915050611649565b508383600201546116f691906138cb565b82101561175c5760405162461bcd60e51b815260206004820152602e60248201527f596f752061726520747279696e6720746f207769746864726177206d6f72652060448201526d7468616e20617661696c61626c6560901b6064820152608401610c7b565b8383600201600082825461177091906138cb565b90915550506002830154811480156117b257508254839061179390600190613877565b815481106117a3576117a361397b565b90600052602060002001544210155b156117f357336000908152601560205260408120906117d1828261361a565b6117df60018301600061361a565b5060006002820155600301805460ff191690555b50505b6118008233612d18565b50610e956001600055565b6000828152600160208190526040909120015461182781612fce565b6118318383612fd8565b50505050565b6001600160a01b03811633146118605760405163334bd91960e11b815260040160405180910390fd5b6112348282613051565b6118733361232f565b61188f5760405162461bcd60e51b8152600401610c7b906138de565b6118976130be565b565b6118a161242e565b6118aa33611524565b6118c65760405162461bcd60e51b8152600401610c7b9061390e565b6001600160a01b03821661191c5760405162461bcd60e51b815260206004820152601d60248201527f546f2077616c6c657420697320746865207a65726f20616464726573730000006044820152606401610c7b565b8047101561196c5760405162461bcd60e51b815260206004820152601860248201527f496e73756666696369656e74204554482062616c616e636500000000000000006044820152606401610c7b565b6119768282613110565b6119806001600055565b5050565b6000806000806000806000611998600d5490565b96506119a360145490565b9550600654945060045493506119b7612296565b92506119c1611249565b9150600954905090919293949596565b60198181548110610f9257600080fd5b600060045442106119f3575060045490565b504290565b611a013361232f565b611a1d5760405162461bcd60e51b8152600401610c7b906138de565b6118976131a7565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b601a8181548110611a6057600080fd5b600091825260209091200154905081565b6001600160a01b03811660009081526015602090815260408083208151815460a09481028201850190935260808101838152606095869590948594859493909284928491840182828015611ae457602002820191906000526020600020905b815481526020019060010190808311611ad0575b5050505050815260200160018201805480602002602001604051908101604052809291908181526020018280548015611b3c57602002820191906000526020600020905b815481526020019060010190808311611b28575b505050918352505060028201546020820152600382015460409091019060ff166004811115611b6d57611b6d613965565b6004811115611b7e57611b7e613965565b81525050905080600001518160200151826040015183606001516004811115611ba957611ba9613965565b9450945094509450509193509193565b611bc161242e565b611bc9612458565b3360009081526015602052604081206003015460ff166004811115611bf057611bf0613965565b1480611c22575060043360009081526015602052604090206003015460ff166004811115611c2057611c20613965565b145b611c945760405162461bcd60e51b815260206004820152603c60248201527f43616e206e6f742070726576696f75736c79206861766520612076657374696e60448201527f67207363686564756c65206f72206d757374206265205075626c6963000000006064820152608401610c7b565b611ca1813333600461247c565b610e956001600055565b611cb433611524565b611cd05760405162461bcd60e51b8152600401610c7b9061390e565b610e95600080516020613a5b833981519152826122f7565b6000806000806000806000806000611cff600d5490565b9850611d0a60145490565b975060065496506004549550611d1e612296565b9450611d28611249565b93506009549250611d4e8a6001600160a01b031660009081526012602052604090205490565b9150611d598a610ba4565b90509193959799909294969850565b611d7133611524565b611d8d5760405162461bcd60e51b8152600401610c7b9061390e565b6001600160a01b038116611db35760405162461bcd60e51b8152600401610c7b9061393c565b6003546001600160a01b0390811690841603611e1b5760405162461bcd60e51b815260206004820152602160248201527f43616e6e6f7420776974686472617720746865207374616b696e6720746f6b656044820152603760f91b6064820152608401610c7b565b6002546001600160a01b03610100909104811690841603611e885760405162461bcd60e51b815260206004820152602160248201527f43616e6e6f7420776974686472617720746865207265776172647320746f6b656044820152603760f91b6064820152608401610c7b565b611e9c6001600160a01b03841682846123cf565b604080516001600160a01b0385168152602081018490527f8c1256b8896378cd5044f80c202f9772b9d77dc85c8a6eb51967210b09bfaa289101610e8a565b6001600160a01b0381166000908152601560205260408120600101541561206f576001600160a01b03821660009081526015602090815260408083208151815460a09481028201850190935260808101838152909391928492849190840182828015611f6657602002820191906000526020600020905b815481526020019060010190808311611f52575b5050505050815260200160018201805480602002602001604051908101604052809291908181526020018280548015611fbe57602002820191906000526020600020905b815481526020019060010190808311611faa575b505050918352505060028201546020820152600382015460409091019060ff166004811115611fef57611fef613965565b600481111561200057612000613965565b905250905060005b81515181101561206c5781518051829081106120265761202661397b565b602002602001015142101561205a5781518051829081106120495761204961397b565b602002602001015192505050919050565b8061206481613991565b915050612008565b50505b506000919050565b61207f61242e565b3361208981612373565b3360009081526011602052604090205480156121635780600f5410156120e85760405162461bcd60e51b8152602060048201526014602482015273496e73756666696369656e74207265776172647360601b6044820152606401610c7b565b336000908152601160205260408120819055600f805483929061210c908490613877565b909155505060025461212d9061010090046001600160a01b031633836123cf565b60405181815233907fe2403640ba68fed3a2f88b7557551d1993f84b99bb10ff833f0cf8db0c5e04869060200160405180910390a25b50506118976001600055565b60168181548110611a6057600080fd5b601c8181548110611a6057600080fd5b60188181548110611a6057600080fd5b6121a83361232f565b6121c45760405162461bcd60e51b8152600401610c7b906138de565b60045442116122615760405162461bcd60e51b815260206004820152605860248201527f50726576696f7573207265776172647320706572696f64206d7573742062652060448201527f636f6d706c657465206265666f7265206368616e67696e67207468652064757260648201527f6174696f6e20666f7220746865206e657720706572696f640000000000000000608482015260a401610c7b565b60068190556040518181527ffb46ca5a5e06d4540d6387b930a7c978bce0db5f449ec6b3f5d07c6e1d44f2d39060200161134e565b6000600d546000036122a9575060085490565b600d546005546007546122ba6119e1565b6122c49190613877565b6122ce919061388a565b6122e090670de0b6b3a764000061388a565b6122ea91906138b7565b60085461125b91906138cb565b6000828152600160208190526040909120015461231381612fce565b6118318383613051565b6000600a54600e5461125b91906138cb565b6000610c1b600080516020613a5b83398151915283611a25565b6014818154811061235957600080fd5b6000918252602090912001546001600160a01b0316905081565b61237b612296565b6008556123866119e1565b6007556001600160a01b03811615610e95576123a181610ba4565b6001600160a01b03821660009081526011602090815260408083209390935560085460109091529190205550565b6040516001600160a01b0383811660248301526044820183905261123491859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506131e4565b60026000540361245157604051633ee5aeb560e01b815260040160405180910390fd5b6002600055565b60025460ff16156118975760405163d93c066560e01b815260040160405180910390fd5b6060808080600185600481111561249557612495613965565b036125ed5760175467ffffffffffffffff8111156124b5576124b56139aa565b6040519080825280602002602001820160405280156124de578160200160208202803683370190505b5060165490945067ffffffffffffffff8111156124fd576124fd6139aa565b604051908082528060200260200182016040528015612526578160200160208202803683370190505b50601780546040805160208084028201810190925282815293965083018282801561258e57602002820191906000526020600020906000905b825461010083900a900460ff1681526020600192830181810494850194909303909202910180841161255f5790505b5050505050915060168054806020026020016040519081016040528092919081815260200182805480156125e157602002820191906000526020600020905b8154815260200190600101908083116125cd575b50505050509050612a6a565b600285600481111561260157612601613965565b036127575760195467ffffffffffffffff811115612621576126216139aa565b60405190808252806020026020018201604052801561264a578160200160208202803683370190505b5060185490945067ffffffffffffffff811115612669576126696139aa565b604051908082528060200260200182016040528015612692578160200160208202803683370190505b5060198054604080516020808402820181019092528281529396508301828280156126fa57602002820191906000526020600020906000905b825461010083900a900460ff168152602060019283018181049485019490930390920291018084116126cb5790505b5050505050915060188054806020026020016040519081016040528092919081815260200182805480156125e157602002820191906000526020600020908154815260200190600101908083116125cd5750505050509050612a6a565b600385600481111561276b5761276b613965565b036128c157601b5467ffffffffffffffff81111561278b5761278b6139aa565b6040519080825280602002602001820160405280156127b4578160200160208202803683370190505b50601a5490945067ffffffffffffffff8111156127d3576127d36139aa565b6040519080825280602002602001820160405280156127fc578160200160208202803683370190505b50601b80546040805160208084028201810190925282815293965083018282801561286457602002820191906000526020600020906000905b825461010083900a900460ff168152602060019283018181049485019490930390920291018084116128355790505b50505050509150601a8054806020026020016040519081016040528092919081815260200182805480156125e157602002820191906000526020600020908154815260200190600101908083116125cd5750505050509050612a6a565b60048560048111156128d5576128d5613965565b03612a2b57601d5467ffffffffffffffff8111156128f5576128f56139aa565b60405190808252806020026020018201604052801561291e578160200160208202803683370190505b50601c5490945067ffffffffffffffff81111561293d5761293d6139aa565b604051908082528060200260200182016040528015612966578160200160208202803683370190505b50601d8054604080516020808402820181019092528281529396508301828280156129ce57602002820191906000526020600020906000905b825461010083900a900460ff1681526020600192830181810494850194909303909202910180841161299f5790505b50505050509150601c8054806020026020016040519081016040528092919081815260200182805480156125e157602002820191906000526020600020908154815260200190600101908083116125cd5750505050509050612a6a565b60405162461bcd60e51b8152602060048201526014602482015273496e76616c69642076657374696e67207479706560601b6044820152606401610c7b565b6001600160a01b038716600090815260126020526040812054612a8e908a906138cb565b905060008060005b85518160ff161015612bcd5760006064878360ff1681518110612abb57612abb61397b565b602002602001015160ff1686612ad1919061388a565b612adb91906138b7565b90506064878360ff1681518110612af457612af461397b565b602002602001015160ff1686612b0a919061388a565b612b1491906139c0565b612b1e90856138cb565b935060648410612b4357612b336001826138cb565b9050612b40606485613877565b93505b80898360ff1681518110612b5957612b5961397b565b602002602001018181525050858260ff1681518110612b7a57612b7a61397b565b602002602001015142612b8d91906138cb565b888360ff1681518110612ba257612ba261397b565b6020908102919091010152612bb781846138cb565b9250508080612bc5906139d4565b915050612a96565b50845115612c1657612bdf8184613877565b8760018751612bee9190613877565b81518110612bfe57612bfe61397b565b60200260200101818151612c1291906138cb565b9052505b604051806080016040528087815260200188815260200160008152602001896004811115612c4657612c46613965565b90526001600160a01b038b166000908152601560209081526040909120825180519192612c7892849290910190613638565b506020828101518051612c919260018501920190613638565b5060408201516002820155606082015160038201805460ff19166001836004811115612cbf57612cbf613965565b0217905550905050612cd28b8b8b613247565b5050505050505050505050565b6040516001600160a01b0384811660248301528381166044830152606482018390526118319186918216906323b872dd906084016123fc565b80612d2281612373565b60008311612d655760405162461bcd60e51b815260206004820152601060248201526f043616e6e6f7420756e7374616b6520360841b6044820152606401610c7b565b6001600160a01b038216600090815260126020526040902054831115612de05760405162461bcd60e51b815260206004820152602a60248201527f43616e6e6f7420756e7374616b65206d6f7265207468616e207468652063757260448201526972656e74207374616b6560b01b6064820152608401610c7b565b82600d54612dee9190613877565b600d556001600160a01b038216600090815260126020526040902054612e15908490613877565b6001600160a01b038316600090815260126020526040812082905503612f2f576001600160a01b038216600090815260136020526040812054601454909190612e6090600190613877565b9050600060148281548110612e7757612e7761397b565b600091825260209091200154601480546001600160a01b039092169250829185908110612ea657612ea661397b565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905591831681526013909152604090208390556014805480612ef057612ef06139f3565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b03871682526013905260408120555050505b6000606460095485612f41919061388a565b612f4b91906138b7565b90506000612f598286613877565b905081600c54612f6991906138cb565b600c55600354612f83906001600160a01b031685836123cf565b60408051868152602081018490526001600160a01b038616917f7fc4727e062e336010f2c282598ef5f14facb3de68cf8195c2f23e1454b2b74e910160405180910390a25050505050565b610e9581336133ec565b6000612fe48383611a25565b6130495760008381526001602081815260408084206001600160a01b0387168086529252808420805460ff19169093179092559051339286917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a4506001610c1b565b506000610c1b565b600061305d8383611a25565b156130495760008381526001602090815260408083206001600160a01b0386168085529252808320805460ff1916905551339286917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a4506001610c1b565b6130c6613425565b6002805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b804710156131335760405163cd78605960e01b8152306004820152602401610c7b565b6000826001600160a01b03168260405160006040518083038185875af1925050503d8060008114613180576040519150601f19603f3d011682016040523d82523d6000602084013e613185565b606091505b505090508061123457604051630a12f52160e11b815260040160405180910390fd5b6131af612458565b6002805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586130f33390565b60006131f96001600160a01b03841683613448565b9050805160001415801561321e57508080602001905181019061321c9190613a09565b155b1561123457604051635274afe760e01b81526001600160a01b0384166004820152602401610c7b565b8161325181612373565b600084116132925760405162461bcd60e51b815260206004820152600e60248201526d043616e6e6f74207374616b6520360941b6044820152606401610c7b565b83600d546132a091906138cb565b600d556001600160a01b038316600090815260126020526040812054900361333557601480546001808201835560008390527fce6d7b5282bd9a3661ae061feed1dbda4e52ab073b1f9285be6e155d9c38d4ec90910180546001600160a01b0319166001600160a01b038716179055905461331b9190613877565b6001600160a01b0384166000908152601360205260409020555b6001600160a01b0383166000908152601260205260409020546133599085906138cb565b6001600160a01b038085166000908152601260205260409020919091556003546133869116833087612cdf565b600b5460ff16156133995761339961345d565b816001600160a01b0316836001600160a01b03167f5dac0c1b1112564a045ba943c9d50270893e8e826c49be8e7073adc713ab7bd7866040516133de91815260200190565b60405180910390a350505050565b6133f68282611a25565b6119805760405163e2517d3f60e01b81526001600160a01b038216600482015260248101839052604401610c7b565b60025460ff1661189757604051638dfc202b60e01b815260040160405180910390fd5b6060613456838360006134f8565b9392505050565b600061346881612373565b600b805460ff19169055600654600a5461348291906138b7565b6005819055600654600091613497919061388a565b905080600a546134a79190613877565b600a554260078190556006546134bc916138cb565b60048190556040805183815260208101929092527f867b60a9d1d7c706c0754dcb3bc7047022d07de0f3e55a79d1b74c69653ff89b9101610f76565b60608147101561351d5760405163cd78605960e01b8152306004820152602401610c7b565b600080856001600160a01b031684866040516135399190613a2b565b60006040518083038185875af1925050503d8060008114613576576040519150601f19603f3d011682016040523d82523d6000602084013e61357b565b606091505b509150915061358b868383613595565b9695505050505050565b6060826135aa576135a5826135f1565b613456565b81511580156135c157506001600160a01b0384163b155b156135ea57604051639996b31560e01b81526001600160a01b0385166004820152602401610c7b565b5080613456565b8051156136015780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b5080546000825590600052602060002090810190610e959190613683565b828054828255906000526020600020908101928215613673579160200282015b82811115613673578251825591602001919060010190613658565b5061367f929150613683565b5090565b5b8082111561367f5760008155600101613684565b6001600160a01b0381168114610e9557600080fd5b6000602082840312156136bf57600080fd5b813561345681613698565b6000602082840312156136dc57600080fd5b81356001600160e01b03198116811461345657600080fd5b60006020828403121561370657600080fd5b5035919050565b60008060006060848603121561372257600080fd5b83359250602084013561373481613698565b915060408401356005811061374857600080fd5b809150509250925092565b6000806040838503121561376657600080fd5b82359150602083013561377881613698565b809150509250929050565b6000806040838503121561379657600080fd5b82356137a181613698565b946020939093013593505050565b600081518084526020808501945080840160005b838110156137df578151875295820195908201906001016137c3565b509495945050505050565b6080815260006137fd60808301876137af565b828103602084015261380f81876137af565b91505083604083015260ff8316606083015295945050505050565b60008060006060848603121561383f57600080fd5b833561384a81613698565b925060208401359150604084013561374881613698565b634e487b7160e01b600052601160045260246000fd5b81810381811115610c1b57610c1b613861565b8082028115828204841417610c1b57610c1b613861565b634e487b7160e01b600052601260045260246000fd5b6000826138c6576138c66138a1565b500490565b80820180821115610c1b57610c1b613861565b6020808252601690820152755265737472696374656420746f206d616e616765727360501b604082015260600190565b6020808252601490820152735265737472696374656420746f2061646d696e7360601b604082015260600190565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b6000600182016139a3576139a3613861565b5060010190565b634e487b7160e01b600052604160045260246000fd5b6000826139cf576139cf6138a1565b500690565b600060ff821660ff81036139ea576139ea613861565b60010192915050565b634e487b7160e01b600052603160045260246000fd5b600060208284031215613a1b57600080fd5b8151801515811461345657600080fd5b6000825160005b81811015613a4c5760208186018101518583015201613a32565b50600092019182525091905056fe241ecf16d79d0f8dbfb92cbc07fe17840425976cf0667f022fe9877caa831b08a26469706673582212203e023a27399f784e4585cdf9ad3a5f3d598aecc3b8c1717e957b38266bc93e2f64736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000312406d71a6f88e78e0ab1b9edb81e85e4e45e0a0000000000000000000000002ac29781547eb7501d3cfd9733ce11e38df5463a0000000000000000000000002ac29781547eb7501d3cfd9733ce11e38df5463a
-----Decoded View---------------
Arg [0] : _multisigAdmin (address): 0x312406d71a6F88e78e0Ab1B9EdB81e85e4E45E0a
Arg [1] : _rewardsToken (address): 0x2ac29781547EB7501d3CfD9733ce11e38dF5463A
Arg [2] : _stakingToken (address): 0x2ac29781547EB7501d3CfD9733ce11e38dF5463A
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000312406d71a6f88e78e0ab1b9edb81e85e4e45e0a
Arg [1] : 0000000000000000000000002ac29781547eb7501d3cfd9733ce11e38df5463a
Arg [2] : 0000000000000000000000002ac29781547eb7501d3cfd9733ce11e38df5463a
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $0.001175 | 87,192,224.4938 | $102,441.68 |
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.