@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.
- package/README.md +44 -24
- package/contracts/core/Core.sol +1392 -0
- package/contracts/core/lib/CoreStorage.sol +171 -0
- package/contracts/nameService/NameService.sol +613 -543
- package/contracts/nameService/lib/IdentityValidation.sol +15 -21
- package/contracts/p2pSwap/P2PSwap.sol +258 -145
- package/contracts/staking/Estimator.sol +25 -44
- package/contracts/staking/Staking.sol +284 -262
- package/contracts/treasury/Treasury.sol +40 -47
- package/contracts/treasuryTwoChains/TreasuryExternalChainStation.sol +585 -198
- package/contracts/treasuryTwoChains/TreasuryHostChainStation.sol +425 -174
- package/contracts/treasuryTwoChains/lib/PayloadUtils.sol +2 -4
- package/interfaces/{IEvvm.sol → ICore.sol} +58 -25
- package/interfaces/IEstimator.sol +1 -1
- package/interfaces/INameService.sol +46 -49
- package/interfaces/IP2PSwap.sol +16 -17
- package/interfaces/IStaking.sol +21 -17
- package/interfaces/ITreasury.sol +2 -1
- package/interfaces/ITreasuryExternalChainStation.sol +15 -9
- package/interfaces/ITreasuryHostChainStation.sol +14 -11
- package/interfaces/IUserValidator.sol +6 -0
- package/library/Erc191TestBuilder.sol +336 -471
- package/library/EvvmService.sol +27 -71
- package/library/errors/CoreError.sol +116 -0
- package/library/errors/CrossChainTreasuryError.sol +36 -0
- package/library/errors/NameServiceError.sol +79 -0
- package/library/errors/StakingError.sol +79 -0
- package/{contracts/treasury/lib/ErrorsLib.sol → library/errors/TreasuryError.sol} +9 -17
- package/library/structs/CoreStructs.sol +146 -0
- package/library/structs/ExternalChainStationStructs.sol +92 -0
- package/library/structs/HostChainStationStructs.sol +77 -0
- package/library/structs/NameServiceStructs.sol +47 -0
- package/library/structs/P2PSwapStructs.sol +127 -0
- package/library/structs/StakingStructs.sol +67 -0
- package/library/utils/AdvancedStrings.sol +62 -44
- package/library/utils/CAUtils.sol +29 -0
- package/library/utils/governance/Admin.sol +66 -0
- package/library/utils/governance/ProposalStructs.sol +49 -0
- package/library/utils/service/CoreExecution.sol +158 -0
- package/library/utils/service/StakingServiceUtils.sol +20 -37
- package/library/utils/signature/CoreHashUtils.sol +73 -0
- package/library/utils/signature/NameServiceHashUtils.sol +156 -0
- package/library/utils/signature/P2PSwapHashUtils.sol +65 -0
- package/library/utils/signature/StakingHashUtils.sol +41 -0
- package/library/utils/signature/TreasuryCrossChainHashUtils.sol +40 -0
- package/package.json +1 -1
- package/contracts/evvm/Evvm.sol +0 -1300
- package/contracts/evvm/lib/ErrorsLib.sol +0 -131
- package/contracts/evvm/lib/EvvmStorage.sol +0 -217
- package/contracts/evvm/lib/EvvmStructs.sol +0 -208
- package/contracts/evvm/lib/SignatureUtils.sol +0 -162
- package/contracts/nameService/lib/ErrorsLib.sol +0 -155
- package/contracts/nameService/lib/NameServiceStructs.sol +0 -125
- package/contracts/nameService/lib/SignatureUtils.sol +0 -420
- package/contracts/p2pSwap/lib/P2PSwapStructs.sol +0 -59
- package/contracts/p2pSwap/lib/SignatureUtils.sol +0 -98
- package/contracts/staking/lib/ErrorsLib.sol +0 -98
- package/contracts/staking/lib/SignatureUtils.sol +0 -105
- package/contracts/staking/lib/StakingStructs.sol +0 -106
- package/contracts/treasuryTwoChains/lib/ErrorsLib.sol +0 -48
- package/contracts/treasuryTwoChains/lib/ExternalChainStationStructs.sol +0 -80
- package/contracts/treasuryTwoChains/lib/HostChainStationStructs.sol +0 -87
- package/contracts/treasuryTwoChains/lib/SignatureUtils.sol +0 -79
- package/library/utils/GovernanceUtils.sol +0 -81
- package/library/utils/nonces/AsyncNonce.sol +0 -74
- package/library/utils/nonces/SyncNonce.sol +0 -71
- 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
|
+
}
|