mirror of
https://github.com/tig-pool-nk/tig-monorepo.git
synced 2026-02-21 15:07:26 +08:00
158 lines
6.4 KiB
Solidity
158 lines
6.4 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.0;
|
|
|
|
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
|
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
|
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
|
|
interface ITokenLocker {
|
|
event TokensLocked(address indexed user, uint256 amount, uint256 locked);
|
|
event TokensUnlocked(address indexed user, uint256 amount, uint256 locked, uint256 withdrawableTime);
|
|
event TokensWithdrawn(address indexed user, uint256 amount);
|
|
event TokensRewarded(address indexed user, uint256 amount, uint256 claimable);
|
|
event TokensRelocked(address indexed user, uint256 amount, uint256 locked);
|
|
event TokensClaimed(address indexed user, uint256 amount, uint256 locked);
|
|
|
|
function getNumPendingWithdrawals(address account) external view returns (uint256);
|
|
function getTimeUntilWithdrawable(address account, uint256 index) external view returns (uint256);
|
|
function lock(uint256 amount) external;
|
|
function unlock(uint256 amount) external;
|
|
function withdraw(uint256 index) external;
|
|
function relock(uint256 index) external;
|
|
function rewardTokens(address account, uint256 amount) external;
|
|
function claim(uint256 amount) external;
|
|
}
|
|
|
|
contract TokenLocker is ITokenLocker, ReentrancyGuard, Ownable {
|
|
using SafeMath for uint256;
|
|
|
|
struct PendingWithdrawal {
|
|
uint256 amount;
|
|
uint256 timeWithdrawable;
|
|
}
|
|
|
|
IERC20 public immutable token;
|
|
uint256 public immutable pendingPeriod;
|
|
|
|
uint256 public totalLocked;
|
|
uint256 public totalPendingWithdrawal;
|
|
uint256 public totalClaimable;
|
|
|
|
mapping(address => uint256) public locked;
|
|
mapping(address => uint256) public claimable;
|
|
mapping(address => PendingWithdrawal[]) public pendingWithdrawals;
|
|
|
|
constructor(address tokenAddress, uint256 _pendingPeriod) public Ownable(msg.sender) {
|
|
token = IERC20(tokenAddress);
|
|
pendingPeriod = _pendingPeriod;
|
|
}
|
|
|
|
function getNumPendingWithdrawals(address account) external view override returns (uint256) {
|
|
return pendingWithdrawals[account].length;
|
|
}
|
|
|
|
function getTimeUntilWithdrawable(address account, uint256 index) external view returns (uint256) {
|
|
require(index < pendingWithdrawals[account].length, "Invalid withdrawal index");
|
|
|
|
uint256 withdrawableTime = pendingWithdrawals[account][index].timeWithdrawable;
|
|
|
|
if (block.timestamp >= withdrawableTime) {
|
|
return 0;
|
|
}
|
|
|
|
return withdrawableTime.sub(block.timestamp);
|
|
}
|
|
|
|
function lock(uint256 amount) external override nonReentrant {
|
|
require(amount > 0, "Amount must be greater than 0");
|
|
|
|
require(token.transferFrom(msg.sender, address(this), amount), "Transfer failed");
|
|
|
|
locked[msg.sender] = locked[msg.sender].add(amount);
|
|
totalLocked = totalLocked.add(amount);
|
|
|
|
emit TokensLocked(msg.sender, amount, locked[msg.sender]);
|
|
}
|
|
|
|
function unlock(uint256 amount) external override nonReentrant {
|
|
require(amount > 0, "Amount must be greater than 0");
|
|
require(locked[msg.sender] >= amount, "Insufficient locked balance");
|
|
|
|
locked[msg.sender] = locked[msg.sender].sub(amount);
|
|
totalLocked = totalLocked.sub(amount);
|
|
totalPendingWithdrawal = totalPendingWithdrawal.add(amount);
|
|
|
|
uint256 timeWithdrawable = block.timestamp.add(pendingPeriod);
|
|
pendingWithdrawals[msg.sender].push(PendingWithdrawal({
|
|
amount: amount,
|
|
timeWithdrawable: timeWithdrawable
|
|
}));
|
|
|
|
emit TokensUnlocked(msg.sender, amount, locked[msg.sender], timeWithdrawable);
|
|
}
|
|
|
|
function withdraw(uint256 index) external override nonReentrant {
|
|
require(index < pendingWithdrawals[msg.sender].length, "Invalid withdrawal index");
|
|
PendingWithdrawal[] storage userWithdrawals = pendingWithdrawals[msg.sender];
|
|
PendingWithdrawal memory withdrawal = userWithdrawals[index];
|
|
|
|
require(block.timestamp >= withdrawal.timeWithdrawable, "Withdrawal not yet available");
|
|
|
|
uint256 amount = withdrawal.amount;
|
|
|
|
// Swap and pop
|
|
userWithdrawals[index] = userWithdrawals[userWithdrawals.length - 1];
|
|
userWithdrawals.pop();
|
|
|
|
totalPendingWithdrawal = totalPendingWithdrawal.sub(amount);
|
|
|
|
require(token.transfer(msg.sender, amount), "Transfer failed");
|
|
|
|
emit TokensWithdrawn(msg.sender, amount);
|
|
}
|
|
|
|
function relock(uint256 index) external override nonReentrant {
|
|
require(index < pendingWithdrawals[msg.sender].length, "Invalid withdrawal index");
|
|
PendingWithdrawal[] storage userWithdrawals = pendingWithdrawals[msg.sender];
|
|
PendingWithdrawal memory withdrawal = userWithdrawals[index];
|
|
|
|
uint256 amount = withdrawal.amount;
|
|
|
|
// Swap and pop
|
|
userWithdrawals[index] = userWithdrawals[userWithdrawals.length - 1];
|
|
userWithdrawals.pop();
|
|
|
|
// Update state
|
|
totalPendingWithdrawal = totalPendingWithdrawal.sub(amount);
|
|
locked[msg.sender] = locked[msg.sender].add(amount);
|
|
totalLocked = totalLocked.add(amount);
|
|
|
|
emit TokensRelocked(msg.sender, amount, locked[msg.sender]);
|
|
}
|
|
|
|
function rewardTokens(address account, uint256 amount) external override onlyOwner nonReentrant {
|
|
require(account != address(0), "Invalid address");
|
|
require(amount > 0, "Amount must be greater than 0");
|
|
|
|
require(token.transferFrom(msg.sender, address(this), amount), "Transfer failed");
|
|
|
|
claimable[account] = claimable[account].add(amount);
|
|
totalClaimable = totalClaimable.add(amount);
|
|
|
|
emit TokensRewarded(account, amount, claimable[account]);
|
|
}
|
|
|
|
function claim(uint256 amount) external override nonReentrant {
|
|
require(amount > 0, "Amount must be greater than 0");
|
|
require(claimable[msg.sender] >= amount, "Insufficient claimable balance");
|
|
|
|
claimable[msg.sender] = claimable[msg.sender].sub(amount);
|
|
totalClaimable = totalClaimable.sub(amount);
|
|
|
|
locked[msg.sender] = locked[msg.sender].add(amount);
|
|
totalLocked = totalLocked.add(amount);
|
|
|
|
emit TokensClaimed(msg.sender, amount, locked[msg.sender]);
|
|
}
|
|
} |