Created
April 4, 2024 09:46
-
-
Save koeppelmann/2cbbd386bb1b862b7c1585907c313448 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.25+commit.b61c2a91.js&optimize=false&runs=200&gist=
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.0; | |
interface IERC20 { | |
function transfer(address recipient, uint256 amount) external returns (bool); | |
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); | |
function balanceOf(address account) external view returns (uint256); | |
} | |
contract DelayTokenWrapper { | |
IERC20 public immutable wrappedToken; | |
string public constant symbol = "WTOKEN"; | |
uint8 public constant decimals = 18; | |
mapping(address => uint256) private balances; | |
mapping(address => uint256) public dailyLimits; | |
struct NewLimit { | |
uint256 amount; | |
uint256 effectiveTimestamp; | |
} | |
mapping(address => NewLimit) private newDailyLimits; | |
mapping(address => uint256) private lastWithdrawalDay; | |
mapping(address => uint256) private withdrawnToday; | |
struct WithdrawalRequest { | |
uint256 amount; | |
uint256 timestamp; | |
} | |
mapping(address => WithdrawalRequest) private withdrawalRequests; | |
address public operator; | |
uint256 public constant delay = 2 minutes; | |
event Deposit(address indexed user, uint256 amount); | |
event WithdrawalRequested(address indexed user, uint256 amount); | |
event Withdraw(address indexed user, uint256 amount); | |
event WithdrawalCancelled(address indexed user); | |
event OperatorWithdraw(address indexed operator, address indexed user, uint256 amount); | |
constructor(address _tokenAddress) { | |
operator = msg.sender; // The deployer is the initial operator | |
wrappedToken = IERC20(_tokenAddress); | |
} | |
modifier onlyOperator() { | |
require(msg.sender == operator, "Caller is not the operator"); | |
_; | |
} | |
function deposit(uint256 amount) external { | |
require(wrappedToken.transferFrom(msg.sender, address(this), amount), "Transfer failed"); | |
balances[msg.sender] += amount; | |
emit Deposit(msg.sender, amount); | |
// Emit an event indicating the transfer of wrapped tokens from the zero address to the user | |
emit Transfer(address(0), msg.sender, amount); | |
} | |
function requestWithdrawal(uint256 amount) external { | |
require(balances[msg.sender] >= amount, "Insufficient balance."); | |
withdrawalRequests[msg.sender] = WithdrawalRequest({ | |
amount: amount, | |
timestamp: block.timestamp | |
}); | |
emit WithdrawalRequested(msg.sender, amount); | |
} | |
function completeWithdrawal() external { | |
WithdrawalRequest storage request = withdrawalRequests[msg.sender]; | |
require(request.amount > 0, "No withdrawal requested."); | |
require(block.timestamp >= request.timestamp + delay, "Withdrawal request is still pending."); | |
if(balances[msg.sender] < request.amount) { | |
emit WithdrawalCancelled(msg.sender); | |
delete withdrawalRequests[msg.sender]; | |
return; | |
} | |
balances[msg.sender] -= request.amount; | |
require(wrappedToken.transfer(msg.sender, request.amount), "Transfer failed"); | |
emit Withdraw(msg.sender, request.amount); | |
// Reset the withdrawal request | |
delete withdrawalRequests[msg.sender]; | |
} | |
function setDailyLimit(uint256 limit) external { | |
if (limit < dailyLimits[msg.sender]) { | |
newDailyLimits[msg.sender] = NewLimit({ | |
amount: limit, | |
effectiveTimestamp: block.timestamp + delay | |
}); | |
} else { | |
dailyLimits[msg.sender] = limit; | |
newDailyLimits[msg.sender] = NewLimit({amount: 0, effectiveTimestamp: 0}); | |
} | |
} | |
function operatorWithdraw(address user, uint256 amount) external onlyOperator { | |
require(balances[user] >= amount, "Insufficient balance."); | |
// Check and apply new daily limit if applicable | |
if (newDailyLimits[user].amount != 0 && block.timestamp >= newDailyLimits[user].effectiveTimestamp) { | |
dailyLimits[user] = newDailyLimits[user].amount; | |
newDailyLimits[user] = NewLimit({amount: 0, effectiveTimestamp: 0}); // Reset the new limit | |
} | |
uint256 currentDay = block.timestamp / 1 days; | |
if(currentDay > lastWithdrawalDay[user]) { | |
withdrawnToday[user] = 0; // Reset daily withdrawn amount for the new day | |
lastWithdrawalDay[user] = currentDay; | |
} | |
require(withdrawnToday[user] + amount <= dailyLimits[user], "Withdrawal amount exceeds daily limit."); | |
withdrawnToday[user] += amount; | |
balances[user] -= amount; | |
require(wrappedToken.transfer(operator, amount), "Failed to transfer tokens to operator."); | |
emit OperatorWithdraw(operator, user, amount); | |
} | |
function getBalance(address user) external view returns (uint256) { | |
return balances[user]; | |
} | |
// The ERC20 Transfer event for compliance | |
event Transfer(address indexed from, address indexed to, uint256 value); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment