Contract
0xbec923f9038f245f90f0bc8ad57ac80ec556f72e
1
Contract Overview
Balance:
0 GLMR
GLMR Value:
$0.00
My Name Tag:
Not Available, login to update
Txn Hash | Method |
Block
|
From
|
To
|
Value | [Txn Fee] | |||
---|---|---|---|---|---|---|---|---|---|
0xd493e056b03b626cc946b62f1abcb28aaffdb3d8d428688a2b8456a883d195ff | 0x60806040 | 1012187 | 389 days 6 hrs ago | Moonwell Artemis: Deployer | IN | Create: StakedWell | 0 GLMR | 0.353955706369 |
[ Download CSV Export ]
Contract Name:
StakedWell
Compiler Version
v0.6.12+commit.27d51765
Optimization Enabled:
Yes with 999999 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {SafeMath} from './libraries/SafeMath.sol'; import {DistributionTypes} from './libraries/DistributionTypes.sol'; import {IDistributionManager} from './interfaces/IDistributionManager.sol'; import {IERC20} from './interfaces/IERC20.sol'; /** * @title DistributionManager * @notice Accounting contract to manage multiple staking distributions * @author Moonwell **/ contract DistributionManager is IDistributionManager { using SafeMath for uint256; struct AssetData { uint128 emissionPerSecond; uint128 lastUpdateTimestamp; uint256 index; mapping(address => uint256) users; } uint256 public DISTRIBUTION_END; address public EMISSION_MANAGER; uint8 public constant PRECISION = 18; mapping(address => AssetData) public assets; event AssetConfigUpdated(address indexed asset, uint256 emission); event AssetIndexUpdated(address indexed asset, uint256 index); event UserIndexUpdated(address indexed user, address indexed asset, uint256 index); function __DistributionManager_init_unchained(address emissionManager, uint256 distributionDuration) internal { require(emissionManager != address(0), 'ZERO_ADDRESS'); DISTRIBUTION_END = block.timestamp.add(distributionDuration); EMISSION_MANAGER = emissionManager; } /** * @dev Configures the distribution of rewards for an asset. This method is useful because it automatically * computes the amount of the asset that is staked. **/ function configureAsset(uint128 emissionsPerSecond, IERC20 underlyingAsset) external override { require(msg.sender == EMISSION_MANAGER, 'ONLY_EMISSION_MANAGER'); // Grab the balance of the underlying asset. uint256 totalStaked = underlyingAsset.balanceOf(address(this)); // Pass data through to the configure assets function. _configureAssetInternal(emissionsPerSecond, totalStaked, address(underlyingAsset)); } /** * @dev Configures the distribution of rewards for a list of assets * @param assetsConfigInput The list of configurations to apply **/ function configureAssets(DistributionTypes.AssetConfigInput[] calldata assetsConfigInput) external override { require(msg.sender == EMISSION_MANAGER, 'ONLY_EMISSION_MANAGER'); for (uint256 i = 0; i < assetsConfigInput.length; ++i) { _configureAssetInternal( assetsConfigInput[i].emissionPerSecond, assetsConfigInput[i].totalStaked, assetsConfigInput[i].underlyingAsset ); } } function _configureAssetInternal(uint128 emissionsPerSecond, uint256 totalStaked, address underlyingAsset) internal { AssetData storage assetConfig = assets[underlyingAsset]; _updateAssetStateInternal( underlyingAsset, assetConfig, totalStaked ); assetConfig.emissionPerSecond = emissionsPerSecond; emit AssetConfigUpdated( underlyingAsset, emissionsPerSecond ); } /** * @dev Updates the state of one distribution, mainly rewards index and timestamp * @param underlyingAsset The address used as key in the distribution, for example stkMFAM or the mTokens addresses on Moonwell * @param assetConfig Storage pointer to the distribution's config * @param totalStaked Current total of staked assets for this distribution * @return The new distribution index **/ function _updateAssetStateInternal( address underlyingAsset, AssetData storage assetConfig, uint256 totalStaked ) internal returns (uint256) { uint256 oldIndex = assetConfig.index; uint128 lastUpdateTimestamp = assetConfig.lastUpdateTimestamp; if (block.timestamp == lastUpdateTimestamp) { return oldIndex; } uint256 newIndex = _getAssetIndex( oldIndex, assetConfig.emissionPerSecond, lastUpdateTimestamp, totalStaked ); if (newIndex != oldIndex) { assetConfig.index = newIndex; emit AssetIndexUpdated(underlyingAsset, newIndex); } assetConfig.lastUpdateTimestamp = uint128(block.timestamp); return newIndex; } /** * @dev Updates the state of an user in a distribution * @param user The user's address * @param asset The address of the reference asset of the distribution * @param stakedByUser Amount of tokens staked by the user in the distribution at the moment * @param totalStaked Total tokens staked in the distribution * @return The accrued rewards for the user until the moment **/ function _updateUserAssetInternal( address user, address asset, uint256 stakedByUser, uint256 totalStaked ) internal returns (uint256) { AssetData storage assetData = assets[asset]; uint256 userIndex = assetData.users[user]; uint256 accruedRewards = 0; uint256 newIndex = _updateAssetStateInternal(asset, assetData, totalStaked); if (userIndex != newIndex) { if (stakedByUser != 0) { accruedRewards = _getRewards(stakedByUser, newIndex, userIndex); } assetData.users[user] = newIndex; emit UserIndexUpdated(user, asset, newIndex); } return accruedRewards; } /** * @dev Used by "frontend" stake contracts to update the data of an user when claiming rewards from there * @param user The address of the user * @param stakes List of structs of the user data related with his stake * @return The accrued rewards for the user until the moment **/ function _claimRewards(address user, DistributionTypes.UserStakeInput[] memory stakes) internal returns (uint256) { uint256 accruedRewards = 0; for (uint256 i = 0; i < stakes.length; ++i) { accruedRewards = accruedRewards.add( _updateUserAssetInternal( user, stakes[i].underlyingAsset, stakes[i].stakedByUser, stakes[i].totalStaked ) ); } return accruedRewards; } /** * @dev Return the accrued rewards for an user over a list of distribution * @param user The address of the user * @param stakes List of structs of the user data related with his stake * @return The accrued rewards for the user until the moment **/ function _getUnclaimedRewards(address user, DistributionTypes.UserStakeInput[] memory stakes) internal view returns (uint256) { uint256 accruedRewards = 0; for (uint256 i = 0; i < stakes.length; ++i) { AssetData storage assetConfig = assets[stakes[i].underlyingAsset]; uint256 assetIndex = _getAssetIndex( assetConfig.index, assetConfig.emissionPerSecond, assetConfig.lastUpdateTimestamp, stakes[i].totalStaked ); accruedRewards = accruedRewards.add( _getRewards(stakes[i].stakedByUser, assetIndex, assetConfig.users[user]) ); } return accruedRewards; } /** * @dev Internal function for the calculation of user's rewards on a distribution * @param principalUserBalance Amount staked by the user on a distribution * @param reserveIndex Current index of the distribution * @param userIndex Index stored for the user, representation his staking moment * @return The rewards **/ function _getRewards( uint256 principalUserBalance, uint256 reserveIndex, uint256 userIndex ) internal pure returns (uint256) { return principalUserBalance.mul(reserveIndex.sub(userIndex)).div(1e18); } /** * @dev Calculates the next value of an specific distribution index, with validations * @param currentIndex Current index of the distribution * @param emissionPerSecond Representing the total rewards distributed per second per asset unit, on the distribution * @param lastUpdateTimestamp Last moment this distribution was updated * @param totalBalance of tokens considered for the distribution * @return The new index. **/ function _getAssetIndex( uint256 currentIndex, uint256 emissionPerSecond, uint128 lastUpdateTimestamp, uint256 totalBalance ) internal view returns (uint256) { if ( emissionPerSecond == 0 || totalBalance == 0 || lastUpdateTimestamp == block.timestamp || lastUpdateTimestamp >= DISTRIBUTION_END ) { return currentIndex; } uint256 currentTimestamp = block.timestamp > DISTRIBUTION_END ? DISTRIBUTION_END : block.timestamp; uint256 timeDelta = currentTimestamp.sub(lastUpdateTimestamp); return emissionPerSecond.mul(timeDelta).mul(1e18).div(totalBalance).add( currentIndex ); } /** * @dev Returns the data of an user on a distribution * @param user Address of the user * @param asset The address of the reference asset of the distribution * @return The new index **/ function getUserAssetData(address user, address asset) public view returns (uint256) { return assets[asset].users[user]; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; /** * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts * 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, 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) { return sub(a, b, 'SafeMath: subtraction overflow'); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * 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); uint256 c = a - b; return c; } /** * @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) { // 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 0; } uint256 c = a * b; require(c / a == b, 'SafeMath: multiplication overflow'); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts 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) { return div(a, b, 'SafeMath: division by zero'); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message 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, string memory errorMessage ) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts 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) { return mod(a, b, 'SafeMath: modulo by zero'); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message 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, string memory errorMessage ) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; library DistributionTypes { struct AssetConfigInput { uint128 emissionPerSecond; uint256 totalStaked; address underlyingAsset; } struct UserStakeInput { address underlyingAsset; uint256 stakedByUser; uint256 totalStaked; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {IERC20} from "./IERC20.sol"; import {DistributionTypes} from "../libraries/DistributionTypes.sol"; interface IDistributionManager { function configureAsset(uint128 emissionPerSecond, IERC20 underlyingAsset) external; function configureAssets(DistributionTypes.AssetConfigInput[] calldata assetsConfigInput) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; /** * @dev Interface of the ERC20 standard as defined in the EIP. * From https://github.com/OpenZeppelin/openzeppelin-contracts */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address sender, address recipient, uint256 amount ) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {IERC20} from "./interfaces/IERC20.sol"; import {IStakedToken} from "./interfaces/IStakedToken.sol"; import {ITransferHook} from "./interfaces/ITransferHook.sol"; import {IEcosystemReserve} from "./interfaces/IEcosystemReserve.sol"; import {ERC20WithSnapshot} from "./libraries/ERC20WithSnapshot.sol"; import {SafeERC20} from "./libraries/SafeERC20.sol"; import {DistributionTypes} from "./libraries/DistributionTypes.sol"; import {Initializable} from "./utils/Initializable.sol"; import {DistributionManager} from "./DistributionManager.sol"; import {ReentrancyGuardUpgradeable} from "./OpenZeppelin/ReentrancyGuardUpgradeable.sol"; /** * @title StakedToken * @notice Contract to stake MFAM token, tokenize the position and get rewards, inheriting from a distribution manager contract * @author Moonwell **/ contract StakedToken is IStakedToken, ERC20WithSnapshot, Initializable, DistributionManager, ReentrancyGuardUpgradeable { using SafeERC20 for IERC20; IERC20 public STAKED_TOKEN; IERC20 public REWARD_TOKEN; uint256 public COOLDOWN_SECONDS; /// @notice Seconds available to redeem once the cooldown period is fullfilled uint256 public UNSTAKE_WINDOW; /// @notice Address to pull from the rewards, needs to have approved this contract address public REWARDS_VAULT; mapping(address => uint256) public stakerRewardsToClaim; mapping(address => uint256) public stakersCooldowns; event Staked(address indexed from, address indexed onBehalfOf, uint256 amount); event Redeem(address indexed from, address indexed to, uint256 amount); event RewardsAccrued(address user, uint256 amount); event RewardsClaimed(address indexed from, address indexed to, uint256 amount); event Cooldown(address indexed user); function __StakedToken_init( IERC20 stakedToken, IERC20 rewardToken, uint256 cooldownSeconds, uint256 unstakeWindow, address rewardsVault, address emissionManager, uint128 distributionDuration, string memory name, string memory symbol, uint8 decimals, address governance ) internal initializer { __ReentrancyGuard_init(); __ERC20_init_unchained(name, symbol, decimals); __DistributionManager_init_unchained(emissionManager, distributionDuration); __StakedToken_init_unchained( stakedToken, rewardToken, cooldownSeconds, unstakeWindow, rewardsVault, governance ); } function __StakedToken_init_unchained( IERC20 stakedToken, IERC20 rewardToken, uint256 cooldownSeconds, uint256 unstakeWindow, address rewardsVault, address governance ) internal { STAKED_TOKEN = stakedToken; REWARD_TOKEN = rewardToken; COOLDOWN_SECONDS = cooldownSeconds; UNSTAKE_WINDOW = unstakeWindow; REWARDS_VAULT = rewardsVault; _setGovernance(ITransferHook(governance)); } function stake(address onBehalfOf, uint256 amount) external override nonReentrant { require(amount != 0, 'INVALID_ZERO_AMOUNT'); require(onBehalfOf != address(0), 'STAKE_ZERO_ADDRESS'); uint256 balanceOfUser = balanceOf(onBehalfOf); uint256 accruedRewards = _updateUserAssetInternal( onBehalfOf, address(this), balanceOfUser, totalSupply() ); if (accruedRewards != 0) { emit RewardsAccrued(onBehalfOf, accruedRewards); stakerRewardsToClaim[onBehalfOf] = stakerRewardsToClaim[onBehalfOf].add(accruedRewards); } stakersCooldowns[onBehalfOf] = getNextCooldownTimestamp(0, amount, onBehalfOf, balanceOfUser); _mint(onBehalfOf, amount); IERC20(STAKED_TOKEN).safeTransferFrom(msg.sender, address(this), amount); emit Staked(msg.sender, onBehalfOf, amount); } /** * @dev Redeems staked tokens, and stop earning rewards * @param to Address to redeem to * @param amount Amount to redeem **/ function redeem(address to, uint256 amount) external override nonReentrant { require(amount != 0, 'INVALID_ZERO_AMOUNT'); require(to != address(0), 'REDEEM_ZERO_ADDRESS'); //solium-disable-next-line uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender]; require( block.timestamp > cooldownStartTimestamp.add(COOLDOWN_SECONDS), 'INSUFFICIENT_COOLDOWN' ); require( block.timestamp.sub(cooldownStartTimestamp.add(COOLDOWN_SECONDS)) <= UNSTAKE_WINDOW, 'UNSTAKE_WINDOW_FINISHED' ); uint256 balanceOfMessageSender = balanceOf(msg.sender); uint256 amountToRedeem = (amount > balanceOfMessageSender) ? balanceOfMessageSender : amount; _updateCurrentUnclaimedRewards(msg.sender, balanceOfMessageSender, true); _burn(msg.sender, amountToRedeem); if (balanceOfMessageSender.sub(amountToRedeem) == 0) { stakersCooldowns[msg.sender] = 0; } IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem); emit Redeem(msg.sender, to, amountToRedeem); } /** * @dev Activates the cooldown period to unstake * - It can't be called if the user is not staking **/ function cooldown() external override { require(balanceOf(msg.sender) != 0, "INVALID_BALANCE_ON_COOLDOWN"); //solium-disable-next-line stakersCooldowns[msg.sender] = block.timestamp; emit Cooldown(msg.sender); } /** * @dev Claims an `amount` of `REWARD_TOKEN` to the address `to` * @param to Address to stake for * @param amount Amount to stake **/ function claimRewards(address to, uint256 amount) external override nonReentrant { uint256 newTotalRewards = _updateCurrentUnclaimedRewards( msg.sender, balanceOf(msg.sender), false ); uint256 amountToClaim = (amount == type(uint256).max) ? newTotalRewards : amount; stakerRewardsToClaim[msg.sender] = newTotalRewards.sub(amountToClaim, "INVALID_AMOUNT"); IERC20(REWARD_TOKEN).safeTransferFrom(REWARDS_VAULT, to, amountToClaim); emit RewardsClaimed(msg.sender, to, amountToClaim); } /** * @dev Internal ERC20 _transfer of the tokenized staked tokens * @param from Address to transfer from * @param to Address to transfer to * @param amount Amount to transfer **/ function _transfer( address from, address to, uint256 amount ) internal override { uint256 balanceOfFrom = balanceOf(from); // Sender _updateCurrentUnclaimedRewards(from, balanceOfFrom, true); // Recipient if (from != to) { uint256 balanceOfTo = balanceOf(to); _updateCurrentUnclaimedRewards(to, balanceOfTo, true); uint256 previousSenderCooldown = stakersCooldowns[from]; stakersCooldowns[to] = getNextCooldownTimestamp(previousSenderCooldown, amount, to, balanceOfTo); // if cooldown was set and whole balance of sender was transferred - clear cooldown if (balanceOfFrom == amount && previousSenderCooldown != 0) { stakersCooldowns[from] = 0; } } super._transfer(from, to, amount); } /** * @dev Updates the user state related with his accrued rewards * @param user Address of the user * @param userBalance The current balance of the user * @param updateStorage Boolean flag used to update or not the stakerRewardsToClaim of the user * @return The unclaimed rewards that were added to the total accrued **/ function _updateCurrentUnclaimedRewards( address user, uint256 userBalance, bool updateStorage ) internal returns (uint256) { uint256 accruedRewards = _updateUserAssetInternal( user, address(this), userBalance, totalSupply() ); uint256 unclaimedRewards = stakerRewardsToClaim[user].add(accruedRewards); if (accruedRewards != 0) { if (updateStorage) { stakerRewardsToClaim[user] = unclaimedRewards; } emit RewardsAccrued(user, accruedRewards); } return unclaimedRewards; } /** * @dev Calculates the how is gonna be a new cooldown timestamp depending on the sender/receiver situation * - If the timestamp of the sender is "better" or the timestamp of the recipient is 0, we take the one of the recipient * - Weighted average of from/to cooldown timestamps if: * # The sender doesn't have the cooldown activated (timestamp 0). * # The sender timestamp is expired * # The sender has a "worse" timestamp * - If the receiver's cooldown timestamp expired (too old), the next is 0 * @param fromCooldownTimestamp Cooldown timestamp of the sender * @param amountToReceive Amount * @param toAddress Address of the recipient * @param toBalance Current balance of the receiver * @return The new cooldown timestamp **/ function getNextCooldownTimestamp( uint256 fromCooldownTimestamp, uint256 amountToReceive, address toAddress, uint256 toBalance ) public returns (uint256) { uint256 toCooldownTimestamp = stakersCooldowns[toAddress]; if (toCooldownTimestamp == 0) { return 0; } uint256 minimalValidCooldownTimestamp = block.timestamp.sub(COOLDOWN_SECONDS).sub( UNSTAKE_WINDOW ); if (minimalValidCooldownTimestamp > toCooldownTimestamp) { toCooldownTimestamp = 0; } else { uint256 fromCooldownTimestampFinal = (minimalValidCooldownTimestamp > fromCooldownTimestamp) ? block.timestamp : fromCooldownTimestamp; if (fromCooldownTimestampFinal < toCooldownTimestamp) { return toCooldownTimestamp; } else { toCooldownTimestamp = ( amountToReceive.mul(fromCooldownTimestampFinal).add(toBalance.mul(toCooldownTimestamp)) ) .div(amountToReceive.add(toBalance)); } } stakersCooldowns[toAddress] = toCooldownTimestamp; return toCooldownTimestamp; } /** * @dev Return the total rewards pending to claim by an staker * @param staker The staker address * @return The rewards */ function getTotalRewardsBalance(address staker) external view returns (uint256) { DistributionTypes.UserStakeInput[] memory userStakeInputs = new DistributionTypes.UserStakeInput[](1); userStakeInputs[0] = DistributionTypes.UserStakeInput({ underlyingAsset: address(this), stakedByUser: balanceOf(staker), totalStaked: totalSupply() }); return stakerRewardsToClaim[staker].add(_getUnclaimedRewards(staker, userStakeInputs)); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; interface IStakedToken { function stake(address to, uint256 amount) external; function redeem(address to, uint256 amount) external; function cooldown() external; function claimRewards(address to, uint256 amount) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; interface ITransferHook { function onTransfer(address from, address to, uint256 amount) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import "../interfaces/IERC20.sol"; interface IEcosystemReserve { function approve(IERC20 token, address recipient, uint256 amount) external; function transfer(IERC20 token, address recipient, uint256 amount) external; function setFundsAdmin(address admin) external; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import "@openzeppelin/upgrades-core/contracts/Initializable.sol"; import {ERC20} from "../libraries/ERC20.sol"; import {ITransferHook} from "../interfaces/ITransferHook.sol"; /** * @title ERC20WithSnapshot * @notice ERC20 including snapshots of balances on transfer-related actions * @author Moonwell **/ contract ERC20WithSnapshot is ERC20 { /// @dev snapshot of a value on a specific block, used for balances struct Snapshot { uint128 blockNumber; uint128 value; } mapping (address => mapping (uint256 => Snapshot)) public _snapshots; mapping (address => uint256) public _countsSnapshots; /// @dev reference to the Moonwell governance contract to call (if initialized) on _beforeTokenTransfer /// !!! IMPORTANT The Moonwell governance is considered a trustable contract, being its responsibility /// to control all potential reentrancies by calling back the this contract ITransferHook public _governance; event SnapshotDone(address owner, uint128 oldValue, uint128 newValue); function _setGovernance(ITransferHook governance) internal virtual { _governance = governance; } /** * @dev Writes a snapshot for an owner of tokens * @param owner The owner of the tokens * @param oldValue The value before the operation that is gonna be executed after the snapshot * @param newValue The value after the operation */ function _writeSnapshot(address owner, uint128 oldValue, uint128 newValue) internal virtual { uint128 currentBlock = uint128(block.number); uint256 ownerCountOfSnapshots = _countsSnapshots[owner]; mapping (uint256 => Snapshot) storage snapshotsOwner = _snapshots[owner]; // Doing multiple operations in the same block if (ownerCountOfSnapshots != 0 && snapshotsOwner[ownerCountOfSnapshots.sub(1)].blockNumber == currentBlock) { snapshotsOwner[ownerCountOfSnapshots.sub(1)].value = newValue; } else { snapshotsOwner[ownerCountOfSnapshots] = Snapshot(currentBlock, newValue); _countsSnapshots[owner] = ownerCountOfSnapshots.add(1); } emit SnapshotDone(owner, oldValue, newValue); } /** * @dev Writes a snapshot before any operation involving transfer of value: _transfer, _mint and _burn * - On _transfer, it writes snapshots for both "from" and "to" * - On _mint, only for _to * - On _burn, only for _from * @param from the from address * @param to the to address * @param amount the amount to transfer */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal override { if (from == to) { return; } if (from != address(0)) { uint256 fromBalance = balanceOf(from); _writeSnapshot(from, uint128(fromBalance), uint128(fromBalance.sub(amount))); } if (to != address(0)) { uint256 toBalance = balanceOf(to); _writeSnapshot(to, uint128(toBalance), uint128(toBalance.add(amount))); } // caching the Moonwell governance address to avoid multiple state loads ITransferHook governance = _governance; if (governance != ITransferHook(0)) { governance.onTransfer(from, to, amount); } } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {IERC20} from "../interfaces/IERC20.sol"; import {SafeMath} from "../libraries/SafeMath.sol"; import {Address} from "../libraries/Address.sol"; /** * @title SafeERC20 * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts * 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)); } function safeApprove(IERC20 token, address spender, uint256 value) internal { 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 _callOptionalReturn(IERC20 token, bytes memory data) private { require(address(token).isContract(), "SafeERC20: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = address(token).call(data); require(success, "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"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.3.2 (proxy/utils/Initializable.sol) pragma solidity 0.6.12; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since a proxied contract can't have a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Modifier to protect an initializer function from being invoked twice. */ modifier initializer() { require(_initializing || !_initialized, "Initializable: contract is already initialized"); bool isTopLevelCall = !_initializing; if (isTopLevelCall) { _initializing = true; _initialized = true; } _; if (isTopLevelCall) { _initializing = false; } } }
pragma solidity 0.6.12; import "../utils/Initializable.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; function __ReentrancyGuard_init() internal initializer { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal initializer { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity >=0.4.24 <0.7.0; /** * @title Initializable * * @dev Helper contract to support initializer functions. To use it, replace * the constructor with a function that has the `initializer` modifier. * WARNING: Unlike constructors, initializer functions must be manually * invoked. This applies both to deploying an Initializable contract, as well * as extending an Initializable contract via inheritance. * WARNING: When used with inheritance, manual care must be taken to not invoke * a parent initializer twice, or ensure that all initializers are idempotent, * because this is not dealt with automatically as with constructors. */ contract Initializable { /** * @dev Indicates that the contract has been initialized. */ bool private initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private initializing; /** * @dev Modifier to use in the initializer function of a contract. */ modifier initializer() { require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized"); bool isTopLevelCall = !initializing; if (isTopLevelCall) { initializing = true; initialized = true; } _; if (isTopLevelCall) { initializing = false; } } /// @dev Returns true if and only if the function is running in the constructor function isConstructor() private view returns (bool) { // extcodesize checks the size of the code stored in an address, and // address returns the current address. Since the code is still not // deployed when running a constructor, any checks on its code size will // yield zero, making it an effective way to detect if a contract is // under construction or not. address self = address(this); uint256 cs; assembly { cs := extcodesize(self) } return cs == 0; } // Reserved storage space to allow for layout changes in the future. uint256[50] private ______gap; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {Context} from "./Context.sol"; import {IERC20} from "../interfaces/IERC20.sol"; import {IERC20Detailed} from "../interfaces/IERC20Detailed.sol"; import {SafeMath} from "./SafeMath.sol"; /** * @title ERC20 * @notice Basic ERC20 implementation * @author Moonwell **/ contract ERC20 is Context, IERC20, IERC20Detailed { using SafeMath for uint256; mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; function __ERC20_init_unchained(string memory name, string memory symbol, uint8 decimals) internal { _name = name; _symbol = symbol; _decimals = decimals; } /** * @return the name of the token **/ function name() public override view returns (string memory) { return _name; } /** * @return the symbol of the token **/ function symbol() public override view returns (string memory) { return _symbol; } /** * @return the decimals of the token **/ function decimals() public override view returns (uint8) { return _decimals; } /** * @return the total supply of the token **/ function totalSupply() public override view returns (uint256) { return _totalSupply; } /** * @return the balance of the token **/ function balanceOf(address account) public override view returns (uint256) { return _balances[account]; } /** * @dev executes a transfer of tokens from msg.sender to recipient * @param recipient the recipient of the tokens * @param amount the amount of tokens being transferred * @return true if the transfer succeeds, false otherwise **/ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev returns the allowance of spender on the tokens owned by owner * @param owner the owner of the tokens * @param spender the user allowed to spend the owner"s tokens * @return the amount of owner"s tokens spender is allowed to spend **/ function allowance(address owner, address spender) public virtual override view returns (uint256) { return _allowances[owner][spender]; } /** * @dev allows spender to spend the tokens owned by msg.sender * @param spender the user allowed to spend msg.sender tokens * @return true **/ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev executes a transfer of token from sender to recipient, if msg.sender is allowed to do so * @param sender the owner of the tokens * @param recipient the recipient of the tokens * @param amount the amount of tokens being transferred * @return true if the transfer succeeds, false otherwise **/ function transferFrom( address sender, address recipient, uint256 amount ) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve( sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, 'ERC20: transfer amount exceeds allowance') ); return true; } /** * @dev increases the allowance of spender to spend msg.sender tokens * @param spender the user allowed to spend on behalf of msg.sender * @param addedValue the amount being added to the allowance * @return true **/ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev decreases the allowance of spender to spend msg.sender tokens * @param spender the user allowed to spend on behalf of msg.sender * @param subtractedValue the amount being subtracted to the allowance * @return true **/ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve( _msgSender(), spender, _allowances[_msgSender()][spender].sub( subtractedValue, 'ERC20: decreased allowance below zero' ) ); return true; } function _transfer( address sender, address recipient, uint256 amount ) internal virtual { require(sender != address(0), 'ERC20: transfer from the zero address'); require(recipient != address(0), 'ERC20: transfer to the zero address'); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, 'ERC20: transfer amount exceeds balance'); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } function _mint(address account, uint256 amount) internal virtual { require(account != address(0), 'ERC20: mint to the zero address'); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } function _burn(address account, uint256 amount) internal virtual { require(account != address(0), 'ERC20: burn from the zero address'); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, 'ERC20: burn amount exceeds balance'); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), 'ERC20: approve from the zero address'); require(spender != address(0), 'ERC20: approve to the zero address'); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } function _setName(string memory newName) internal { _name = newName; } function _setSymbol(string memory newSymbol) internal { _symbol = newSymbol; } function _setDecimals(uint8 newDecimals) internal { _decimals = newDecimals; } function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; /** * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts * 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 virtual view returns (address payable) { return msg.sender; } function _msgData() internal virtual view returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {IERC20} from "./IERC20.sol"; /** * @dev Interface for ERC20 including metadata **/ interface IERC20Detailed is IERC20 { function name() external view returns (string memory); function symbol() external view returns (string memory); function decimals() external view returns (uint8); }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; /** * @dev Collection of functions related to the address type * From https://github.com/OpenZeppelin/openzeppelin-contracts */ 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) { // According to EIP-1052, 0x0 is the value returned for not-yet created accounts // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned // for accounts without code, i.e. `keccak256('')` bytes32 codehash; bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; // solhint-disable-next-line no-inline-assembly assembly { codehash := extcodehash(account) } return (codehash != accountHash && codehash != 0x0); } /** * @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'); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "./interfaces/IERC20.sol"; import {StakedToken} from "./StakedToken.sol"; /** * @title StakedWell * @notice StakedToken with WELL token as staked token * @author Moonwell **/ contract StakedWell is StakedToken { string internal constant NAME = "Staked WELL"; string internal constant SYMBOL = "stkWELL"; uint8 internal constant DECIMALS = 18; /** * @dev Called by the proxy contract **/ function initialize( IERC20 stakedToken, IERC20 rewardToken, uint256 cooldownSeconds, uint256 unstakeWindow, address rewardsVault, address emissionManager, uint128 distributionDuration, address governance ) external { __StakedToken_init( stakedToken, rewardToken, cooldownSeconds, unstakeWindow, rewardsVault, emissionManager, distributionDuration, NAME, SYMBOL, DECIMALS, governance ); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import {IERC20} from "./interfaces/IERC20.sol"; import {IStakedToken} from "./interfaces/IStakedToken.sol"; import {ITransferHook} from "./interfaces/ITransferHook.sol"; import {IEcosystemReserve} from "./interfaces/IEcosystemReserve.sol"; import {ERC20WithSnapshot} from "./libraries/ERC20WithSnapshot.sol"; import {SafeERC20} from "./libraries/SafeERC20.sol"; import {DistributionTypes} from "./libraries/DistributionTypes.sol"; import {Initializable} from "./utils/Initializable.sol"; import {DistributionManager} from "./DistributionManager.sol"; /** * @title StakedToken * * This contract embeds logic for UpgradeableReentrancyGuard in the contract rather than inheriting. This is * to work around storage layout incompatibilities because the Moonriver contracts were deployed without a reentrancy * guard. * * @notice Contract to stake MFAM token, tokenize the position and get rewards, inheriting from a distribution manager contract * @author Moonwell **/ contract StakedMfam is IStakedToken, ERC20WithSnapshot, Initializable, DistributionManager { /** * StakedToken * * Code below this line is copied verbatim from StakedToken.sol */ using SafeERC20 for IERC20; IERC20 public STAKED_TOKEN; IERC20 public REWARD_TOKEN; uint256 public COOLDOWN_SECONDS; /// @notice Seconds available to redeem once the cooldown period is fullfilled uint256 public UNSTAKE_WINDOW; /// @notice Address to pull from the rewards, needs to have approved this contract address public REWARDS_VAULT; mapping(address => uint256) public stakerRewardsToClaim; mapping(address => uint256) public stakersCooldowns; event Staked(address indexed from, address indexed onBehalfOf, uint256 amount); event Redeem(address indexed from, address indexed to, uint256 amount); event RewardsAccrued(address user, uint256 amount); event RewardsClaimed(address indexed from, address indexed to, uint256 amount); event Cooldown(address indexed user); function __StakedMfam_init( IERC20 stakedToken, IERC20 rewardToken, uint256 cooldownSeconds, uint256 unstakeWindow, address rewardsVault, address emissionManager, uint128 distributionDuration, string memory name, string memory symbol, uint8 decimals, address governance ) internal initializer { __ERC20_init_unchained(name, symbol, decimals); __DistributionManager_init_unchained(emissionManager, distributionDuration); __StakedMfam_init_unchained( stakedToken, rewardToken, cooldownSeconds, unstakeWindow, rewardsVault, governance ); } function __StakedMfam_init_unchained( IERC20 stakedToken, IERC20 rewardToken, uint256 cooldownSeconds, uint256 unstakeWindow, address rewardsVault, address governance ) internal { STAKED_TOKEN = stakedToken; REWARD_TOKEN = rewardToken; COOLDOWN_SECONDS = cooldownSeconds; UNSTAKE_WINDOW = unstakeWindow; REWARDS_VAULT = rewardsVault; _setGovernance(ITransferHook(governance)); } function stake(address onBehalfOf, uint256 amount) external override nonReentrant { require(amount != 0, 'INVALID_ZERO_AMOUNT'); require(onBehalfOf != address(0), 'STAKE_ZERO_ADDRESS'); uint256 balanceOfUser = balanceOf(onBehalfOf); uint256 accruedRewards = _updateUserAssetInternal( onBehalfOf, address(this), balanceOfUser, totalSupply() ); if (accruedRewards != 0) { emit RewardsAccrued(onBehalfOf, accruedRewards); stakerRewardsToClaim[onBehalfOf] = stakerRewardsToClaim[onBehalfOf].add(accruedRewards); } stakersCooldowns[onBehalfOf] = getNextCooldownTimestamp(0, amount, onBehalfOf, balanceOfUser); _mint(onBehalfOf, amount); IERC20(STAKED_TOKEN).safeTransferFrom(msg.sender, address(this), amount); emit Staked(msg.sender, onBehalfOf, amount); } /** * @dev Redeems staked tokens, and stop earning rewards * @param to Address to redeem to * @param amount Amount to redeem **/ function redeem(address to, uint256 amount) external override nonReentrant { require(amount != 0, 'INVALID_ZERO_AMOUNT'); require(to != address(0), 'REDEEM_ZERO_ADDRESS'); //solium-disable-next-line uint256 cooldownStartTimestamp = stakersCooldowns[msg.sender]; require( block.timestamp > cooldownStartTimestamp.add(COOLDOWN_SECONDS), 'INSUFFICIENT_COOLDOWN' ); require( block.timestamp.sub(cooldownStartTimestamp.add(COOLDOWN_SECONDS)) <= UNSTAKE_WINDOW, 'UNSTAKE_WINDOW_FINISHED' ); uint256 balanceOfMessageSender = balanceOf(msg.sender); uint256 amountToRedeem = (amount > balanceOfMessageSender) ? balanceOfMessageSender : amount; _updateCurrentUnclaimedRewards(msg.sender, balanceOfMessageSender, true); _burn(msg.sender, amountToRedeem); if (balanceOfMessageSender.sub(amountToRedeem) == 0) { stakersCooldowns[msg.sender] = 0; } IERC20(STAKED_TOKEN).safeTransfer(to, amountToRedeem); emit Redeem(msg.sender, to, amountToRedeem); } /** * @dev Activates the cooldown period to unstake * - It can't be called if the user is not staking **/ function cooldown() external override { require(balanceOf(msg.sender) != 0, "INVALID_BALANCE_ON_COOLDOWN"); //solium-disable-next-line stakersCooldowns[msg.sender] = block.timestamp; emit Cooldown(msg.sender); } /** * @dev Claims an `amount` of `REWARD_TOKEN` to the address `to` * @param to Address to stake for * @param amount Amount to stake **/ function claimRewards(address to, uint256 amount) external override nonReentrant { uint256 newTotalRewards = _updateCurrentUnclaimedRewards( msg.sender, balanceOf(msg.sender), false ); uint256 amountToClaim = (amount == type(uint256).max) ? newTotalRewards : amount; stakerRewardsToClaim[msg.sender] = newTotalRewards.sub(amountToClaim, "INVALID_AMOUNT"); IERC20(REWARD_TOKEN).safeTransferFrom(REWARDS_VAULT, to, amountToClaim); emit RewardsClaimed(msg.sender, to, amountToClaim); } /** * @dev Internal ERC20 _transfer of the tokenized staked tokens * @param from Address to transfer from * @param to Address to transfer to * @param amount Amount to transfer **/ function _transfer( address from, address to, uint256 amount ) internal override { uint256 balanceOfFrom = balanceOf(from); // Sender _updateCurrentUnclaimedRewards(from, balanceOfFrom, true); // Recipient if (from != to) { uint256 balanceOfTo = balanceOf(to); _updateCurrentUnclaimedRewards(to, balanceOfTo, true); uint256 previousSenderCooldown = stakersCooldowns[from]; stakersCooldowns[to] = getNextCooldownTimestamp(previousSenderCooldown, amount, to, balanceOfTo); // if cooldown was set and whole balance of sender was transferred - clear cooldown if (balanceOfFrom == amount && previousSenderCooldown != 0) { stakersCooldowns[from] = 0; } } super._transfer(from, to, amount); } /** * @dev Updates the user state related with his accrued rewards * @param user Address of the user * @param userBalance The current balance of the user * @param updateStorage Boolean flag used to update or not the stakerRewardsToClaim of the user * @return The unclaimed rewards that were added to the total accrued **/ function _updateCurrentUnclaimedRewards( address user, uint256 userBalance, bool updateStorage ) internal returns (uint256) { uint256 accruedRewards = _updateUserAssetInternal( user, address(this), userBalance, totalSupply() ); uint256 unclaimedRewards = stakerRewardsToClaim[user].add(accruedRewards); if (accruedRewards != 0) { if (updateStorage) { stakerRewardsToClaim[user] = unclaimedRewards; } emit RewardsAccrued(user, accruedRewards); } return unclaimedRewards; } /** * @dev Calculates the how is gonna be a new cooldown timestamp depending on the sender/receiver situation * - If the timestamp of the sender is "better" or the timestamp of the recipient is 0, we take the one of the recipient * - Weighted average of from/to cooldown timestamps if: * # The sender doesn't have the cooldown activated (timestamp 0). * # The sender timestamp is expired * # The sender has a "worse" timestamp * - If the receiver's cooldown timestamp expired (too old), the next is 0 * @param fromCooldownTimestamp Cooldown timestamp of the sender * @param amountToReceive Amount * @param toAddress Address of the recipient * @param toBalance Current balance of the receiver * @return The new cooldown timestamp **/ function getNextCooldownTimestamp( uint256 fromCooldownTimestamp, uint256 amountToReceive, address toAddress, uint256 toBalance ) public returns (uint256) { uint256 toCooldownTimestamp = stakersCooldowns[toAddress]; if (toCooldownTimestamp == 0) { return 0; } uint256 minimalValidCooldownTimestamp = block.timestamp.sub(COOLDOWN_SECONDS).sub( UNSTAKE_WINDOW ); if (minimalValidCooldownTimestamp > toCooldownTimestamp) { toCooldownTimestamp = 0; } else { uint256 fromCooldownTimestampFinal = (minimalValidCooldownTimestamp > fromCooldownTimestamp) ? block.timestamp : fromCooldownTimestamp; if (fromCooldownTimestampFinal < toCooldownTimestamp) { return toCooldownTimestamp; } else { toCooldownTimestamp = ( amountToReceive.mul(fromCooldownTimestampFinal).add(toBalance.mul(toCooldownTimestamp)) ) .div(amountToReceive.add(toBalance)); } } stakersCooldowns[toAddress] = toCooldownTimestamp; return toCooldownTimestamp; } /** * @dev Return the total rewards pending to claim by an staker * @param staker The staker address * @return The rewards */ function getTotalRewardsBalance(address staker) external view returns (uint256) { DistributionTypes.UserStakeInput[] memory userStakeInputs = new DistributionTypes.UserStakeInput[](1); userStakeInputs[0] = DistributionTypes.UserStakeInput({ underlyingAsset: address(this), stakedByUser: balanceOf(staker), totalStaked: totalSupply() }); return stakerRewardsToClaim[staker].add(_getUnclaimedRewards(staker, userStakeInputs)); } /** * StakedWell * * Code below this line is copied verbatim from StakedWell.sol */ /** * @dev Called by the proxy contract **/ function initialize( IERC20 stakedToken, IERC20 rewardToken, uint256 cooldownSeconds, uint256 unstakeWindow, address rewardsVault, address emissionManager, uint128 distributionDuration, address governance ) external initializer { __StakedMfam_init( stakedToken, rewardToken, cooldownSeconds, unstakeWindow, rewardsVault, emissionManager, distributionDuration, NAME, SYMBOL, DECIMALS, governance ); } /** * ReentrancyGuardUpgradeable * * Code below this line is copied verbatim from ReentrancyGuardUpgradeable.sol */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * Additional Storage Layout - StakedMFam.sol * * Add fields that were originally in StakedMfam.sol, which inherited from this contract. This aligns all storage * in the contract. */ string internal constant NAME = "Staked MFAM"; string internal constant SYMBOL = "stkMFAM"; uint8 internal constant DECIMALS = 18; /** * Additional Storage Layout - ReentrancyGuardUpgradeable.sol * * Add fields that were originally in ReentrancyGuardUpgradeable.sol. These fields are extra and are added to the * existing storage layout. */ uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import "./interfaces/IERC20.sol"; import "./interfaces/IEcosystemReserve.sol"; import {SafeERC20} from "./libraries/SafeERC20.sol"; import {Initializable} from "./utils/Initializable.sol"; /** * @title EcosystemReserve * * This contract embeds logic for UpgradeableReentrancyGuard in the contract rather than inheriting. This is * to work around storage layout incompatibilities because the Moonriver contracts were deployed without a reentrancy * guard. * * @notice Stores all the mTokens kept for incentives, just adding different systems to whitelist * that will pull MFAM funds for their specific use case * @author Moonwell */ contract EcosystemReserveMoonriver is IEcosystemReserve, Initializable { /** * EcosystemReserveMoonriver * * Code below this line is copied verbatim from EcosystemReserve.sol */ using SafeERC20 for IERC20; address internal _fundsAdmin; event NewFundsAdmin(address indexed fundsAdmin); function getFundsAdmin() external view returns (address) { return _fundsAdmin; } modifier onlyFundsAdmin() { require(msg.sender == _fundsAdmin, "ONLY_BY_FUNDS_ADMIN"); _; } function initialize(address reserveController) external initializer { require(reserveController != address(0), "ZERO_ADDRESS"); _setFundsAdmin(reserveController); } function approve( IERC20 token, address recipient, uint256 amount ) external override onlyFundsAdmin { token.approve(recipient, amount); } function transfer( IERC20 token, address recipient, uint256 amount ) external override onlyFundsAdmin nonReentrant { token.transfer(recipient, amount); } function setFundsAdmin(address admin) external override onlyFundsAdmin { _setFundsAdmin(admin); } function _setFundsAdmin(address admin) internal { require(admin != address(0), "ZERO_ADDRESS"); _fundsAdmin = admin; emit NewFundsAdmin(admin); } /** * ReentrancyGuardUpgradeable * * Code below this line is copied verbatim from ReentrancyGuardUpgradeable.sol */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import "../libraries/ERC20.sol"; contract FaucetERC20 is ERC20 { function mint(uint256 amount) external { _mint(msg.sender, amount); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import {Context} from "./Context.sol"; /** * @dev From https://github.com/OpenZeppelin/openzeppelin-contracts * 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. */ 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() public { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view 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; } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import "./interfaces/IEcosystemReserve.sol"; import "./interfaces/IERC20.sol"; import {Ownable} from "./libraries/Ownable.sol"; /* * @title EcosystemReserveController * @dev Proxy smart contract to control the EcosystemReserve, in order for the governance to call its * user-face functions (as the governance is also the proxy admin of the EcosystemReserve) * @author Moonwell */ contract EcosystemReserveController is Ownable { IEcosystemReserve public ECOSYSTEM_RESERVE; bool public initialized; function setEcosystemReserve(address ecosystemReserve) external onlyOwner { require(!initialized, "ECOSYSTEM_RESERVE has been initialized"); initialized = true; ECOSYSTEM_RESERVE = IEcosystemReserve(ecosystemReserve); } function approve(IERC20 token, address recipient, uint256 amount) external onlyOwner { ECOSYSTEM_RESERVE.approve(token, recipient, amount); } function transfer(IERC20 token, address recipient, uint256 amount) external onlyOwner { ECOSYSTEM_RESERVE.transfer(token, recipient, amount); } function setFundsAdmin(address admin) external onlyOwner { ECOSYSTEM_RESERVE.setFundsAdmin(admin); } }
// SPDX-License-Identifier: agpl-3.0 pragma solidity 0.6.12; import "./interfaces/IERC20.sol"; import "./interfaces/IEcosystemReserve.sol"; import {SafeERC20} from "./libraries/SafeERC20.sol"; import {Initializable} from "./utils/Initializable.sol"; import {ReentrancyGuardUpgradeable} from "./OpenZeppelin/ReentrancyGuardUpgradeable.sol"; /** * @title EcosystemReserve * @notice Stores all the mTokens kept for incentives, just adding different systems to whitelist * that will pull MFAM funds for their specific use case * @author Moonwell **/ contract EcosystemReserve is IEcosystemReserve, Initializable, ReentrancyGuardUpgradeable { using SafeERC20 for IERC20; address internal _fundsAdmin; event NewFundsAdmin(address indexed fundsAdmin); function getFundsAdmin() external view returns (address) { return _fundsAdmin; } modifier onlyFundsAdmin() { require(msg.sender == _fundsAdmin, "ONLY_BY_FUNDS_ADMIN"); _; } function initialize(address reserveController) external initializer { require(reserveController != address(0), "ZERO_ADDRESS"); __ReentrancyGuard_init(); _setFundsAdmin(reserveController); } function approve( IERC20 token, address recipient, uint256 amount ) external override onlyFundsAdmin { token.approve(recipient, amount); } function transfer( IERC20 token, address recipient, uint256 amount ) external override onlyFundsAdmin nonReentrant { token.transfer(recipient, amount); } function setFundsAdmin(address admin) external override onlyFundsAdmin { _setFundsAdmin(admin); } function _setFundsAdmin(address admin) internal { require(admin != address(0), "ZERO_ADDRESS"); _fundsAdmin = admin; emit NewFundsAdmin(admin); } }
{ "optimizer": { "enabled": true, "runs": 999999 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"emission","type":"uint256"}],"name":"AssetConfigUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"AssetIndexUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"Cooldown","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsAccrued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint128","name":"oldValue","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"newValue","type":"uint128"}],"name":"SnapshotDone","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"onBehalfOf","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"}],"name":"UserIndexUpdated","type":"event"},{"inputs":[],"name":"COOLDOWN_SECONDS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DISTRIBUTION_END","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EMISSION_MANAGER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRECISION","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARDS_VAULT","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REWARD_TOKEN","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"STAKED_TOKEN","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UNSTAKE_WINDOW","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_countsSnapshots","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_governance","outputs":[{"internalType":"contract ITransferHook","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"_snapshots","outputs":[{"internalType":"uint128","name":"blockNumber","type":"uint128"},{"internalType":"uint128","name":"value","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"assets","outputs":[{"internalType":"uint128","name":"emissionPerSecond","type":"uint128"},{"internalType":"uint128","name":"lastUpdateTimestamp","type":"uint128"},{"internalType":"uint256","name":"index","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"claimRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"emissionsPerSecond","type":"uint128"},{"internalType":"contract IERC20","name":"underlyingAsset","type":"address"}],"name":"configureAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint128","name":"emissionPerSecond","type":"uint128"},{"internalType":"uint256","name":"totalStaked","type":"uint256"},{"internalType":"address","name":"underlyingAsset","type":"address"}],"internalType":"struct DistributionTypes.AssetConfigInput[]","name":"assetsConfigInput","type":"tuple[]"}],"name":"configureAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cooldown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fromCooldownTimestamp","type":"uint256"},{"internalType":"uint256","name":"amountToReceive","type":"uint256"},{"internalType":"address","name":"toAddress","type":"address"},{"internalType":"uint256","name":"toBalance","type":"uint256"}],"name":"getNextCooldownTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"}],"name":"getTotalRewardsBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"asset","type":"address"}],"name":"getUserAssetData","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"stakedToken","type":"address"},{"internalType":"contract IERC20","name":"rewardToken","type":"address"},{"internalType":"uint256","name":"cooldownSeconds","type":"uint256"},{"internalType":"uint256","name":"unstakeWindow","type":"uint256"},{"internalType":"address","name":"rewardsVault","type":"address"},{"internalType":"address","name":"emissionManager","type":"address"},{"internalType":"uint128","name":"distributionDuration","type":"uint128"},{"internalType":"address","name":"governance","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"onBehalfOf","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakerRewardsToClaim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"stakersCooldowns","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b5061389d806100206000396000f3fe608060405234801561001057600080fd5b50600436106102415760003560e01c80637e90d7ef11610145578063a457c2d7116100bd578063b2a5dbfa1161008c578063dd62ed3e11610071578063dd62ed3e14610466578063f11b818814610479578063f1cc432a1461049b57610241565b8063b2a5dbfa1461044b578063cbcbb5071461045e57610241565b8063a457c2d71461040a578063a9059cbb1461041d578063aaf5eb6814610430578063adc9772e1461043857610241565b8063919cd40f1161011457806395d89b41116100f957806395d89b41146103e757806399248ea7146103ef5780639a99b4f0146103f757610241565b8063919cd40f146103d7578063946776cd146103df57610241565b80637e90d7ef1461038b5780638779588c1461039e57806389e72095146103b15780638dbefee2146103c457610241565b8063312f6b83116101d857806339509351116101a757806370a082311161018c57806370a082311461036857806372b49d631461037b578063787a08a61461038357610241565b80633950935114610342578063398340251461035557610241565b8063312f6b831461030a578063313ce567146103125780633373ee4c14610327578063359c4a961461033a57610241565b80631c2f3e3d116102145780631c2f3e3d146102ac5780631e9a6950146102c157806323b872dd146102d65780632acbf823146102e957610241565b806306fdde0314610246578063091030c314610264578063095ea7b31461028457806318160ddd146102a4575b600080fd5b61024e6104ae565b60405161025b91906130fe565b60405180910390f35b610277610272366004612d99565b610562565b60405161025b919061376d565b610297610292366004612e2d565b610574565b60405161025b91906130f3565b610277610592565b6102b4610598565b60405161025b919061303b565b6102d46102cf366004612e2d565b6105b4565b005b6102976102e4366004612ded565b61081f565b6102fc6102f7366004612e2d565b6108c1565b60405161025b92919061371d565b6102b4610908565b61031a610924565b60405161025b9190613776565b610277610335366004612db5565b61092d565b61027761096a565b610297610350366004612e2d565b610970565b6102d4610363366004612fad565b6109cb565b610277610376366004612d99565b610ad3565b610277610afb565b6102d4610b01565b610277610399366004612d99565b610b7a565b6102776103ac366004612d99565b610b8c565b6102d46103bf366004612ee8565b610b9e565b6102776103d2366004612d99565b610c26565b610277610cef565b6102b4610cf5565b61024e610d11565b6102b4610d90565b6102d4610405366004612e2d565b610dac565b610297610418366004612e2d565b610f2e565b61029761042b366004612e2d565b610fa3565b61031a610fb7565b6102d4610446366004612e2d565b610fbc565b6102d4610459366004612e58565b6111fb565b6102b46112c3565b610277610474366004612db5565b6112df565b61048c610487366004612d99565b611317565b60405161025b93929190613740565b6102776104a9366004612fe1565b61135b565b60038054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156105585780601f1061052d57610100808354040283529160200191610558565b820191906000526020600020905b81548152906001019060200180831161053b57829003601f168201915b5050505050905090565b60446020526000908152604090205481565b6000610588610581611454565b8484611458565b5060015b92915050565b60025490565b60085473ffffffffffffffffffffffffffffffffffffffff1681565b6002600c5414156105fa576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f19061365b565b60405180910390fd5b6002600c5580610636576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f190613240565b73ffffffffffffffffffffffffffffffffffffffff8216610683576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f190613533565b33600090815260446020526040908190205490546106a2908290611567565b42116106da576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f1906132ac565b6041546106fc6106f56040548461156790919063ffffffff16565b42906115a6565b1115610734576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f1906134fc565b600061073f33610ad3565b905060008184116107505783610752565b815b9050610760338360016115e8565b5061076b33826116a7565b61077582826115a6565b61078a57336000908152604460205260408120555b603e546107ae9073ffffffffffffffffffffffffffffffffffffffff1686836117d7565b8473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fd12200efa34901b99367694174c3b0d32c99585fdf37c7c26892136ddd0836d98360405161080b919061376d565b60405180910390a350506001600c55505050565b600061082c848484611878565b6108b684610838611454565b6108b18560405180606001604052806028815260200161381b6028913973ffffffffffffffffffffffffffffffffffffffff8a16600090815260016020526040812090610883611454565b73ffffffffffffffffffffffffffffffffffffffff1681526020810191909152604001600020549190611985565b611458565b5060015b9392505050565b60066020908152600092835260408084209091529082529020546fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041682565b603e5473ffffffffffffffffffffffffffffffffffffffff1681565b60055460ff1690565b73ffffffffffffffffffffffffffffffffffffffff8082166000908152600b60209081526040808320938616835260029093019052205492915050565b60415481565b600061058861097d611454565b846108b1856001600061098e611454565b73ffffffffffffffffffffffffffffffffffffffff908116825260208083019390935260409182016000908120918c168152925290205490611567565b600a5473ffffffffffffffffffffffffffffffffffffffff163314610a1c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f1906133d4565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815260009073ffffffffffffffffffffffffffffffffffffffff8316906370a0823190610a7190309060040161303b565b60206040518083038186803b158015610a8957600080fd5b505afa158015610a9d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ac19190612fc9565b9050610ace8382846119cb565b505050565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b60405481565b610b0a33610ad3565b610b40576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f190613624565b33600081815260446020526040808220429055517ff52f50426b32362d3e6bb8cb36b7074756b224622def6352a59eac7f66ebe6e89190a2565b60436020526000908152604090205481565b60076020526000908152604090205481565b610c1c888888888888886040518060400160405280600b81526020017f5374616b65642057454c4c0000000000000000000000000000000000000000008152506040518060400160405280600781526020017f73746b57454c4c0000000000000000000000000000000000000000000000000081525060128b611a8c565b5050505050505050565b60408051600180825281830190925260009160609190816020015b610c49612caf565b815260200190600190039081610c4157905050905060405180606001604052803073ffffffffffffffffffffffffffffffffffffffff168152602001610c8e85610ad3565b8152602001610c9b610592565b81525081600081518110610cab57fe5b60200260200101819052506108ba610cc38483611c23565b73ffffffffffffffffffffffffffffffffffffffff851660009081526043602052604090205490611567565b60095481565b60425473ffffffffffffffffffffffffffffffffffffffff1681565b60048054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156105585780601f1061052d57610100808354040283529160200191610558565b603f5473ffffffffffffffffffffffffffffffffffffffff1681565b6002600c541415610de9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f19061365b565b6002600c556000610e0433610dfd81610ad3565b60006115e8565b905060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8314610e355782610e37565b815b9050610e83816040518060400160405280600e81526020017f494e56414c49445f414d4f554e54000000000000000000000000000000000000815250846119859092919063ffffffff16565b33600090815260436020526040902055604254603f54610ebe9173ffffffffffffffffffffffffffffffffffffffff91821691168684611d54565b8373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f9310ccfcb8de723f578a9e4282ea9f521f05ae40dc08f3068dfad528a65ee3c783604051610f1b919061376d565b60405180910390a350506001600c555050565b6000610588610f3b611454565b846108b1856040518060600160405280602581526020016138436025913960016000610f65611454565b73ffffffffffffffffffffffffffffffffffffffff908116825260208083019390935260409182016000908120918d16815292529020549190611985565b6000610588610fb0611454565b8484611878565b601281565b6002600c541415610ff9576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f19061365b565b6002600c5580611035576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f190613240565b73ffffffffffffffffffffffffffffffffffffffff8216611082576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f19061340b565b600061108d83610ad3565b905060006110a484308461109f610592565b611d75565b9050801561113c577f2468f9268c60ad90e2d49edb0032c8a001e733ae888b3ab8e982edf535be1a7684826040516110dd9291906130cd565b60405180910390a173ffffffffffffffffffffffffffffffffffffffff84166000908152604360205260409020546111159082611567565b73ffffffffffffffffffffffffffffffffffffffff85166000908152604360205260409020555b611149600084868561135b565b73ffffffffffffffffffffffffffffffffffffffff85166000908152604460205260409020556111798484611e4e565b603e5461119e9073ffffffffffffffffffffffffffffffffffffffff16333086611d54565b8373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f5dac0c1b1112564a045ba943c9d50270893e8e826c49be8e7073adc713ab7bd785604051610f1b919061376d565b600a5473ffffffffffffffffffffffffffffffffffffffff16331461124c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f1906133d4565b60005b81811015610ace576112bb83838381811061126657fe5b61127c9260206060909202019081019150612f92565b84848481811061128857fe5b9050606002016020013585858581811061129e57fe5b90506060020160400160208101906112b69190612d99565b6119cb565b60010161124f565b600a5473ffffffffffffffffffffffffffffffffffffffff1681565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b600b60205260009081526040902080546001909101546fffffffffffffffffffffffffffffffff808316927001000000000000000000000000000000009004169083565b73ffffffffffffffffffffffffffffffffffffffff82166000908152604460205260408120548061139057600091505061144c565b60006113b36041546113ad604054426115a690919063ffffffff16565b906115a6565b9050818111156113c65760009150611420565b60008782116113d557876113d7565b425b9050828110156113ec5782935050505061144c565b61141c6113f98887611567565b6114166114068887611f43565b6114108b86611f43565b90611567565b90611f97565b9250505b5073ffffffffffffffffffffffffffffffffffffffff8416600090815260446020526040902081905590505b949350505050565b3390565b73ffffffffffffffffffffffffffffffffffffffff83166114a5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f19061356a565b73ffffffffffffffffffffffffffffffffffffffff82166114f2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f1906131ac565b73ffffffffffffffffffffffffffffffffffffffff80841660008181526001602090815260408083209487168084529490915290819020849055517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259061155a90859061376d565b60405180910390a3505050565b6000828201838110156108ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f190613209565b60006108ba83836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250611985565b6000806115f985308661109f610592565b73ffffffffffffffffffffffffffffffffffffffff86166000908152604360205260408120549192509061162d9083611567565b9050811561169e5783156116645773ffffffffffffffffffffffffffffffffffffffff861660009081526043602052604090208190555b7f2468f9268c60ad90e2d49edb0032c8a001e733ae888b3ab8e982edf535be1a7686836040516116959291906130cd565b60405180910390a15b95945050505050565b73ffffffffffffffffffffffffffffffffffffffff82166116f4576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f190613442565b61170082600083611fd9565b61174a816040518060600160405280602281526020016137d36022913973ffffffffffffffffffffffffffffffffffffffff85166000908152602081905260409020549190611985565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205560025461177d90826115a6565b60025560405160009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906117cb90859061376d565b60405180910390a35050565b610ace8363a9059cbb60e01b84846040516024016117f69291906130cd565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261212e565b600061188384610ad3565b9050611891848260016115e8565b508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16146119745760006118d084610ad3565b90506118de848260016115e8565b5073ffffffffffffffffffffffffffffffffffffffff85166000908152604460205260409020546119118185878561135b565b73ffffffffffffffffffffffffffffffffffffffff8616600090815260446020526040902055828414801561194557508015155b156119715773ffffffffffffffffffffffffffffffffffffffff86166000908152604460205260408120555b50505b61197f84848461227b565b50505050565b600081848411156119c3576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f191906130fe565b505050900390565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600b602052604090206119fb828285612405565b5080547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff851617815560405173ffffffffffffffffffffffffffffffffffffffff8316907f87fa03892a0556cb6b8f97e6d533a150d4d55fcbf275fff5fa003fa636bcc7fa90611a7e908790613700565b60405180910390a250505050565b6008547501000000000000000000000000000000000000000000900460ff1680611ad1575060085474010000000000000000000000000000000000000000900460ff16155b611b07576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f19061331a565b6008547501000000000000000000000000000000000000000000900460ff16158015611ba957600880547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff90911675010000000000000000000000000000000000000000001716740100000000000000000000000000000000000000001790555b611bb1612504565b611bbc85858561265b565b611bd887876fffffffffffffffffffffffffffffffff166126b9565b611be68c8c8c8c8c8761275b565b8015611c1557600880547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1690555b505050505050505050505050565b600080805b8351811015611d4c576000600b6000868481518110611c4357fe5b6020908102919091018101515173ffffffffffffffffffffffffffffffffffffffff1682528101919091526040016000908120600181015481548851929450611cce926fffffffffffffffffffffffffffffffff80831692700100000000000000000000000000000000900416908a9088908110611cbd57fe5b6020026020010151604001516127d8565b9050611d3d611d36878581518110611ce257fe5b602002602001015160200151838560020160008c73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054612892565b8590611567565b93505050806001019050611c28565b509392505050565b61197f846323b872dd60e01b8585856040516024016117f69392919061305c565b73ffffffffffffffffffffffffffffffffffffffff8084166000908152600b602090815260408083209388168352600284019091528120549091908280611dbd888588612405565b9050808314611e42578615611dda57611dd7878285612892565b91505b73ffffffffffffffffffffffffffffffffffffffff808a1660008181526002870160205260409081902084905551918a16917fbb123b5c06d5408bbea3c4fef481578175cfb432e3b482c6186f02ed9086585b90611e3990859061376d565b60405180910390a35b50979650505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216611e9b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f1906136c9565b611ea760008383611fd9565b600254611eb49082611567565b60025573ffffffffffffffffffffffffffffffffffffffff8216600090815260208190526040902054611ee79082611567565b73ffffffffffffffffffffffffffffffffffffffff83166000818152602081905260408082209390935591519091907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906117cb90859061376d565b600082611f525750600061058c565b82820282848281611f5f57fe5b04146108ba576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f190613377565b60006108ba83836040518060400160405280601a81526020017f536166654d6174683a206469766973696f6e206279207a65726f0000000000008152506128b4565b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16141561201257610ace565b73ffffffffffffffffffffffffffffffffffffffff83161561205157600061203984610ad3565b905061204f848261204a81866115a6565b612905565b505b73ffffffffffffffffffffffffffffffffffffffff82161561208b57600061207883610ad3565b9050612089838261204a8186611567565b505b60085473ffffffffffffffffffffffffffffffffffffffff16801561197f576040517f4a39314900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821690634a393149906121009087908790879060040161305c565b600060405180830381600087803b15801561211a57600080fd5b505af1158015610c1c573d6000803e3d6000fd5b61214d8273ffffffffffffffffffffffffffffffffffffffff16612adc565b612183576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f190613692565b600060608373ffffffffffffffffffffffffffffffffffffffff16836040516121ac919061301f565b6000604051808303816000865af19150503d80600081146121e9576040519150601f19603f3d011682016040523d82523d6000602084013e6121ee565b606091505b50915091508161222a576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f190613277565b80511561197f57808060200190518101906122459190612ec8565b61197f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f1906135c7565b73ffffffffffffffffffffffffffffffffffffffff83166122c8576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f19061349f565b73ffffffffffffffffffffffffffffffffffffffff8216612315576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f19061314f565b612320838383611fd9565b61236a816040518060600160405280602681526020016137f56026913973ffffffffffffffffffffffffffffffffffffffff86166000908152602081905260409020549190611985565b73ffffffffffffffffffffffffffffffffffffffff80851660009081526020819052604080822093909355908416815220546123a69082611567565b73ffffffffffffffffffffffffffffffffffffffff80841660008181526020819052604090819020939093559151908516907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9061155a90859061376d565b600182015482546000919070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1642811415612446575090506108ba565b845460009061246a9084906fffffffffffffffffffffffffffffffff1684886127d8565b90508281146124cb578086600101819055508673ffffffffffffffffffffffffffffffffffffffff167f5777ca300dfe5bead41006fbce4389794dbc0ed8d6cccebfaf94630aa04184bc826040516124c2919061376d565b60405180910390a25b85546fffffffffffffffffffffffffffffffff428116700100000000000000000000000000000000029116178655925050509392505050565b6008547501000000000000000000000000000000000000000000900460ff1680612549575060085474010000000000000000000000000000000000000000900460ff16155b61257f576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f19061331a565b6008547501000000000000000000000000000000000000000000900460ff1615801561262157600880547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff90911675010000000000000000000000000000000000000000001716740100000000000000000000000000000000000000001790555b612629612b15565b801561265857600880547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff1690555b50565b825161266e906003906020860190612ce6565b508151612682906004906020850190612ce6565b50600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff929092169190911790555050565b73ffffffffffffffffffffffffffffffffffffffff8216612706576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f1906132e3565b6127104282611567565b60095550600a80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b603e805473ffffffffffffffffffffffffffffffffffffffff8089167fffffffffffffffffffffffff000000000000000000000000000000000000000092831617909255603f80548884169083161790556040869055604185905560428054928516929091169190911790556127d081612c68565b505050505050565b60008315806127e5575081155b80612801575042836fffffffffffffffffffffffffffffffff16145b806128205750600954836fffffffffffffffffffffffffffffffff1610155b1561282c57508361144c565b6000600954421161283d5742612841565b6009545b90506000612861826fffffffffffffffffffffffffffffffff87166115a6565b90506128878761141086611416670de0b6b3a76400006128818c88611f43565b90611f43565b979650505050505050565b600061144c670de0b6b3a76400006114166128ad86866115a6565b8790611f43565b600081836128ef576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f191906130fe565b5060008385816128fb57fe5b0495945050505050565b73ffffffffffffffffffffffffffffffffffffffff83166000908152600760209081526040808320546006909252909120439190811580159061298957506fffffffffffffffffffffffffffffffff83168160006129648560016115a6565b81526020810191909152604001600020546fffffffffffffffffffffffffffffffff16145b156129e3578381600061299d8560016115a6565b8152602081019190915260400160002080546fffffffffffffffffffffffffffffffff928316700100000000000000000000000000000000029216919091179055612a99565b6040805180820182526fffffffffffffffffffffffffffffffff80861682528681166020808401918252600087815290869052939093209151825493518216700100000000000000000000000000000000029082167fffffffffffffffffffffffffffffffff000000000000000000000000000000009094169390931716919091179055612a72826001611567565b73ffffffffffffffffffffffffffffffffffffffff87166000908152600760205260409020555b7f2cd3c83ddac2953ee75f53265d9ea4463eaf05030e5459a1b7e63819b7ce88f7868686604051612acc9392919061308d565b60405180910390a1505050505050565b6000813f7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a47081811480159061144c575050151592915050565b6008547501000000000000000000000000000000000000000000900460ff1680612b5a575060085474010000000000000000000000000000000000000000900460ff16155b612b90576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016105f19061331a565b6008547501000000000000000000000000000000000000000000900460ff16158015612c3257600880547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff90911675010000000000000000000000000000000000000000001716740100000000000000000000000000000000000000001790555b6001600c55801561265857600880547fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff16905550565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6040518060600160405280600073ffffffffffffffffffffffffffffffffffffffff16815260200160008152602001600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10612d2757805160ff1916838001178555612d54565b82800160010185558215612d54579182015b82811115612d54578251825591602001919060010190612d39565b50612d60929150612d64565b5090565b5b80821115612d605760008155600101612d65565b80356fffffffffffffffffffffffffffffffff8116811461058c57600080fd5b600060208284031215612daa578081fd5b81356108ba816137b0565b60008060408385031215612dc7578081fd5b8235612dd2816137b0565b91506020830135612de2816137b0565b809150509250929050565b600080600060608486031215612e01578081fd5b8335612e0c816137b0565b92506020840135612e1c816137b0565b929592945050506040919091013590565b60008060408385031215612e3f578182fd5b8235612e4a816137b0565b946020939093013593505050565b60008060208385031215612e6a578182fd5b823567ffffffffffffffff80821115612e81578384fd5b818501915085601f830112612e94578384fd5b813581811115612ea2578485fd5b866020606083028501011115612eb6578485fd5b60209290920196919550909350505050565b600060208284031215612ed9578081fd5b815180151581146108ba578182fd5b600080600080600080600080610100898b031215612f04578384fd5b8835612f0f816137b0565b97506020890135612f1f816137b0565b965060408901359550606089013594506080890135612f3d816137b0565b935060a0890135612f4d816137b0565b925060c08901356fffffffffffffffffffffffffffffffff81168114612f71578283fd5b915060e0890135612f81816137b0565b809150509295985092959890939650565b600060208284031215612fa3578081fd5b6108ba8383612d79565b60008060408385031215612fbf578182fd5b612dd28484612d79565b600060208284031215612fda578081fd5b5051919050565b60008060008060808587031215612ff6578384fd5b8435935060208501359250604085013561300f816137b0565b9396929550929360600135925050565b60008251613031818460208701613784565b9190910192915050565b73ffffffffffffffffffffffffffffffffffffffff91909116815260200190565b73ffffffffffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b73ffffffffffffffffffffffffffffffffffffffff9390931683526fffffffffffffffffffffffffffffffff918216602084015216604082015260600190565b73ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b901515815260200190565b600060208252825180602084015261311d816040850160208701613784565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b60208082526023908201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260408201527f6573730000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526022908201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560408201527f7373000000000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252601b908201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604082015260600190565b60208082526013908201527f494e56414c49445f5a45524f5f414d4f554e5400000000000000000000000000604082015260600190565b6020808252818101527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564604082015260600190565b60208082526015908201527f494e53554646494349454e545f434f4f4c444f574e0000000000000000000000604082015260600190565b6020808252600c908201527f5a45524f5f414444524553530000000000000000000000000000000000000000604082015260600190565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201527f647920696e697469616c697a6564000000000000000000000000000000000000606082015260800190565b60208082526021908201527f536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f60408201527f7700000000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526015908201527f4f4e4c595f454d495353494f4e5f4d414e414745520000000000000000000000604082015260600190565b60208082526012908201527f5354414b455f5a45524f5f414444524553530000000000000000000000000000604082015260600190565b60208082526021908201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360408201527f7300000000000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526025908201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460408201527f6472657373000000000000000000000000000000000000000000000000000000606082015260800190565b60208082526017908201527f554e5354414b455f57494e444f575f46494e4953484544000000000000000000604082015260600190565b60208082526013908201527f52454445454d5f5a45524f5f4144445245535300000000000000000000000000604082015260600190565b60208082526024908201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460408201527f7265737300000000000000000000000000000000000000000000000000000000606082015260800190565b6020808252602a908201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60408201527f6f74207375636365656400000000000000000000000000000000000000000000606082015260800190565b6020808252601b908201527f494e56414c49445f42414c414e43455f4f4e5f434f4f4c444f574e0000000000604082015260600190565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b6020808252601f908201527f5361666545524332303a2063616c6c20746f206e6f6e2d636f6e747261637400604082015260600190565b6020808252601f908201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604082015260600190565b6fffffffffffffffffffffffffffffffff91909116815260200190565b6fffffffffffffffffffffffffffffffff92831681529116602082015260400190565b6fffffffffffffffffffffffffffffffff9384168152919092166020820152604081019190915260600190565b90815260200190565b60ff91909116815260200190565b60005b8381101561379f578181015183820152602001613787565b8381111561197f5750506000910152565b73ffffffffffffffffffffffffffffffffffffffff8116811461265857600080fdfe45524332303a206275726e20616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a2064656372656173656420616c6c6f77616e63652062656c6f77207a65726fa264697066735822122087cd5349e95769397efe4bd867e199d373b412197a6e7e34318ca8ac96a423d064736f6c634300060c0033
Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.