@@ -7,6 +7,12 @@ pragma solidity ^0.8.20;
7
7
import { Ownable } from "openzeppelin-contracts/access/Ownable.sol " ;
8
8
import { IERC20 , SafeERC20 } from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol " ;
9
9
10
+ // @audit-info consider making this contract a clone for gas efficiency
11
+ // @audit-info 2-step ownership?
12
+ // @audit-info missing interface with errors and events?
13
+ // @audit-info "total vested Amount" is not stored. Any concern regarding this design choice and that
14
+ // `token.balanceOf(address(this))` can be increased anytime (which modifies `_vestingSchedule` return value)? All OK by
15
+ // me btw!
10
16
contract EnsoVestingWallet is Ownable {
11
17
using SafeERC20 for IERC20 ;
12
18
@@ -15,10 +21,13 @@ contract EnsoVestingWallet is Ownable {
15
21
16
22
uint256 public released;
17
23
bool public revoked;
24
+ // @audit-info `revoker == address(0)` means non-revocable forever.
18
25
bool public immutable revocable;
19
26
20
27
IERC20 private immutable _token;
28
+ // @audit-info revoker immutable? risks?
21
29
address private immutable _revoker;
30
+ // @audit-info consider using `uint48` (safe for timestamps), which will pack 3 timestamps in 12 bytes
22
31
uint64 private immutable _start;
23
32
uint64 private immutable _duration;
24
33
uint64 private immutable _cliff;
@@ -45,6 +54,7 @@ contract EnsoVestingWallet is Ownable {
45
54
_duration = durationSeconds;
46
55
_cliff = startTimestamp + cliffSeconds;
47
56
_token = token;
57
+ // @audit-info simplify to `_revoker = revoker` if `address(0)` means non-revocable forever
48
58
if (revoker != address (0 )) {
49
59
_revoker = revoker;
50
60
revocable = true ;
@@ -69,7 +79,6 @@ contract EnsoVestingWallet is Ownable {
69
79
* @dev Getter for the end timestamp.
70
80
*/
71
81
function end () public view returns (uint256 ) {
72
- // @audit return _start + _duration?
73
82
return start () + duration ();
74
83
}
75
84
@@ -98,7 +107,6 @@ contract EnsoVestingWallet is Ownable {
98
107
* @dev Getter for the amount of releasable tokens.
99
108
*/
100
109
function releasable () public view returns (uint256 ) {
101
- // @audit return `_vestingSchedule(...)`?
102
110
return vestedAmount (uint64 (block .timestamp )) - released;
103
111
}
104
112
@@ -116,7 +124,9 @@ contract EnsoVestingWallet is Ownable {
116
124
*/
117
125
function release () public {
118
126
if (revoked) revert Revoked ();
127
+ // @audit-info call `_vestingSchedule(...)`?
119
128
uint256 amount = releasable ();
129
+ // @audit-info skip checking amount for gas efficiency (and allow 0 transfers)
120
130
if (amount > 0 ) {
121
131
released += amount;
122
132
emit TokenReleased (amount);
@@ -145,6 +155,10 @@ contract EnsoVestingWallet is Ownable {
145
155
* an asset given its total historical allocation. Returns 0 if the {cliff} timestamp is not met.
146
156
*/
147
157
function _vestingSchedule (uint256 totalAllocation , uint64 timestamp ) internal view returns (uint256 ) {
158
+ // @audit-info if revoked revert with Revoked?
159
+ // @audit-info if `timestamp < _cliff` revert with BlockTimestampLtCliff(uint256 blockTimestamp, uint256 cliff)
160
+ // or `CliffNotReached(uint256 blockTimestamp, uint256 cliff)`?
161
+ // @audit-info no need for `if..else if..else`
148
162
if (revoked || timestamp < cliff ()) {
149
163
return 0 ;
150
164
} else if (timestamp >= end ()) {
0 commit comments