-
Notifications
You must be signed in to change notification settings - Fork 0
/
tokenomics.sol
185 lines (158 loc) · 8.09 KB
/
tokenomics.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
// by jnbez
//More generally than required.
//The owner is allowed to choose the appropriate distribution for him as circumstances or plans change
pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "openzeppelin-solidity/contracts/utils/math/SafeMath.sol";
contract tokenomics {
// boolean to prevent reentrancy
bool internal locked;
// Library usage
using SafeERC20 for IERC20;
using SafeMath for uint256;
// Contract owner
address payable public owner;
// Contract owner access
bool public allIncomingDepositsFinalised;
// Timestamp related variables
uint256 public initialTimestamp;
bool public timestampSet;
uint256 public timePeriod;
// Token amount variables
mapping(address => uint256) public alreadyWithdrawn;
mapping(address => uint256) public balances;
uint256 public contractBalance;
// ERC20 contract address
IERC20 public erc20Contract;
// Events
event TokensDeposited(address from, uint256 amount);
event AllocationPerformed(address recipient, uint256 amount);
event TokensUnlocked(address recipient, uint256 amount);
/// @dev Deploys contract and links the ERC20 token which we are timelocking, also sets owner as msg.sender and sets timestampSet bool to false.
/// @param _erc20_contract_address.
constructor(IERC20 _erc20_contract_address) {
// Allow this contract's owner to make deposits by setting allIncomingDepositsFinalised to false
allIncomingDepositsFinalised = false;
// Set contract owner
owner = payable(msg.sender);
// Timestamp values not set yet
timestampSet = false;
// Set the erc20 contract address which this timelock is deliberately paired to
require(address(_erc20_contract_address) != address(0), "_erc20_contract_address address can not be zero");
erc20Contract = _erc20_contract_address;
// Initialize the reentrancy variable to not locked
locked = false;
}
// Modifier
/**
* @dev Prevents reentrancy
*/
modifier noReentrant() {
require(!locked, "No re-entrancy");
locked = true;
_;
locked = false;
}
// Modifier
/**
* @dev Throws if allIncomingDepositsFinalised is true.
*/
modifier incomingDepositsStillAllowed() {
require(allIncomingDepositsFinalised == false, "Incoming deposits have been finalised.");
_;
}
// Modifier
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner, "Message sender must be the contract's owner.");
_;
}
// Modifier
/**
* @dev Throws if timestamp already set.
*/
modifier timestampNotSet() {
require(timestampSet == false, "The time stamp has already been set.");
_;
}
// Modifier
/**
* @dev Throws if timestamp not set.
*/
modifier timestampIsSet() {
require(timestampSet == true, "Please set the time stamp first, then try again.");
_;
}
receive() payable external incomingDepositsStillAllowed {
contractBalance = contractBalance.add(msg.value);
emit TokensDeposited(msg.sender, msg.value);
}
// @dev Takes away any ability (for the contract owner) to assign any tokens to any recipients. This function is only to be called by the contract owner. Calling this function can not be undone. Calling this function must only be performed when all of the addresses and amounts are allocated (to the recipients). This function finalizes the contract owners involvement and at this point the contract's timelock functionality is non-custodial
function finalizeAllIncomingDeposits() public onlyOwner timestampIsSet incomingDepositsStillAllowed {
allIncomingDepositsFinalised = true;
}
/// @dev Sets the initial timestamp and calculates locking period variables i.e. twelveMonths etc.
/// @param _timePeriodInSeconds amount of seconds to add to the initial timestamp i.e. we are essemtially creating the lockup period here
function setTimestamp(uint256 _timePeriodInSeconds) public onlyOwner timestampNotSet {
timestampSet = true;
initialTimestamp = block.timestamp;
timePeriod = initialTimestamp.add(_timePeriodInSeconds);
}
/// @dev Function to withdraw Eth in case Eth is accidently sent to this contract.
/// @param amount of network tokens to withdraw (in wei).
function withdrawEth(uint256 amount) public onlyOwner noReentrant{
require(amount <= contractBalance, "Insufficient funds");
contractBalance = contractBalance.sub(amount);
// Transfer the specified amount of Eth to the owner of this contract
owner.transfer(amount);
}
/// @dev Allows the contract owner to allocate official ERC20 tokens to each future recipient (only one at a time).
/// @param recipient, address of recipient.
/// @param amount to allocate to recipient.
function depositTokens(address recipient, uint256 amount) public onlyOwner timestampIsSet incomingDepositsStillAllowed {
require(recipient != address(0), "ERC20: transfer to the zero address");
balances[recipient] += amount;
emit AllocationPerformed(recipient, amount);
}
/// @dev Allows the contract owner to allocate official ERC20 tokens to multiple future recipient in bulk.
/// @param recipients, an array of addresses of the many recipient.
/// @param amounts to allocate to each of the many recipient.
function bulkDepositTokens(address[] calldata recipients, uint256[] calldata amounts) external onlyOwner timestampIsSet incomingDepositsStillAllowed {
require(recipients.length == amounts.length, "The recipients and amounts arrays must be the same size in length");
for(uint256 i = 0; i < recipients.length; i++) {
require(recipients[i] != address(0), "ERC20: transfer to the zero address");
balances[recipients[i]] +=amounts[i];
emit AllocationPerformed(recipients[i], amounts[i]);
}
}
/// @dev Allows recipient to unlock tokens after 12 month period has elapsed
/// @param token - address of the official ERC20 token which is being unlocked here.
/// @param to - the recipient's account address.
/// @param amount - the amount to unlock (in wei)
function transferTimeLockedTokensAfterTimePeriod(IERC20 token, address to, uint256 amount) public timestampIsSet noReentrant {
require(to != address(0), "ERC20: transfer to the zero address");
require(balances[to] >= amount, "Insufficient token balance, try lesser amount");
require(msg.sender == to, "Only the token recipient can perform the unlock");
require(token == erc20Contract, "Token parameter must be the same as the erc20 contract address which was passed into the constructor");
if (block.timestamp >= timePeriod) {
alreadyWithdrawn[to] += amount;
balances[to] -= amount;
token.safeTransfer(to, amount);
emit TokensUnlocked(to, amount);
} else {
revert("Tokens are only available after correct time period has elapsed");
}
}
/// @dev Transfer accidentally locked ERC20 tokens.
/// @param token - ERC20 token address.
/// @param amount of ERC20 tokens to remove.
function transferAccidentallyLockedTokens(IERC20 token, uint256 amount) public onlyOwner noReentrant {
require(address(token) != address(0), "Token address can not be zero");
// This function can not access the official timelocked tokens; just other random ERC20 tokens that may have been accidently sent here
require(token != erc20Contract, "Token address can not be ERC20 address which was passed into the constructor");
// Transfer the amount of the specified ERC20 tokens, to the owner of this contract
token.safeTransfer(owner, amount);
}
}