I created simple upgradeable ERC20 contract so I need to use function token as recommended by the documentations, It is deployed and working using proxy contract but I just noticed that initialize function can still be executed again so it will create another token into the contract address.
Deploying 'MyContract': 0x12FC65cc9D1c06BB94D88CB785e8987867769E53 // has new token due to successful initialize function execution
ProxyAdmin: 0x7a1bc6dd98Cc126cE454fad687242ddcE05e7E52
TransparentUpgradeableProxy: 0x246F162B9f7A082Bd49F805fbbE32cD730975cb9
Code to reproduce
ontract MyContract is IERC20Upgradeable, OwnableUpgradeable, ERC20BurnableUpgradeable, StakingRewards {
using SafeMathUpgradeable for uint256;
using SafeERC20Upgradeable for IERC20Upgradeable;
bool private initialized;
function initialize(string memory name, string memory symbol, uint256 initialSupply) public initializer {
require(!initialized, "Contract instance has already been initialized");
initialized = true;
require(owner() != address(this), "Owner must be set");
// create initial token values
__ERC20_init(name, symbol);
__ERC20Burnable_init();
__Ownable_init();
_mint(_msgSender(), initialSupply);
periodFinish = 0;
rewardRate = 0;
rewardsDuration = 7 days;
rewardsToken = IERC20Upgradeable(_msgSender());
stakingToken = IERC20Upgradeable(_msgSender());
rewardsDistribution = _msgSender();
_status = _NOT_ENTERED;
}
}
contract StakingRewards is IStakingRewards, RewardsDistributionRecipient, ReentrancyGuard, Pausable {
using SafeMathUpgradeable for uint256;
using SafeERC20Upgradeable for IERC20Upgradeable;
// /* ========== STATE VARIABLES ========== */
IERC20Upgradeable public rewardsToken;
IERC20Upgradeable public stakingToken;
uint256 public periodFinish;
uint256 public rewardRate;
uint256 public rewardsDuration;
uint256 public lastUpdateTime;
uint256 public rewardPerTokenStored;
uint256 private _totalSupply;
mapping(address => uint256) public userRewardPerTokenPaid;
mapping(address => uint256) public rewards;
mapping(address => uint256) private _balances;
// /* ========== VIEWS ========== */
function lastTimeRewardApplicable() public override view returns (uint256) {
return block.timestamp < periodFinish ? block.timestamp : periodFinish;
}
function rewardPerToken() public override view returns (uint256) {
if (_totalSupply == 0) {
return rewardPerTokenStored;
}
return
rewardPerTokenStored.add(
lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate).mul(1e18).div(_totalSupply)
);
}
function earned(address account) public override view returns (uint256) {
return _balances[account].mul(rewardPerToken().sub(userRewardPerTokenPaid[account])).div(1e18).add(rewards[account]);
}
function getRewardForDuration() external override view returns (uint256) {
return rewardRate.mul(rewardsDuration);
}
/* ========== MUTATIVE FUNCTIONS ========== */
function stake(uint256 amount) external override nonReentrant notPaused updateReward(msg.sender) {
require(amount > 0, "Cannot stake 0");
_totalSupply = _totalSupply.add(amount);
_balances[msg.sender] = _balances[msg.sender].add(amount);
stakingToken.safeTransferFrom(msg.sender, address(this), amount);
emit Staked(msg.sender, amount);
}
function withdraw(uint256 amount) public override nonReentrant updateReward(msg.sender) {
require(amount > 0, "Cannot withdraw 0");
_totalSupply = _totalSupply.sub(amount);
_balances[msg.sender] = _balances[msg.sender].sub(amount);
stakingToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount);
}
function getReward() public override nonReentrant updateReward(msg.sender) {
uint256 reward = rewards[msg.sender];
if (reward > 0) {
rewards[msg.sender] = 0;
rewardsToken.safeTransfer(msg.sender, reward);
emit RewardPaid(msg.sender, reward);
}
}
function exit() external override {
withdraw(_balances[msg.sender]);
getReward();
}
/* ========== RESTRICTED FUNCTIONS ========== */
function notifyRewardAmount(uint256 reward) external override onlyRewardsDistribution updateReward(address(0)) {
if (block.timestamp >= periodFinish) {
rewardRate = reward.div(rewardsDuration);
} else {
uint256 remaining = periodFinish.sub(block.timestamp);
uint256 leftover = remaining.mul(rewardRate);
rewardRate = reward.add(leftover).div(rewardsDuration);
}
// Ensure the provided reward amount is not more than the balance in the contract.
// This keeps the reward rate in the right range, preventing overflows due to
// very high values of rewardRate in the earned and rewardsPerToken functions;
// Reward + leftover must be less than 2^256 / 10^18 to avoid overflow.
uint balance = rewardsToken.balanceOf(address(this));
require(rewardRate <= balance.div(rewardsDuration), "Provided reward too high");
lastUpdateTime = block.timestamp;
periodFinish = block.timestamp.add(rewardsDuration);
emit RewardAdded(reward);
}
// Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders
function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyOwner {
require(tokenAddress != address(stakingToken), "Cannot withdraw the staking token");
IERC20Upgradeable(tokenAddress).safeTransfer(owner(), tokenAmount);
emit Recovered(tokenAddress, tokenAmount);
}
function setRewardsDuration(uint256 _rewardsDuration) external onlyOwner {
require(
block.timestamp > periodFinish,
"Previous rewards period must be complete before changing the duration for the new period"
);
rewardsDuration = _rewardsDuration;
emit RewardsDurationUpdated(rewardsDuration);
}
/* ========== MODIFIERS ========== */
modifier updateReward(address account) {
rewardPerTokenStored = rewardPerToken();
lastUpdateTime = lastTimeRewardApplicable();
if (account != address(0)) {
rewards[account] = earned(account);
userRewardPerTokenPaid[account] = rewardPerTokenStored;
}
_;
}
}
Environment
Truffle v5.4.17 (core: 5.4.17)
Solidity - 0.8.0 (solc-js)
Node v14.18.1
Web3.js v1.5.3