-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrateLimitWhitelisted.sol
198 lines (182 loc) · 8.09 KB
/
rateLimitWhitelisted.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender)
external
view
returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
contract rateLimit {
mapping(address => TokenInfo) public infoL;
struct TokenInfo {
uint32 timestamp; //tracks most recent send
uint224 rate; //tracks amount sent for the last timeLimit time
}
uint256 public timeLimit = 3600; //how long in seconds to limit within, recommend 1h = 3600
uint16 public rateLimit = 1000; //the basis points (00.0x%) to allow as the max sent within the last timeLimit time
mapping(address => uint8) public whitelisted; //stores whitelisted addresses to not be rate limited, useful for yearn style or deposit contracts
address public authAddress; //allows a set address that can whitelist addresses
//updates the time as well as the relative amount in basis points to track and rate limit outflows in which to track
//_rateLimit = 00.x%, _timeLimit = time in seconds
function updateLimits(uint16 _rateLimit, uint256 _timeLimit) internal {
rateLimit = _rateLimit;
timeLimit = _timeLimit;
}
//change the auth address that can whitelist addresses
function changeAuthAddress(address newAddrs) public {
require(msg.sender == authAddress);
authAddress = newAddrs;
}
//whitelist addresses as the msg.sender to not be rate limited
function whitelist(address addrs) public {
require(msg.sender == authAddress);
whitelisted[addrs] = 1;
}
//used to replace ERC20 erc20token.transfer() and ETH address.transfer() fns in all public accessible fns which change the balances for the contract as outflow transactions.
//to used for the recipient address, amount for value amount in raw value, token as the tokens contract address to check, for ETH use address(0x0)
function transferL(
address to,
uint256 amount,
address token
) public {
TokenInfo storage info = infoL[token];
if (whitelisted[msg.sender] == 1) {
if (address(token) == address(0)) {
payable(to).transfer(amount);
} else {
IERC20(token).transfer(to, amount);
}
}
//used if the asset is ETH and not an ERC20 token
else {
if (address(token) == address(0)) {
//used to get around solidity 0.8 reverts
unchecked {
//checks to see if the last transaction was outside the time window to track outflow for limiting
if (timeLimit <= block.timestamp - info.timestamp) {
info.rate = 0;
}
//if the last transaction was within the time window, decreases the tracked outflow rate relative to the time elapsed, so that the limit is able to update in realtime rather than in blocks, making flows smooth, and increasing the rate available as time increases without a transaction
else {
info.rate -= uint224(
(address(this).balance * rateLimit) /
(timeLimit /
(block.timestamp - info.timestamp)) /
10000
);
}
}
//increases the tracked rate for the current time window by the amount sent out
info.rate += uint224(amount);
//revert if the outflow exceeds rate limit
require(
info.rate <= (rateLimit * address(this).balance) / 10000
);
//sets the current time as the last transfer for the token
info.timestamp = uint32(block.timestamp);
//transfers out
payable(to).transfer(amount);
//if the token is a ERC20 token
} else {
//used to get around solidity 0.8 reverts
unchecked {
//checks to see if the last transaction was outside the time window to track outflow for limiting
if (timeLimit <= block.timestamp - info.timestamp) {
info.rate = 0;
}
//if the last transaction was within the time window, decreases the tracked outflow rate relative to the time elapsed, so that the limit is able to update in realtime rather than in blocks, making flows smooth, and increasing the rate available as time increases without a transaction
else {
info.rate -= uint224(
(IERC20(token).balanceOf(address(this)) *
rateLimit) /
(timeLimit /
(block.timestamp - info.timestamp)) /
10000
);
}
//increases the tracked rate for the current time window by the amount sent out
info.rate += uint224(amount);
//revert if the outflow exceeds rate limit
require(
info.rate <=
(rateLimit *
IERC20(token).balanceOf(address(this))) /
10000
);
//sets the current time as the last transfer for the token
info.timestamp = uint32(block.timestamp);
//transfers out
IERC20(token).transfer(to, amount);
}
}
}
}
}