contracts-imtbl/contracts/activity/CECDistributor.sol
2024-09-07 22:24:03 +08:00

141 lines
4.7 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Governable} from "../core/Governable.sol";
/**
* @title CECDistributor
* @dev CECDistributor is a contract for distributing CEC token with unlock time
* after all data is set, transfer owner to timelock contract
*/
contract CECDistributor is ReentrancyGuard, Pausable, Ownable, Governable {
using SafeERC20 for IERC20;
mapping(address account => uint256 amount) public balanceMap;
mapping(address account => uint256 amount) public releaseMap;
string public name;
IERC20 public immutable cecToken;
uint256 public constant DURATION = 86400 * 30;
uint256 private releaseAllMonth;
uint256 public start = 0;
// release ratio when tge
uint256 public tgeRatio;
uint256 public constant TGE_PRECISION = 1000000;
//
uint256 public lockDuration;
address public wallet;
event EventBalanceUpdated(address indexed account, uint256 amount);
event EventCECClaimed(address indexed user, address indexed to, uint256 amount);
event EventChangeAddress(address oldAddr, address newAddr);
constructor(
string memory _name,
address _cecToken,
address _wallet,
uint256 _lockDuration,
uint256 _releaseAllMonth,
uint256 _tgeRatio
) {
name = _name;
cecToken = IERC20(_cecToken);
wallet = _wallet;
lockDuration = _lockDuration;
releaseAllMonth = _releaseAllMonth;
tgeRatio = _tgeRatio;
}
/**
* @dev Throws if called by any account other than the owner or gov.
*/
modifier ownerOrGov() {
require(msg.sender == owner() || msg.sender == gov, "CECDistributor: forbidden");
_;
}
function setGov(address _gov) external override onlyOwner {
gov = _gov;
}
/**
* @dev update pause state
* When encountering special circumstances that require an emergency pause of the contract,
* the pause function can be called by the gov account to quickly pause the contract and minimize losses.
*/
function pause() external ownerOrGov {
_pause();
}
/**
* @dev update unpause state
*/
function unpause() external ownerOrGov {
_unpause();
}
function setStart(uint256 newStart) external ownerOrGov {
require(newStart > 0 && start == 0, "CECDistributor: it's already initialized");
start = newStart;
}
function updateBalances(address[] calldata accounts, uint256[] calldata amounts) external onlyOwner {
require(accounts.length == amounts.length, "CECDistributor: invalid input");
for (uint256 i = 0; i < accounts.length; i++) {
balanceMap[accounts[i]] = amounts[i];
emit EventBalanceUpdated(accounts[i], amounts[i]);
}
}
function calcClaimAmount(address user) public view whenNotPaused returns (uint256) {
require(balanceMap[user] > 0, "CECDistributor: not in whitelist");
require(block.timestamp >= start, "CECDistributor: not in claim time");
uint256 claimAmount = 0;
uint256 tgeAmount = 0;
if (tgeRatio > 0) {
tgeAmount = ((balanceMap[user] * tgeRatio) / TGE_PRECISION);
claimAmount += tgeAmount;
}
if (block.timestamp > start + lockDuration) {
uint256 monthNum = (block.timestamp - start - lockDuration) / DURATION;
if (monthNum <= releaseAllMonth) {
claimAmount += (((balanceMap[user] - tgeAmount) * monthNum) / releaseAllMonth);
} else {
claimAmount = balanceMap[user];
}
}
claimAmount -= releaseMap[user];
return claimAmount;
}
function claim(address to) external nonReentrant whenNotPaused returns (uint256) {
require(start > 0, "CECDistributor: start isn't init");
require(to != address(0), "CECDistributor: invalid address");
address _user = _msgSender();
uint256 amount = calcClaimAmount(_user);
if (amount > 0) {
releaseMap[_user] = amount;
cecToken.safeTransferFrom(wallet, to, amount);
emit EventCECClaimed(_user, to, amount);
}
return amount;
}
function changeAddress(address from, address to) external {
require(balanceMap[to] == 0, "CECDistributor: new addr is in whitelist");
require(balanceMap[from] > 0, "CECDistributor: not in whitelist");
address _sender = _msgSender();
require(_sender == owner() || _sender == gov || _sender == from, "CECDistributor: sender not allowed");
balanceMap[to] = balanceMap[from];
balanceMap[from] = 0;
releaseMap[to] = releaseMap[from];
releaseMap[from] = 0;
emit EventChangeAddress(from, to);
}
}