Skip to content

Instantly share code, notes, and snippets.

@barchef
Created August 6, 2023 22:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save barchef/99e5d2694f04ca75e35258259390819d to your computer and use it in GitHub Desktop.
Save barchef/99e5d2694f04ca75e35258259390819d to your computer and use it in GitHub Desktop.
Locker.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
/**
* Locker
*/
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./LockerStaking.sol";
contract Locker is Ownable {
using SafeMath for uint256;
IERC20 public lockerToken;
LockerStaking public stakingPool;
/*
* deposit vars
*/
struct Items {
address tokenAddress;
address withdrawalAddress;
uint256 tokenAmount;
uint256 unlockTime;
bool withdrawn;
}
uint256 public depositId;
uint256[] public allDepositIds;
mapping(address => uint256[]) public depositsByWithdrawalAddress;
mapping(uint256 => Items) public lockedToken;
mapping(address => mapping(address => uint256)) public walletTokenBalance;
mapping(address => uint256) public tokenFees;
mapping(address => uint256) public tokenFeesClaimed;
mapping(address => mapping(address => bool)) public isClaimed;
uint256 public feePercentage = 100; // 1% * 100; no fee if staking locker token
uint256 public minStakeAmount = 10000 * 1e18;
event LogLock(
address tokenAddress,
address fromAddress,
uint256 amount,
uint256 unlockTime,
uint256 depositId
);
event LogWithdrawal(address SentToAddress, uint256 AmountTransferred);
/**
* Constrctor function
*/
constructor(address _token, address _staking) public {
lockerToken = IERC20(_token);
stakingPool = LockerStaking(_staking);
}
/**
*lock tokens
*/
function lockTokens(
address _tokenAddress,
address _withdrawalAddress,
uint256 _lockAmount,
uint256 _unlockTime
) public returns (uint256 _id) {
require(
_unlockTime < 10000000000,
"Enter an unix timestamp in seconds, not milliseconds"
);
require(
_unlockTime >= block.timestamp,
"Enter an unix timestamp in the future"
);
require(
IERC20(_tokenAddress).approve(address(this), _lockAmount),
"Approve tokens failed"
);
// support fee tokens by chking contract balance before & after xfer
uint256 _balanceBefore = IERC20(_tokenAddress).balanceOf(address(this));
require(
IERC20(_tokenAddress).transferFrom(
msg.sender,
address(this),
_lockAmount
),
"Transfer of tokens failed"
);
uint256 _balanceAfter = IERC20(_tokenAddress).balanceOf(address(this));
_lockAmount = _balanceAfter.sub(_balanceBefore);
require(_lockAmount > 0, "token amount must be greater than zero");
uint256 balance;
(balance, , ) = stakingPool.accountInfos(_withdrawalAddress);
uint256 feeAmount;
if (balance < minStakeAmount) {
feeAmount = _lockAmount.mul(feePercentage).div(10000);
}
tokenFees[_tokenAddress] = tokenFees[_tokenAddress].add(feeAmount);
uint256 _amount = _lockAmount.sub(feeAmount);
//update balance in address
walletTokenBalance[_tokenAddress][
_withdrawalAddress
] = walletTokenBalance[_tokenAddress][_withdrawalAddress].add(_amount);
_id = ++depositId;
lockedToken[_id].tokenAddress = _tokenAddress;
lockedToken[_id].withdrawalAddress = _withdrawalAddress;
lockedToken[_id].tokenAmount = _amount;
lockedToken[_id].unlockTime = _unlockTime;
lockedToken[_id].withdrawn = false;
allDepositIds.push(_id);
depositsByWithdrawalAddress[_withdrawalAddress].push(_id);
emit LogLock(
_tokenAddress,
_withdrawalAddress,
_amount,
_unlockTime,
_id
);
}
/**
*withdraw tokens
*/
function withdrawTokens(uint256 _id) public {
require(
block.timestamp >= lockedToken[_id].unlockTime,
"Lock time has not passed"
);
require(
msg.sender == lockedToken[_id].withdrawalAddress,
"Can withdraw by withdrawal Address only"
);
require(!lockedToken[_id].withdrawn, "Tokens already withdrawn");
lockedToken[_id].withdrawn = true;
require(
IERC20(lockedToken[_id].tokenAddress).transfer(
msg.sender,
lockedToken[_id].tokenAmount
),
"Transfer of tokens failed"
);
//update balance in address
walletTokenBalance[lockedToken[_id].tokenAddress][
msg.sender
] = walletTokenBalance[lockedToken[_id].tokenAddress][msg.sender].sub(
lockedToken[_id].tokenAmount
);
//remove this id from this address
uint256 i;
uint256 j;
for (
j = 0;
j <
depositsByWithdrawalAddress[lockedToken[_id].withdrawalAddress]
.length;
j++
) {
if (
depositsByWithdrawalAddress[lockedToken[_id].withdrawalAddress][
j
] == _id
) {
for (
i = j;
i <
depositsByWithdrawalAddress[
lockedToken[_id].withdrawalAddress
]
.length -
1;
i++
) {
depositsByWithdrawalAddress[
lockedToken[_id].withdrawalAddress
][i] = depositsByWithdrawalAddress[
lockedToken[_id].withdrawalAddress
][i + 1];
}
depositsByWithdrawalAddress[lockedToken[_id].withdrawalAddress]
.pop();
break;
}
}
emit LogWithdrawal(msg.sender, lockedToken[_id].tokenAmount);
}
function pendingRewards(address sender, address _tokenAddress)
public
view
returns (uint256)
{
uint256 balance;
(balance, , ) = stakingPool.accountInfos(sender);
uint256 totalStaked = lockerToken.balanceOf(address(stakingPool));
uint256 totalTokenFee =
tokenFees[_tokenAddress].add(tokenFeesClaimed[_tokenAddress]);
uint256 reward = balance.mul(totalTokenFee).div(totalStaked);
return reward;
}
function claimFee(address _tokenAddress) public {
require(!isClaimed[_tokenAddress][msg.sender], "Already claimed");
require(tokenFees[_tokenAddress] > 0, "All distributed");
uint256 userRewardAmount = pendingRewards(msg.sender, _tokenAddress);
if (tokenFees[_tokenAddress] < userRewardAmount) {
userRewardAmount = tokenFees[_tokenAddress];
}
isClaimed[_tokenAddress][msg.sender] = true;
IERC20(_tokenAddress).transfer(msg.sender, userRewardAmount);
tokenFees[_tokenAddress] = tokenFees[_tokenAddress].sub(
userRewardAmount
);
tokenFeesClaimed[_tokenAddress] = tokenFeesClaimed[_tokenAddress].add(
userRewardAmount
);
}
/*get total token balance in contract*/
function getTotalTokenBalance(address _tokenAddress)
public
view
returns (uint256)
{
return IERC20(_tokenAddress).balanceOf(address(this));
}
/*get total token balance by address*/
function getTokenBalanceByAddress(
address _tokenAddress,
address _walletAddress
) public view returns (uint256) {
return walletTokenBalance[_tokenAddress][_walletAddress];
}
/*get allDepositIds*/
function getAllDepositIds() public view returns (uint256[] memory) {
return allDepositIds;
}
/*get getDepositDetails*/
function getDepositDetails(uint256 _id)
public
view
returns (
address,
address,
uint256,
uint256,
bool
)
{
return (
lockedToken[_id].tokenAddress,
lockedToken[_id].withdrawalAddress,
lockedToken[_id].tokenAmount,
lockedToken[_id].unlockTime,
lockedToken[_id].withdrawn
);
}
/*get DepositsByWithdrawalAddress*/
function getDepositsByWithdrawalAddress(address _withdrawalAddress)
public
view
returns (uint256[] memory)
{
return depositsByWithdrawalAddress[_withdrawalAddress];
}
function setFeePercentage(uint256 _feePercentage)
external
onlyOwner
{
require (_feePercentage <= 500, "fee cannot exceed 500");
feePercentage = _feePercentage;
}
function setMinStakeAmount(uint256 _minStakeAmount) external onlyOwner {
minStakeAmount = _minStakeAmount;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment