@bloxchain/contracts 1.0.0-alpha.7 → 1.0.0

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