-
Notifications
You must be signed in to change notification settings - Fork 22
/
Token.sol
182 lines (163 loc) · 6.39 KB
/
Token.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
// SPDX-License-Identifier: APACHE-2.0
pragma solidity ^0.8.20;
// We are utilizing the 'openzeppelin/contracts-upgradeable' library instead of 'openzeppelin/contracts'.
// The reason for this is that the Upgradeable contracts use an 'initialize' function as a substitute for the constructor.
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "./MintAllowanceUpgradeable.sol";
import "./SystemRoleUpgradeable.sol";
import "./IValidator.sol";
/**
* @dev Token contract with upgradeable patterns, mint allowance, and system roles.
*/
contract Token is
Initializable,
ERC20PermitUpgradeable,
UUPSUpgradeable,
MintAllowanceUpgradeable,
SystemRoleUpgradeable
{
// Subsequent contract versions must retain this variable to avoid storage conflicts with the proxy.
IValidator public validator;
using SignatureChecker for address;
/**
* @dev Emitted when the contract owner recovers tokens.
* @param from Sender address.
* @param to Recipient address.
* @param amount Number of tokens.
*/
event Recovered(address indexed from, address indexed to, uint256 amount);
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
// This line is necessary for the upgradable pattern. It disables the initializers of the parent contracts to prevent them from being called twice.
}
function initialize(
string memory name,
string memory symbol,
address _validator
) public virtual initializer {
// Those line replaces the inheritance call in the constructor, as we are using the upgradeable pattern.
// In the upgradeable pattern, the 'initialize' function replaces the constructor. It's not called automatically.
// The proxy contract calls this function using delegatecall.
// This results in storing all new variables from ERC20, Ownable, etc., in the proxy's storage.
__ERC20_init(name, symbol);
__ERC20Permit_init(name);
__UUPSUpgradeable_init();
__SystemRole_init();
validator = IValidator(_validator);
require(validator.CONTRACT_ID() == keccak256("monerium.validator"), "Not Monerium Validator Contract");
}
// _authorizeUpgrade is a crucial part of the UUPS upgrade pattern in OpenZeppelin.
// By defining this function, we can control the upgrade process and prevent unauthorized changes.
// This function can be customized to include additional checks or logic, such as a timelock or a multisig requirement.
function _authorizeUpgrade(
address newImplementation
) internal override onlyOwner {}
function mint(address to, uint256 amount) public onlySystemAccounts {
_useMintAllowance(_msgSender(), amount);
_mint(to, amount);
}
function burn(
address from,
uint256 amount,
bytes32 ,
bytes memory signature
) public onlySystemAccounts {
require(
from.isValidSignatureNow(0xb77c35c892a1b24b10a2ce49b424e578472333ee8d2456234fff90626332c50f, signature),
"signature/hash does not match"
);
_burn(from, amount);
}
function recover(
address from,
address to,
bytes32 ,
uint8 v,
bytes32 r,
bytes32 s
) external onlySystemAccounts returns (uint256) {
bytes memory signature;
if (r != bytes32(0) || s != bytes32(0)) {
signature = abi.encodePacked(r, s, v);
}
require(
from.isValidSignatureNow(0xb77c35c892a1b24b10a2ce49b424e578472333ee8d2456234fff90626332c50f, signature),
"signature/hash does not match"
);
uint256 amount = balanceOf(from);
_burn(from, amount);
_mint(to, amount);
emit Recovered(from, to, amount);
return amount;
}
// Function to set the validator, restricted to owner
function setValidator(address _validator) public onlyOwner {
validator = IValidator(_validator);
require(validator.CONTRACT_ID() == keccak256("monerium.validator"), "Not Monerium Validator Contract");
}
// Override transfer function to invoke validator
function transfer(
address to,
uint256 amount
) public override returns (bool) {
require(
validator.validate(_msgSender(), to, amount),
"Transfer not validated"
);
return super.transfer(to, amount);
}
// Override transferFrom function to invoke validator
function transferFrom(
address from,
address to,
uint256 amount
) public override returns (bool) {
require(validator.validate(from, to, amount), "Transfer not validated");
return super.transferFrom(from, to, amount);
}
// setMaxMintAllowance is only callable by the owner
function setMaxMintAllowance(uint256 amount) public onlyOwner {
_setMaxMintAllowance(amount);
}
// setMintAllowance is only callable by the admins
function setMintAllowance(
address account,
uint256 amount
) public onlyAdminAccounts {
_setMintAllowance(account, amount);
}
// EIP-2612 helper
function getPermitDigest(
address owner,
address spender,
uint256 value,
uint256 nonce,
uint256 deadline
) public view returns (bytes32) {
return
keccak256(
abi.encodePacked(
"\x19\x01",
_domainSeparatorV4(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonce,
deadline
)
)
)
);
}
}