- Contract name:
- Rewarder
- Optimization enabled
- true
- Compiler version
- v0.6.12+commit.27d51765
- Optimization runs
- 200
- EVM Version
- default
- Verified at
- 2022-02-18T01:44:02.593440Z
Constructor Arguments
000000000000000000000000dd73dea10abc2bff99c60882ec5b2b81bb1dc5b2000000000000000000000000000000000000000000000a604b9a42df9ca0000000000000000000000000000000000000000000000000000000000000620f5200000000000000000000000000000000000000000000000000000000006285f900000000000000000000000000dccd6455ae04b03d785f12196b492b18129564bc000000000000000000000000bc149c62efe8afc61728fc58b1b66a0661712e76
Arg [0] (address) : 0xdd73dea10abc2bff99c60882ec5b2b81bb1dc5b2
Arg [1] (uint256) : 49000000000000000000000
Arg [2] (uint256) : 1645171200
Arg [3] (uint256) : 1652947200
Arg [4] (address) : 0xdccd6455ae04b03d785f12196b492b18129564bc
Arg [5] (address) : 0xbc149c62efe8afc61728fc58b1b66a0661712e76
Contract source code
// Sources flattened with hardhat v2.6.7 https://hardhat.org // File @openzeppelin/contracts/utils/[email protected] // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // File @openzeppelin/contracts/access/[email protected] pragma solidity >=0.6.0 <0.8.0; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() internal { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // File @openzeppelin/contracts/math/[email protected] pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod( uint256 a, uint256 b, string memory errorMessage ) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } } // File @openzeppelin/contracts/token/ERC20/[email protected] pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // File @openzeppelin/contracts/utils/[email protected] pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{value: value}(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) private pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // File @openzeppelin/contracts/token/ERC20/[email protected] pragma solidity >=0.6.0 <0.8.0; /** * @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 SafeMath for uint256; using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender).sub( value, "SafeERC20: decreased allowance below zero" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // File contracts/ICraftsman.sol pragma solidity 0.6.12; /** * @title ICraftsman * @notice It is an interface for Craftsman.sol */ interface ICraftsman { /** * @notice deposit LP tokens for VVS allocation. * @param _pid poolId * @param _amount amount of LP token to deposit */ function deposit(uint256 _pid, uint256 _amount) external; /** * @notice withdraw LP tokens * @param _pid poolId * @param _amount amount of LP token to deposit */ function withdraw(uint256 _pid, uint256 _amount) external; /** * @notice withdraw without caring about rewards. EMERGENCY ONLY. * @param _pid poolId */ function emergencyWithdraw(uint256 _pid) external; /** * @notice view function to check user pending vvs * @param _pid poolId * @param _user address of user */ function pendingVVS(uint256 _pid, address _user) external view returns (uint256); /** * @notice view function to check user's deposited LP and rewardDebt * @param _pid poolId * @param _user address of user */ function userInfo(uint256 _pid, address _user) external view returns (uint256 amount, uint256 rewardDebt); /** * @notice view function to check pool info * @param _pid poolId */ function poolInfo(uint256 _pid) external view returns ( address lpToken, uint256 allocPoint, uint256 lastRewardBlock, uint256 accVVSPerShare ); /** * @notice view function to check vvs token address registered in craftsman */ function vvs() external view returns (address); } // File contracts/Rewarder.sol pragma solidity 0.6.12; contract Rewarder is Ownable { using SafeMath for uint256; using SafeERC20 for IERC20; struct UserInfo { uint256 rewardDebt; // Reward debt - amount of reward token paid out to user } struct PoolInfo { uint256 allocPoint; uint256 lastRewardTime; uint256 accRewardPerShare; // Accumulated reward tokens per share, times ACC_TOKEN_PRECISION. See below. } IERC20 public immutable rewardToken; mapping(uint256 => PoolInfo) public poolInfo; uint256[] public poolIds; /// @notice Info of each user that stakes LP tokens. mapping(uint256 => mapping(address => UserInfo)) public userInfo; /// @dev Total allocation points. Must be the sum of all allocation points in all pools. uint256 public totalAllocPoint; uint256 public rewardPerSecond; // number of token per sec uint256 public rewardStartTimestamp; // timestamp when reward emission starts uint256 public rewardEndTimestamp; // timestamp when reward emission ends uint256 private constant ACC_TOKEN_PRECISION = 1e12; ICraftsman public immutable craftsman; ICraftsman public immutable craftsmanV2; /// @dev pending represent how much rewardToken user received event OnVVSReward(uint256 indexed pid, address indexed user, uint256 amount, uint256 pending); event AddPool(uint256 indexed pid, uint256 allocPoint); event SetPool(uint256 indexed pid, uint256 allocPoint); event SetRewardPerSecond(uint256 rewardPerSecond); event SetRewardStartTimestamp(uint256 rewardStartTimestamp); event SetRewardEndTimestamp(uint256 rewardEndTimestamp); event EmergencyRewardWithdraw(address indexed user, uint256 amount); modifier onlyCraftsmanV2() { require(msg.sender == address(craftsmanV2), "Only CraftsmanV2 can call this function."); _; } constructor( IERC20 _rewardToken, uint256 _rewardPerSecond, uint256 _rewardStartTimestamp, uint256 _rewardEndTimestamp, ICraftsman _craftsman, ICraftsman _craftsmanV2 ) public { rewardToken = _rewardToken; rewardPerSecond = _rewardPerSecond; rewardStartTimestamp = _rewardStartTimestamp; rewardEndTimestamp = _rewardEndTimestamp; craftsman = _craftsman; craftsmanV2 = _craftsmanV2; } /// @notice send any pending rewardToken to user /// @dev MUST call this function before update user.amount at craftsmanV2. If contract has not enough /// reward token, this function will throw an error. /// @param _pid pid to check for reward /// @param _user address of user /// @param _currentAmount current amount of the user after deposit | withdraw function onVVSReward( uint256 _pid, address _user, uint256 _currentAmount ) external onlyCraftsmanV2 { updatePool(_pid); PoolInfo storage pool = poolInfo[_pid]; UserInfo storage user = userInfo[_pid][_user]; (uint256 userAmount, ) = craftsmanV2.userInfo(_pid, _user); uint256 pending; if (userAmount > 0) { pending = (userAmount.mul(pool.accRewardPerShare).div(ACC_TOKEN_PRECISION)).sub(user.rewardDebt); rewardToken.safeTransfer(_user, pending); } user.rewardDebt = _currentAmount.mul(pool.accRewardPerShare).div(ACC_TOKEN_PRECISION); emit OnVVSReward(_pid, _user, _currentAmount, pending); } /* * @notice withdraw excess token. * @dev Only callable by owner. Needs to be for emergency. */ function emergencyRewardWithdraw(uint256 _amount) external onlyOwner { require(block.timestamp > rewardEndTimestamp, "Reward not ended"); rewardToken.safeTransfer(address(msg.sender), _amount); emit EmergencyRewardWithdraw(msg.sender, _amount); } /// @notice Sets the reward per second to be distributed. Can only be called by the owner. /// @param _rewardPerSecond The amount of reward token to be distributed per second. function setRewardPerSecond(uint256 _rewardPerSecond) public onlyOwner { massUpdatePools(); rewardPerSecond = _rewardPerSecond; emit SetRewardPerSecond(_rewardPerSecond); } /// @notice Returns the number of pools. function poolLength() public view returns (uint256 pools) { pools = poolIds.length; } /// @notice Add a new LP to the pool. Can only be called by the owner. /// @param _allocPoint AP of the new pool. /// @param _pid Pid on CraftsmanV2 function add( uint256 _allocPoint, uint256 _pid, bool _withUpdate ) public onlyOwner { require(poolInfo[_pid].lastRewardTime == 0, "Pool already exists"); if (_withUpdate) { massUpdatePools(); } uint256 lastRewardTime = block.timestamp > rewardStartTimestamp ? block.timestamp : rewardStartTimestamp; totalAllocPoint = totalAllocPoint.add(_allocPoint); poolInfo[_pid] = PoolInfo({allocPoint: _allocPoint, lastRewardTime: lastRewardTime, accRewardPerShare: 0}); poolIds.push(_pid); emit AddPool(_pid, _allocPoint); } /// @notice Update the given pool's reward allocation point. Can only be called by the owner. /// @param _pid The index of the pool. See `poolInfo`. /// @param _allocPoint New AP of the pool. /// @param _withUpdate Update the pool.accRewardPerShare before alloc point adjustment function set( uint256 _pid, uint256 _allocPoint, bool _withUpdate ) public onlyOwner { if (_withUpdate) { massUpdatePools(); } totalAllocPoint = totalAllocPoint.sub(poolInfo[_pid].allocPoint).add(_allocPoint); poolInfo[_pid].allocPoint = _allocPoint; emit SetPool(_pid, _allocPoint); } /// @notice View function to see pending Token /// @param _pid The index of the pool. See `poolInfo`. /// @param _user Address of user. /// @return pending reward for a given user. function pendingToken(uint256 _pid, address _user) public view returns (address, uint256) { PoolInfo memory pool = poolInfo[_pid]; UserInfo storage user = userInfo[_pid][_user]; uint256 accRewardPerShare = pool.accRewardPerShare; (uint256 lpSupply, ) = craftsman.userInfo(_pid, address(craftsmanV2)); if (block.timestamp > pool.lastRewardTime && lpSupply != 0) { uint256 multiplier = _getMultiplier(pool.lastRewardTime, block.timestamp); uint256 reward = multiplier.mul(rewardPerSecond).mul(pool.allocPoint).div(totalAllocPoint); accRewardPerShare = accRewardPerShare.add(reward.mul(ACC_TOKEN_PRECISION).div(lpSupply)); } (uint256 userAmount, ) = craftsmanV2.userInfo(_pid, _user); uint256 pending = userAmount.mul(accRewardPerShare).div(ACC_TOKEN_PRECISION).sub(user.rewardDebt); return (address(rewardToken), pending); } function setRewardStartTimestamp(uint256 _rewardStartTimestamp) public onlyOwner { require(block.timestamp < rewardStartTimestamp, "Reward has started"); require(block.timestamp < _rewardStartTimestamp, "New start time must be after current timestamp"); require(rewardEndTimestamp > _rewardStartTimestamp, "New start time must be before end timestamp"); rewardStartTimestamp = _rewardStartTimestamp; massUpdatePools(); emit SetRewardStartTimestamp(_rewardStartTimestamp); } function setRewardEndTimestamp(uint256 _rewardEndTimestamp) public onlyOwner { require(_rewardEndTimestamp > rewardStartTimestamp, "New end time must be after start timestamp"); rewardEndTimestamp = _rewardEndTimestamp; massUpdatePools(); emit SetRewardEndTimestamp(_rewardEndTimestamp); } /// Update reward variables for all pools. Be careful of gas spending! function massUpdatePools() public { uint256 length = poolIds.length; for (uint256 i = 0; i < length; ++i) { updatePool(poolIds[i]); } } /// @notice Update reward variables of the given pool. /// @param _pid The index of the pool function updatePool(uint256 _pid) public { PoolInfo storage pool = poolInfo[_pid]; if (block.timestamp <= pool.lastRewardTime) { return; } (uint256 lpSupply, ) = craftsman.userInfo(_pid, address(craftsmanV2)); if (lpSupply == 0) { pool.lastRewardTime = block.timestamp; return; } uint256 multiplier = _getMultiplier(pool.lastRewardTime, block.timestamp); uint256 reward = multiplier.mul(rewardPerSecond).mul(pool.allocPoint).div(totalAllocPoint); pool.accRewardPerShare = pool.accRewardPerShare.add((reward.mul(ACC_TOKEN_PRECISION) / lpSupply)); pool.lastRewardTime = block.timestamp; } /// @dev get the number of seconds passed since the last reward /// @param _lastRewardTime pool.lastRewardTime /// @param _currentTimestamp - block.timestamp function _getMultiplier(uint256 _lastRewardTime, uint256 _currentTimestamp) internal view returns (uint256) { // Scenario 1: Not started yet if (block.timestamp < rewardStartTimestamp) { return 0; } // Scenario 2: Reward started and not ended. (on-going) if (_currentTimestamp <= rewardEndTimestamp) { return _currentTimestamp.sub(_lastRewardTime); } // Scenario 3: pool's last reward already over rewardEndTimestamp if (_lastRewardTime >= rewardEndTimestamp) { return 0; } // Scenario 4: reward ended, calculate the diff from last claim return rewardEndTimestamp.sub(_lastRewardTime); } }
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_rewardToken","internalType":"contract IERC20"},{"type":"uint256","name":"_rewardPerSecond","internalType":"uint256"},{"type":"uint256","name":"_rewardStartTimestamp","internalType":"uint256"},{"type":"uint256","name":"_rewardEndTimestamp","internalType":"uint256"},{"type":"address","name":"_craftsman","internalType":"contract ICraftsman"},{"type":"address","name":"_craftsmanV2","internalType":"contract ICraftsman"}]},{"type":"event","name":"AddPool","inputs":[{"type":"uint256","name":"pid","internalType":"uint256","indexed":true},{"type":"uint256","name":"allocPoint","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"EmergencyRewardWithdraw","inputs":[{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OnVVSReward","inputs":[{"type":"uint256","name":"pid","internalType":"uint256","indexed":true},{"type":"address","name":"user","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false},{"type":"uint256","name":"pending","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"SetPool","inputs":[{"type":"uint256","name":"pid","internalType":"uint256","indexed":true},{"type":"uint256","name":"allocPoint","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SetRewardEndTimestamp","inputs":[{"type":"uint256","name":"rewardEndTimestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SetRewardPerSecond","inputs":[{"type":"uint256","name":"rewardPerSecond","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"SetRewardStartTimestamp","inputs":[{"type":"uint256","name":"rewardStartTimestamp","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"add","inputs":[{"type":"uint256","name":"_allocPoint","internalType":"uint256"},{"type":"uint256","name":"_pid","internalType":"uint256"},{"type":"bool","name":"_withUpdate","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ICraftsman"}],"name":"craftsman","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract ICraftsman"}],"name":"craftsmanV2","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"emergencyRewardWithdraw","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"massUpdatePools","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"onVVSReward","inputs":[{"type":"uint256","name":"_pid","internalType":"uint256"},{"type":"address","name":"_user","internalType":"address"},{"type":"uint256","name":"_currentAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"pendingToken","inputs":[{"type":"uint256","name":"_pid","internalType":"uint256"},{"type":"address","name":"_user","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"poolIds","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"allocPoint","internalType":"uint256"},{"type":"uint256","name":"lastRewardTime","internalType":"uint256"},{"type":"uint256","name":"accRewardPerShare","internalType":"uint256"}],"name":"poolInfo","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"pools","internalType":"uint256"}],"name":"poolLength","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"rewardEndTimestamp","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"rewardPerSecond","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"rewardStartTimestamp","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IERC20"}],"name":"rewardToken","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"set","inputs":[{"type":"uint256","name":"_pid","internalType":"uint256"},{"type":"uint256","name":"_allocPoint","internalType":"uint256"},{"type":"bool","name":"_withUpdate","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRewardEndTimestamp","inputs":[{"type":"uint256","name":"_rewardEndTimestamp","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRewardPerSecond","inputs":[{"type":"uint256","name":"_rewardPerSecond","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRewardStartTimestamp","inputs":[{"type":"uint256","name":"_rewardStartTimestamp","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalAllocPoint","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updatePool","inputs":[{"type":"uint256","name":"_pid","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"rewardDebt","internalType":"uint256"}],"name":"userInfo","inputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"address","name":"","internalType":"address"}]}]
Deployed ByteCode
0x608060405234801561001057600080fd5b50600436106101585760003560e01c8063715018a6116100c357806393f1a40b1161007c57806393f1a40b14610370578063f2fde38b1461039c578063f577988e146103c2578063f7c618c1146103df578063f861306e146103e7578063fcb685bc146103ef57610158565b8063715018a614610301578063823c27ff146103095780638b183040146103265780638bc1d8c0146103585780638da5cb5b146103605780638f10369a1461036857610158565b806348e43af41161011557806348e43af41461022857806351eb05a614610277578063630b5ba11461029457806364482f791461029c57806366da5815146102c757806369883b4e146102e457610158565b806307f6a6c21461015d578063081e3eda1461018a5780631526fe27146101a457806317caf6f1146101df57806327c2bca8146101e75780633279beab1461020b575b600080fd5b6101886004803603606081101561017357600080fd5b508035906020810135906040013515156103f7565b005b61019261058d565b60408051918252519081900360200190f35b6101c1600480360360208110156101ba57600080fd5b5035610593565b60408051938452602084019290925282820152519081900360600190f35b6101926105b3565b6101ef6105b9565b604080516001600160a01b039092168252519081900360200190f35b6101886004803603602081101561022157600080fd5b50356105dd565b6102546004803603604081101561023e57600080fd5b50803590602001356001600160a01b03166106f5565b604080516001600160a01b03909316835260208301919091528051918290030190f35b6101886004803603602081101561028d57600080fd5b503561097d565b610188610aea565b610188600480360360608110156102b257600080fd5b50803590602081013590604001351515610b24565b610188600480360360208110156102dd57600080fd5b5035610c0a565b610192600480360360208110156102fa57600080fd5b5035610caf565b610188610ccd565b6101886004803603602081101561031f57600080fd5b5035610d79565b6101886004803603606081101561033c57600080fd5b508035906001600160a01b036020820135169060400135610ee7565b6101926110f5565b6101ef6110fb565b61019261110a565b6101926004803603604081101561038657600080fd5b50803590602001356001600160a01b0316611110565b610188600480360360208110156103b257600080fd5b50356001600160a01b031661112d565b610188600480360360208110156103d857600080fd5b503561122f565b6101ef611313565b6101ef611337565b61019261135b565b6103ff611361565b6001600160a01b03166104106110fb565b6001600160a01b031614610459576040805162461bcd60e51b815260206004820181905260248201526000805160206118e7833981519152604482015290519081900360640190fd5b60008281526001602081905260409091200154156104b4576040805162461bcd60e51b8152602060048201526013602482015272506f6f6c20616c72656164792065786973747360681b604482015290519081900360640190fd5b80156104c2576104c2610aea565b600060065442116104d5576006546104d7565b425b6004549091506104e79085611365565b60045560408051606081018252858152602080820184815260008385018181528882526001808552868320955186559251858401555160029485015583549182018455929092527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace9091018590558151868152915185927fa6b36ea399c1eae2ba98a011138f78722b48f46ad93349269348ccc6e8f1cced92908290030190a250505050565b60025490565b600160208190526000918252604090912080549181015460029091015483565b60045481565b7f000000000000000000000000dccd6455ae04b03d785f12196b492b18129564bc81565b6105e5611361565b6001600160a01b03166105f66110fb565b6001600160a01b03161461063f576040805162461bcd60e51b815260206004820181905260248201526000805160206118e7833981519152604482015290519081900360640190fd5b6007544211610688576040805162461bcd60e51b815260206004820152601060248201526f14995dd85c99081b9bdd08195b99195960821b604482015290519081900360640190fd5b6106bc6001600160a01b037f000000000000000000000000dd73dea10abc2bff99c60882ec5b2b81bb1dc5b21633836113c8565b60408051828152905133917f2d4434bb59801e733e9ce3df40b0c5518861a5fcdeec1906e83e03c755872b42919081900360200190a250565b600080610700611858565b506000848152600160208181526040808420815160608101835281548152938101548484015260020154838201908152888552600383528185206001600160a01b03808a1687529352818520905182516393f1a40b60e01b8152600481018b90527f000000000000000000000000bc149c62efe8afc61728fc58b1b66a0661712e7685166024820152835195969295919492937f000000000000000000000000dccd6455ae04b03d785f12196b492b18129564bc909316926393f1a40b92604480840193829003018186803b1580156107d857600080fd5b505afa1580156107ec573d6000803e3d6000fd5b505050506040513d604081101561080257600080fd5b505160208501519091504211801561081957508015155b1561088557600061082e85602001514261141f565b9050600061086160045461085b88600001516108556005548761146b90919063ffffffff16565b9061146b565b906114c4565b90506108806108798461085b8464e8d4a5100061146b565b8590611365565b935050505b60007f000000000000000000000000bc149c62efe8afc61728fc58b1b66a0661712e766001600160a01b03166393f1a40b8a8a6040518363ffffffff1660e01b815260040180838152602001826001600160a01b0316815260200192505050604080518083038186803b1580156108fb57600080fd5b505afa15801561090f573d6000803e3d6000fd5b505050506040513d604081101561092557600080fd5b5051845490915060009061094c9061094664e8d4a5100061085b868961146b565b9061152b565b7f000000000000000000000000dd73dea10abc2bff99c60882ec5b2b81bb1dc5b29b909a5098505050505050505050565b600081815260016020819052604090912090810154421161099e5750610ae7565b60007f000000000000000000000000dccd6455ae04b03d785f12196b492b18129564bc6001600160a01b03166393f1a40b847f000000000000000000000000bc149c62efe8afc61728fc58b1b66a0661712e766040518363ffffffff1660e01b815260040180838152602001826001600160a01b0316815260200192505050604080518083038186803b158015610a3457600080fd5b505afa158015610a48573d6000803e3d6000fd5b505050506040513d6040811015610a5e57600080fd5b5051905080610a74575042600190910155610ae7565b6000610a8483600101544261141f565b90506000610aab60045461085b86600001546108556005548761146b90919063ffffffff16565b9050610ad483610ac08364e8d4a5100061146b565b81610ac757fe5b6002870154919004611365565b6002850155505042600190920191909155505b50565b60025460005b81811015610b2057610b1860028281548110610b0857fe5b906000526020600020015461097d565b600101610af0565b5050565b610b2c611361565b6001600160a01b0316610b3d6110fb565b6001600160a01b031614610b86576040805162461bcd60e51b815260206004820181905260248201526000805160206118e7833981519152604482015290519081900360640190fd5b8015610b9457610b94610aea565b600083815260016020526040902054600454610bbb918491610bb59161152b565b90611365565b6004556000838152600160209081526040918290208490558151848152915185927fc0cfd54d2de2b55f1e6e108d3ec53ff0a1abe6055401d32c61e9433b747ef9f892908290030190a2505050565b610c12611361565b6001600160a01b0316610c236110fb565b6001600160a01b031614610c6c576040805162461bcd60e51b815260206004820181905260248201526000805160206118e7833981519152604482015290519081900360640190fd5b610c74610aea565b60058190556040805182815290517f9981f93efb1f00e191e4911e94be7586e0643ea2948cd594baa6c3fe23ae654d9181900360200190a150565b60028181548110610cbc57fe5b600091825260209091200154905081565b610cd5611361565b6001600160a01b0316610ce66110fb565b6001600160a01b031614610d2f576040805162461bcd60e51b815260206004820181905260248201526000805160206118e7833981519152604482015290519081900360640190fd5b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b610d81611361565b6001600160a01b0316610d926110fb565b6001600160a01b031614610ddb576040805162461bcd60e51b815260206004820181905260248201526000805160206118e7833981519152604482015290519081900360640190fd5b6006544210610e26576040805162461bcd60e51b815260206004820152601260248201527114995dd85c99081a185cc81cdd185c9d195960721b604482015290519081900360640190fd5b804210610e645760405162461bcd60e51b815260040180806020018281038252602e8152602001806119ae602e913960400191505060405180910390fd5b8060075411610ea45760405162461bcd60e51b815260040180806020018281038252602b81526020018061192f602b913960400191505060405180910390fd5b6006819055610eb1610aea565b6040805182815290517f333fbfe183fcd0d2b5c40ad00a9aea8f2ef8d7187c5e0d7c76f40b060286b87c9181900360200190a150565b336001600160a01b037f000000000000000000000000bc149c62efe8afc61728fc58b1b66a0661712e761614610f4e5760405162461bcd60e51b81526004018080602001828103825260288152602001806119076028913960400191505060405180910390fd5b610f578361097d565b6000838152600160209081526040808320600383528184206001600160a01b038088168087529190945282852083516393f1a40b60e01b8152600481018a9052602481019290925283519295909490937f000000000000000000000000bc149c62efe8afc61728fc58b1b66a0661712e76909116926393f1a40b926044808201939291829003018186803b158015610fee57600080fd5b505afa158015611002573d6000803e3d6000fd5b505050506040513d604081101561101857600080fd5b50519050600081156110825761104c836000015461094664e8d4a5100061085b88600201548761146b90919063ffffffff16565b90506110826001600160a01b037f000000000000000000000000dd73dea10abc2bff99c60882ec5b2b81bb1dc5b21687836113c8565b6110a264e8d4a5100061085b86600201548861146b90919063ffffffff16565b8355604080518681526020810183905281516001600160a01b038916928a927fa5d23287037261ad22a2e3d11de58347493136c30d30cb40e1d1f746b69962b5929081900390910190a350505050505050565b60075481565b6000546001600160a01b031690565b60055481565b600360209081526000928352604080842090915290825290205481565b611135611361565b6001600160a01b03166111466110fb565b6001600160a01b03161461118f576040805162461bcd60e51b815260206004820181905260248201526000805160206118e7833981519152604482015290519081900360640190fd5b6001600160a01b0381166111d45760405162461bcd60e51b815260040180806020018281038252602681526020018061187a6026913960400191505060405180910390fd5b600080546040516001600160a01b03808516939216917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3600080546001600160a01b0319166001600160a01b0392909216919091179055565b611237611361565b6001600160a01b03166112486110fb565b6001600160a01b031614611291576040805162461bcd60e51b815260206004820181905260248201526000805160206118e7833981519152604482015290519081900360640190fd5b60065481116112d15760405162461bcd60e51b815260040180806020018281038252602a815260200180611984602a913960400191505060405180910390fd5b60078190556112de610aea565b6040805182815290517ec120dc5e082694674c24ab7b88010f8ec91c9cfa404fc7b0418d5d267468ec9181900360200190a150565b7f000000000000000000000000dd73dea10abc2bff99c60882ec5b2b81bb1dc5b281565b7f000000000000000000000000bc149c62efe8afc61728fc58b1b66a0661712e7681565b60065481565b3390565b6000828201838110156113bf576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fd5b90505b92915050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b17905261141a908490611588565b505050565b6000600654421015611433575060006113c2565b600754821161144d57611446828461152b565b90506113c2565b600754831061145e575060006113c2565b6007546113bf908461152b565b60008261147a575060006113c2565b8282028284828161148757fe5b04146113bf5760405162461bcd60e51b81526004018080602001828103825260218152602001806118c66021913960400191505060405180910390fd5b600080821161151a576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b81838161152357fe5b049392505050565b600082821115611582576040805162461bcd60e51b815260206004820152601e60248201527f536166654d6174683a207375627472616374696f6e206f766572666c6f770000604482015290519081900360640190fd5b50900390565b60606115dd826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166116399092919063ffffffff16565b80519091501561141a578080602001905160208110156115fc57600080fd5b505161141a5760405162461bcd60e51b815260040180806020018281038252602a81526020018061195a602a913960400191505060405180910390fd5b60606116488484600085611652565b90505b9392505050565b6060824710156116935760405162461bcd60e51b81526004018080602001828103825260268152602001806118a06026913960400191505060405180910390fd5b61169c856117ae565b6116ed576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b60006060866001600160a01b031685876040518082805190602001908083835b6020831061172c5780518252601f19909201916020918201910161170d565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d806000811461178e576040519150601f19603f3d011682016040523d82523d6000602084013e611793565b606091505b50915091506117a38282866117b4565b979650505050505050565b3b151590565b606083156117c357508161164b565b8251156117d35782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561181d578181015183820152602001611805565b50505050905090810190601f16801561184a5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b6040518060600160405280600081526020016000815260200160008152509056fe4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061646472657373416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f774f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65724f6e6c79204372616674736d616e56322063616e2063616c6c20746869732066756e6374696f6e2e4e65772073746172742074696d65206d757374206265206265666f726520656e642074696d657374616d705361666545524332303a204552433230206f7065726174696f6e20646964206e6f7420737563636565644e657720656e642074696d65206d7573742062652061667465722073746172742074696d657374616d704e65772073746172742074696d65206d7573742062652061667465722063757272656e742074696d657374616d70a2646970667358221220cd76a1ee8c912404c8065e18042d8ba8799e548c4b0af4f2490ba3370febb1ff64736f6c634300060c0033