-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathFrakToken.sol
138 lines (117 loc) · 5.27 KB
/
FrakToken.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
// SPDX-License-Identifier: GNU GPLv3
pragma solidity 0.8.23;
import { ERC20Upgradeable } from "@oz-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import { FrakRoles } from "../roles/FrakRoles.sol";
import { InvalidSigner } from "../utils/FrakErrors.sol";
import { FrakAccessControlUpgradeable } from "../roles/FrakAccessControlUpgradeable.sol";
import { IFrakToken } from "./IFrakToken.sol";
import { EIP712Diamond } from "../utils/EIP712Diamond.sol";
import { ECDSA } from "solady/utils/ECDSA.sol";
/// @author @KONFeature
/// @title FrakToken
/// @notice ERC20 Contract for the FRAK token
/// @dev Compliant with ERC20 - EIP712 - EIP2612
/// @custom:security-contact [email protected]
contract FrakToken is ERC20Upgradeable, FrakAccessControlUpgradeable, EIP712Diamond, IFrakToken {
/* -------------------------------------------------------------------------- */
/* Constants */
/* -------------------------------------------------------------------------- */
/// @dev Maximum cap of token, at 3 billion FRK
uint256 private constant _cap = 3_000_000_000 ether;
/* -------------------------------------------------------------------------- */
/* Custom errors */
/* -------------------------------------------------------------------------- */
/// @dev Gap variable for the previous domain separator variable from the EIP712 Base contract
bytes32 private _gapOldDomainSeparator;
/// @dev Gap variable for the previous nonce variable from the EIP712 Base contract
mapping(address => uint256) private _gapOldNonces;
/// @dev 'bytes4(keccak256(bytes("PermitDelayExpired()")))'
uint256 private constant _PERMIT_DELAYED_EXPIRED_SELECTOR = 0x95fc6e60;
/// @dev 'bytes4(keccak256(bytes("InvalidSigner()")))'
uint256 private constant _INVALID_SIGNER_SELECTOR = 0x815e1d64;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/* -------------------------------------------------------------------------- */
/* Versioning */
/* -------------------------------------------------------------------------- */
function initialize() external initializer {
string memory name = "Frak";
__ERC20_init(name, "FRK");
__FrakAccessControlUpgradeable_Minter_init();
_initializeEIP712(name);
// Current version is 2, since we use a version to reset the domain separator post EIP712 updates
}
/// @dev Update to diamond Eip712
function updateToDiamondEip712() external reinitializer(3) {
_initializeEIP712("FRK");
}
/* -------------------------------------------------------------------------- */
/* External write functions */
/* -------------------------------------------------------------------------- */
/// @dev Mint some FRK
function mint(address to, uint256 amount) external override onlyRole(FrakRoles.MINTER) {
if (totalSupply() + amount > _cap) revert CapExceed();
_mint(to, amount);
}
/// @dev Burn some FRK
function burn(uint256 amount) external override {
_burn(msg.sender, amount);
}
/// @dev Returns the cap on the token's total supply.
function cap() external view virtual override returns (uint256) {
return _cap;
}
/* -------------------------------------------------------------------------- */
/* Permit logic (greatly inspired by solmate) */
/* -------------------------------------------------------------------------- */
/// @dev EIP 2612, allow the owner to spend the given amount of FRK
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
)
external
payable
override
{
assembly {
if gt(timestamp(), deadline) {
mstore(0x00, _PERMIT_DELAYED_EXPIRED_SELECTOR)
revert(0x1c, 0x04)
}
}
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ECDSA.recover(
toTypedMessageHash(
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
useNonce(owner),
deadline
)
)
),
v,
r,
s
);
// Don't need to check for 0 address, or send event's, since approve already do it for us
if (recoveredAddress != owner) revert InvalidSigner();
// Approve the token
_approve(recoveredAddress, spender, value);
}
}
}