@evvm/testnet-contracts 2.3.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.md +44 -24
  2. package/contracts/core/Core.sol +1392 -0
  3. package/contracts/core/lib/CoreStorage.sol +171 -0
  4. package/contracts/nameService/NameService.sol +613 -543
  5. package/contracts/nameService/lib/IdentityValidation.sol +15 -21
  6. package/contracts/p2pSwap/P2PSwap.sol +258 -145
  7. package/contracts/staking/Estimator.sol +25 -44
  8. package/contracts/staking/Staking.sol +284 -262
  9. package/contracts/treasury/Treasury.sol +40 -47
  10. package/contracts/treasuryTwoChains/TreasuryExternalChainStation.sol +585 -198
  11. package/contracts/treasuryTwoChains/TreasuryHostChainStation.sol +425 -174
  12. package/contracts/treasuryTwoChains/lib/PayloadUtils.sol +2 -4
  13. package/interfaces/{IEvvm.sol → ICore.sol} +58 -25
  14. package/interfaces/IEstimator.sol +1 -1
  15. package/interfaces/INameService.sol +46 -49
  16. package/interfaces/IP2PSwap.sol +16 -17
  17. package/interfaces/IStaking.sol +21 -17
  18. package/interfaces/ITreasury.sol +2 -1
  19. package/interfaces/ITreasuryExternalChainStation.sol +15 -9
  20. package/interfaces/ITreasuryHostChainStation.sol +14 -11
  21. package/interfaces/IUserValidator.sol +6 -0
  22. package/library/Erc191TestBuilder.sol +336 -471
  23. package/library/EvvmService.sol +27 -71
  24. package/library/errors/CoreError.sol +116 -0
  25. package/library/errors/CrossChainTreasuryError.sol +36 -0
  26. package/library/errors/NameServiceError.sol +79 -0
  27. package/library/errors/StakingError.sol +79 -0
  28. package/{contracts/treasury/lib/ErrorsLib.sol → library/errors/TreasuryError.sol} +9 -17
  29. package/library/structs/CoreStructs.sol +146 -0
  30. package/library/structs/ExternalChainStationStructs.sol +92 -0
  31. package/library/structs/HostChainStationStructs.sol +77 -0
  32. package/library/structs/NameServiceStructs.sol +47 -0
  33. package/library/structs/P2PSwapStructs.sol +127 -0
  34. package/library/structs/StakingStructs.sol +67 -0
  35. package/library/utils/AdvancedStrings.sol +62 -44
  36. package/library/utils/CAUtils.sol +29 -0
  37. package/library/utils/governance/Admin.sol +66 -0
  38. package/library/utils/governance/ProposalStructs.sol +49 -0
  39. package/library/utils/service/CoreExecution.sol +158 -0
  40. package/library/utils/service/StakingServiceUtils.sol +20 -37
  41. package/library/utils/signature/CoreHashUtils.sol +73 -0
  42. package/library/utils/signature/NameServiceHashUtils.sol +156 -0
  43. package/library/utils/signature/P2PSwapHashUtils.sol +65 -0
  44. package/library/utils/signature/StakingHashUtils.sol +41 -0
  45. package/library/utils/signature/TreasuryCrossChainHashUtils.sol +40 -0
  46. package/package.json +1 -1
  47. package/contracts/evvm/Evvm.sol +0 -1300
  48. package/contracts/evvm/lib/ErrorsLib.sol +0 -131
  49. package/contracts/evvm/lib/EvvmStorage.sol +0 -217
  50. package/contracts/evvm/lib/EvvmStructs.sol +0 -208
  51. package/contracts/evvm/lib/SignatureUtils.sol +0 -162
  52. package/contracts/nameService/lib/ErrorsLib.sol +0 -155
  53. package/contracts/nameService/lib/NameServiceStructs.sol +0 -125
  54. package/contracts/nameService/lib/SignatureUtils.sol +0 -420
  55. package/contracts/p2pSwap/lib/P2PSwapStructs.sol +0 -59
  56. package/contracts/p2pSwap/lib/SignatureUtils.sol +0 -98
  57. package/contracts/staking/lib/ErrorsLib.sol +0 -98
  58. package/contracts/staking/lib/SignatureUtils.sol +0 -105
  59. package/contracts/staking/lib/StakingStructs.sol +0 -106
  60. package/contracts/treasuryTwoChains/lib/ErrorsLib.sol +0 -48
  61. package/contracts/treasuryTwoChains/lib/ExternalChainStationStructs.sol +0 -80
  62. package/contracts/treasuryTwoChains/lib/HostChainStationStructs.sol +0 -87
  63. package/contracts/treasuryTwoChains/lib/SignatureUtils.sol +0 -79
  64. package/library/utils/GovernanceUtils.sol +0 -81
  65. package/library/utils/nonces/AsyncNonce.sol +0 -74
  66. package/library/utils/nonces/SyncNonce.sol +0 -71
  67. package/library/utils/service/EvvmPayments.sol +0 -144
