@bloxchain/contracts 1.0.0-alpha.19 → 1.0.0-alpha.20

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.
@@ -1,492 +1,540 @@
1
- // SPDX-License-Identifier: MPL-2.0
2
- pragma solidity 0.8.34;
3
-
4
- /**
5
- * @title SharedValidation
6
- * @dev Optimized shared library containing common validation functions using enhanced custom errors
7
- *
8
- * This library is designed to reduce contract size by centralizing common validation logic
9
- * and using gas-efficient custom errors instead of string constants. This approach provides
10
- * significant gas savings and contract size reduction while maintaining clear error context.
11
- *
12
- * Features:
13
- * - Enhanced custom errors with contextual parameters
14
- * - Address validation functions
15
- * - Time and deadline validation
16
- * - Signature validation utilities
17
- * - Permission and authorization checks
18
- * - Operation type validation
19
- * - Gas and transaction validation
20
- *
21
- * This library follows the security rules defined in .cursorrules and implements
22
- * the Checks-Effects-Interactions pattern where applicable.
23
- *
24
- * Gas Optimization Benefits:
25
- * - ~50% gas reduction compared to string-based errors
26
- * - Significant contract size reduction
27
- * - Enhanced error context with parameters
28
- * - Modern Solidity best practices (0.8.4+)
29
- */
30
- library SharedValidation {
31
-
32
- // ============ ENHANCED CUSTOM ERRORS ============
33
-
34
- // Address validation errors with context
35
- error InvalidAddress(address provided);
36
- error NotNewAddress(address newAddress, address currentAddress);
37
-
38
- // Time and deadline errors with context
39
- error InvalidTimeLockPeriod(uint256 provided);
40
- error TimeLockPeriodZero(uint256 provided);
41
- error DeadlineInPast(uint256 deadline, uint256 currentTime);
42
- error MetaTxExpired(uint256 deadline, uint256 currentTime);
43
- error BeforeReleaseTime(uint256 releaseTime, uint256 currentTime);
44
- error NewTimelockSame(uint256 newPeriod, uint256 currentPeriod);
45
-
46
- // Permission and authorization errors with context
47
- error NoPermission(address caller);
48
- error NoPermissionForFunction(address caller, bytes4 functionSelector);
49
- error RestrictedOwner(address caller, address owner);
50
- error RestrictedOwnerRecovery(address caller, address owner, address recovery);
51
- error RestrictedRecovery(address caller, address recovery);
52
- error RestrictedBroadcaster(address caller, address broadcaster);
53
- error SignerNotAuthorized(address signer);
54
- error OnlyCallableByContract(address caller, address contractAddress);
55
-
56
- // Transaction and operation errors with context
57
- error NotSupported();
58
- error InvalidOperationType(bytes32 actualType, bytes32 expectedType);
59
- error ZeroOperationTypeNotAllowed();
60
- error TransactionStatusMismatch(uint8 expectedStatus, uint8 currentStatus);
61
- error AlreadyInitialized();
62
- error NotInitialized();
63
- error TransactionIdMismatch(uint256 expectedTxId, uint256 providedTxId);
64
- error PendingSecureRequest();
65
-
66
- // Signature and meta-transaction errors with context
67
- error InvalidSignatureLength(uint256 providedLength, uint256 expectedLength);
68
- error InvalidSignature(bytes signature);
69
- error InvalidNonce(uint256 providedNonce, uint256 expectedNonce);
70
- error ChainIdMismatch(uint256 providedChainId, uint256 expectedChainId);
71
- error InvalidHandlerSelector(bytes4 selector);
72
- error InvalidSValue(bytes32 s);
73
- error InvalidVValue(uint8 v);
74
- error ECDSAInvalidSignature(address recoveredSigner);
75
- error GasPriceExceedsMax(uint256 currentGasPrice, uint256 maxGasPrice);
76
- error MetaTxRecordMismatchStoredTx(uint256 txId);
77
- error MetaTxPaymentMismatchStoredTx(uint256 txId);
78
-
79
- // Consolidated resource errors
80
- error ResourceNotFound(bytes32 resourceId);
81
- error ResourceAlreadyExists(bytes32 resourceId);
82
- error CannotModifyProtected(bytes32 resourceId);
83
-
84
- // Consolidated item errors (for addresses: wallets, policies, etc.)
85
- error ItemAlreadyExists(address item);
86
- error ItemNotFound(address item);
87
- error InvalidOperation(address item);
88
- error DefinitionNotIDefinition(address definition);
89
-
90
- // Role and function errors with context
91
- error RoleWalletLimitReached(uint256 currentCount, uint256 maxWallets);
92
- error MaxWalletsZero(uint256 provided);
93
- error ConflictingMetaTxPermissions(bytes4 functionSelector);
94
- error InternalFunctionNotAccessible(bytes4 functionSelector);
95
- error ContractFunctionMustBeProtected(bytes4 functionSelector);
96
- error TargetNotWhitelisted(address target, bytes4 functionSelector);
97
- error FunctionSelectorMismatch(bytes4 providedSelector, bytes4 derivedSelector);
98
- error HandlerForSelectorMismatch(bytes4 schemaHandlerForSelector, bytes4 permissionHandlerForSelector);
99
- error InvalidRange(uint256 from, uint256 to);
100
- error OperationFailed();
101
-
102
- // Payment and balance errors with context
103
- error InvalidPayment();
104
- error InsufficientBalance(uint256 currentBalance, uint256 requiredAmount);
105
- error PaymentFailed(address recipient, uint256 amount, bytes reason);
106
-
107
- // Array validation errors with context
108
- error ArrayLengthMismatch(uint256 array1Length, uint256 array2Length);
109
- error IndexOutOfBounds(uint256 index, uint256 arrayLength);
110
-
111
- // System limit errors
112
- error BatchSizeExceeded(uint256 currentSize, uint256 maxSize);
113
- error MaxRolesExceeded(uint256 currentCount, uint256 maxRoles);
114
- error MaxHooksExceeded(uint256 currentCount, uint256 maxHooks);
115
- error MaxFunctionsExceeded(uint256 currentCount, uint256 maxFunctions);
116
- error RangeSizeExceeded(uint256 rangeSize, uint256 maxRangeSize);
117
-
118
- // ============ ADDRESS VALIDATION FUNCTIONS ============
119
-
120
- /**
121
- * @dev Validates that an address is not the zero address
122
- * @param addr The address to validate
123
- */
124
- function validateNotZeroAddress(address addr) internal pure {
125
- if (addr == address(0)) revert InvalidAddress(addr);
126
- }
127
-
128
-
129
- /**
130
- * @dev Validates that a new address is different from the current address
131
- * @param newAddress The proposed new address
132
- * @param currentAddress The current address to compare against
133
- */
134
- function validateNewAddress(address newAddress, address currentAddress) internal pure {
135
- if (newAddress == currentAddress) revert NotNewAddress(newAddress, currentAddress);
136
- }
137
-
138
- /**
139
- * @dev Validates that an address is not the zero address and is different from current
140
- * @param newAddress The proposed new address
141
- * @param currentAddress The current address to compare against
142
- */
143
- function validateAddressUpdate(
144
- address newAddress,
145
- address currentAddress
146
- ) internal pure {
147
- validateNotZeroAddress(newAddress);
148
- validateNewAddress(newAddress, currentAddress);
149
- }
150
-
151
- /**
152
- * @dev Validates that a target address is not zero
153
- * @param target The target address to validate
154
- */
155
- function validateTargetAddress(address target) internal pure {
156
- if (target == address(0)) revert InvalidAddress(target);
157
- }
158
-
159
- /**
160
- * @dev Validates that a handler contract address is not zero
161
- * @param handler The handler contract address to validate
162
- */
163
- function validateHandlerContract(address handler) internal pure {
164
- if (handler == address(0)) revert InvalidAddress(handler);
165
- }
166
-
167
- // ============ TIME AND DEADLINE VALIDATION FUNCTIONS ============
168
-
169
- /**
170
- * @dev Validates that a time lock period is greater than zero
171
- * @param timeLockPeriod The time lock period to validate
172
- */
173
- function validateTimeLockPeriod(uint256 timeLockPeriod) internal pure {
174
- if (timeLockPeriod == 0) revert TimeLockPeriodZero(timeLockPeriod);
175
- }
176
-
177
- /**
178
- * @dev Validates that a deadline is in the future
179
- * @param deadline The deadline timestamp to validate
180
- */
181
- function validateDeadline(uint256 deadline) internal view {
182
- if (deadline <= block.timestamp) revert DeadlineInPast(deadline, block.timestamp);
183
- }
184
-
185
- /**
186
- * @dev Validates that a new time lock period is different from the current one
187
- * @param newPeriod The new time lock period
188
- * @param currentPeriod The current time lock period
189
- */
190
- function validateTimeLockUpdate(uint256 newPeriod, uint256 currentPeriod) internal pure {
191
- validateTimeLockPeriod(newPeriod);
192
- if (newPeriod == currentPeriod) revert NewTimelockSame(newPeriod, currentPeriod);
193
- }
194
-
195
- /**
196
- * @dev Validates that the current time is after the release time
197
- * @param releaseTime The release time to check against
198
- */
199
- function validateReleaseTime(uint256 releaseTime) internal view {
200
- if (block.timestamp < releaseTime) revert BeforeReleaseTime(releaseTime, block.timestamp);
201
- }
202
-
203
- /**
204
- * @dev Validates that a meta-transaction has not expired
205
- * @param deadline The deadline of the meta-transaction
206
- */
207
- function validateMetaTxDeadline(uint256 deadline) internal view {
208
- if (block.timestamp > deadline) revert MetaTxExpired(deadline, block.timestamp);
209
- }
210
-
211
- // ============ SIGNATURE VALIDATION FUNCTIONS ============
212
-
213
- /**
214
- * @dev Validates that a signature has the correct length (65 bytes)
215
- * @param signature The signature to validate
216
- */
217
- function validateSignatureLength(bytes memory signature) internal pure {
218
- if (signature.length != 65) revert InvalidSignatureLength(signature.length, 65);
219
- }
220
-
221
- /**
222
- * @dev Validates ECDSA signature parameters
223
- * @param s The s parameter of the signature
224
- * @param v The v parameter of the signature
225
- */
226
- function validateSignatureParams(bytes32 s, uint8 v) internal pure {
227
- if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
228
- revert InvalidSValue(s);
229
- }
230
- if (v != 27 && v != 28) revert InvalidVValue(v);
231
- }
232
-
233
- /**
234
- * @dev Validates that a recovered signer is not the zero address
235
- * @param signer The recovered signer address
236
- */
237
- function validateRecoveredSigner(address signer) internal pure {
238
- if (signer == address(0)) revert ECDSAInvalidSignature(signer);
239
- }
240
-
241
- // ============ PERMISSION AND AUTHORIZATION FUNCTIONS ============
242
-
243
- /**
244
- * @dev Validates that the caller is the owner
245
- * @param owner The current owner address
246
- */
247
- function validateOwner(address owner) internal view {
248
- if (owner != msg.sender) revert RestrictedOwner(msg.sender, owner);
249
- }
250
-
251
- /**
252
- * @dev Validates that the caller is either the owner or recovery
253
- * @param owner The current owner address
254
- * @param recovery The current recovery address
255
- */
256
- function validateOwnerOrRecovery(address owner, address recovery) internal view {
257
- if (msg.sender != owner && msg.sender != recovery) {
258
- revert RestrictedOwnerRecovery(msg.sender, owner, recovery);
259
- }
260
- }
261
-
262
- /**
263
- * @dev Validates that the caller is the recovery address
264
- * @param recovery The current recovery address
265
- */
266
- function validateRecovery(address recovery) internal view {
267
- if (msg.sender != recovery) revert RestrictedRecovery(msg.sender, recovery);
268
- }
269
-
270
- /**
271
- * @dev Validates that the caller is the broadcaster
272
- * @param broadcaster The current broadcaster address
273
- */
274
- function validateBroadcaster(address broadcaster) internal view {
275
- if (msg.sender != broadcaster) revert RestrictedBroadcaster(msg.sender, broadcaster);
276
- }
277
-
278
- /**
279
- * @dev Validates that the signer of a meta-transaction is the owner
280
- * @param signer The signer address from the meta-transaction
281
- * @param owner The current owner address
282
- */
283
- function validateOwnerIsSigner(address signer, address owner) internal pure {
284
- if (signer != owner) revert NoPermission(signer);
285
- }
286
-
287
- /**
288
- * @dev Validates that the function is being called internally by the contract itself
289
- * @param contractAddress The address of the contract
290
- */
291
- function validateInternalCall(address contractAddress) internal view {
292
- if (msg.sender != contractAddress) revert OnlyCallableByContract(msg.sender, contractAddress);
293
- }
294
-
295
- // ============ TRANSACTION AND OPERATION VALIDATION FUNCTIONS ============
296
-
297
- /**
298
- * @dev Validates that an operation type is not zero
299
- * @param operationType The operation type to validate
300
- */
301
- function validateOperationTypeNotZero(bytes32 operationType) internal pure {
302
- if (operationType == bytes32(0)) revert ZeroOperationTypeNotAllowed();
303
- }
304
-
305
- /**
306
- * @dev Validates that an operation type matches the expected type
307
- * @param actualType The actual operation type
308
- * @param expectedType The expected operation type
309
- */
310
- function validateOperationType(bytes32 actualType, bytes32 expectedType) internal pure {
311
- if (actualType != expectedType) revert InvalidOperationType(actualType, expectedType);
312
- }
313
-
314
- /**
315
- * @dev Validates that a transaction exists (has non-zero ID)
316
- * @param txId The transaction ID to validate
317
- */
318
- function validateTransactionExists(uint256 txId) internal pure {
319
- if (txId == 0) revert ResourceNotFound(bytes32(uint256(txId)));
320
- }
321
-
322
- /**
323
- * @dev Validates that a transaction ID matches the expected value
324
- * @param txId The transaction ID to validate
325
- * @param expectedTxId The expected transaction ID
326
- */
327
- function validateTransactionId(uint256 txId, uint256 expectedTxId) internal pure {
328
- if (txId != expectedTxId) revert TransactionIdMismatch(expectedTxId, txId);
329
- }
330
-
331
- // ============ META-TRANSACTION VALIDATION FUNCTIONS ============
332
-
333
- /**
334
- * @dev Validates chain ID matches the current chain
335
- * @param chainId The chain ID to validate
336
- */
337
- function validateChainId(uint256 chainId) internal view {
338
- if (chainId != block.chainid) revert ChainIdMismatch(chainId, block.chainid);
339
- }
340
-
341
- /**
342
- * @dev Validates that a handler selector is not zero
343
- * @param selector The handler selector to validate
344
- */
345
- function validateHandlerSelector(bytes4 selector) internal pure {
346
- if (selector == bytes4(0)) revert InvalidHandlerSelector(selector);
347
- }
348
-
349
- /**
350
- * @dev Validates that a handler selector matches the expected selector
351
- * @param actualSelector The actual handler selector from the meta transaction
352
- * @param expectedSelector The expected handler selector to validate against
353
- */
354
- function validateHandlerSelectorMatch(bytes4 actualSelector, bytes4 expectedSelector) internal pure {
355
- if (actualSelector != expectedSelector) revert InvalidHandlerSelector(actualSelector);
356
- }
357
-
358
- /**
359
- * @dev Validates that a nonce matches the expected value
360
- * @param nonce The nonce to validate
361
- * @param expectedNonce The expected nonce value
362
- */
363
- function validateNonce(uint256 nonce, uint256 expectedNonce) internal pure {
364
- if (nonce != expectedNonce) revert InvalidNonce(nonce, expectedNonce);
365
- }
366
-
367
- /**
368
- * @dev Validates that the current transaction's gas price is within limits
369
- * @param maxGasPrice The maximum allowed gas price (in wei)
370
- */
371
- function validateGasPrice(uint256 maxGasPrice) internal view {
372
- if (maxGasPrice == 0) return; // No limit set
373
-
374
- uint256 currentGasPrice = tx.gasprice;
375
- if (currentGasPrice > maxGasPrice) {
376
- revert GasPriceExceedsMax(currentGasPrice, maxGasPrice);
377
- }
378
- }
379
-
380
- // ============ ROLE AND FUNCTION VALIDATION FUNCTIONS ============
381
-
382
- /**
383
- * @dev Validates that a role hasn't reached its wallet limit
384
- * @param currentCount The current number of wallets in the role
385
- * @param maxWallets The maximum number of wallets allowed
386
- */
387
- function validateWalletLimit(uint256 currentCount, uint256 maxWallets) internal pure {
388
- if (currentCount >= maxWallets) revert RoleWalletLimitReached(currentCount, maxWallets);
389
- }
390
-
391
- /**
392
- * @dev Validates that max wallets is greater than zero
393
- * @param maxWallets The maximum number of wallets
394
- */
395
- function validateMaxWalletsGreaterThanZero(uint256 maxWallets) internal pure {
396
- if (maxWallets == 0) revert MaxWalletsZero(maxWallets);
397
- }
398
-
399
- /**
400
- * @dev Validates that a role name is not empty
401
- * @param roleName The role name to validate
402
- */
403
- function validateRoleNameNotEmpty(string memory roleName) internal pure {
404
- if (bytes(roleName).length == 0) revert ResourceNotFound(keccak256(bytes(roleName)));
405
- }
406
-
407
- // ============ UTILITY FUNCTIONS ============
408
-
409
- /**
410
- * @dev Validates that the first value is not greater than the second (allows inclusive range: from <= to)
411
- * @param from The first value (must be <= 'to' for a valid range)
412
- * @param to The second value (must be >= 'from')
413
- */
414
- function validateLessThan(uint256 from, uint256 to) internal pure {
415
- if (from > to) revert InvalidRange(from, to);
416
- }
417
-
418
- /**
419
- * @dev Validates that two arrays have the same length
420
- * @param array1Length The length of the first array
421
- * @param array2Length The length of the second array
422
- */
423
- function validateArrayLengthMatch(uint256 array1Length, uint256 array2Length) internal pure {
424
- if (array1Length != array2Length) revert ArrayLengthMismatch(array1Length, array2Length);
425
- }
426
-
427
- // ============ SYSTEM LIMIT VALIDATION FUNCTIONS ============
428
-
429
- /**
430
- * @dev Validates that batch size doesn't exceed limit
431
- * @param batchSize The current batch size
432
- * @param maxBatchSize The maximum allowed batch size (0 = unlimited)
433
- */
434
- function validateBatchSize(uint256 batchSize, uint256 maxBatchSize) internal pure {
435
- if (maxBatchSize > 0 && batchSize > maxBatchSize) {
436
- revert BatchSizeExceeded(batchSize, maxBatchSize);
437
- }
438
- }
439
-
440
- /**
441
- * @dev Validates that role count doesn't exceed limit
442
- * @param currentCount The current role count
443
- * @param maxRoles The maximum allowed roles (0 = unlimited)
444
- */
445
- function validateRoleCount(uint256 currentCount, uint256 maxRoles) internal pure {
446
- if (maxRoles > 0 && currentCount >= maxRoles) {
447
- revert MaxRolesExceeded(currentCount, maxRoles);
448
- }
449
- }
450
-
451
- /**
452
- * @dev Validates that hook count doesn't exceed limit
453
- * @param currentCount The current hook count
454
- * @param maxHooks The maximum allowed hooks (0 = unlimited)
455
- */
456
- function validateHookCount(uint256 currentCount, uint256 maxHooks) internal pure {
457
- if (maxHooks > 0 && currentCount >= maxHooks) {
458
- revert MaxHooksExceeded(currentCount, maxHooks);
459
- }
460
- }
461
-
462
- /**
463
- * @dev Validates that function count doesn't exceed limit
464
- * @param currentCount The current function count
465
- * @param maxFunctions The maximum allowed functions (0 = unlimited)
466
- */
467
- function validateFunctionCount(uint256 currentCount, uint256 maxFunctions) internal pure {
468
- if (maxFunctions > 0 && currentCount >= maxFunctions) {
469
- revert MaxFunctionsExceeded(currentCount, maxFunctions);
470
- }
471
- }
472
-
473
- /**
474
- * @dev Validates that range size doesn't exceed limit
475
- * @param rangeSize The range size
476
- * @param maxRangeSize The maximum allowed range size (0 = unlimited)
477
- */
478
- function validateRangeSize(uint256 rangeSize, uint256 maxRangeSize) internal pure {
479
- if (maxRangeSize > 0 && rangeSize > maxRangeSize) {
480
- revert RangeSizeExceeded(rangeSize, maxRangeSize);
481
- }
482
- }
483
-
484
- /**
485
- * @dev Validates that an index is within bounds of an array
486
- * @param index The index to validate
487
- * @param arrayLength The length of the array
488
- */
489
- function validateIndexInBounds(uint256 index, uint256 arrayLength) internal pure {
490
- if (index >= arrayLength) revert IndexOutOfBounds(index, arrayLength);
491
- }
492
- }
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ pragma solidity 0.8.34;
3
+
4
+ /**
5
+ * @title SharedValidation
6
+ * @dev Optimized shared library containing common validation functions using enhanced custom errors
7
+ *
8
+ * This library is designed to reduce contract size by centralizing common validation logic
9
+ * and using gas-efficient custom errors instead of string constants. This approach provides
10
+ * significant gas savings and contract size reduction while maintaining clear error context.
11
+ *
12
+ * Features:
13
+ * - Enhanced custom errors with contextual parameters
14
+ * - Address validation functions
15
+ * - Time and deadline validation
16
+ * - Signature validation utilities
17
+ * - Permission and authorization checks
18
+ * - Operation type validation
19
+ * - Gas and transaction validation
20
+ *
21
+ * This library follows the security rules defined in .cursorrules and implements
22
+ * the Checks-Effects-Interactions pattern where applicable.
23
+ *
24
+ * Gas Optimization Benefits:
25
+ * - ~50% gas reduction compared to string-based errors
26
+ * - Significant contract size reduction
27
+ * - Enhanced error context with parameters
28
+ * - Modern Solidity best practices (0.8.4+)
29
+ */
30
+ library SharedValidation {
31
+
32
+ // ============ ENHANCED CUSTOM ERRORS ============
33
+
34
+ // Address validation errors with context
35
+ error InvalidAddress(address provided);
36
+ error NotNewAddress(address newAddress, address currentAddress);
37
+
38
+ // Time and deadline errors with context
39
+ error InvalidTimeLockPeriod(uint256 provided);
40
+ error TimeLockPeriodZero(uint256 provided);
41
+ error DeadlineInPast(uint256 deadline, uint256 currentTime);
42
+ error MetaTxExpired(uint256 deadline, uint256 currentTime);
43
+ error BeforeReleaseTime(uint256 releaseTime, uint256 currentTime);
44
+ error NewTimelockSame(uint256 newPeriod, uint256 currentPeriod);
45
+
46
+ // Permission and authorization errors with context
47
+ error NoPermission(address caller);
48
+ error NoPermissionForFunction(address caller, bytes4 functionSelector);
49
+ error RestrictedOwner(address caller, address owner);
50
+ error RestrictedOwnerRecovery(address caller, address owner, address recovery);
51
+ error RestrictedRecovery(address caller, address recovery);
52
+ error RestrictedBroadcaster(address caller, address broadcaster);
53
+ error SignerNotAuthorized(address signer);
54
+ error MetaTxHandlerSelectorMismatch(bytes4 signedSelector, bytes4 entrySelector);
55
+ error MetaTxHandlerContractMismatch(address signedContract, address entryContract);
56
+ error OnlyCallableByContract(address caller, address contractAddress);
57
+
58
+ // Transaction and operation errors with context
59
+ error NotSupported();
60
+ error InvalidOperationType(bytes32 actualType, bytes32 expectedType);
61
+ error ZeroOperationTypeNotAllowed();
62
+ error TransactionStatusMismatch(uint8 expectedStatus, uint8 currentStatus);
63
+ error AlreadyInitialized();
64
+ error NotInitialized();
65
+ error TransactionIdMismatch(uint256 expectedTxId, uint256 providedTxId);
66
+ error PendingSecureRequest();
67
+
68
+ // Signature and meta-transaction errors with context
69
+ error InvalidSignatureLength(uint256 providedLength, uint256 expectedLength);
70
+ error InvalidSignature(bytes signature);
71
+ error InvalidNonce(uint256 providedNonce, uint256 expectedNonce);
72
+ error ChainIdMismatch(uint256 providedChainId, uint256 expectedChainId);
73
+ error InvalidHandlerSelector(bytes4 selector);
74
+ error InvalidSValue(bytes32 s);
75
+ error InvalidVValue(uint8 v);
76
+ error ECDSAInvalidSignature(address recoveredSigner);
77
+ error GasPriceExceedsMax(uint256 currentGasPrice, uint256 maxGasPrice);
78
+ error MetaTxRecordMismatchStoredTx(uint256 txId);
79
+ error MetaTxPaymentMismatchStoredTx(uint256 txId);
80
+
81
+ // Consolidated resource errors
82
+ error ResourceNotFound(bytes32 resourceId);
83
+ error ResourceAlreadyExists(bytes32 resourceId);
84
+ error CannotModifyProtected(bytes32 resourceId);
85
+
86
+ // Consolidated item errors (for addresses: wallets, policies, etc.)
87
+ error ItemAlreadyExists(address item);
88
+ error ItemNotFound(address item);
89
+ error InvalidOperation(address item);
90
+ error DefinitionNotIDefinition(address definition);
91
+
92
+ // Role and function errors with context
93
+ error RoleWalletLimitReached(uint256 currentCount, uint256 maxWallets);
94
+ error MaxWalletsZero(uint256 provided);
95
+ error ConflictingMetaTxPermissions(bytes4 functionSelector);
96
+ error InternalFunctionNotAccessible(bytes4 functionSelector);
97
+ error ContractFunctionMustBeProtected(bytes4 functionSelector);
98
+ error TargetNotWhitelisted(address target, bytes4 functionSelector);
99
+ error FunctionSelectorMismatch(bytes4 providedSelector, bytes4 derivedSelector);
100
+ error HandlerForSelectorMismatch(bytes4 schemaHandlerForSelector, bytes4 permissionHandlerForSelector);
101
+ error InvalidRange(uint256 from, uint256 to);
102
+ error OperationFailed();
103
+
104
+ // Payment and balance errors with context
105
+ error InvalidPayment();
106
+ error InsufficientBalance(uint256 currentBalance, uint256 requiredAmount);
107
+ error PaymentFailed(address recipient, uint256 amount, bytes reason);
108
+
109
+ // Array validation errors with context
110
+ error ArrayLengthMismatch(uint256 array1Length, uint256 array2Length);
111
+ error IndexOutOfBounds(uint256 index, uint256 arrayLength);
112
+
113
+ // System limit errors
114
+ error BatchSizeExceeded(uint256 currentSize, uint256 maxSize);
115
+ error MaxRolesExceeded(uint256 currentCount, uint256 maxRoles);
116
+ error MaxHooksExceeded(uint256 currentCount, uint256 maxHooks);
117
+ error MaxFunctionsExceeded(uint256 currentCount, uint256 maxFunctions);
118
+ error RangeSizeExceeded(uint256 rangeSize, uint256 maxRangeSize);
119
+
120
+ // ============ ADDRESS VALIDATION FUNCTIONS ============
121
+
122
+ /**
123
+ * @dev Validates that an address is not the zero address
124
+ * @param addr The address to validate
125
+ */
126
+ function validateNotZeroAddress(address addr) internal pure {
127
+ if (addr == address(0)) revert InvalidAddress(addr);
128
+ }
129
+
130
+ /**
131
+ * @dev Validates that payment details are empty (no attached payment).
132
+ * @param recipient The payment recipient address
133
+ * @param nativeTokenAmount The native token payment amount
134
+ * @param erc20TokenAddress The ERC20 token address
135
+ * @param erc20TokenAmount The ERC20 payment amount
136
+ */
137
+ function validateEmptyPayment(
138
+ address recipient,
139
+ uint256 nativeTokenAmount,
140
+ address erc20TokenAddress,
141
+ uint256 erc20TokenAmount
142
+ ) internal pure {
143
+ if (
144
+ recipient != address(0) ||
145
+ nativeTokenAmount != 0 ||
146
+ erc20TokenAddress != address(0) ||
147
+ erc20TokenAmount != 0
148
+ ) revert InvalidPayment();
149
+ }
150
+
151
+ /**
152
+ * @dev Validates that a new address is different from the current address
153
+ * @param newAddress The proposed new address
154
+ * @param currentAddress The current address to compare against
155
+ */
156
+ function validateNewAddress(address newAddress, address currentAddress) internal pure {
157
+ if (newAddress == currentAddress) revert NotNewAddress(newAddress, currentAddress);
158
+ }
159
+
160
+ /**
161
+ * @dev Validates that an address is not the zero address and is different from current
162
+ * @param newAddress The proposed new address
163
+ * @param currentAddress The current address to compare against
164
+ */
165
+ function validateAddressUpdate(
166
+ address newAddress,
167
+ address currentAddress
168
+ ) internal pure {
169
+ validateNotZeroAddress(newAddress);
170
+ validateNewAddress(newAddress, currentAddress);
171
+ }
172
+
173
+ /**
174
+ * @dev Validates that a target address is not zero
175
+ * @param target The target address to validate
176
+ */
177
+ function validateTargetAddress(address target) internal pure {
178
+ if (target == address(0)) revert InvalidAddress(target);
179
+ }
180
+
181
+ /**
182
+ * @dev Validates that a handler contract address is not zero
183
+ * @param handler The handler contract address to validate
184
+ */
185
+ function validateHandlerContract(address handler) internal pure {
186
+ if (handler == address(0)) revert InvalidAddress(handler);
187
+ }
188
+
189
+ /**
190
+ * @dev Validates that the signed handler selector matches the live wrapper entrypoint selector.
191
+ * IMPORTANT: pass the wrapper selector from wrapper context (`bytes4(msg.sig)` in the wrapper),
192
+ * not from external-library context.
193
+ * @param handlerSelector Selector embedded in signed MetaTxParams
194
+ * @param entrySelector Selector of the wrapper entrypoint executing the meta-tx
195
+ */
196
+ function validateMetaTxHandlerSelectorBinding(
197
+ bytes4 handlerSelector,
198
+ bytes4 entrySelector
199
+ ) internal pure {
200
+ if (handlerSelector != entrySelector) {
201
+ revert MetaTxHandlerSelectorMismatch(handlerSelector, entrySelector);
202
+ }
203
+ }
204
+
205
+ /**
206
+ * @dev Validates that the signed handler contract matches the active verifying contract (`address(this)`).
207
+ * @param handlerContract Address embedded in signed MetaTxParams
208
+ */
209
+ function validateMetaTxHandlerContractBinding(address handlerContract) internal view {
210
+ if (handlerContract != address(this)) {
211
+ revert MetaTxHandlerContractMismatch(handlerContract, address(this));
212
+ }
213
+ }
214
+
215
+ // ============ TIME AND DEADLINE VALIDATION FUNCTIONS ============
216
+
217
+ /**
218
+ * @dev Validates that a time lock period is greater than zero
219
+ * @param timeLockPeriod The time lock period to validate
220
+ */
221
+ function validateTimeLockPeriod(uint256 timeLockPeriod) internal pure {
222
+ if (timeLockPeriod == 0) revert TimeLockPeriodZero(timeLockPeriod);
223
+ }
224
+
225
+ /**
226
+ * @dev Validates that a deadline is in the future
227
+ * @param deadline The deadline timestamp to validate
228
+ */
229
+ function validateDeadline(uint256 deadline) internal view {
230
+ if (deadline <= block.timestamp) revert DeadlineInPast(deadline, block.timestamp);
231
+ }
232
+
233
+ /**
234
+ * @dev Validates that a new time lock period is different from the current one
235
+ * @param newPeriod The new time lock period
236
+ * @param currentPeriod The current time lock period
237
+ */
238
+ function validateTimeLockUpdate(uint256 newPeriod, uint256 currentPeriod) internal pure {
239
+ validateTimeLockPeriod(newPeriod);
240
+ if (newPeriod == currentPeriod) revert NewTimelockSame(newPeriod, currentPeriod);
241
+ }
242
+
243
+ /**
244
+ * @dev Validates that the current time is after the release time
245
+ * @param releaseTime The release time to check against
246
+ */
247
+ function validateReleaseTime(uint256 releaseTime) internal view {
248
+ if (block.timestamp < releaseTime) revert BeforeReleaseTime(releaseTime, block.timestamp);
249
+ }
250
+
251
+ /**
252
+ * @dev Validates that a meta-transaction has not expired
253
+ * @param deadline The deadline of the meta-transaction
254
+ */
255
+ function validateMetaTxDeadline(uint256 deadline) internal view {
256
+ if (block.timestamp > deadline) revert MetaTxExpired(deadline, block.timestamp);
257
+ }
258
+
259
+ // ============ SIGNATURE VALIDATION FUNCTIONS ============
260
+
261
+ /**
262
+ * @dev Validates that a signature has the correct length (65 bytes)
263
+ * @param signature The signature to validate
264
+ */
265
+ function validateSignatureLength(bytes memory signature) internal pure {
266
+ if (signature.length != 65) revert InvalidSignatureLength(signature.length, 65);
267
+ }
268
+
269
+ /**
270
+ * @dev Validates ECDSA signature parameters
271
+ * @param s The s parameter of the signature
272
+ * @param v The v parameter of the signature
273
+ */
274
+ function validateSignatureParams(bytes32 s, uint8 v) internal pure {
275
+ if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
276
+ revert InvalidSValue(s);
277
+ }
278
+ if (v != 27 && v != 28) revert InvalidVValue(v);
279
+ }
280
+
281
+ /**
282
+ * @dev Validates that a recovered signer is not the zero address
283
+ * @param signer The recovered signer address
284
+ */
285
+ function validateRecoveredSigner(address signer) internal pure {
286
+ if (signer == address(0)) revert ECDSAInvalidSignature(signer);
287
+ }
288
+
289
+ // ============ PERMISSION AND AUTHORIZATION FUNCTIONS ============
290
+
291
+ /**
292
+ * @dev Validates that the caller is the owner
293
+ * @param owner The current owner address
294
+ */
295
+ function validateOwner(address owner) internal view {
296
+ if (owner != msg.sender) revert RestrictedOwner(msg.sender, owner);
297
+ }
298
+
299
+ /**
300
+ * @dev Validates that the caller is either the owner or recovery
301
+ * @param owner The current owner address
302
+ * @param recovery The current recovery address
303
+ */
304
+ function validateOwnerOrRecovery(address owner, address recovery) internal view {
305
+ if (msg.sender != owner && msg.sender != recovery) {
306
+ revert RestrictedOwnerRecovery(msg.sender, owner, recovery);
307
+ }
308
+ }
309
+
310
+ /**
311
+ * @dev Validates that the caller is the recovery address
312
+ * @param recovery The current recovery address
313
+ */
314
+ function validateRecovery(address recovery) internal view {
315
+ if (msg.sender != recovery) revert RestrictedRecovery(msg.sender, recovery);
316
+ }
317
+
318
+ /**
319
+ * @dev Validates that the caller is the broadcaster
320
+ * @param broadcaster The current broadcaster address
321
+ */
322
+ function validateBroadcaster(address broadcaster) internal view {
323
+ if (msg.sender != broadcaster) revert RestrictedBroadcaster(msg.sender, broadcaster);
324
+ }
325
+
326
+ /**
327
+ * @dev Validates that the signer of a meta-transaction is the owner
328
+ * @param signer The signer address from the meta-transaction
329
+ * @param owner The current owner address
330
+ */
331
+ function validateOwnerIsSigner(address signer, address owner) internal pure {
332
+ if (signer != owner) revert NoPermission(signer);
333
+ }
334
+
335
+ /**
336
+ * @dev Validates that the function is being called internally by the contract itself
337
+ * @param contractAddress The address of the contract
338
+ */
339
+ function validateInternalCall(address contractAddress) internal view {
340
+ if (msg.sender != contractAddress) revert OnlyCallableByContract(msg.sender, contractAddress);
341
+ }
342
+
343
+ // ============ TRANSACTION AND OPERATION VALIDATION FUNCTIONS ============
344
+
345
+ /**
346
+ * @dev Validates that an operation type is not zero
347
+ * @param operationType The operation type to validate
348
+ */
349
+ function validateOperationTypeNotZero(bytes32 operationType) internal pure {
350
+ if (operationType == bytes32(0)) revert ZeroOperationTypeNotAllowed();
351
+ }
352
+
353
+ /**
354
+ * @dev Validates that an operation type matches the expected type
355
+ * @param actualType The actual operation type
356
+ * @param expectedType The expected operation type
357
+ */
358
+ function validateOperationType(bytes32 actualType, bytes32 expectedType) internal pure {
359
+ if (actualType != expectedType) revert InvalidOperationType(actualType, expectedType);
360
+ }
361
+
362
+ /**
363
+ * @dev Validates that a transaction exists (has non-zero ID)
364
+ * @param txId The transaction ID to validate
365
+ */
366
+ function validateTransactionExists(uint256 txId) internal pure {
367
+ if (txId == 0) revert ResourceNotFound(bytes32(uint256(txId)));
368
+ }
369
+
370
+ /**
371
+ * @dev Validates that a transaction ID matches the expected value
372
+ * @param txId The transaction ID to validate
373
+ * @param expectedTxId The expected transaction ID
374
+ */
375
+ function validateTransactionId(uint256 txId, uint256 expectedTxId) internal pure {
376
+ if (txId != expectedTxId) revert TransactionIdMismatch(expectedTxId, txId);
377
+ }
378
+
379
+ // ============ META-TRANSACTION VALIDATION FUNCTIONS ============
380
+
381
+ /**
382
+ * @dev Validates chain ID matches the current chain
383
+ * @param chainId The chain ID to validate
384
+ */
385
+ function validateChainId(uint256 chainId) internal view {
386
+ if (chainId != block.chainid) revert ChainIdMismatch(chainId, block.chainid);
387
+ }
388
+
389
+ /**
390
+ * @dev Validates that a handler selector is not zero
391
+ * @param selector The handler selector to validate
392
+ */
393
+ function validateHandlerSelector(bytes4 selector) internal pure {
394
+ if (selector == bytes4(0)) revert InvalidHandlerSelector(selector);
395
+ }
396
+
397
+ /**
398
+ * @dev Validates that a handler selector matches the expected selector
399
+ * @param actualSelector The actual handler selector from the meta transaction
400
+ * @param expectedSelector The expected handler selector to validate against
401
+ */
402
+ function validateHandlerSelectorMatch(bytes4 actualSelector, bytes4 expectedSelector) internal pure {
403
+ if (actualSelector != expectedSelector) revert InvalidHandlerSelector(actualSelector);
404
+ }
405
+
406
+ /**
407
+ * @dev Validates that a nonce matches the expected value
408
+ * @param nonce The nonce to validate
409
+ * @param expectedNonce The expected nonce value
410
+ */
411
+ function validateNonce(uint256 nonce, uint256 expectedNonce) internal pure {
412
+ if (nonce != expectedNonce) revert InvalidNonce(nonce, expectedNonce);
413
+ }
414
+
415
+ /**
416
+ * @dev Validates that the current transaction's gas price is within limits
417
+ * @param maxGasPrice The maximum allowed gas price (in wei)
418
+ */
419
+ function validateGasPrice(uint256 maxGasPrice) internal view {
420
+ if (maxGasPrice == 0) return; // No limit set
421
+
422
+ uint256 currentGasPrice = tx.gasprice;
423
+ if (currentGasPrice > maxGasPrice) {
424
+ revert GasPriceExceedsMax(currentGasPrice, maxGasPrice);
425
+ }
426
+ }
427
+
428
+ // ============ ROLE AND FUNCTION VALIDATION FUNCTIONS ============
429
+
430
+ /**
431
+ * @dev Validates that a role hasn't reached its wallet limit
432
+ * @param currentCount The current number of wallets in the role
433
+ * @param maxWallets The maximum number of wallets allowed
434
+ */
435
+ function validateWalletLimit(uint256 currentCount, uint256 maxWallets) internal pure {
436
+ if (currentCount >= maxWallets) revert RoleWalletLimitReached(currentCount, maxWallets);
437
+ }
438
+
439
+ /**
440
+ * @dev Validates that max wallets is greater than zero
441
+ * @param maxWallets The maximum number of wallets
442
+ */
443
+ function validateMaxWalletsGreaterThanZero(uint256 maxWallets) internal pure {
444
+ if (maxWallets == 0) revert MaxWalletsZero(maxWallets);
445
+ }
446
+
447
+ /**
448
+ * @dev Validates that a role name is not empty
449
+ * @param roleName The role name to validate
450
+ */
451
+ function validateRoleNameNotEmpty(string memory roleName) internal pure {
452
+ if (bytes(roleName).length == 0) revert ResourceNotFound(keccak256(bytes(roleName)));
453
+ }
454
+
455
+ // ============ UTILITY FUNCTIONS ============
456
+
457
+ /**
458
+ * @dev Validates that the first value is not greater than the second (allows inclusive range: from <= to)
459
+ * @param from The first value (must be <= 'to' for a valid range)
460
+ * @param to The second value (must be >= 'from')
461
+ */
462
+ function validateLessThan(uint256 from, uint256 to) internal pure {
463
+ if (from > to) revert InvalidRange(from, to);
464
+ }
465
+
466
+ /**
467
+ * @dev Validates that two arrays have the same length
468
+ * @param array1Length The length of the first array
469
+ * @param array2Length The length of the second array
470
+ */
471
+ function validateArrayLengthMatch(uint256 array1Length, uint256 array2Length) internal pure {
472
+ if (array1Length != array2Length) revert ArrayLengthMismatch(array1Length, array2Length);
473
+ }
474
+
475
+ // ============ SYSTEM LIMIT VALIDATION FUNCTIONS ============
476
+
477
+ /**
478
+ * @dev Validates that batch size doesn't exceed limit
479
+ * @param batchSize The current batch size
480
+ * @param maxBatchSize The maximum allowed batch size (0 = unlimited)
481
+ */
482
+ function validateBatchSize(uint256 batchSize, uint256 maxBatchSize) internal pure {
483
+ if (maxBatchSize > 0 && batchSize > maxBatchSize) {
484
+ revert BatchSizeExceeded(batchSize, maxBatchSize);
485
+ }
486
+ }
487
+
488
+ /**
489
+ * @dev Validates that role count doesn't exceed limit
490
+ * @param currentCount The current role count
491
+ * @param maxRoles The maximum allowed roles (0 = unlimited)
492
+ */
493
+ function validateRoleCount(uint256 currentCount, uint256 maxRoles) internal pure {
494
+ if (maxRoles > 0 && currentCount >= maxRoles) {
495
+ revert MaxRolesExceeded(currentCount, maxRoles);
496
+ }
497
+ }
498
+
499
+ /**
500
+ * @dev Validates that hook count doesn't exceed limit
501
+ * @param currentCount The current hook count
502
+ * @param maxHooks The maximum allowed hooks (0 = unlimited)
503
+ */
504
+ function validateHookCount(uint256 currentCount, uint256 maxHooks) internal pure {
505
+ if (maxHooks > 0 && currentCount >= maxHooks) {
506
+ revert MaxHooksExceeded(currentCount, maxHooks);
507
+ }
508
+ }
509
+
510
+ /**
511
+ * @dev Validates that function count doesn't exceed limit
512
+ * @param currentCount The current function count
513
+ * @param maxFunctions The maximum allowed functions (0 = unlimited)
514
+ */
515
+ function validateFunctionCount(uint256 currentCount, uint256 maxFunctions) internal pure {
516
+ if (maxFunctions > 0 && currentCount >= maxFunctions) {
517
+ revert MaxFunctionsExceeded(currentCount, maxFunctions);
518
+ }
519
+ }
520
+
521
+ /**
522
+ * @dev Validates that range size doesn't exceed limit
523
+ * @param rangeSize The range size
524
+ * @param maxRangeSize The maximum allowed range size (0 = unlimited)
525
+ */
526
+ function validateRangeSize(uint256 rangeSize, uint256 maxRangeSize) internal pure {
527
+ if (maxRangeSize > 0 && rangeSize > maxRangeSize) {
528
+ revert RangeSizeExceeded(rangeSize, maxRangeSize);
529
+ }
530
+ }
531
+
532
+ /**
533
+ * @dev Validates that an index is within bounds of an array
534
+ * @param index The index to validate
535
+ * @param arrayLength The length of the array
536
+ */
537
+ function validateIndexInBounds(uint256 index, uint256 arrayLength) internal pure {
538
+ if (index >= arrayLength) revert IndexOutOfBounds(index, arrayLength);
539
+ }
540
+ }