-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPRC20.sol
More file actions
261 lines (212 loc) · 8.61 KB
/
Copy pathPRC20.sol
File metadata and controls
261 lines (212 loc) · 8.61 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
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IPRC20} from "./interfaces/IPRC20.sol";
import {PRC20Errors, CommonErrors} from "./libraries/Errors.sol";
/**
* @title PRC20 (Push Chain Synthetic Token)
* @notice ERC-20 compatible synthetic token minted/burned by Push Chain protocol.
* @dev PRC20 token represents and acts as an alias for an already existing token
* of an external chain.
* All PRC20 tokens must adhere to the IPRC20 interface.
*/
contract PRC20 is IPRC20, Initializable {
// =========================
// PRC20: STATE VARIABLES
// =========================
/// @notice The protocol's privileged executor module (auth & fee sink).
address public immutable UNIVERSAL_EXECUTOR_MODULE = 0x14191Ea54B4c176fCf86f51b0FAc7CB1E71Df7d7;
/// @notice Source chain this PRC20 mirrors (used for oracle lookups).
string public SOURCE_CHAIN_NAMESPACE;
/// @notice Source chain ERC20 address of the PRC20.
string public SOURCE_TOKEN_ADDRESS;
/// @notice Classification of this synthetic.
TokenType public TOKEN_TYPE;
/// @notice UniversalCore contract providing gas oracles (gas coin token & gas price).
address public UNIVERSAL_CORE;
string private _name;
string private _symbol;
uint8 private _decimals;
uint256 private _totalSupply;
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
// =========================
// PRC20: MODIFIERS
// =========================
/// @notice Restricts to the Universal Executor Module (protocol owner).
modifier onlyUniversalExecutor() {
if (msg.sender != UNIVERSAL_EXECUTOR_MODULE) {
revert PRC20Errors.CallerIsNotUniversalExecutor();
}
_;
}
// =========================
// PRC20: CONSTRUCTOR
// =========================
constructor() {
_disableInitializers();
}
/// @dev Initializer for the upgradeable PRC20 token.
/// @param name_ ERC-20 name
/// @param symbol_ ERC-20 symbol
/// @param decimals_ ERC-20 decimals
/// @param sourceChainNamespace_ Source chain identifier this PRC20 represents
/// @param tokenType_ Token classification (PC, NATIVE, ERC20)
/// @param universalCore_ UniversalCore contract address
/// @param sourceTokenAddress_ Source chain token address
function initialize(
string memory name_,
string memory symbol_,
uint8 decimals_,
string memory sourceChainNamespace_,
TokenType tokenType_,
address universalCore_,
string memory sourceTokenAddress_
) public virtual initializer {
if (universalCore_ == address(0)) revert CommonErrors.ZeroAddress();
_name = name_;
_symbol = symbol_;
_decimals = decimals_;
SOURCE_CHAIN_NAMESPACE = sourceChainNamespace_;
TOKEN_TYPE = tokenType_;
UNIVERSAL_CORE = universalCore_;
SOURCE_TOKEN_ADDRESS = sourceTokenAddress_;
}
// =========================
// PRC20_1: ERC-20 VIEW
// =========================
/// @inheritdoc IPRC20
function name() external view returns (string memory) {
return _name;
}
/// @inheritdoc IPRC20
function symbol() external view returns (string memory) {
return _symbol;
}
/// @inheritdoc IPRC20
function decimals() external view returns (uint8) {
return _decimals;
}
/// @inheritdoc IPRC20
function totalSupply() external view returns (uint256) {
return _totalSupply;
}
/// @inheritdoc IPRC20
function balanceOf(address account) external view returns (uint256) {
return _balances[account];
}
/// @inheritdoc IPRC20
function allowance(address owner, address spender) external view returns (uint256) {
return _allowances[owner][spender];
}
// =========================
// PRC20_2: ERC-20 MUTATIVE
// =========================
/// @inheritdoc IPRC20
function transfer(address recipient, uint256 amount) external returns (bool) {
_transfer(msg.sender, recipient, amount);
return true;
}
/// @inheritdoc IPRC20
function approve(address spender, uint256 amount) external returns (bool) {
if (spender == address(0)) revert CommonErrors.ZeroAddress();
_allowances[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
/// @inheritdoc IPRC20
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool) {
_transfer(sender, recipient, amount);
uint256 currentAllowance = _allowances[sender][msg.sender];
if (currentAllowance < amount) revert PRC20Errors.LowAllowance();
unchecked {
_allowances[sender][msg.sender] = currentAllowance - amount;
}
emit Approval(sender, msg.sender, _allowances[sender][msg.sender]);
return true;
}
/// @inheritdoc IPRC20
function burn(uint256 amount) external returns (bool) {
_burn(msg.sender, amount);
return true;
}
// =========================
// PRC20_3: BRIDGE ENTRYPOINTS
// =========================
/// @inheritdoc IPRC20
function deposit(address to, uint256 amount) external returns (bool) {
if (msg.sender != UNIVERSAL_CORE && msg.sender != UNIVERSAL_EXECUTOR_MODULE) {
revert PRC20Errors.InvalidSender();
}
_mint(to, amount);
emit Deposit(abi.encodePacked(UNIVERSAL_EXECUTOR_MODULE), to, amount);
return true;
}
// =========================
// PRC20_4: ADMIN ACTIONS
// =========================
/// @notice Update UniversalCore contract (gas coin & price oracle source).
/// @param addr New UniversalCore address
function updateUniversalCore(address addr) external onlyUniversalExecutor {
if (addr == address(0)) revert CommonErrors.ZeroAddress();
UNIVERSAL_CORE = addr;
emit UpdatedUniversalCore(addr);
}
/// @notice Update token name.
/// @param newName New name string
function setName(string memory newName) external onlyUniversalExecutor {
_name = newName;
}
/// @notice Update token symbol.
/// @param newSymbol New symbol string
function setSymbol(string memory newSymbol) external onlyUniversalExecutor {
_symbol = newSymbol;
}
// =========================
// PRC20_5: INTERNAL HELPERS
// =========================
/// @dev Internal transfer with balance and zero-address checks.
/// @param sender Source address
/// @param recipient Destination address
/// @param amount Amount to transfer
function _transfer(address sender, address recipient, uint256 amount) internal {
if (sender == address(0) || recipient == address(0)) {
revert CommonErrors.ZeroAddress();
}
uint256 senderBalance = _balances[sender];
if (senderBalance < amount) {
revert CommonErrors.InsufficientBalance();
}
unchecked {
_balances[sender] = senderBalance - amount;
_balances[recipient] += amount;
}
emit Transfer(sender, recipient, amount);
}
/// @dev Internal mint — creates tokens and assigns to account.
/// @param account Address to mint to
/// @param amount Amount to mint
function _mint(address account, uint256 amount) internal {
if (account == address(0)) revert CommonErrors.ZeroAddress();
if (amount == 0) revert CommonErrors.ZeroAmount();
unchecked {
_totalSupply += amount;
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
}
/// @dev Internal burn — destroys tokens from account.
/// @param account Address to burn from
/// @param amount Amount to burn
function _burn(address account, uint256 amount) internal {
if (account == address(0)) revert CommonErrors.ZeroAddress();
if (amount == 0) revert CommonErrors.ZeroAmount();
uint256 bal = _balances[account];
if (bal < amount) revert CommonErrors.InsufficientBalance();
unchecked {
_balances[account] = bal - amount;
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
}
}