Contract Name:
SonicChainSafeLocker
Contract Source Code:
File 1 of 1 : SonicChainSafeLocker
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// IERC20 interface
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// ReentrancyGuard implementation
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
// Ownable implementation
abstract contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor(address initialOwner) {
_transferOwnership(initialOwner);
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == msg.sender, "Ownable: caller is not the owner");
_;
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// TokenLocker contract
contract SonicChainSafeLocker is ReentrancyGuard, Ownable {
struct Lock {
address token;
uint256 amount;
uint256 unlockTime;
bool isUnlocked;
}
mapping(address => Lock[]) public userLocks;
event TokensLocked(
address indexed user,
address indexed token,
uint256 amount,
uint256 unlockTime,
uint256 lockId
);
event TokensUnlocked(
address indexed user,
address indexed token,
uint256 amount,
uint256 lockId
);
constructor() Ownable(msg.sender) {}
function lockTokens(
address _token,
uint256 _amount,
uint256 _duration
) external nonReentrant {
require(_token != address(0), "Invalid token address");
require(_amount > 0, "Amount must be greater than 0");
require(_duration > 0, "Duration must be greater than 0");
IERC20 token = IERC20(_token);
require(
token.balanceOf(msg.sender) >= _amount,
"Insufficient token balance"
);
require(
token.allowance(msg.sender, address(this)) >= _amount,
"Insufficient allowance"
);
uint256 unlockTime = block.timestamp + _duration;
// Transfer tokens to contract
require(
token.transferFrom(msg.sender, address(this), _amount),
"Token transfer failed"
);
// Create new lock
userLocks[msg.sender].push(Lock({
token: _token,
amount: _amount,
unlockTime: unlockTime,
isUnlocked: false
}));
emit TokensLocked(
msg.sender,
_token,
_amount,
unlockTime,
userLocks[msg.sender].length - 1
);
}
function unlockTokens(uint256 _lockId) external nonReentrant {
require(_lockId < userLocks[msg.sender].length, "Invalid lock ID");
Lock storage lock = userLocks[msg.sender][_lockId];
require(!lock.isUnlocked, "Tokens already unlocked");
require(block.timestamp >= lock.unlockTime, "Tokens are still locked");
lock.isUnlocked = true;
IERC20 token = IERC20(lock.token);
require(
token.transfer(msg.sender, lock.amount),
"Token transfer failed"
);
emit TokensUnlocked(
msg.sender,
lock.token,
lock.amount,
_lockId
);
}
function getUserLocks(address _user) external view returns (Lock[] memory) {
return userLocks[_user];
}
function getLockInfo(address _user, uint256 _lockId) external view returns (
address token,
uint256 amount,
uint256 unlockTime,
bool isUnlocked
) {
require(_lockId < userLocks[_user].length, "Invalid lock ID");
Lock memory lock = userLocks[_user][_lockId];
return (
lock.token,
lock.amount,
lock.unlockTime,
lock.isUnlocked
);
}
// Emergency function to recover stuck tokens
function emergencyWithdraw(address _token) external onlyOwner {
IERC20 token = IERC20(_token);
uint256 balance = token.balanceOf(address(this));
require(balance > 0, "No tokens to withdraw");
require(
token.transfer(owner(), balance),
"Token transfer failed"
);
}
}