-
Notifications
You must be signed in to change notification settings - Fork 0
/
BITMarketsTokenAllocations.sol
166 lines (134 loc) · 5.11 KB
/
BITMarketsTokenAllocations.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
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.14;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {VestingWallet} from "@openzeppelin/contracts/finance/VestingWallet.sol";
import {BITMarketsToken} from "./BITMarketsToken.sol";
import {IBITMarketsTokenAllocations} from "./utils/IBITMarketsTokenAllocations.sol";
import {IVestingWallet} from "./utils/IVestingWallet.sol";
/// @custom:security-contact security@bitmarkets.com
contract BITMarketsTokenAllocations is IBITMarketsTokenAllocations {
using SafeERC20 for IERC20;
IERC20 private _token;
BITMarketsToken private _btmt;
address payable private _tokenWallet;
address private _allocationsAdmin;
uint64 private _cliffSeconds;
uint64 private _vestingDurationAfterCliffSeconds;
mapping(address => address) public _vestingWallets;
/**
* @dev Constructor
*
* @param w The wallet holding the tokens to be allocated
* @param allocationsAdminWallet The wallet that can call allocate
* @param t Address of the token being sold (token)
* @param cliff Seconds after current timestamp that tokens are locked
* @param vesting Seconds after cliff is finished where tokens vest linearly
*/
constructor(
address payable w,
address allocationsAdminWallet,
IERC20 t,
uint64 cliff,
uint64 vesting
) {
require(address(t) != address(0), "Token 0 address");
require(w != address(0), "Token wallet 0 address");
require(allocationsAdminWallet != address(0), "Admin wallet 0 address");
_tokenWallet = w;
_allocationsAdmin = allocationsAdminWallet;
_token = t;
_btmt = BITMarketsToken(address(t));
_cliffSeconds = cliff;
_vestingDurationAfterCliffSeconds = vesting;
}
/**
* @dev Expects amount converted to 10 ** 18
*/
function allocate(
address beneficiary,
uint256 amount,
uint64 cliffSeconds
) public virtual override {
require(msg.sender == _allocationsAdmin, "Invalid message sender");
require(
amount <=
Math.min(_token.balanceOf(_tokenWallet), _token.allowance(_tokenWallet, address(this))),
"Amount too large"
);
address vestingWalletAddress = _vestingWallets[beneficiary];
if (vestingWalletAddress == address(0)) {
uint64 cliffAdd = cliffSeconds > 0 && cliffSeconds < _cliffSeconds
? cliffSeconds
: _cliffSeconds;
VestingWallet vwallet = new VestingWallet(
beneficiary,
// solhint-disable-next-line not-rely-on-time
uint64(block.timestamp + cliffAdd),
_vestingDurationAfterCliffSeconds
);
_vestingWallets[beneficiary] = address(vwallet);
vestingWalletAddress = address(vwallet);
_btmt.addFeeless(vestingWalletAddress);
}
_token.safeTransferFrom(_tokenWallet, vestingWalletAddress, amount);
}
/**
* @dev Function to withdraw already vested tokens.
*/
function withdraw(address beneficiary) public virtual override {
address vestingWalletAddress = _vestingWallets[beneficiary];
require(vestingWalletAddress != address(0), "No vesting wallet");
IVestingWallet vwallet = IVestingWallet(vestingWalletAddress);
vwallet.release(address(_token));
}
/**
* @return the token being allocated
*/
function token() public view override returns (IERC20) {
return _token;
}
/**
* @return the address holding the tokens initially
*/
function wallet() public view override returns (address payable) {
return _tokenWallet;
}
/**
* @return the allocations admin wallet
*/
function admin() public view override returns (address) {
return _allocationsAdmin;
}
/**
* @return the address of the vesting wallet.
*/
function vestingWallet(address beneficiary) public view override returns (address) {
address vestingWalletAddress = _vestingWallets[beneficiary];
require(vestingWalletAddress != address(0), "No vesting wallet");
return vestingWalletAddress;
}
/**
* @dev Checks the cliff timestamp of the beneficiary's vesting wallet.
* @return Amount of retrievable tokens from vesting wallet.
*/
function getVestingWalletCliff(address beneficiary) public view returns (uint256) {
address vestingWalletAddress = _vestingWallets[beneficiary];
require(vestingWalletAddress != address(0), "No vesting wallet");
IVestingWallet vwallet = IVestingWallet(vestingWalletAddress);
// solhint-disable-next-line not-rely-on-time
return vwallet.start();
}
/**
* @dev Checks the amount of tokens that can be released.
* @return Amount of retrievable tokens from vesting wallet.
*/
function vestedAmount(address beneficiary) public view override returns (uint256) {
address vestingWalletAddress = _vestingWallets[beneficiary];
require(vestingWalletAddress != address(0), "No vesting wallet");
IVestingWallet vwallet = IVestingWallet(vestingWalletAddress);
// solhint-disable-next-line not-rely-on-time
return vwallet.vestedAmount(address(_token), uint64(block.timestamp));
}
}