@@ -0,0 +1,1392 @@
1
+ // SPDX-License-Identifier: EVVM-NONCOMMERCIAL-1.0
2
+ // Full license terms available at: https://www.evvm.info/docs/EVVMNoncommercialLicense
3
+
4
+ pragma solidity ^0.8.0;
5
+
6
+ import {
7
+ CoreStorage as Storage
8
+ } from "@evvm/testnet-contracts/contracts/core/lib/CoreStorage.sol";
9
+ import {
10
+ CoreError as Error
11
+ } from "@evvm/testnet-contracts/library/errors/CoreError.sol";
12
+ import {
13
+ CoreHashUtils as Hash
14
+ } from "@evvm/testnet-contracts/library/utils/signature/CoreHashUtils.sol";
15
+ import {
16
+ CoreStructs as Structs
17
+ } from "@evvm/testnet-contracts/library/structs/CoreStructs.sol";
18
+ import {
19
+ IUserValidator as UserValidator
20
+ } from "@evvm/testnet-contracts/interfaces/IUserValidator.sol";
21
+
22
+ import {
23
+ NameService
24
+ } from "@evvm/testnet-contracts/contracts/nameService/NameService.sol";
25
+
26
+ import {
27
+ AdvancedStrings
28
+ } from "@evvm/testnet-contracts/library/utils/AdvancedStrings.sol";
29
+ import {
30
+ SignatureRecover
31
+ } from "@evvm/testnet-contracts/library/primitives/SignatureRecover.sol";
32
+ import {CAUtils} from "@evvm/testnet-contracts/library/utils/CAUtils.sol";
33
+ import {
34
+ ProposalStructs
35
+ } from "@evvm/testnet-contracts/library/utils/governance/ProposalStructs.sol";
36
+
37
+ /**
38
+ ░▒▓██████▓▒░ ░▒▓██████▓▒░░▒▓███████▓▒░░▒▓████████▓▒░
39
+ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░
40
+ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░
41
+ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓███████▓▒░░▒▓██████▓▒░
42
+ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░
43
+ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░
44
+ ░▒▓██████▓▒░ ░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓████████▓▒░
45
+
46
+ ████████╗███████╗███████╗████████╗███╗ ██╗███████╗████████╗
47
+ ╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝████╗ ██║██╔════╝╚══██╔══╝
48
+ ██║ █████╗ ███████╗ ██║ ██╔██╗ ██║█████╗ ██║
49
+ ██║ ██╔══╝ ╚════██║ ██║ ██║╚██╗██║██╔══╝ ██║
50
+ ██║ ███████╗███████║ ██║ ██║ ╚████║███████╗ ██║
51
+ ╚═╝ ╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═══╝╚══════╝ ╚═╝
52
+ * @title EVVM Core
53
+ * @author Mate labs
54
+ * @notice Central logic for EVVM payments, token management, and nonce tracking.
55
+ * @dev Combines payment operations and nonce management.
56
+ * Features multi-token payments with EIP-191 signatures, dual nonce system (sync/async),
57
+ * and staker rewards. Governed by a time-delayed admin and implementation upgrade system.
58
+ */
59
+
60
+ contract Core is Storage {
61
+ /**
62
+ * @notice Restricts access to the system administrator.
63
+ */
64
+ modifier onlyAdmin() {
65
+ if (msg.sender != admin.current) revert Error.SenderIsNotAdmin();
66
+
67
+ _;
68
+ }
69
+
70
+ /**
71
+ * @notice Initializes the EVVM Core with basic system parameters.
72
+ * @param _initialOwner Address granted administrative control.
73
+ * @param _stakingContractAddress Address of the Staking contract.
74
+ * @param _evvmMetadata Initial configuration (token info, reward amounts, etc.).
75
+ */
76
+ constructor(
77
+ address _initialOwner,
78
+ address _stakingContractAddress,
79
+ Structs.EvvmMetadata memory _evvmMetadata
80
+ ) {
81
+ if (
82
+ _initialOwner == address(0) || _stakingContractAddress == address(0)
83
+ ) revert Error.AddressCantBeZero();
84
+
85
+ evvmMetadata = _evvmMetadata;
86
+
87
+ stakingContractAddress = _stakingContractAddress;
88
+
89
+ admin.current = _initialOwner;
90
+
91
+ balances[_stakingContractAddress][evvmMetadata.principalTokenAddress] =
92
+ getRewardAmount() *
93
+ 2;
94
+
95
+ stakerList[_stakingContractAddress] = FLAG_IS_STAKER;
96
+
97
+ breakerSetupNameServiceAddress = FLAG_IS_STAKER;
98
+ }
99
+
100
+ /**
101
+ * @notice Configures NameService and Treasury addresses once.
102
+ * @dev Uses a breaker flag to prevent re-initialization.
103
+ * @param _nameServiceAddress Address of the NameService contract.
104
+ * @param _treasuryAddress Address of the Treasury contract.
105
+ */
106
+ function initializeSystemContracts(
107
+ address _nameServiceAddress,
108
+ address _treasuryAddress
109
+ ) external {
110
+ if (breakerSetupNameServiceAddress == 0x00)
111
+ revert Error.BreakerExploded();
112
+
113
+ if (_nameServiceAddress == address(0) || _treasuryAddress == address(0))
114
+ revert Error.AddressCantBeZero();
115
+
116
+ nameServiceAddress = _nameServiceAddress;
117
+ balances[nameServiceAddress][evvmMetadata.principalTokenAddress] =
118
+ 10000 *
119
+ 10 ** 18;
120
+ stakerList[nameServiceAddress] = FLAG_IS_STAKER;
121
+
122
+ treasuryAddress = _treasuryAddress;
123
+ }
124
+
125
+ /**
126
+ * @notice Updates the EVVM ID within a 24-hour window after deployment or change.
127
+ * @param newEvvmID New unique identifier for EIP-191 signatures.
128
+ */
129
+ function setEvvmID(uint256 newEvvmID) external onlyAdmin {
130
+ if (evvmMetadata.EvvmID != 0) {
131
+ if (block.timestamp > windowTimeToChangeEvvmID)
132
+ revert Error.WindowExpired();
133
+ }
134
+
135
+ evvmMetadata.EvvmID = newEvvmID;
136
+
137
+ windowTimeToChangeEvvmID = block.timestamp + 24 hours;
138
+ }
139
+
140
+ /**
141
+ * @notice Proxy fallback forwarding calls to the active implementation.
142
+ * @dev Uses delegatecall to execute logic within this contract's storage context.
143
+ * Reverts if currentImplementation is address(0).
144
+ */
145
+ fallback() external {
146
+ if (currentImplementation == address(0))
147
+ revert Error.ImplementationIsNotActive();
148
+
149
+ assembly {
150
+ /**
151
+ * Copy the data of the call
152
+ * copy s bytes of calldata from position
153
+ * f to mem in position t
154
+ * calldatacopy(t, f, s)
155
+ */
156
+ calldatacopy(0, 0, calldatasize())
157
+
158
+ /**
159
+ * 2. We make a delegatecall to the implementation
160
+ * and we copy the result
161
+ */
162
+ let result := delegatecall(
163
+ gas(), // Send all the available gas
164
+ sload(currentImplementation.slot), // Address of the implementation
165
+ 0, // Start of the memory where the data is
166
+ calldatasize(), // Size of the data
167
+ 0, // Where we will store the response
168
+ 0 // Initial size of the response
169
+ )
170
+
171
+ /// Copy the response
172
+ returndatacopy(0, 0, returndatasize())
173
+
174
+ /// Handle the result
175
+ switch result
176
+ case 0 {
177
+ revert(0, returndatasize()) // If it failed, revert
178
+ }
179
+ default {
180
+ return(0, returndatasize()) // If it worked, return
181
+ }
182
+ }
183
+ }
184
+
185
+ /**
186
+ * @notice Faucet: Adds balance to a user for testing (Testnet only).
187
+ * @param user Recipient address.
188
+ * @param token Token contract address.
189
+ * @param quantity Amount to add.
190
+ */
191
+ function addBalance(
192
+ address user,
193
+ address token,
194
+ uint256 quantity
195
+ ) external {
196
+ balances[user][token] += quantity;
197
+ }
198
+
199
+ /**
200
+ * @notice Faucet: Sets staker status for testing (Testnet only).
201
+ * @param user User address.
202
+ * @param answer Status flag (e.g., FLAG_IS_STAKER).
203
+ */
204
+ function setPointStaker(address user, bytes1 answer) external {
205
+ stakerList[user] = answer;
206
+ }
207
+
208
+ //░▒▓█ Payment Functions ██████████████████████████████████████████████████████▓▒░
209
+
210
+ /**
211
+ * @notice Processes a single token payment with signature verification.
212
+ * @dev Validates nonce (sync/async), resolves identity (if provided), and updates balances.
213
+ * Rewarded if the executor is a staker.
214
+ * @param from Sender address.
215
+ * @param to_address Recipient address (overridden if to_identity is set).
216
+ * @param to_identity Recipient username (resolved via NameService).
217
+ * @param token Token address (address(0) for ETH).
218
+ * @param amount Tokens to transfer.
219
+ * @param priorityFee Fee paid to the executor (if staker).
220
+ * @param senderExecutor Optional authorized executor (address(0) for any).
221
+ * @param nonce Transaction nonce.
222
+ * @param isAsyncExec True for parallel nonces, false for sequential.
223
+ * @param signature EIP-191 authorization signature.
224
+ */
225
+ function pay(
226
+ address from,
227
+ address to_address,
228
+ string memory to_identity,
229
+ address token,
230
+ uint256 amount,
231
+ uint256 priorityFee,
232
+ address senderExecutor,
233
+ uint256 nonce,
234
+ bool isAsyncExec,
235
+ bytes memory signature
236
+ ) external {
237
+ if (
238
+ SignatureRecover.recoverSigner(
239
+ AdvancedStrings.buildSignaturePayload(
240
+ evvmMetadata.EvvmID,
241
+ address(this),
242
+ Hash.hashDataForPay(
243
+ to_address,
244
+ to_identity,
245
+ token,
246
+ amount,
247
+ priorityFee
248
+ ),
249
+ senderExecutor,
250
+ nonce,
251
+ isAsyncExec
252
+ ),
253
+ signature
254
+ ) != from
255
+ ) revert Error.InvalidSignature();
256
+
257
+ if (!canExecuteUserTransaction(from))
258
+ revert Error.UserCannotExecuteTransaction();
259
+
260
+ if (isAsyncExec) {
261
+ bytes1 statusNonce = asyncNonceStatus(from, nonce);
262
+ if (asyncNonceStatus(from, nonce) == 0x01)
263
+ revert Error.AsyncNonceAlreadyUsed();
264
+
265
+ if (
266
+ statusNonce == 0x02 &&
267
+ asyncNonceReservedPointers[from][nonce] != address(this)
268
+ ) revert Error.AsyncNonceIsReservedByAnotherService();
269
+
270
+ asyncNonce[from][nonce] = true;
271
+ } else {
272
+ if (nonce != nextSyncNonce[from]) revert Error.SyncNonceMismatch();
273
+
274
+ unchecked {
275
+ ++nextSyncNonce[from];
276
+ }
277
+ }
278
+
279
+ if ((senderExecutor != address(0)) && (msg.sender != senderExecutor))
280
+ revert Error.SenderIsNotTheSenderExecutor();
281
+
282
+ address to = !AdvancedStrings.equal(to_identity, "")
283
+ ? NameService(nameServiceAddress).verifyStrictAndGetOwnerOfIdentity(
284
+ to_identity
285
+ )
286
+ : to_address;
287
+
288
+ _updateBalance(from, to, token, amount);
289
+
290
+ if (isAddressStaker(msg.sender)) {
291
+ if (priorityFee > 0) {
292
+ _updateBalance(from, msg.sender, token, priorityFee);
293
+ }
294
+ _giveReward(msg.sender, 1);
295
+ }
296
+ }
297
+
298
+ /**
299
+ * @notice Processes multiple payments in a single transaction.
300
+ * @dev Each payment is validated and executed independently.
301
+ * @param batchData Array of payment details and signatures.
302
+ * @return successfulTransactions Count of successful payments.
303
+ * @return results Success status for each payment in the batch.
304
+ */
305
+ function batchPay(
306
+ Structs.BatchData[] memory batchData
307
+ ) external returns (uint256 successfulTransactions, bool[] memory results) {
308
+ bool isSenderStaker = isAddressStaker(msg.sender);
309
+ address to_aux;
310
+ Structs.BatchData memory payment;
311
+ results = new bool[](batchData.length);
312
+
313
+ for (uint256 iteration = 0; iteration < batchData.length; iteration++) {
314
+ payment = batchData[iteration];
315
+
316
+ if (
317
+ SignatureRecover.recoverSigner(
318
+ AdvancedStrings.buildSignaturePayload(
319
+ evvmMetadata.EvvmID,
320
+ address(this),
321
+ Hash.hashDataForPay(
322
+ payment.to_address,
323
+ payment.to_identity,
324
+ payment.token,
325
+ payment.amount,
326
+ payment.priorityFee
327
+ ),
328
+ payment.senderExecutor,
329
+ payment.nonce,
330
+ payment.isAsyncExec
331
+ ),
332
+ payment.signature
333
+ ) !=
334
+ payment.from ||
335
+ !canExecuteUserTransaction(payment.from)
336
+ ) {
337
+ results[iteration] = false;
338
+ continue;
339
+ }
340
+
341
+ if (payment.isAsyncExec) {
342
+ bytes1 statusNonce = asyncNonceStatus(
343
+ payment.from,
344
+ payment.nonce
345
+ );
346
+ if (
347
+ statusNonce == 0x01 ||
348
+ (statusNonce == 0x02 &&
349
+ asyncNonceReservedPointers[payment.from][
350
+ payment.nonce
351
+ ] !=
352
+ address(this))
353
+ ) {
354
+ results[iteration] = false;
355
+ continue;
356
+ }
357
+
358
+ asyncNonce[payment.from][payment.nonce] = true;
359
+ } else {
360
+ if (payment.nonce != nextSyncNonce[payment.from]) {
361
+ results[iteration] = false;
362
+ continue;
363
+ }
364
+
365
+ unchecked {
366
+ ++nextSyncNonce[payment.from];
367
+ }
368
+ }
369
+
370
+ if (
371
+ (payment.senderExecutor != address(0) &&
372
+ msg.sender != payment.senderExecutor) ||
373
+ ((isSenderStaker ? payment.priorityFee : 0) + payment.amount >
374
+ balances[payment.from][payment.token])
375
+ ) {
376
+ results[iteration] = false;
377
+ continue;
378
+ }
379
+
380
+ if (!AdvancedStrings.equal(payment.to_identity, "")) {
381
+ to_aux = NameService(nameServiceAddress).getOwnerOfIdentity(
382
+ payment.to_identity
383
+ );
384
+ if (to_aux == address(0)) {
385
+ results[iteration] = false;
386
+ continue;
387
+ }
388
+ } else {
389
+ to_aux = payment.to_address;
390
+ }
391
+
392
+ /// @dev Because of the previous check, _updateBalance can´t fail
393
+
394
+ _updateBalance(payment.from, to_aux, payment.token, payment.amount);
395
+
396
+ if (payment.priorityFee > 0 && isSenderStaker)
397
+ _updateBalance(
398
+ payment.from,
399
+ msg.sender,
400
+ payment.token,
401
+ payment.priorityFee
402
+ );
403
+
404
+ successfulTransactions++;
405
+ results[iteration] = true;
406
+ }
407
+
408
+ if (isSenderStaker) _giveReward(msg.sender, successfulTransactions);
409
+ }
410
+
411
+ /**
412
+ * @notice Distributes tokens from one sender to multiple recipients with a single signature.
413
+ * @param from Sender address.
414
+ * @param toData Array of recipient addresses/identities and their respective amounts.
415
+ * @param token Token address.
416
+ * @param amount Total amount to distribute (sum of toData).
417
+ * @param priorityFee Fee for the executor (if staker).
418
+ * @param nonce Transaction nonce.
419
+ * @param isAsyncExec True for parallel nonces.
420
+ * @param senderExecutor Optional authorized executor.
421
+ * @param signature EIP-191 authorization signature.
422
+ */
423
+ function dispersePay(
424
+ address from,
425
+ Structs.DispersePayMetadata[] memory toData,
426
+ address token,
427
+ uint256 amount,
428
+ uint256 priorityFee,
429
+ address senderExecutor,
430
+ uint256 nonce,
431
+ bool isAsyncExec,
432
+ bytes memory signature
433
+ ) external {
434
+ if (
435
+ SignatureRecover.recoverSigner(
436
+ AdvancedStrings.buildSignaturePayload(
437
+ evvmMetadata.EvvmID,
438
+ address(this),
439
+ Hash.hashDataForDispersePay(
440
+ toData,
441
+ token,
442
+ amount,
443
+ priorityFee
444
+ ),
445
+ senderExecutor,
446
+ nonce,
447
+ isAsyncExec
448
+ ),
449
+ signature
450
+ ) != from
451
+ ) revert Error.InvalidSignature();
452
+
453
+ if (!canExecuteUserTransaction(from))
454
+ revert Error.UserCannotExecuteTransaction();
455
+
456
+ if (isAsyncExec) {
457
+ bytes1 statusNonce = asyncNonceStatus(from, nonce);
458
+ if (asyncNonceStatus(from, nonce) == 0x01)
459
+ revert Error.AsyncNonceAlreadyUsed();
460
+
461
+ if (
462
+ statusNonce == 0x02 &&
463
+ asyncNonceReservedPointers[from][nonce] != address(this)
464
+ ) revert Error.AsyncNonceIsReservedByAnotherService();
465
+
466
+ asyncNonce[from][nonce] = true;
467
+ } else {
468
+ if (nonce != nextSyncNonce[from]) revert Error.SyncNonceMismatch();
469
+
470
+ unchecked {
471
+ ++nextSyncNonce[from];
472
+ }
473
+ }
474
+
475
+ if ((senderExecutor != address(0)) && (msg.sender != senderExecutor))
476
+ revert Error.SenderIsNotTheSenderExecutor();
477
+
478
+ bool isSenderStaker = isAddressStaker(msg.sender);
479
+
480
+ if (balances[from][token] < amount + (isSenderStaker ? priorityFee : 0))
481
+ revert Error.InsufficientBalance();
482
+
483
+ uint256 acomulatedAmount = 0;
484
+ balances[from][token] -= (amount + (isSenderStaker ? priorityFee : 0));
485
+ address to_aux;
486
+ for (uint256 i = 0; i < toData.length; i++) {
487
+ acomulatedAmount += toData[i].amount;
488
+
489
+ if (!AdvancedStrings.equal(toData[i].to_identity, "")) {
490
+ if (
491
+ NameService(nameServiceAddress).strictVerifyIfIdentityExist(
492
+ toData[i].to_identity
493
+ )
494
+ ) {
495
+ to_aux = NameService(nameServiceAddress).getOwnerOfIdentity(
496
+ toData[i].to_identity
497
+ );
498
+ }
499
+ } else {
500
+ to_aux = toData[i].to_address;
501
+ }
502
+
503
+ balances[to_aux][token] += toData[i].amount;
504
+ }
505
+
506
+ if (acomulatedAmount != amount) revert Error.InvalidAmount();
507
+
508
+ if (isSenderStaker) {
509
+ _giveReward(msg.sender, 1);
510
+ balances[msg.sender][token] += priorityFee;
511
+ }
512
+ }
513
+
514
+ /**
515
+ * @notice Contract-to-address payment function for authorized
516
+ * smart contracts
517
+ * @dev Allows registered contracts to distribute tokens without
518
+ * signature verification
519
+ *
520
+ * Authorization Model:
521
+ /**
522
+ * @notice Allows a smart contract (CA) to pay a recipient directly.
523
+ * @dev No signature required as the contract itself is the caller.
524
+ * @param to Recipient address.
525
+ * @param token Token address.
526
+ * @param amount Tokens to transfer.
527
+ */
528
+ function caPay(address to, address token, uint256 amount) external {
529
+ address from = msg.sender;
530
+
531
+ if (!CAUtils.verifyIfCA(from)) revert Error.NotAnCA();
532
+
533
+ _updateBalance(from, to, token, amount);
534
+
535
+ if (isAddressStaker(msg.sender)) _giveReward(msg.sender, 1);
536
+ }
537
+
538
+ /**
539
+ * @notice Allows a smart contract (CA) to distribute tokens to multiple recipients.
540
+ * @param toData Array of recipient addresses/identities and amounts.
541
+ * @param token Token address.
542
+ * @param amount Total amount to distribute.
543
+ */
544
+ function disperseCaPay(
545
+ Structs.DisperseCaPayMetadata[] memory toData,
546
+ address token,
547
+ uint256 amount
548
+ ) external {
549
+ address from = msg.sender;
550
+
551
+ if (!CAUtils.verifyIfCA(from)) revert Error.NotAnCA();
552
+
553
+ if (balances[from][token] < amount) revert Error.InsufficientBalance();
554
+
555
+ uint256 acomulatedAmount = 0;
556
+
557
+ balances[from][token] -= amount;
558
+
559
+ for (uint256 i = 0; i < toData.length; i++) {
560
+ acomulatedAmount += toData[i].amount;
561
+ balances[toData[i].toAddress][token] += toData[i].amount;
562
+ }
563
+
564
+ if (acomulatedAmount != amount) revert Error.InvalidAmount();
565
+
566
+ if (isAddressStaker(from)) _giveReward(from, 1);
567
+ }
568
+
569
+ //░▒▓█ Nonce and Signature Functions ██████████████████████████████████████████▓▒░
570
+
571
+ /**
572
+ * @notice Validates a user signature and consumes a nonce for an EVVM service.
573
+ * @dev Only callable by smart contracts (EVVM services). Atomic verification/consumption.
574
+ * @param user Address of the transaction signer.
575
+ * @param hashPayload Hash of the transaction parameters.
576
+ * @param originExecutor Optional tx.origin restriction (address(0) for none).
577
+ * @param nonce Nonce to validate and consume.
578
+ * @param isAsyncExec True for non-sequential nonces.
579
+ * @param signature User's authorization signature.
580
+ */
581
+ function validateAndConsumeNonce(
582
+ address user,
583
+ bytes32 hashPayload,
584
+ address originExecutor,
585
+ uint256 nonce,
586
+ bool isAsyncExec,
587
+ bytes memory signature
588
+ ) external {
589
+ address servicePointer = msg.sender;
590
+
591
+ if (!CAUtils.verifyIfCA(servicePointer))
592
+ revert Error.MsgSenderIsNotAContract();
593
+
594
+ if (
595
+ SignatureRecover.recoverSigner(
596
+ AdvancedStrings.buildSignaturePayload(
597
+ evvmMetadata.EvvmID,
598
+ servicePointer,
599
+ hashPayload,
600
+ originExecutor,
601
+ nonce,
602
+ isAsyncExec
603
+ ),
604
+ signature
605
+ ) != user
606
+ ) revert Error.InvalidSignature();
607
+
608
+ if (originExecutor != address(0) && tx.origin != originExecutor)
609
+ revert Error.OriginIsNotTheOriginExecutor();
610
+
611
+ if (!canExecuteUserTransaction(user))
612
+ revert Error.UserCannotExecuteTransaction();
613
+
614
+ if (isAsyncExec) {
615
+ bytes1 statusNonce = asyncNonceStatus(user, nonce);
616
+ if (asyncNonceStatus(user, nonce) == 0x01)
617
+ revert Error.AsyncNonceAlreadyUsed();
618
+
619
+ if (
620
+ statusNonce == 0x02 &&
621
+ asyncNonceReservedPointers[user][nonce] != servicePointer
622
+ ) revert Error.AsyncNonceIsReservedByAnotherService();
623
+
624
+ asyncNonce[user][nonce] = true;
625
+ } else {
626
+ if (nonce != nextSyncNonce[user]) revert Error.SyncNonceMismatch();
627
+
628
+ unchecked {
629
+ ++nextSyncNonce[user];
630
+ }
631
+ }
632
+ }
633
+
634
+ //░▒▓█ Nonce Reservation Functions ████████████████████████████████████████████▓▒░
635
+
636
+ /**
637
+ * @notice Reserves an async nonce for exclusive service use
638
+ * @dev Allows users to pre-allocate nonces to specific services
639
+ *
640
+ * Reservation System:
641
+ * - Users reserve nonces for specific service addresses
642
+ * - Prevents other services from using reserved nonces
643
+ * - Useful for multi-step or delayed operations
644
+ * - Reservation persists until revoked or nonce is used
645
+ *
646
+ * Use Cases:
647
+ * - Cross-chain operations requiring coordination
648
+ * - Multi-signature workflows with specific executors
649
+ * - Service-specific transaction queues
650
+ * - Preventing front-running by other services
651
+ *
652
+ * Security Features:
653
+ * - User-controlled reservation (msg.sender)
654
+ * - Validates service address is not zero
655
+ * - Prevents double reservation of same nonce
656
+ * - Cannot reserve already-used nonces
657
+ *
658
+ * @param nonce The async nonce value to reserve
659
+ * @param serviceAddress Service contract that can use nonce
660
+ */
661
+ function reserveAsyncNonce(uint256 nonce, address serviceAddress) external {
662
+ if (serviceAddress == address(0)) revert Error.InvalidServiceAddress();
663
+
664
+ if (asyncNonce[msg.sender][nonce]) revert Error.AsyncNonceAlreadyUsed();
665
+
666
+ if (asyncNonceReservedPointers[msg.sender][nonce] != address(0))
667
+ revert Error.AsyncNonceAlreadyReserved();
668
+
669
+ asyncNonceReservedPointers[msg.sender][nonce] = serviceAddress;
670
+ }
671
+
672
+ /**
673
+ * @notice Revokes a previously reserved async nonce
674
+ * @dev Allows clearing of nonce reservations for reuse
675
+ *
676
+ * Revocation Process:
677
+ * - Validates nonce has not been used yet
678
+ * - Checks that nonce is currently reserved
679
+ * - Clears the service address reservation
680
+ * - Nonce becomes available for any service
681
+ *
682
+ * Authorization:
683
+ * - Currently callable by anyone (potential security issue)
684
+ * - Should validate msg.sender is user or authorized
685
+ * - Allows cancellation of mistaken reservations
686
+ *
687
+ * Use Cases:
688
+ * - Canceling pending service operations
689
+ * - Correcting accidental reservations
690
+ * - Freeing nonces for different services
691
+ * @param nonce The async nonce to revoke reservation for
692
+ */
693
+ function revokeAsyncNonce(uint256 nonce) external {
694
+ if (asyncNonce[msg.sender][nonce]) revert Error.AsyncNonceAlreadyUsed();
695
+
696
+ if (asyncNonceReservedPointers[msg.sender][nonce] == address(0))
697
+ revert Error.AsyncNonceNotReserved();
698
+
699
+ asyncNonceReservedPointers[msg.sender][nonce] = address(0);
700
+ }
701
+
702
+ //░▒▓█ UserValidator Management Functions █████████████████████████████████████▓▒░
703
+
704
+ /**
705
+ * @notice Proposes new UserValidator contract address
706
+ * @dev Initiates time-delayed governance for validator changes
707
+ *
708
+ * Governance Process:
709
+ * - Admin proposes new validator contract address
710
+ * - 1-day delay enforced before acceptance
711
+ * - Allows community review of validator changes
712
+ * - Can be canceled before acceptance
713
+ *
714
+ * UserValidator Integration:
715
+ * - Optional contract for transaction filtering
716
+ * - Called during validateAndConsumeNonce execution
717
+ * - Can block specific users from executing transactions
718
+ * - Useful for compliance or security requirements
719
+ *
720
+ * Security Features:
721
+ * - Time-delayed governance (DELAY constant)
722
+ * - Admin-only proposal capability
723
+ * - Cancellation mechanism before activation
724
+ *
725
+ * @param newValidator Address of proposed UserValidator
726
+ */
727
+ function proposeUserValidator(address newValidator) external onlyAdmin {
728
+ userValidatorAddress.proposal = newValidator;
729
+ userValidatorAddress.timeToAccept =
730
+ block.timestamp +
731
+ TIME_TO_ACCEPT_PROPOSAL;
732
+ }
733
+
734
+ /**
735
+ * @notice Cancels pending UserValidator proposal
736
+ * @dev Resets proposal state before time-lock expires
737
+ *
738
+ * @custom:access Admin only
739
+ */
740
+ function cancelUserValidatorProposal() external onlyAdmin {
741
+ userValidatorAddress.proposal = address(0);
742
+ userValidatorAddress.timeToAccept = 0;
743
+ }
744
+
745
+ /**
746
+ * @notice Accepts UserValidator proposal after time-lock
747
+ * @dev Activates new validator after delay period expires
748
+ *
749
+ * Activation Process:
750
+ * - Validates time-lock period has passed
751
+ * - Sets new validator as current active validator
752
+ * - Clears proposal state
753
+ * - Validator becomes active immediately
754
+ *
755
+ * Impact:
756
+ * - All future transactions checked by new validator
757
+ * - Affects validateAndConsumeNonce behavior
758
+ * - Can block users from executing transactions
759
+ *
760
+ * @custom:access Admin only
761
+ * @custom:timelock Requires DELAY (1 day) to have passed
762
+ */
763
+ function acceptUserValidatorProposal() external onlyAdmin {
764
+ if (block.timestamp < userValidatorAddress.timeToAccept)
765
+ revert Error.ProposalForUserValidatorNotReady();
766
+
767
+ userValidatorAddress.current = userValidatorAddress.proposal;
768
+ userValidatorAddress.proposal = address(0);
769
+ userValidatorAddress.timeToAccept = 0;
770
+ }
771
+
772
+ //░▒▓█ Treasury Exclusive Functions ███████████████████████████████████████████▓▒░
773
+
774
+ /**
775
+ * @notice Adds tokens to a user's balance in the EVVM system
776
+ * @dev Restricted function that can only be called by the authorized treasury contract
777
+ *
778
+ * Treasury Operations:
779
+ * - Allows treasury to mint or credit tokens to user accounts
780
+ * - Used for reward distributions, airdrops, or token bridging
781
+ * - Direct balance manipulation bypasses normal transfer restrictions
782
+ * - No signature verification required (treasury authorization)
783
+ *
784
+ * Access Control:
785
+ * - Only the registered treasury contract can call this function
786
+ * - Reverts with SenderIsNotTreasury error for unauthorized callers
787
+ * - Provides centralized token distribution mechanism
788
+ *
789
+ * Use Cases:
790
+ * - Cross-chain bridge token minting
791
+ * - Administrative reward distributions
792
+ * - System-level token allocations
793
+ * - Emergency balance corrections
794
+ *
795
+ * @param user Address of the user to receive tokens
796
+ * @param token Address of the token contract to add balance for
797
+ * @param amount Amount of tokens to add to the user's balance
798
+ *
799
+ * @custom:access-control Only treasury contract
800
+ * @custom:security No overflow protection needed due to controlled access
801
+ */
802
+ function addAmountToUser(
803
+ address user,
804
+ address token,
805
+ uint256 amount
806
+ ) external {
807
+ if (msg.sender != treasuryAddress) revert Error.SenderIsNotTreasury();
808
+
809
+ balances[user][token] += amount;
810
+ }
811
+
812
+ /**
813
+ * @notice Deducts tokens from a user's system balance.
814
+ * @dev Restricted to the authorized Treasury contract.
815
+ * @param user Account to debit.
816
+ * @param token Token address.
817
+ * @param amount Amount to remove.
818
+ */
819
+ function removeAmountFromUser(
820
+ address user,
821
+ address token,
822
+ uint256 amount
823
+ ) external {
824
+ if (msg.sender != treasuryAddress) revert Error.SenderIsNotTreasury();
825
+
826
+ balances[user][token] -= amount;
827
+ }
828
+
829
+ //░▒▓█ Administrative Functions ████████████████████████████████████████████████████████▓▒░
830
+
831
+ //██ Proxy Management █████████████████████████████████████████████
832
+
833
+ /**
834
+ * @notice Proposes a new implementation contract for the proxy (30-day delay).
835
+ * @param _newImpl Address of the new logic contract.
836
+ */
837
+ function proposeImplementation(address _newImpl) external onlyAdmin {
838
+ if (_newImpl == address(0)) revert Error.IncorrectAddressInput();
839
+ proposalImplementation = _newImpl;
840
+ timeToAcceptImplementation =
841
+ block.timestamp +
842
+ TIME_TO_ACCEPT_IMPLEMENTATION;
843
+ }
844
+
845
+ /**
846
+ * @notice Cancels a pending implementation upgrade proposal.
847
+ */
848
+ function rejectUpgrade() external onlyAdmin {
849
+ proposalImplementation = address(0);
850
+ timeToAcceptImplementation = 0;
851
+ }
852
+
853
+ /**
854
+ * @notice Finalizes the implementation upgrade after the time delay.
855
+ */
856
+ function acceptImplementation() external onlyAdmin {
857
+ if (block.timestamp < timeToAcceptImplementation)
858
+ revert Error.TimeLockNotExpired();
859
+
860
+ currentImplementation = proposalImplementation;
861
+ proposalImplementation = address(0);
862
+ timeToAcceptImplementation = 0;
863
+ }
864
+
865
+ //██ Admin Management █████████████████████████████████████████████─
866
+
867
+ /**
868
+ * @notice Proposes a new administrator (1-day delay).
869
+ * @param _newOwner Address of the proposed admin.
870
+ */
871
+ function proposeAdmin(address _newOwner) external onlyAdmin {
872
+ if (_newOwner == address(0) || _newOwner == admin.current)
873
+ revert Error.IncorrectAddressInput();
874
+
875
+ admin = ProposalStructs.AddressTypeProposal({
876
+ current: admin.current,
877
+ proposal: _newOwner,
878
+ timeToAccept: block.timestamp + TIME_TO_ACCEPT_PROPOSAL
879
+ });
880
+ }
881
+
882
+ /**
883
+ * @notice Cancels a pending admin change proposal.
884
+ */
885
+ function rejectProposalAdmin() external onlyAdmin {
886
+ admin = ProposalStructs.AddressTypeProposal({
887
+ current: admin.current,
888
+ proposal: address(0),
889
+ timeToAccept: 0
890
+ });
891
+ }
892
+
893
+ /**
894
+ * @notice Finalizes the admin change after the time delay.
895
+ * @dev Must be called by the proposed admin.
896
+ */
897
+ function acceptAdmin() external {
898
+ if (block.timestamp < admin.timeToAccept)
899
+ revert Error.TimeLockNotExpired();
900
+
901
+ if (msg.sender != admin.proposal)
902
+ revert Error.SenderIsNotTheProposedAdmin();
903
+
904
+ admin = ProposalStructs.AddressTypeProposal({
905
+ current: admin.proposal,
906
+ proposal: address(0),
907
+ timeToAccept: 0
908
+ });
909
+ }
910
+
911
+ //░▒▓█ Reward System Functions █████████████████████████████████████████████████████████▓▒░
912
+
913
+ /**
914
+ * @notice Triggers a reward recalculation and era transition in the token economy
915
+ * @dev Implements deflationary tokenomics with halving mechanism and random rewards
916
+ *
917
+ * Era Transition Mechanism:
918
+ * - Activates when total supply exceeds current era token threshold
919
+ * - Moves half of remaining tokens to next era threshold
920
+ * - Halves the base reward amount for future transactions
921
+ * - Provides random Principal Token bonus to caller (1-5083x reward)
922
+ *
923
+ * Economic Impact:
924
+ * - Gradually reduces inflation through reward halving
925
+ * - Creates scarcity as era thresholds become harder to reach
926
+ * - Incentivizes early participation with higher rewards
927
+ * - Provides lottery-style bonus for triggering era transitions
928
+ *
929
+ * Requirements:
930
+ * - Total supply must exceed current era token threshold
931
+ * - Can be called by anyone when conditions are met
932
+ */
933
+ function recalculateReward() public {
934
+ if (evvmMetadata.totalSupply > evvmMetadata.eraTokens) {
935
+ evvmMetadata.eraTokens += ((evvmMetadata.totalSupply -
936
+ evvmMetadata.eraTokens) / 2);
937
+ balances[msg.sender][evvmMetadata.principalTokenAddress] +=
938
+ evvmMetadata.reward *
939
+ getRandom(1, 5083);
940
+ evvmMetadata.reward = evvmMetadata.reward / 2;
941
+ } else {
942
+ revert();
943
+ }
944
+ }
945
+
946
+ /**
947
+ * @notice Generates a pseudo-random number within a specified range
948
+ * @dev Uses block timestamp and prevrandao for randomness (suitable for non-critical randomness)
949
+ *
950
+ * Randomness Source:
951
+ * - Combines block.timestamp and block.prevrandao
952
+ * - Suitable for reward bonuses and non-security-critical randomness
953
+ * - Not suitable for high-stakes randomness requiring true unpredictability
954
+ *
955
+ * @param min Minimum value (inclusive)
956
+ * @param max Maximum value (inclusive)
957
+ * @return Random number between min and max (inclusive)
958
+ */
959
+ function getRandom(
960
+ uint256 min,
961
+ uint256 max
962
+ ) internal view returns (uint256) {
963
+ return
964
+ min +
965
+ (uint256(
966
+ keccak256(abi.encodePacked(block.timestamp, block.prevrandao))
967
+ ) % (max - min + 1));
968
+ }
969
+
970
+ //░▒▓█ Staking Integration Functions █████████████████████████████████████████████████▓▒░
971
+
972
+ /**
973
+ * @notice Updates staker status for a user address
974
+ * @dev Can only be called by the authorized staking contract
975
+ *
976
+ * Staker Status Management:
977
+ * - Controls who can earn staking rewards and process transactions
978
+ * - Integrates with external staking contract for validation
979
+ * - Updates affect payment processing privileges and reward eligibility
980
+ *
981
+ * Access Control:
982
+ * - Only the registered staking contract can call this function
983
+ * - Ensures staker status changes are properly authorized
984
+ *
985
+ * @param user Address to update staker status for
986
+ * @param answer Bytes1 flag indicating staker status/type
987
+ */
988
+ function pointStaker(address user, bytes1 answer) public {
989
+ if (msg.sender != stakingContractAddress) revert();
990
+
991
+ stakerList[user] = answer;
992
+ }
993
+
994
+ //░▒▓█ View Functions █████████████████████████████████████████████████████████████████▓▒░
995
+
996
+ /**
997
+ * @notice Returns the complete EVVM metadata configuration
998
+ * @dev Provides access to system-wide configuration and economic parameters
999
+ *
1000
+ * Metadata Contents:
1001
+ * - Principal token address (Principal Token)
1002
+ * - Current reward amount per transaction
1003
+ * - Total supply tracking
1004
+ * - Era tokens threshold for reward transitions
1005
+ * - System configuration parameters
1006
+ *
1007
+ * @return Complete EvvmMetadata struct with all system parameters
1008
+ */
1009
+ function getEvvmMetadata()
1010
+ external
1011
+ view
1012
+ returns (Structs.EvvmMetadata memory)
1013
+ {
1014
+ return evvmMetadata;
1015
+ }
1016
+
1017
+ /**
1018
+ * @notice Gets the address representing the Principal Token in balance mappings
1019
+ * @dev Returns the virtual address used to track Principal Token balances in the balances mapping
1020
+ * This is not an ERC20 contract address but a sentinel value for the EVVM-native token
1021
+ * @return Address used as the key for Principal Token balances
1022
+ */
1023
+ function getPrincipalTokenAddress() external view returns (address) {
1024
+ return evvmMetadata.principalTokenAddress;
1025
+ }
1026
+
1027
+ /**
1028
+ * @notice Gets the address representing native chain currency (ETH/MATIC) in balance mappings
1029
+ * @dev Returns address(0) which is the standard sentinel for native blockchain tokens
1030
+ * Use this address as the token parameter when dealing with ETH or chain-native assets
1031
+ * @return address(0) representing the native chain currency
1032
+ */
1033
+ function getChainHostCoinAddress() external pure returns (address) {
1034
+ return address(0);
1035
+ }
1036
+
1037
+ /**
1038
+ * @notice Gets the unique identifier string for this EVVM instance
1039
+ * @dev Returns the EvvmID used for distinguishing different EVVM deployments
1040
+ * @return Unique EvvmID string
1041
+ */
1042
+ function getEvvmID() external view returns (uint256) {
1043
+ return evvmMetadata.EvvmID;
1044
+ }
1045
+
1046
+ /**
1047
+ * @notice Gets the acceptance deadline for pending token whitelist proposals
1048
+ * @dev Returns timestamp when prepared tokens can be added to whitelist
1049
+ * @return Timestamp when pending token can be whitelisted (0 if no pending proposal)
1050
+ */
1051
+ function getWhitelistTokenToBeAddedDateToSet()
1052
+ external
1053
+ view
1054
+ returns (uint256)
1055
+ {
1056
+ return whitelistTokenToBeAdded_dateToSet;
1057
+ }
1058
+
1059
+ /**
1060
+ * @notice Gets the current NameService contract address
1061
+ * @dev Returns the address used for identity resolution in payments
1062
+ * @return Address of the integrated NameService contract
1063
+ */
1064
+ function getNameServiceAddress() external view returns (address) {
1065
+ return nameServiceAddress;
1066
+ }
1067
+
1068
+ /**
1069
+ * @notice Gets the authorized staking contract address
1070
+ * @dev Returns the address that can modify staker status and receive rewards
1071
+ * @return Address of the integrated staking contract
1072
+ */
1073
+ function getStakingContractAddress() external view returns (address) {
1074
+ return stakingContractAddress;
1075
+ }
1076
+
1077
+ /**
1078
+ * @notice Gets the next Fisher Bridge deposit nonce for a user
1079
+ * @dev Returns the expected nonce for the next cross-chain deposit
1080
+ * @param user Address to check deposit nonce for
1081
+ * @return Next Fisher Bridge deposit nonce
1082
+ */
1083
+ function getNextFisherDepositNonce(
1084
+ address user
1085
+ ) external view returns (uint256) {
1086
+ return nextFisherDepositNonce[user];
1087
+ }
1088
+
1089
+ /**
1090
+ * @notice Gets the balance of a specific token for a user
1091
+ * @dev Returns the current balance stored in the EVVM system
1092
+ * @param user Address to check balance for
1093
+ * @param token Token contract address to check
1094
+ * @return Current token balance for the user
1095
+ */
1096
+ function getBalance(
1097
+ address user,
1098
+ address token
1099
+ ) external view returns (uint) {
1100
+ return balances[user][token];
1101
+ }
1102
+
1103
+ /**
1104
+ * @notice Checks if an address is registered as a staker
1105
+ * @dev Verifies staker status for transaction processing privileges and rewards
1106
+ * @param user Address to check staker status for
1107
+ * @return True if the address is a registered staker
1108
+ */
1109
+ function isAddressStaker(address user) public view returns (bool) {
1110
+ return stakerList[user] == FLAG_IS_STAKER;
1111
+ }
1112
+
1113
+ /**
1114
+ * @notice Gets the current era token threshold for reward transitions
1115
+ * @dev Returns the token supply threshold that triggers the next reward halving
1116
+ * @return Current era tokens threshold
1117
+ */
1118
+ function getEraPrincipalToken() public view returns (uint256) {
1119
+ return evvmMetadata.eraTokens;
1120
+ }
1121
+
1122
+ /**
1123
+ * @notice Gets the current Principal Token reward amount per transaction
1124
+ * @dev Returns the base reward distributed to stakers for transaction processing
1125
+ * @return Current reward amount in Principal Tokens
1126
+ */
1127
+ function getRewardAmount() public view returns (uint256) {
1128
+ return evvmMetadata.reward;
1129
+ }
1130
+
1131
+ /**
1132
+ * @notice Gets the total supply of the Principal Token
1133
+ * @dev Returns the current total supply used for era transition calculations
1134
+ * @return Total supply of Principal Tokens
1135
+ */
1136
+ function getPrincipalTokenTotalSupply() public view returns (uint256) {
1137
+ return evvmMetadata.totalSupply;
1138
+ }
1139
+
1140
+ /**
1141
+ * @notice Gets the current active implementation contract address
1142
+ * @dev Returns the implementation used by the proxy for delegatecalls
1143
+ * @return Address of the current implementation contract
1144
+ */
1145
+ function getCurrentImplementation() public view returns (address) {
1146
+ return currentImplementation;
1147
+ }
1148
+
1149
+ /**
1150
+ * @notice Gets the proposed implementation contract address
1151
+ * @dev Returns the implementation pending approval for proxy upgrade
1152
+ * @return Address of the proposed implementation contract (zero if none)
1153
+ */
1154
+ function getProposalImplementation() public view returns (address) {
1155
+ return proposalImplementation;
1156
+ }
1157
+
1158
+ /**
1159
+ * @notice Gets the acceptance deadline for the pending implementation upgrade
1160
+ * @dev Returns timestamp when the proposed implementation can be accepted
1161
+ * @return Timestamp when implementation upgrade can be executed (0 if no pending proposal)
1162
+ */
1163
+ function getTimeToAcceptImplementation() public view returns (uint256) {
1164
+ return timeToAcceptImplementation;
1165
+ }
1166
+
1167
+ /**
1168
+ * @notice Gets the current admin address
1169
+ * @dev Returns the address with administrative privileges over the contract
1170
+ * @return Address of the current admin
1171
+ */
1172
+ function getCurrentAdmin() public view returns (address) {
1173
+ return admin.current;
1174
+ }
1175
+
1176
+ /**
1177
+ * @notice Gets the proposed admin address
1178
+ * @dev Returns the address pending approval for admin privileges
1179
+ * @return Address of the proposed admin (zero if no pending proposal)
1180
+ */
1181
+ function getProposalAdmin() public view returns (address) {
1182
+ return admin.proposal;
1183
+ }
1184
+
1185
+ /**
1186
+ * @notice Gets the acceptance deadline for the pending admin change
1187
+ * @dev Returns timestamp when the proposed admin can accept the role
1188
+ * @return Timestamp when admin change can be executed (0 if no pending proposal)
1189
+ */
1190
+ function getTimeToAcceptAdmin() public view returns (uint256) {
1191
+ return admin.timeToAccept;
1192
+ }
1193
+
1194
+ /**
1195
+ * @notice Gets the address of the token pending whitelist approval
1196
+ * @dev Returns the token address that can be whitelisted after time delay
1197
+ * @return Address of the token prepared for whitelisting (zero if none)
1198
+ */
1199
+ function getWhitelistTokenToBeAdded() public view returns (address) {
1200
+ return whitelistTokenToBeAdded_address;
1201
+ }
1202
+
1203
+ /**
1204
+ * @notice Gets service address that reserved an async nonce
1205
+ * @dev Returns address(0) if nonce is not reserved
1206
+ *
1207
+ * @param user Address of the user who owns the nonce
1208
+ * @param nonce Async nonce to check reservation for
1209
+ * @return Service address that reserved the nonce, or
1210
+ * address(0) if not reserved
1211
+ */
1212
+ function getAsyncNonceReservation(
1213
+ address user,
1214
+ uint256 nonce
1215
+ ) public view returns (address) {
1216
+ return asyncNonceReservedPointers[user][nonce];
1217
+ }
1218
+
1219
+ /**
1220
+ * @notice Gets comprehensive status of an async nonce
1221
+ * @dev Returns byte code indicating nonce state
1222
+ *
1223
+ * Status Codes:
1224
+ * - 0x00: Available (can be used by any service)
1225
+ * - 0x01: Used (already consumed, cannot be reused)
1226
+ * - 0x02: Reserved (allocated to specific service)
1227
+ *
1228
+ * @param user Address of the user who owns the nonce
1229
+ * @param nonce Async nonce to check status for
1230
+ * @return Status code: 0x00 (available), 0x01 (used),
1231
+ * or 0x02 (reserved)
1232
+ */
1233
+ function asyncNonceStatus(
1234
+ address user,
1235
+ uint256 nonce
1236
+ ) public view returns (bytes1) {
1237
+ if (asyncNonce[user][nonce]) {
1238
+ return 0x01;
1239
+ } else if (asyncNonceReservedPointers[user][nonce] != address(0)) {
1240
+ return 0x02;
1241
+ } else {
1242
+ return 0x00;
1243
+ }
1244
+ }
1245
+
1246
+ /**
1247
+ * @notice Checks if a specific nonce has been used by a user
1248
+ * @dev Public view function for external queries and UI integration
1249
+ * @param user Address of the user to check
1250
+ * @param nonce The nonce value to query
1251
+ * @return True if the nonce has been used, false if available
1252
+ */
1253
+ function getIfUsedAsyncNonce(
1254
+ address user,
1255
+ uint256 nonce
1256
+ ) public view virtual returns (bool) {
1257
+ return asyncNonce[user][nonce];
1258
+ }
1259
+
1260
+ /**
1261
+ * @notice Gets the current (next expected) nonce for a user
1262
+ * @dev Public view function for external queries and transaction preparation
1263
+ * @param user Address of the user to query
1264
+ * @return The next nonce value that must be used by the user
1265
+ */
1266
+ function getNextCurrentSyncNonce(
1267
+ address user
1268
+ ) public view virtual returns (uint256) {
1269
+ return nextSyncNonce[user];
1270
+ }
1271
+
1272
+ /**
1273
+ * @notice Gets current UserValidator contract address
1274
+ * @dev Returns address(0) if no validator is configured
1275
+ *
1276
+ * @return Address of active UserValidator contract
1277
+ */
1278
+ function getUserValidatorAddress() public view returns (address) {
1279
+ return userValidatorAddress.current;
1280
+ }
1281
+
1282
+ /**
1283
+ * @notice Gets full UserValidator proposal details
1284
+ * @dev Returns current, proposed address and time-lock info
1285
+ *
1286
+ * @return Proposal struct with current validator address,
1287
+ * proposed address, and time to accept
1288
+ */
1289
+ function getUserValidatorAddressDetails()
1290
+ public
1291
+ view
1292
+ returns (ProposalStructs.AddressTypeProposal memory)
1293
+ {
1294
+ return userValidatorAddress;
1295
+ }
1296
+
1297
+ //░▒▓█ Internal Functions █████████████████████████████████████████████████████▓▒░
1298
+
1299
+ //██ Balance Management █████████████████████████████████████████████
1300
+
1301
+ /**
1302
+ * @notice Internal function to safely transfer tokens between addresses
1303
+ * @dev Performs balance validation and atomic transfer with overflow protection
1304
+ *
1305
+ * Transfer Process:
1306
+ * - Validates sender has sufficient balance
1307
+ * - Performs atomic balance updates using unchecked arithmetic
1308
+ * - Returns success/failure status for error handling
1309
+ *
1310
+ * Security Features:
1311
+ * - Balance validation prevents overdrafts
1312
+ * - Unchecked arithmetic for gas optimization (overflow impossible)
1313
+ * - Returns boolean for caller error handling
1314
+ *
1315
+ * @param from Address to transfer tokens from
1316
+ * @param to Address to transfer tokens to
1317
+ * @param token Address of the token contract
1318
+ * @param value Amount of tokens to transfer
1319
+ */
1320
+ function _updateBalance(
1321
+ address from,
1322
+ address to,
1323
+ address token,
1324
+ uint256 value
1325
+ ) internal {
1326
+ uint256 fromBalance = balances[from][token];
1327
+ if (fromBalance < value) revert Error.InsufficientBalance();
1328
+
1329
+ unchecked {
1330
+ balances[from][token] = fromBalance - value;
1331
+ balances[to][token] += value;
1332
+ }
1333
+ }
1334
+
1335
+ /**
1336
+ * @notice Internal function to distribute Principal Token rewards to stakers
1337
+ * @dev Provides incentive distribution for transaction processing and staking participation
1338
+ *
1339
+ * Reward System:
1340
+ * - Calculates reward based on system reward rate and transaction count
1341
+ * - Directly increases principal token balance for gas efficiency
1342
+ * - Returns success status for error handling in calling functions
1343
+ *
1344
+ * Reward Calculation:
1345
+ * - Base reward per transaction: evvmMetadata.reward
1346
+ * - Total reward: base_reward × transaction_amount
1347
+ * - Added directly to user's Principal Token balance
1348
+ *
1349
+ * @param user Address of the staker to receive principal token rewards
1350
+ * @param amount Number of transactions or reward multiplier
1351
+ * @return success True if reward distribution completed successfully
1352
+ */
1353
+ function _giveReward(address user, uint256 amount) internal returns (bool) {
1354
+ uint256 principalReward = evvmMetadata.reward * amount;
1355
+ uint256 userBalance = balances[user][
1356
+ evvmMetadata.principalTokenAddress
1357
+ ];
1358
+
1359
+ balances[user][evvmMetadata.principalTokenAddress] =
1360
+ userBalance +
1361
+ principalReward;
1362
+
1363
+ return (userBalance + principalReward ==
1364
+ balances[user][evvmMetadata.principalTokenAddress]);
1365
+ }
1366
+
1367
+ //██ User Validation █████████████████████████████████████████████
1368
+
1369
+ /**
1370
+ * @notice Validates if user can execute transactions
1371
+ * @dev Checks with UserValidator if configured, allows all if not
1372
+ *
1373
+ * Validation Logic:
1374
+ * - If no validator configured: Returns true (all allowed)
1375
+ * - If validator configured: Delegates to validator.canExecute
1376
+ * - Used by validateAndConsumeNonce before nonce consumption
1377
+ *
1378
+ * Integration:
1379
+ * - Called during every transaction validation
1380
+ * - Allows external filtering of user transactions
1381
+ * - Supports compliance and security requirements
1382
+ *
1383
+ * @param user Address to check execution permission for
1384
+ * @return True if user can execute, false if blocked
1385
+ */
1386
+ function canExecuteUserTransaction(
1387
+ address user
1388
+ ) internal view returns (bool) {
1389
+ if (userValidatorAddress.current == address(0)) return true;
1390
+ return UserValidator(userValidatorAddress.current).canExecute(user);
1391
+ }
1392
+ }