generated from PaulRBerg/hardhat-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathLiqualityToken.sol
More file actions
219 lines (185 loc) · 7.15 KB
/
LiqualityToken.sol
File metadata and controls
219 lines (185 loc) · 7.15 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
// SPDX-License-Identifier:MIT
pragma solidity 0.8.10;
import "./interfaces/ILiqualityToken.sol";
// Lightweight token modelled after UNI-LP:
// https://github.com/Uniswap/uniswap-v2-core/blob/v1.0.1/contracts/UniswapV2ERC20.sol
// Adds:
// - An exposed `mint()` with minting role
// - An exposed `burn()`
// - ERC-3009 (`transferWithAuthorization()`)
contract LiqualityToken is ILiqualityToken {
// bytes32 private constant EIP712DOMAIN_HASH =
// keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
bytes32 private constant EIP712DOMAIN_HASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
// bytes32 private constant NAME_HASH = keccak256("Liquality")
bytes32 private constant NAME_HASH =
0x7a7f4d1e33581297a7a1ad17d8d156602873773e72e0125abf5eac69cb500cb7;
// bytes32 private constant VERSION_HASH = keccak256("1")
bytes32 private constant VERSION_HASH =
0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
// bytes32 public constant PERMIT_TYPEHASH =
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 public constant PERMIT_TYPEHASH =
0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
// bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH =
// keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)");
bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH =
0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267;
string public constant name = "Liquality";
string public constant symbol = "LIQ";
uint8 public constant decimals = 18;
address public minter;
uint256 public override totalSupply;
mapping(address => uint256) public override balanceOf;
mapping(address => mapping(address => uint256)) public override allowance;
// ERC-2612, ERC-3009 state
mapping(address => uint256) public nonces;
mapping(address => mapping(bytes32 => bool)) public authorizationState;
event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);
event ChangeMinter(address indexed minter);
modifier onlyMinter() {
require(msg.sender == minter, "LIQ:NOT_MINTER");
_;
}
constructor(address initialMinter) {
_changeMinter(initialMinter);
}
function getChainId() public view returns (uint256 chainId) {
// solhint-disable-next-line no-inline-assembly
assembly {
chainId := chainid()
}
}
function getDomainSeparator() public view override returns (bytes32) {
return
keccak256(
abi.encode(EIP712DOMAIN_HASH, NAME_HASH, VERSION_HASH, getChainId(), address(this))
);
}
function mint(address to, uint256 value) external override onlyMinter returns (bool) {
_mint(to, value);
return true;
}
function burn(uint256 value) external override returns (bool) {
_burn(msg.sender, value);
return true;
}
function approve(address spender, uint256 value) external override returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
function transfer(address to, uint256 value) external override returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
function transferFrom(
address from,
address to,
uint256 value
) external override returns (bool) {
uint256 fromAllowance = allowance[from][msg.sender];
if (fromAllowance != type(uint256).max) {
// Allowance is implicitly checked with Solidity's underflow protection
allowance[from][msg.sender] = fromAllowance - value;
}
_transfer(from, to, value);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external override {
require(deadline >= block.timestamp, "LIQ:AUTH_EXPIRED");
bytes32 encodeData = keccak256(
abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner], deadline)
);
nonces[owner] = nonces[owner] + 1;
_validateSignedData(owner, encodeData, v, r, s);
_approve(owner, spender, value);
}
function transferWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
) external override {
require(block.timestamp > validAfter, "LIQ:AUTH_NOT_YET_VALID");
require(block.timestamp < validBefore, "LIQ:AUTH_EXPIRED");
require(!authorizationState[from][nonce], "LIQ:AUTH_ALREADY_USED");
bytes32 encodeData = keccak256(
abi.encode(
TRANSFER_WITH_AUTHORIZATION_TYPEHASH,
from,
to,
value,
validAfter,
validBefore,
nonce
)
);
_validateSignedData(from, encodeData, v, r, s);
authorizationState[from][nonce] = true;
emit AuthorizationUsed(from, nonce);
_transfer(from, to, value);
}
function _changeMinter(address newMinter) internal {
minter = newMinter;
emit ChangeMinter(newMinter);
}
function _validateSignedData(
address signer,
bytes32 encodeData,
uint8 v,
bytes32 r,
bytes32 s
) internal view {
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", getDomainSeparator(), encodeData));
address recoveredAddress = ecrecover(digest, v, r, s);
// Explicitly disallow authorizations for address(0) as ecrecover returns address(0) on malformed messages
require(
recoveredAddress != address(0) && recoveredAddress == signer,
"LIQ:INVALID_SIGNATURE"
);
}
function _mint(address to, uint256 value) internal {
totalSupply = totalSupply + value;
balanceOf[to] = balanceOf[to] + value;
emit Transfer(address(0), to, value);
}
function _burn(address from, uint256 value) internal {
// Balance is implicitly checked with Solidity's underflow protection
balanceOf[from] = balanceOf[from] - value;
totalSupply = totalSupply - value;
emit Transfer(from, address(0), value);
}
function _approve(
address owner,
address spender,
uint256 value
) private {
allowance[owner][spender] = value;
emit Approval(owner, spender, value);
}
function _transfer(
address from,
address to,
uint256 value
) private {
require(to != address(this) && to != address(0), "LIQ:RECEIVER_IS_TOKEN_OR_ZERO");
// Balance is implicitly checked with Solidity's underflow protection
balanceOf[from] = balanceOf[from] - value;
balanceOf[to] = balanceOf[to] + value;
emit Transfer(from, to, value);
}
}