More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 190 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Withdraw | 26984412 | 768 days ago | IN | 0 xDAI | 0.00022318 | ||||
Exit | 25427817 | 862 days ago | IN | 0 xDAI | 0.00025137 | ||||
Withdraw | 25074430 | 884 days ago | IN | 0 xDAI | 0.0001237 | ||||
Claim | 25074426 | 884 days ago | IN | 0 xDAI | 0.0002115 | ||||
Exit | 24913062 | 896 days ago | IN | 0 xDAI | 0.00018652 | ||||
Withdraw | 24891178 | 898 days ago | IN | 0 xDAI | 0.0001237 | ||||
Claim | 24891118 | 898 days ago | IN | 0 xDAI | 0.00021146 | ||||
Exit | 24887276 | 898 days ago | IN | 0 xDAI | 0.00025137 | ||||
Exit | 24878448 | 899 days ago | IN | 0 xDAI | 0.00025137 | ||||
Exit | 24848051 | 901 days ago | IN | 0 xDAI | 0.00020007 | ||||
Exit | 24847359 | 901 days ago | IN | 0 xDAI | 0.00025137 | ||||
Exit | 24846828 | 901 days ago | IN | 0 xDAI | 0.00028317 | ||||
Exit | 24837819 | 902 days ago | IN | 0 xDAI | 0.00025137 | ||||
Exit | 24834108 | 902 days ago | IN | 0 xDAI | 0.00022572 | ||||
Exit | 24819741 | 904 days ago | IN | 0 xDAI | 0.00022568 | ||||
Exit | 24816756 | 904 days ago | IN | 0 xDAI | 0.00025137 | ||||
Exit | 24814973 | 904 days ago | IN | 0 xDAI | 0.00025135 | ||||
Exit | 24796826 | 905 days ago | IN | 0 xDAI | 0.00025137 | ||||
Exit | 24794750 | 905 days ago | IN | 0 xDAI | 0.00027702 | ||||
Exit | 24791144 | 906 days ago | IN | 0 xDAI | 0.00025137 | ||||
Exit | 24788401 | 906 days ago | IN | 0 xDAI | 0.00025137 | ||||
Exit | 24785978 | 906 days ago | IN | 0 xDAI | 0.0003251 | ||||
Exit | 24782084 | 907 days ago | IN | 0 xDAI | 0.00025137 | ||||
Exit | 24779490 | 907 days ago | IN | 0 xDAI | 0.00022989 | ||||
Exit | 24772896 | 907 days ago | IN | 0 xDAI | 0.00022572 |
Latest 1 internal transaction
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
24392457 | 937 days ago | Contract Creation | 0 xDAI |
Loading...
Loading
Minimal Proxy Contract for 0x1c72cb809cdf66e2cf0f0f46aaa01d2f936cc695
Contract Name:
ERC20StakingRewardsDistribution
Compiler Version
v0.8.4+commit.c7e474f2
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity)
/** *Submitted for verification at gnosisscan.io on 2022-08-04 */ // Sources flattened with hardhat v2.3.0 https://hardhat.org // File @openzeppelin/contracts/utils/math/[email protected] pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow, so we distribute return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); } } // File @openzeppelin/contracts/token/ERC20/[email protected] pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // File @openzeppelin/contracts/utils/[email protected] pragma solidity ^0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // 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/utils/[email protected] pragma solidity ^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 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) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // File contracts/interfaces/IERC20StakingRewardsDistributionFactory.sol pragma solidity >=0.8.0; interface IERC20StakingRewardsDistributionFactory { function createDistribution( address[] calldata _rewardTokenAddresses, address _stakableTokenAddress, uint256[] calldata _rewardAmounts, uint64 _startingTimestamp, uint64 _endingTimestamp, bool _locked, uint256 _stakingCap ) external; function getDistributionsAmount() external view returns (uint256); function implementation() external view returns (address); function upgradeTo(address newImplementation) external; function distributions(uint256 _index) external returns (address); function stakingPaused() external returns (bool); } // File contracts/ERC20StakingRewardsDistribution.sol // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.4; /** * Errors codes: * * SRD01: invalid starting timestamp * SRD02: invalid time duration * SRD03: inconsistent reward token/amount * SRD04: 0 address as reward token * SRD05: no reward * SRD06: no funding * SRD07: 0 address as stakable token * SRD08: distribution already started * SRD09: tried to stake nothing * SRD10: staking cap hit * SRD11: tried to withdraw nothing * SRD12: funds locked until the distribution ends * SRD13: withdrawn amount greater than current stake * SRD14: inconsistent claimed amounts * SRD15: insufficient claimable amount * SRD16: 0 address owner * SRD17: caller not owner * SRD18: already initialized * SRD19: invalid state for cancel to be called * SRD20: not started * SRD21: already ended * SRD22: no rewards are recoverable * SRD23: no rewards are claimable while claiming all * SRD24: no rewards are claimable while manually claiming an arbitrary amount of rewards * SRD25: staking is currently paused */ contract ERC20StakingRewardsDistribution { using SafeERC20 for IERC20; uint224 constant MULTIPLIER = 2**112; struct Reward { address token; uint256 amount; uint256 perStakedToken; uint256 recoverableSeconds; uint256 claimed; } struct StakerRewardInfo { uint256 consolidatedPerStakedToken; uint256 earned; uint256 claimed; } struct Staker { uint256 stake; mapping(address => StakerRewardInfo) rewardInfo; } Reward[] public rewards; mapping(address => Staker) public stakers; uint64 public startingTimestamp; uint64 public endingTimestamp; uint64 public secondsDuration; uint64 public lastConsolidationTimestamp; IERC20 public stakableToken; address public owner; address public factory; bool public locked; bool public canceled; bool public initialized; uint256 public totalStakedTokensAmount; uint256 public stakingCap; event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); event Initialized( address[] rewardsTokenAddresses, address stakableTokenAddress, uint256[] rewardsAmounts, uint64 startingTimestamp, uint64 endingTimestamp, bool locked, uint256 stakingCap ); event Canceled(); event Staked(address indexed staker, uint256 amount); event Withdrawn(address indexed withdrawer, uint256 amount); event Claimed(address indexed claimer, uint256[] amounts); event Recovered(uint256[] amounts); function initialize( address[] calldata _rewardTokenAddresses, address _stakableTokenAddress, uint256[] calldata _rewardAmounts, uint64 _startingTimestamp, uint64 _endingTimestamp, bool _locked, uint256 _stakingCap ) external onlyUninitialized { require(_startingTimestamp > block.timestamp, "SRD01"); require(_endingTimestamp > _startingTimestamp, "SRD02"); require(_rewardTokenAddresses.length == _rewardAmounts.length, "SRD03"); secondsDuration = _endingTimestamp - _startingTimestamp; // Initializing reward tokens and amounts for (uint32 _i = 0; _i < _rewardTokenAddresses.length; _i++) { address _rewardTokenAddress = _rewardTokenAddresses[_i]; uint256 _rewardAmount = _rewardAmounts[_i]; require(_rewardTokenAddress != address(0), "SRD04"); require(_rewardAmount > 0, "SRD05"); IERC20 _rewardToken = IERC20(_rewardTokenAddress); require( _rewardToken.balanceOf(address(this)) >= _rewardAmount, "SRD06" ); rewards.push( Reward({ token: _rewardTokenAddress, amount: _rewardAmount, perStakedToken: 0, recoverableSeconds: 0, claimed: 0 }) ); } require(_stakableTokenAddress != address(0), "SRD07"); stakableToken = IERC20(_stakableTokenAddress); owner = msg.sender; factory = msg.sender; startingTimestamp = _startingTimestamp; endingTimestamp = _endingTimestamp; lastConsolidationTimestamp = _startingTimestamp; locked = _locked; stakingCap = _stakingCap; initialized = true; canceled = false; emit Initialized( _rewardTokenAddresses, _stakableTokenAddress, _rewardAmounts, _startingTimestamp, _endingTimestamp, _locked, _stakingCap ); } function cancel() external onlyOwner { require(initialized && !canceled, "SRD19"); require(block.timestamp < startingTimestamp, "SRD08"); for (uint256 _i; _i < rewards.length; _i++) { Reward storage _reward = rewards[_i]; IERC20(_reward.token).safeTransfer( owner, IERC20(_reward.token).balanceOf(address(this)) ); } canceled = true; emit Canceled(); } function recoverUnassignedRewards() external onlyStarted { consolidateReward(); uint256[] memory _recoveredUnassignedRewards = new uint256[](rewards.length); bool _atLeastOneNonZeroRecovery = false; for (uint256 _i; _i < rewards.length; _i++) { Reward storage _reward = rewards[_i]; // recoverable rewards are going to be recovered in this tx (if it does not revert), // so we add them to the claimed rewards right now _reward.claimed += ((_reward.recoverableSeconds * _reward.amount) / (uint256(secondsDuration) * MULTIPLIER)); delete _reward.recoverableSeconds; uint256 _recoverableRewards = IERC20(_reward.token).balanceOf(address(this)) - (_reward.amount - _reward.claimed); if (!_atLeastOneNonZeroRecovery && _recoverableRewards > 0) _atLeastOneNonZeroRecovery = true; _recoveredUnassignedRewards[_i] = _recoverableRewards; IERC20(_reward.token).safeTransfer(owner, _recoverableRewards); } require(_atLeastOneNonZeroRecovery, "SRD22"); emit Recovered(_recoveredUnassignedRewards); } function stake(uint256 _amount) external onlyRunning { require( !IERC20StakingRewardsDistributionFactory(factory).stakingPaused(), "SRD25" ); require(_amount > 0, "SRD09"); if (stakingCap > 0) { require(totalStakedTokensAmount + _amount <= stakingCap, "SRD10"); } consolidateReward(); Staker storage _staker = stakers[msg.sender]; _staker.stake += _amount; totalStakedTokensAmount += _amount; stakableToken.safeTransferFrom(msg.sender, address(this), _amount); emit Staked(msg.sender, _amount); } function withdraw(uint256 _amount) public onlyStarted { require(_amount > 0, "SRD11"); if (locked) { require(block.timestamp > endingTimestamp, "SRD12"); } consolidateReward(); Staker storage _staker = stakers[msg.sender]; require(_staker.stake >= _amount, "SRD13"); _staker.stake -= _amount; totalStakedTokensAmount -= _amount; stakableToken.safeTransfer(msg.sender, _amount); emit Withdrawn(msg.sender, _amount); } function claim(uint256[] memory _amounts, address _recipient) external onlyStarted { require(_amounts.length == rewards.length, "SRD14"); consolidateReward(); Staker storage _staker = stakers[msg.sender]; uint256[] memory _claimedRewards = new uint256[](rewards.length); bool _atLeastOneNonZeroClaim = false; for (uint256 _i; _i < rewards.length; _i++) { Reward storage _reward = rewards[_i]; StakerRewardInfo storage _stakerRewardInfo = _staker.rewardInfo[_reward.token]; uint256 _claimableReward = _stakerRewardInfo.earned - _stakerRewardInfo.claimed; uint256 _wantedAmount = _amounts[_i]; require(_claimableReward >= _wantedAmount, "SRD15"); if (!_atLeastOneNonZeroClaim && _wantedAmount > 0) _atLeastOneNonZeroClaim = true; _stakerRewardInfo.claimed += _wantedAmount; _reward.claimed += _wantedAmount; IERC20(_reward.token).safeTransfer(_recipient, _wantedAmount); _claimedRewards[_i] = _wantedAmount; } require(_atLeastOneNonZeroClaim, "SRD24"); emit Claimed(msg.sender, _claimedRewards); } function claimAll(address _recipient) public onlyStarted { consolidateReward(); Staker storage _staker = stakers[msg.sender]; uint256[] memory _claimedRewards = new uint256[](rewards.length); bool _atLeastOneNonZeroClaim = false; for (uint256 _i; _i < rewards.length; _i++) { Reward storage _reward = rewards[_i]; StakerRewardInfo storage _stakerRewardInfo = _staker.rewardInfo[_reward.token]; uint256 _claimableReward = _stakerRewardInfo.earned - _stakerRewardInfo.claimed; if (!_atLeastOneNonZeroClaim && _claimableReward > 0) _atLeastOneNonZeroClaim = true; _stakerRewardInfo.claimed += _claimableReward; _reward.claimed += _claimableReward; IERC20(_reward.token).safeTransfer(_recipient, _claimableReward); _claimedRewards[_i] = _claimableReward; } require(_atLeastOneNonZeroClaim, "SRD23"); emit Claimed(msg.sender, _claimedRewards); } function exit(address _recipient) external onlyStarted { claimAll(_recipient); withdraw(stakers[msg.sender].stake); } function consolidateReward() private { uint64 _consolidationTimestamp = uint64(Math.min(block.timestamp, endingTimestamp)); uint256 _lastPeriodDuration = uint256(_consolidationTimestamp - lastConsolidationTimestamp); Staker storage _staker = stakers[msg.sender]; for (uint256 _i; _i < rewards.length; _i++) { Reward storage _reward = rewards[_i]; StakerRewardInfo storage _stakerRewardInfo = _staker.rewardInfo[_reward.token]; if (_lastPeriodDuration > 0) { if (totalStakedTokensAmount == 0) { _reward.recoverableSeconds += _lastPeriodDuration * MULTIPLIER; // no need to update the reward per staked token since in this period // there have been no staked tokens, so no reward has been given out to stakers } else { _reward.perStakedToken += ((_lastPeriodDuration * _reward.amount * MULTIPLIER) / (totalStakedTokensAmount * secondsDuration)); } } uint256 _rewardSinceLastConsolidation = (_staker.stake * (_reward.perStakedToken - _stakerRewardInfo.consolidatedPerStakedToken)) / MULTIPLIER; if (_rewardSinceLastConsolidation > 0) { _stakerRewardInfo.earned += _rewardSinceLastConsolidation; } _stakerRewardInfo.consolidatedPerStakedToken = _reward .perStakedToken; } lastConsolidationTimestamp = _consolidationTimestamp; } function claimableRewards(address _account) public view returns (uint256[] memory) { uint256[] memory _outstandingRewards = new uint256[](rewards.length); if (!initialized || block.timestamp < startingTimestamp) { for (uint256 _i; _i < rewards.length; _i++) { _outstandingRewards[_i] = 0; } return _outstandingRewards; } Staker storage _staker = stakers[_account]; uint64 _consolidationTimestamp = uint64(Math.min(block.timestamp, endingTimestamp)); uint256 _lastPeriodDuration = uint256(_consolidationTimestamp - lastConsolidationTimestamp); for (uint256 _i; _i < rewards.length; _i++) { Reward storage _reward = rewards[_i]; StakerRewardInfo storage _stakerRewardInfo = _staker.rewardInfo[_reward.token]; uint256 _localRewardPerStakedToken = _reward.perStakedToken; if (_lastPeriodDuration > 0 && totalStakedTokensAmount > 0) { _localRewardPerStakedToken += ((_lastPeriodDuration * _reward.amount * MULTIPLIER) / (totalStakedTokensAmount * secondsDuration)); } uint256 _rewardSinceLastConsolidation = (_staker.stake * (_localRewardPerStakedToken - _stakerRewardInfo.consolidatedPerStakedToken)) / MULTIPLIER; _outstandingRewards[_i] = _rewardSinceLastConsolidation + (_stakerRewardInfo.earned - _stakerRewardInfo.claimed); } return _outstandingRewards; } function getRewardTokens() external view returns (address[] memory) { address[] memory _rewardTokens = new address[](rewards.length); for (uint256 _i = 0; _i < rewards.length; _i++) { _rewardTokens[_i] = rewards[_i].token; } return _rewardTokens; } function rewardAmount(address _rewardToken) external view returns (uint256) { for (uint256 _i = 0; _i < rewards.length; _i++) { Reward storage _reward = rewards[_i]; if (_rewardToken == _reward.token) return _reward.amount; } return 0; } function stakedTokensOf(address _staker) external view returns (uint256) { return stakers[_staker].stake; } function earnedRewardsOf(address _staker) external view returns (uint256[] memory) { Staker storage _stakerFromStorage = stakers[_staker]; uint256[] memory _earnedRewards = new uint256[](rewards.length); for (uint256 _i; _i < rewards.length; _i++) { _earnedRewards[_i] = _stakerFromStorage.rewardInfo[ rewards[_i].token ] .earned; } return _earnedRewards; } function recoverableUnassignedReward(address _rewardToken) external view returns (uint256) { for (uint256 _i = 0; _i < rewards.length; _i++) { Reward storage _reward = rewards[_i]; if (_reward.token == _rewardToken) { uint256 _nonRequiredFunds = _reward.claimed + ((_reward.recoverableSeconds * _reward.amount) / (uint256(secondsDuration) * MULTIPLIER)); return IERC20(_reward.token).balanceOf(address(this)) - (_reward.amount - _nonRequiredFunds); } } return 0; } function getClaimedRewards(address _claimer) external view returns (uint256[] memory) { Staker storage _staker = stakers[_claimer]; uint256[] memory _claimedRewards = new uint256[](rewards.length); for (uint256 _i = 0; _i < rewards.length; _i++) { Reward storage _reward = rewards[_i]; _claimedRewards[_i] = _staker.rewardInfo[_reward.token].claimed; } return _claimedRewards; } function renounceOwnership() public onlyOwner { owner = address(0); emit OwnershipTransferred(owner, address(0)); } function transferOwnership(address _newOwner) public onlyOwner { require(_newOwner != address(0), "SRD16"); emit OwnershipTransferred(owner, _newOwner); owner = _newOwner; } modifier onlyOwner() { require(owner == msg.sender, "SRD17"); _; } modifier onlyUninitialized() { require(!initialized, "SRD18"); _; } modifier onlyStarted() { require( initialized && !canceled && block.timestamp >= startingTimestamp, "SRD20" ); _; } modifier onlyRunning() { require( initialized && !canceled && block.timestamp >= startingTimestamp && block.timestamp <= endingTimestamp, "SRD21" ); _; } }
Contract ABI
API[{"anonymous":false,"inputs":[],"name":"Canceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"claimer","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"rewardsTokenAddresses","type":"address[]"},{"indexed":false,"internalType":"address","name":"stakableTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"rewardsAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint64","name":"startingTimestamp","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"endingTimestamp","type":"uint64"},{"indexed":false,"internalType":"bool","name":"locked","type":"bool"},{"indexed":false,"internalType":"uint256","name":"stakingCap","type":"uint256"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"Recovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"withdrawer","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"cancel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"canceled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"claimAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"claimableRewards","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"earnedRewardsOf","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"endingTimestamp","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_claimer","type":"address"}],"name":"getClaimedRewards","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewardTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_rewardTokenAddresses","type":"address[]"},{"internalType":"address","name":"_stakableTokenAddress","type":"address"},{"internalType":"uint256[]","name":"_rewardAmounts","type":"uint256[]"},{"internalType":"uint64","name":"_startingTimestamp","type":"uint64"},{"internalType":"uint64","name":"_endingTimestamp","type":"uint64"},{"internalType":"bool","name":"_locked","type":"bool"},{"internalType":"uint256","name":"_stakingCap","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastConsolidationTimestamp","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"locked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"recoverUnassignedRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardToken","type":"address"}],"name":"recoverableUnassignedReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_rewardToken","type":"address"}],"name":"rewardAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewards","outputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"perStakedToken","type":"uint256"},{"internalType":"uint256","name":"recoverableSeconds","type":"uint256"},{"internalType":"uint256","name":"claimed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"secondsDuration","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakableToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_staker","type":"address"}],"name":"stakedTokensOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakers","outputs":[{"internalType":"uint256","name":"stake","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stakingCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startingTimestamp","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalStakedTokensAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Loading...
Loading
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.