-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathToken.sol
More file actions
410 lines (373 loc) · 22.2 KB
/
Token.sol
File metadata and controls
410 lines (373 loc) · 22.2 KB
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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.23;
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20SnapshotUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/metatx/ERC2771ContextUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "./AllowList.sol";
import "./interfaces/IFeeSettings.sol";
/**
* @title tokenize.it Token
* @author malteish, cjentzsch
* @notice This contract implements the token used to tokenize companies, which follows the ERC20 standard and adds the following features:
* - pausing
* - access control with dedicated roles
* - burning (burner role can burn any token from any address)
* - requirements for sending and receiving tokens
* - allow list (documents which address satisfies which requirement)
* Decimals is inherited as 18 from ERC20. This should be the standard to adhere by for all deployments of this token.
*
* @dev The contract inherits from ERC2771Context in order to be usable with Gas Station Network (GSN) https://docs.opengsn.org/faq/troubleshooting.html#my-contract-is-using-openzeppelin-how-do-i-add-gsn-support and meta-transactions.
*/
contract Token is
ERC2771ContextUpgradeable,
ERC20PermitUpgradeable,
ERC20SnapshotUpgradeable,
PausableUpgradeable,
AccessControlUpgradeable,
UUPSUpgradeable
{
/// @notice The role that has the ability to define which requirements an address must satisfy to receive tokens
bytes32 public constant REQUIREMENT_ROLE = keccak256("REQUIREMENT_ROLE");
/// @notice The role that has the ability to grant minting allowances
bytes32 public constant MINTALLOWER_ROLE = keccak256("MINTALLOWER_ROLE");
/// @notice The role that has the ability to burn tokens from anywhere. Usage is planned for legal purposes and error recovery.
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
/// @notice The role that has the ability to grant transfer rights to other addresses
bytes32 public constant TRANSFERERADMIN_ROLE = keccak256("TRANSFERERADMIN_ROLE");
/// @notice Addresses with this role do not need to satisfy any requirements to send or receive tokens
bytes32 public constant TRANSFERER_ROLE = keccak256("TRANSFERER_ROLE");
/// @notice The role that has the ability to pause the token. Transferring, burning and minting will not be possible while the contract is paused.
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
/// @notice The role that can create snapshots of the token balances
bytes32 public constant SNAPSHOTCREATOR_ROLE = keccak256("SNAPSHOTCREATOR_ROLE");
/**
* @dev This empty reserved space is put in place to allow future versions to inherit
* from contracts that need storage. This mimics openzeppelin's approach.
* For more information, see https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
* Its size is chosen for the first used variable (allowList) to be in storage slot 1000.
* This is checked in the testTokenStorageGap unit test.
* Whenever an inheritance is added, removed or changed, the storage location of allowList and
* all following variables is likely to change too, with catastrophic results. So remember to update
* this gap in order to keep allowList at position 1000!
* More info at [../docs/upgradeability.md]
*/
uint256[446] private __gap;
// Map managed by tokenize.it, which assigns addresses requirements which they fulfill
AllowList public allowList;
// Fee settings of tokenize.it
IFeeSettingsV2 public feeSettings;
// Suggested new fee settings, which will be applied after admin approval
IFeeSettingsV2 public suggestedFeeSettings;
/**
* @notice defines requirements to send or receive tokens for non-TRANSFERER_ROLE. If zero, everbody can transfer the token. If non-zero, then only those who have met the requirements can send or receive tokens.
* Requirements can be defined by the REQUIREMENT_ROLE, and are validated against the allowList. They can include things like "must have a verified email address", "must have a verified phone number", "must have a verified identity", etc.
* Also, tiers from 0 to four can be used.
* @dev Requirements are defined as bit mask, with the bit position encoding it's meaning and the bit's value whether this requirement will be enforced.
* Example:
* - position 0: 1 = must be KYCed (0 = no KYC required)
* - position 1: 1 = must be american citizen (0 = american citizenship not required)
* - position 2: 1 = must be a penguin (0 = penguin status not required)
* These meanings are not defined within code, neither in the token contract nor the allowList. Nevertheless, the definition used by the people responsible for both contracts MUST match,
* or the token contract will not work as expected. E.g. if the allowList defines position 2 as "is a penguin", while the token contract uses position 2 as "is a hedgehog", then the tokens
* might be sold to hedgehogs, which was never the intention.
* Here some examples of how requirements can be used in practice:
* With requirements 0b0000000000000000000000000000000000000000000000000000000000000101, only KYCed penguins will be allowed to send or receive tokens.
* With requirements 0b0000000000000000000000000000000000000000000000000000000000000111, only KYCed american penguins will be allowed to send or receive tokens.
* With requirements 0b0000000000000000000000000000000000000000000000000000000000000000, even french hedgehogs will be allowed to send or receive tokens.
*
* The highest four bits are defined as tiers as follows:
* - 0b0000000000000000000000000000000000000000000000000000000000000000 = tier 0 is required
* - 0b0001000000000000000000000000000000000000000000000000000000000000 = tier 1 is required
* - 0b0010000000000000000000000000000000000000000000000000000000000000 = tier 2 is required
* - 0b0100000000000000000000000000000000000000000000000000000000000000 = tier 3 is required
* - 0b1000000000000000000000000000000000000000000000000000000000000000 = tier 4 is required
*
* Keep in mind that addresses with the TRANSFERER_ROLE do not need to satisfy any requirements to send or receive tokens.
*/
uint256 public requirements;
/**
* @notice defines the maximum amount of tokens that can be minted by a specific address. If zero, no tokens can be minted.
* Tokens paid as fees, as specified in the `feeSettings` contract, do not require an allowance.
* Example: Fee is set to 1% and mintingAllowance is 100. When executing the `mint` function with 100 as `amount`,
* 100 tokens will be minted to the `to` address, and 1 token to the feeCollector.
*/
mapping(address => uint256) public mintingAllowance; // used for token generating events such as vesting or new financing rounds
/**
* @notice The version of the token contract. This is used to keep track of upgrades.
* @dev The token contract is upgradeable, so the version is stored in the contract itself. Updating the logic contract address
* in the proxy will not change the version. If desired, the version must be changed in the new initializer function.
*/
uint256 public version;
/// @param newRequirements The new requirements that will be enforced from now on.
event RequirementsChanged(uint newRequirements);
/// @param newAllowList The AllowList contract that is in use from now on.
event AllowListChanged(AllowList indexed newAllowList);
/// @param suggestedFeeSettings The FeeSettings contract that has been suggested, but not yet approved by the admin.
event NewFeeSettingsSuggested(IFeeSettingsV2 indexed suggestedFeeSettings);
/// @param newFeeSettings The FeeSettings contract that is in use from now on.
event FeeSettingsChanged(IFeeSettingsV2 indexed newFeeSettings);
/// @param minter The address for which the minting allowance has been changed.
/// @param newAllowance The new minting allowance for the address (does not include fees).
event MintingAllowanceChanged(address indexed minter, uint256 newAllowance);
/**
* @notice Constructor a token logic contract that can be used by clones. It does not initialize state variables properly, so the resulting contract will not be functional.
* @param _trustedForwarder trusted forwarder for the ERC2771Context constructor - used for meta-transactions. OpenGSN v2 Forwarder should be used.
*/
constructor(address _trustedForwarder) ERC2771ContextUpgradeable(_trustedForwarder) {
_disableInitializers();
}
/**
* @notice Initializer for the token, replaces the constructor
* @param _feeSettings fee settings contract that determines the fee for minting tokens
* @param _admin address of the admin. Admin will initially have all roles and can grant roles to other addresses.
* @param _name name of the specific token, e.g. "MyGmbH Token"
* @param _symbol symbol of the token, e.g. "MGT"
* @param _allowList allowList contract that defines which addresses satisfy which requirements
* @param _requirements requirements an address has to meet for sending or receiving tokens
*/
function initialize(
IFeeSettingsV2 _feeSettings,
address _admin,
AllowList _allowList,
uint256 _requirements,
string memory _name,
string memory _symbol
) public initializer {
// Grant admin roles
_grantRole(DEFAULT_ADMIN_ROLE, _admin); // except for the Transferer role, the _admin is the roles admin for all other roles
_setRoleAdmin(TRANSFERER_ROLE, TRANSFERERADMIN_ROLE);
// grant all roles to admin for now. Can be changed later, see https://docs.openzeppelin.com/contracts/4.x/api/access#IAccessControl
_grantRole(REQUIREMENT_ROLE, _admin);
_grantRole(MINTALLOWER_ROLE, _admin);
_grantRole(BURNER_ROLE, _admin);
_grantRole(TRANSFERERADMIN_ROLE, _admin);
_grantRole(PAUSER_ROLE, _admin);
_grantRole(SNAPSHOTCREATOR_ROLE, _admin);
// set up fee collection
_checkIfFeeSettingsImplementsInterface(_feeSettings);
feeSettings = _feeSettings;
// set up allowList
require(address(_allowList) != address(0), "AllowList must not be zero address");
allowList = _allowList;
// set requirements (can be 0 to allow everyone to send and receive tokens)
requirements = _requirements;
// set version (can be updated in proxy storage by later implementation contracts)
version = 1;
__ERC20Permit_init(_name);
__ERC20Snapshot_init();
__ERC20_init(_name, _symbol);
__UUPSUpgradeable_init();
}
/**
* This function is called during the upgrade process. The modifier onlyRole(DEFAULT_ADMIN_ROLE)
* is used to make sure that only the admin can perform upgrades.
* @dev REMEMBER to make the new logic contract ERC1967Upgrade compatible, and, if required,
* include a new initializer function!
* @param newImplementation address of the new implementation contract
*/
function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) {}
/**
* @notice Change the AllowList that defines which addresses satisfy which requirements to `_allowList`.
* @dev An interface check is not necessary because AllowList can not break the token like FeeSettings could.
* @param _allowList new AllowList contract
*/
function setAllowList(AllowList _allowList) external onlyRole(DEFAULT_ADMIN_ROLE) {
require(address(_allowList) != address(0), "AllowList must not be zero address");
allowList = _allowList;
emit AllowListChanged(_allowList);
}
/**
* @notice Change the requirements an address has to meet for sending or receiving tokens to `_requirements`.
* @param _requirements requirements an address has to meet for sending or receiving tokens
*/
function setRequirements(uint256 _requirements) external onlyRole(REQUIREMENT_ROLE) {
requirements = _requirements;
emit RequirementsChanged(_requirements);
}
/**
* @notice This function can only be used by the feeSettings owner to suggest switching to a new feeSettings contract.
* The new feeSettings contract will be applied immediately after admin approval.
* @dev This is a possibility to change fees without honoring the delay enforced in the feeSettings contract. Therefore, approval of the admin is required.
* The feeSettings contract can brick the token, so an interface check is necessary.
* @param _feeSettings the new feeSettings contract
*/
function suggestNewFeeSettings(IFeeSettingsV2 _feeSettings) external {
require(_msgSender() == feeSettings.owner(), "Only fee settings owner can suggest fee settings update");
_checkIfFeeSettingsImplementsInterface(_feeSettings);
suggestedFeeSettings = _feeSettings;
emit NewFeeSettingsSuggested(_feeSettings);
}
/**
* @notice This function can only be used by the default admin to approve switching to the new feeSettings contract.
* The new feeSettings contract will be applied immediately.
* @dev Enforcing the suggested and accepted new contract to be the same is necessary to prevent frontrunning the acceptance with a new suggestion.
* Checking if the address implements the interface also prevents the 0 address from being accepted.
* @param _feeSettings the new feeSettings contract
*/
function acceptNewFeeSettings(IFeeSettingsV2 _feeSettings) external onlyRole(DEFAULT_ADMIN_ROLE) {
// after deployment, suggestedFeeSettings is 0x0. Therefore, this check is necessary, otherwise the admin could accept 0x0 as new feeSettings.
// Checking that the suggestedFeeSettings is not 0x0 would work, too, but this check is used in other places, too.
_checkIfFeeSettingsImplementsInterface(_feeSettings);
require(_feeSettings == suggestedFeeSettings, "Only suggested fee settings can be accepted");
feeSettings = suggestedFeeSettings;
emit FeeSettingsChanged(_feeSettings);
}
/**
* @notice Increase the amount of tokens `_minter` can mint by `_allowance`. Any address can be used, e.g. of an investment contract like PrivateOffer, a vesting contract, or an EOA.
* The contract does not keep track of how many tokens a minter has minted over time
* @param _minter address of the minter
* @param _allowance how many tokens can be minted by this minter, in addition to their current allowance (excluding the tokens minted as a fee)
*/
function increaseMintingAllowance(address _minter, uint256 _allowance) external onlyRole(MINTALLOWER_ROLE) {
mintingAllowance[_minter] += _allowance;
emit MintingAllowanceChanged(_minter, mintingAllowance[_minter]);
}
/**
* @notice Reduce the amount of tokens `_minter` can mint by `_allowance`.
* @dev Underflow is cast to 0 in order to be able to use decreaseMintingAllowance(minter, UINT256_MAX) to reset the allowance to 0.
* @param _minter address of the minter
* @param _allowance how many tokens should be deducted from the current minting allowance (excluding the tokens minted as a fee)
*/
function decreaseMintingAllowance(address _minter, uint256 _allowance) external onlyRole(MINTALLOWER_ROLE) {
if (mintingAllowance[_minter] > _allowance) {
mintingAllowance[_minter] -= _allowance;
emit MintingAllowanceChanged(_minter, mintingAllowance[_minter]);
} else {
mintingAllowance[_minter] = 0;
emit MintingAllowanceChanged(_minter, 0);
}
}
/**
* @notice Mint `_amount` tokens to `_to` and pay the fee to the fee collector
* @param _to address that receives the tokens
* @param _amount how many tokens to mint
*/
function mint(address _to, uint256 _amount) external {
if (!hasRole(MINTALLOWER_ROLE, _msgSender())) {
require(mintingAllowance[_msgSender()] >= _amount, "MintingAllowance too low");
mintingAllowance[_msgSender()] -= _amount;
}
// this check is executed here, because later minting of the buy amount can not be differentiated from minting of the fee amount
_checkIfAllowedToTransact(_to);
_mint(_to, _amount);
// collect fees
uint256 fee = feeSettings.tokenFee(_amount, address(this));
if (fee != 0) {
// the fee collector is always allowed to receive tokens
_mint(feeSettings.tokenFeeCollector(address(this)), fee);
}
}
/**
* @notice Burn `_amount` tokens from `_from`.
* @param _from address that holds the tokens
* @param _amount how many tokens to burn
*/
function burn(address _from, uint256 _amount) external onlyRole(BURNER_ROLE) {
_burn(_from, _amount);
}
/**
* This token can keep track of historical balances. In order to store all current balances, a snapshot must be created.
* Call this function to create a snapshot. The historical balances can be accessed with the balanceOfAt function.
* They could be used to determine eligibility for airdrops or dividends.
* @return id of the snapshot
*/
function createSnapshot() external onlyRole(SNAPSHOTCREATOR_ROLE) returns (uint256) {
return _snapshot();
}
/**
* @notice There are 3 types of transfers:
* 1. minting: transfers from the zero address to another address. Only minters can do this, which is checked in the mint function. The recipient must be allowed to transact.
* 2. burning: transfers from an address to the zero address. Only burners can do this, which is checked in the burn function.
* 3. transfers from one address to another. The sender and recipient must be allowed to transact.
* @dev this hook is executed before the transfer function itself
*/
function _beforeTokenTransfer(
address _from,
address _to,
uint256 _amount
) internal virtual override(ERC20SnapshotUpgradeable, ERC20Upgradeable) {
super._beforeTokenTransfer(_from, _to, _amount);
_requireNotPaused();
if (_from != address(0) && _to != address(0)) {
// token transfer
_checkIfAllowedToTransact(_from);
_checkIfAllowedToTransact(_to);
}
/* if _from is 0x0, tokens are minted:
- receiver's properties are checked in the mint function
- the minter's allowance is checked in the mint function
- extra tokens can be minted for feeCollector in the mint function
if _to is 0x0, tokens are burned:
- only burner is allowed to do this, which is checked in the burn function
*/
}
/**
* @notice checks if `_address` is either a transferer or satisfies the requirements.
* @param _address address to check
*/
function _checkIfAllowedToTransact(address _address) internal view {
require(
requirements == 0 ||
hasRole(TRANSFERER_ROLE, _address) ||
allowList.map(_address) & requirements == requirements,
"Sender or Receiver is not allowed to transact. Either locally issue the role as a TRANSFERER or they must meet requirements as defined in the allowList"
);
}
/**
* @notice Make sure `_feeSettings` actually implements the interfaces that are needed.
* This is a sanity check to make sure that the FeeSettings contract is actually compatible with this token.
* @dev This check uses EIP165, see https://eips.ethereum.org/EIPS/eip-165
* @param _feeSettings address of the FeeSettings contract
*/
function _checkIfFeeSettingsImplementsInterface(IFeeSettingsV2 _feeSettings) internal view {
// step 1: needs to return true if EIP165 is supported
require(_feeSettings.supportsInterface(0x01ffc9a7) == true, "FeeSettings must implement IFeeSettingsV2");
// step 2: needs to return false if EIP165 is supported
require(_feeSettings.supportsInterface(0xffffffff) == false, "FeeSettings must implement IFeeSettingsV2");
// now we know EIP165 is supported
// step 3: needs to return true if IFeeSettingsV2 is supported
require(
_feeSettings.supportsInterface(type(IFeeSettingsV2).interfaceId),
"FeeSettings must implement IFeeSettingsV2"
);
}
/**
* pause the token contract. No transfers, burning or minting will be possible while the contract is paused.
*/
function pause() external onlyRole(PAUSER_ROLE) {
_pause();
}
/**
* unpause the token contract. Transfers, burning and minting will be possible again.
*/
function unpause() external onlyRole(PAUSER_ROLE) {
_unpause();
}
/**
* @dev both ERC20Pausable and ERC2771Context have a _msgSender() function, so we need to override and select which one to use.
*/
function _msgSender() internal view override(ContextUpgradeable, ERC2771ContextUpgradeable) returns (address) {
return ERC2771ContextUpgradeable._msgSender();
}
/**
* @dev both ERC20Pausable and ERC2771Context have a _msgData() function, so we need to override and select which one to use.
*/
function _msgData() internal view override(ContextUpgradeable, ERC2771ContextUpgradeable) returns (bytes calldata) {
return ERC2771ContextUpgradeable._msgData();
}
/**
* @dev both Ownable and ERC2771Context have a _contextSuffixLength() function, so we need to override and select which one to use.
*/
function _contextSuffixLength()
internal
view
virtual
override(ContextUpgradeable, ERC2771ContextUpgradeable)
returns (uint256)
{
return ERC2771ContextUpgradeable._contextSuffixLength();
}
}