@bloxchain/contracts 1.0.0-alpha → 1.0.0-alpha.10

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 (55) hide show
  1. package/README.md +55 -18
  2. package/abi/{ControlBlox.abi.json → AccountBlox.abi.json} +699 -2974
  3. package/abi/BareBlox.abi.json +127 -90
  4. package/abi/BaseStateMachine.abi.json +127 -90
  5. package/abi/EngineBlox.abi.json +11 -31
  6. package/abi/GuardController.abi.json +217 -895
  7. package/abi/GuardControllerDefinitions.abi.json +380 -0
  8. package/abi/IDefinition.abi.json +19 -0
  9. package/abi/RoleBlox.abi.json +818 -2404
  10. package/abi/RuntimeRBAC.abi.json +122 -328
  11. package/abi/RuntimeRBACDefinitions.abi.json +243 -0
  12. package/abi/SecureBlox.abi.json +620 -1952
  13. package/abi/SecureOwnable.abi.json +469 -1801
  14. package/abi/SecureOwnableDefinitions.abi.json +57 -0
  15. package/abi/SimpleRWA20.abi.json +486 -1999
  16. package/abi/SimpleRWA20Definitions.abi.json +19 -0
  17. package/abi/SimpleVault.abi.json +884 -2685
  18. package/abi/SimpleVaultDefinitions.abi.json +19 -0
  19. package/components/README.md +8 -0
  20. package/core/access/RuntimeRBAC.sol +184 -0
  21. package/core/access/interface/IRuntimeRBAC.sol +55 -0
  22. package/{contracts/core → core}/access/lib/definitions/RuntimeRBACDefinitions.sol +121 -1
  23. package/{contracts/core → core}/base/BaseStateMachine.sol +187 -54
  24. package/{contracts/core → core}/base/interface/IBaseStateMachine.sol +7 -0
  25. package/{contracts/core → core}/execution/GuardController.sol +89 -155
  26. package/{contracts/core → core}/execution/interface/IGuardController.sol +52 -12
  27. package/{contracts/core → core}/execution/lib/definitions/GuardControllerDefinitions.sol +91 -2
  28. package/{contracts/core → core}/lib/EngineBlox.sol +167 -64
  29. package/{contracts → core/lib}/interfaces/IDefinition.sol +15 -6
  30. package/{contracts → core/lib}/interfaces/IEventForwarder.sol +1 -1
  31. package/{contracts → core/lib}/utils/SharedValidation.sol +490 -486
  32. package/core/pattern/Account.sol +75 -0
  33. package/core/research/BloxchainWallet.sol +133 -0
  34. package/core/research/FactoryBlox/FactoryBlox.sol +344 -0
  35. package/core/research/FactoryBlox/FactoryBloxDefinitions.sol +144 -0
  36. package/core/research/erc1155-blox/ERC1155Blox.sol +170 -0
  37. package/core/research/erc1155-blox/lib/definitions/ERC1155BloxDefinitions.sol +203 -0
  38. package/core/research/erc20-blox/ERC20Blox.sol +135 -0
  39. package/core/research/erc20-blox/lib/definitions/ERC20BloxDefinitions.sol +185 -0
  40. package/core/research/erc721-blox/ERC721Blox.sol +131 -0
  41. package/core/research/erc721-blox/lib/definitions/ERC721BloxDefinitions.sol +172 -0
  42. package/core/research/lending-blox/.gitkeep +1 -0
  43. package/core/research/p2p-blox/P2PBlox.sol +266 -0
  44. package/core/research/p2p-blox/README.md +85 -0
  45. package/core/research/p2p-blox/lib/definitions/P2PBloxDefinitions.sol +19 -0
  46. package/{contracts/core → core}/security/SecureOwnable.sol +390 -419
  47. package/{contracts/core → core}/security/interface/ISecureOwnable.sol +27 -40
  48. package/{contracts/core → core}/security/lib/definitions/SecureOwnableDefinitions.sol +786 -757
  49. package/package.json +49 -47
  50. package/standards/README.md +12 -0
  51. package/standards/behavior/ICopyable.sol +36 -0
  52. package/standards/hooks/IOnActionHook.sol +21 -0
  53. package/contracts/core/access/RuntimeRBAC.sol +0 -344
  54. package/contracts/core/access/interface/IRuntimeRBAC.sol +0 -108
  55. package/contracts/interfaces/IOnActionHook.sol +0 -79
@@ -0,0 +1,75 @@
1
+ // SPDX-License-Identifier: MPL-2.0
2
+ pragma solidity 0.8.33;
3
+
4
+ import "../execution/GuardController.sol";
5
+ import "../access/RuntimeRBAC.sol";
6
+ import "../security/SecureOwnable.sol";
7
+ import "../lib/utils/SharedValidation.sol";
8
+
9
+ /**
10
+ * @title Account
11
+ * @dev Abstract account pattern combining GuardController, RuntimeRBAC, and SecureOwnable.
12
+ *
13
+ * Use this as the base for account-style contracts (e.g. AccountBlox) to avoid duplicating
14
+ * initialization, interface support, and receive/fallback boilerplate.
15
+ *
16
+ * Combines:
17
+ * - GuardController: Execution workflows and time-locked transactions
18
+ * - RuntimeRBAC: Runtime role creation and management
19
+ * - SecureOwnable: Secure ownership transfer and management
20
+ *
21
+ * @custom:security-contact security@particlecs.com
22
+ */
23
+ abstract contract Account is GuardController, RuntimeRBAC, SecureOwnable {
24
+ /**
25
+ * @dev Emitted when plain ETH is received (receive()).
26
+ * @param sender Address that sent the ETH
27
+ * @param value Amount of wei received
28
+ * @custom:security Gas-efficient so receive() stays within 2,300 gas stipend (transfer/send compatible).
29
+ */
30
+ event EthReceived(address indexed sender, uint256 value);
31
+
32
+ /**
33
+ * @notice Initializer for the Account pattern (GuardController + RuntimeRBAC + SecureOwnable).
34
+ * @param initialOwner The initial owner address
35
+ * @param broadcaster The broadcaster address
36
+ * @param recovery The recovery address
37
+ * @param timeLockPeriodSec The timelock period in seconds
38
+ * @param eventForwarder The event forwarder address (optional)
39
+ */
40
+ function initialize(
41
+ address initialOwner,
42
+ address broadcaster,
43
+ address recovery,
44
+ uint256 timeLockPeriodSec,
45
+ address eventForwarder
46
+ ) public virtual override(GuardController, RuntimeRBAC, SecureOwnable) onlyInitializing {
47
+ GuardController.initialize(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
48
+ RuntimeRBAC.initialize(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
49
+ SecureOwnable.initialize(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
50
+ }
51
+
52
+ /**
53
+ * @dev See {IERC165-supportsInterface}.
54
+ */
55
+ function supportsInterface(bytes4 interfaceId) public view virtual override(GuardController, RuntimeRBAC, SecureOwnable) returns (bool) {
56
+ return GuardController.supportsInterface(interfaceId) || RuntimeRBAC.supportsInterface(interfaceId) || SecureOwnable.supportsInterface(interfaceId);
57
+ }
58
+
59
+ /**
60
+ * @dev Accepts plain ETH transfers (no calldata).
61
+ * @notice General-use wallet: ETH can be sent naturally; balance is credited.
62
+ * @custom:security No external calls—reentrancy-safe; outgoing ETH only via GuardController execution. Uses simple emit to stay within 2,300 gas stipend (transfer/send compatible).
63
+ */
64
+ receive() external payable virtual {
65
+ emit EthReceived(msg.sender, msg.value);
66
+ }
67
+
68
+ /**
69
+ * @dev Rejects calls with unknown selector (with or without value).
70
+ * @notice Only plain transfers hit receive(); all other calls revert.
71
+ */
72
+ fallback() external payable virtual {
73
+ revert SharedValidation.NotSupported();
74
+ }
75
+ }
@@ -0,0 +1,133 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (c) 2025 Particle Crypto Security
3
+ pragma solidity 0.8.33;
4
+
5
+ import "../pattern/Account.sol";
6
+ import "../base/BaseStateMachine.sol";
7
+ import "../lib/utils/SharedValidation.sol";
8
+ import "../lib/interfaces/IDefinition.sol";
9
+ import "../../standards/behavior/ICopyable.sol";
10
+
11
+ /**
12
+ * @title BloxchainWallet
13
+ * @dev Official ParticleCS wallet controller built on Bloxchain Protocol.
14
+ *
15
+ * Extends the Account pattern (GuardController + RuntimeRBAC + SecureOwnable) with
16
+ * timelock bounds, optional roles/definitions init, and ICopyable for factory cloning.
17
+ */
18
+ contract BloxchainWallet is Account, ICopyable {
19
+ uint256 public constant MIN_TIME_LOCK_PERIOD = 1 days;
20
+ uint256 public constant MAX_TIME_LOCK_PERIOD = 90 days;
21
+ uint256 public constant MAX_DEFINITION_CONTRACTS = 50;
22
+ uint256 public constant MAX_INITIAL_ROLES = 50;
23
+ uint256 public constant MAX_SCHEMAS_PER_DEFINITION = 100;
24
+ uint256 public constant MAX_PERMISSIONS_PER_DEFINITION = 200;
25
+
26
+ bool private _cloneDataSet;
27
+
28
+ struct RoleConfig {
29
+ string roleName;
30
+ uint256 maxWallets;
31
+ }
32
+
33
+ function initialize(
34
+ address initialOwner,
35
+ address broadcaster,
36
+ address recovery,
37
+ uint256 timeLockPeriodSec,
38
+ address eventForwarder
39
+ ) public virtual override(Account) initializer {
40
+ _initializeBase(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
41
+ }
42
+
43
+ function initializeWithData(
44
+ address initialOwner,
45
+ address broadcaster,
46
+ address recovery,
47
+ uint256 timeLockPeriodSec,
48
+ address eventForwarder,
49
+ bytes calldata initData
50
+ ) external initializer {
51
+ _initializeBase(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
52
+ if (initData.length > 0) {
53
+ (RoleConfig[] memory roles, IDefinition[] memory definitionContracts) =
54
+ abi.decode(initData, (RoleConfig[], IDefinition[]));
55
+ _applyRolesAndDefinitions(roles, definitionContracts);
56
+ }
57
+ }
58
+
59
+ function _initializeBase(
60
+ address initialOwner,
61
+ address broadcaster,
62
+ address recovery,
63
+ uint256 timeLockPeriodSec,
64
+ address eventForwarder
65
+ ) internal {
66
+ _validateTimeLockPeriod(timeLockPeriodSec);
67
+ super.initialize(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
68
+ }
69
+
70
+ function setCloneData(bytes calldata initData) external {
71
+ if (!initialized()) revert SharedValidation.NotInitialized();
72
+ if (_cloneDataSet) revert SharedValidation.AlreadyInitialized();
73
+ _cloneDataSet = true;
74
+ (RoleConfig[] memory roles, IDefinition[] memory definitionContracts) =
75
+ abi.decode(initData, (RoleConfig[], IDefinition[]));
76
+ _applyRolesAndDefinitions(roles, definitionContracts);
77
+ }
78
+
79
+ function supportsInterface(bytes4 interfaceId)
80
+ public
81
+ view
82
+ virtual
83
+ override(Account)
84
+ returns (bool)
85
+ {
86
+ return interfaceId == type(ICopyable).interfaceId || super.supportsInterface(interfaceId);
87
+ }
88
+
89
+ function _updateTimeLockPeriod(uint256 newTimeLockPeriodSec)
90
+ internal
91
+ virtual
92
+ override(BaseStateMachine)
93
+ {
94
+ _validateTimeLockPeriod(newTimeLockPeriodSec);
95
+ super._updateTimeLockPeriod(newTimeLockPeriodSec);
96
+ }
97
+
98
+ function _validateTimeLockPeriod(uint256 timeLockPeriodSec) internal pure {
99
+ if (timeLockPeriodSec < MIN_TIME_LOCK_PERIOD || timeLockPeriodSec > MAX_TIME_LOCK_PERIOD) {
100
+ revert SharedValidation.InvalidTimeLockPeriod(timeLockPeriodSec);
101
+ }
102
+ }
103
+
104
+ function _applyRolesAndDefinitions(
105
+ RoleConfig[] memory roles,
106
+ IDefinition[] memory definitionContracts
107
+ ) internal {
108
+ if (roles.length > MAX_INITIAL_ROLES) {
109
+ revert SharedValidation.BatchSizeExceeded(roles.length, MAX_INITIAL_ROLES);
110
+ }
111
+ for (uint256 i = 0; i < roles.length; i++) {
112
+ _createRole(roles[i].roleName, roles[i].maxWallets, false);
113
+ }
114
+ if (definitionContracts.length > MAX_DEFINITION_CONTRACTS) {
115
+ revert SharedValidation.BatchSizeExceeded(definitionContracts.length, MAX_DEFINITION_CONTRACTS);
116
+ }
117
+ for (uint256 i = 0; i < definitionContracts.length; i++) {
118
+ address def = address(definitionContracts[i]);
119
+ if (!definitionContracts[i].supportsInterface(type(IDefinition).interfaceId)) {
120
+ revert SharedValidation.DefinitionNotIDefinition(def);
121
+ }
122
+ EngineBlox.FunctionSchema[] memory schemas = definitionContracts[i].getFunctionSchemas();
123
+ IDefinition.RolePermission memory permissions = definitionContracts[i].getRolePermissions();
124
+ if (schemas.length > MAX_SCHEMAS_PER_DEFINITION) {
125
+ revert SharedValidation.BatchSizeExceeded(schemas.length, MAX_SCHEMAS_PER_DEFINITION);
126
+ }
127
+ if (permissions.roleHashes.length > MAX_PERMISSIONS_PER_DEFINITION) {
128
+ revert SharedValidation.BatchSizeExceeded(permissions.roleHashes.length, MAX_PERMISSIONS_PER_DEFINITION);
129
+ }
130
+ _loadDefinitions(schemas, permissions.roleHashes, permissions.functionPermissions, false);
131
+ }
132
+ }
133
+ }
@@ -0,0 +1,344 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (c) 2025 Particle Crypto Security
3
+ pragma solidity 0.8.33;
4
+
5
+ import "@openzeppelin/contracts/proxy/Clones.sol";
6
+ import "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
7
+ import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
8
+ import "../../execution/GuardController.sol";
9
+ import "../../access/RuntimeRBAC.sol";
10
+ import "../../security/SecureOwnable.sol";
11
+ import "../../base/interface/IBaseStateMachine.sol";
12
+ import "../../lib/EngineBlox.sol";
13
+ import "../../../standards/behavior/ICopyable.sol";
14
+ import "../../lib/interfaces/IDefinition.sol";
15
+ import "../../lib/interfaces/IEventForwarder.sol";
16
+ import "../../lib/utils/SharedValidation.sol";
17
+ import "./FactoryBloxDefinitions.sol";
18
+
19
+ /**
20
+ * @title FactoryBlox
21
+ * @dev CopyBlox-style blox factory with RBAC and GuardController like AccountBlox
22
+ *
23
+ * Combines:
24
+ * - GuardController: Execution workflows and time-locked transactions
25
+ * - RuntimeRBAC: Runtime role creation and management
26
+ * - SecureOwnable: Secure ownership transfer and management
27
+ * - Clone factory: Clone blox contracts (EIP-1167) with RBAC-protected cloneBlox
28
+ * - IEventForwarder: Centralize events from clones
29
+ *
30
+ * Only wallets with clone permission (OWNER_ROLE by default; custom roles via RuntimeRBAC)
31
+ * can call cloneBlox. Clones are initialized with eventForwarder set to this contract.
32
+ * When initData is non-empty, the implementation must support ICopyable and is initialized
33
+ * via initializeWithData; otherwise initialize(address,address,address,uint256,address) is used.
34
+ *
35
+ * clonesWhitelist: lightweight set of trusted blox implementation addresses. Only addresses in the
36
+ * whitelist can be cloned via cloneBlox, giving certainty about deploying the same contract.
37
+ * addToWhitelist and removeFromWhitelist are controller-only (macro + _validateExecuteBySelf).
38
+ */
39
+ contract FactoryBlox is GuardController, RuntimeRBAC, SecureOwnable, IEventForwarder {
40
+ using Clones for address;
41
+ using ERC165Checker for address;
42
+ using EnumerableSet for EnumerableSet.AddressSet;
43
+
44
+ EnumerableSet.AddressSet private _clones;
45
+ /// @dev Trusted blox implementation addresses; only these can be cloned via cloneBlox
46
+ EnumerableSet.AddressSet private _clonesWhitelist;
47
+ /// @notice Whitelist entry containing implementation address and its configured clone price
48
+ struct CloneWhitelistEntry {
49
+ address bloxImplementation;
50
+ EngineBlox.PaymentDetails price;
51
+ }
52
+
53
+ /// @dev Per-implementation clone pricing configuration
54
+ mapping(address => EngineBlox.PaymentDetails) private _clonePrices;
55
+
56
+ /**
57
+ * @notice Initializer for FactoryBlox
58
+ */
59
+ function initialize(
60
+ address initialOwner,
61
+ address broadcaster,
62
+ address recovery,
63
+ uint256 timeLockPeriodSec,
64
+ address eventForwarder
65
+ ) public virtual override(GuardController, RuntimeRBAC, SecureOwnable) initializer {
66
+ GuardController.initialize(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
67
+ RuntimeRBAC.initialize(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
68
+ SecureOwnable.initialize(initialOwner, broadcaster, recovery, timeLockPeriodSec, eventForwarder);
69
+
70
+ IDefinition.RolePermission memory factoryPermissions = FactoryBloxDefinitions.getRolePermissions();
71
+ _loadDefinitions(
72
+ FactoryBloxDefinitions.getFunctionSchemas(),
73
+ factoryPermissions.roleHashes,
74
+ factoryPermissions.functionPermissions,
75
+ true
76
+ );
77
+
78
+ // Register macro selectors so controller can target address(this) for time-lock/meta-tx execution
79
+ _addMacroSelector(FactoryBloxDefinitions.CLONE_BLOX_SELECTOR);
80
+ _addMacroSelector(FactoryBloxDefinitions.ADD_TO_WHITELIST_SELECTOR);
81
+ _addMacroSelector(FactoryBloxDefinitions.REMOVE_FROM_WHITELIST_SELECTOR);
82
+
83
+ // Allow cloning this factory's implementation (or proxy) by default
84
+ _clonesWhitelist.add(address(this));
85
+ }
86
+
87
+ function supportsInterface(bytes4 interfaceId)
88
+ public
89
+ view
90
+ virtual
91
+ override(GuardController, RuntimeRBAC, SecureOwnable)
92
+ returns (bool)
93
+ {
94
+ return interfaceId == type(IEventForwarder).interfaceId
95
+ || GuardController.supportsInterface(interfaceId)
96
+ || RuntimeRBAC.supportsInterface(interfaceId)
97
+ || SecureOwnable.supportsInterface(interfaceId);
98
+ }
99
+
100
+ /**
101
+ * @notice Clone a blox contract (RBAC-protected). Caller must have clone permission (e.g. OWNER_ROLE).
102
+ * @param initData When non-empty, target must implement ICopyable and is initialized via initializeWithData; otherwise standard initialize(...) is used.
103
+ */
104
+ function cloneBlox(
105
+ address bloxAddress,
106
+ address initialOwner,
107
+ address broadcaster,
108
+ address recovery,
109
+ uint256 timeLockPeriodSec,
110
+ bytes calldata initData
111
+ ) external nonReentrant returns (address cloneAddress) {
112
+ _validateExecuteBySelf();
113
+ _validateBloxImplementation(bloxAddress);
114
+
115
+ if (initData.length > 0 && !bloxAddress.supportsInterface(type(ICopyable).interfaceId)) {
116
+ revert SharedValidation.InvalidOperation(bloxAddress);
117
+ }
118
+ if (!_clonesWhitelist.contains(bloxAddress)) {
119
+ revert SharedValidation.InvalidOperation(bloxAddress);
120
+ }
121
+
122
+ cloneAddress = Clones.clone(bloxAddress);
123
+ address eventForwarder = address(this);
124
+ bool success;
125
+
126
+ if (initData.length > 0) {
127
+ success = _initializeCloneWithData(
128
+ cloneAddress,
129
+ initialOwner,
130
+ broadcaster,
131
+ recovery,
132
+ timeLockPeriodSec,
133
+ eventForwarder,
134
+ initData
135
+ );
136
+ } else {
137
+ success = _initializeClone(
138
+ cloneAddress,
139
+ initialOwner,
140
+ broadcaster,
141
+ recovery,
142
+ timeLockPeriodSec,
143
+ eventForwarder
144
+ );
145
+ }
146
+
147
+ if (!success) revert SharedValidation.OperationFailed();
148
+
149
+ _clones.add(cloneAddress);
150
+ _logComponentEvent(abi.encode(bloxAddress, cloneAddress, initialOwner, _clones.length()));
151
+ return cloneAddress;
152
+ }
153
+
154
+ /**
155
+ * @notice Add a trusted blox implementation to the whitelist with an associated clone price.
156
+ * Only callable by the controller (macro + _validateExecuteBySelf).
157
+ * @param bloxImplementation Address of the blox implementation contract (must be IBaseStateMachine, not zero, not this).
158
+ * @param price Per-clone payment configuration for this implementation.
159
+ */
160
+ function addToWhitelist(address bloxImplementation, EngineBlox.PaymentDetails calldata price) external nonReentrant {
161
+ _validateExecuteBySelf();
162
+ _validateBloxImplementation(bloxImplementation);
163
+ if (_clonesWhitelist.add(bloxImplementation)) {
164
+ _clonePrices[bloxImplementation] = price;
165
+ _logComponentEvent(
166
+ abi.encode(
167
+ bloxImplementation,
168
+ price.recipient,
169
+ price.nativeTokenAmount,
170
+ price.erc20TokenAddress,
171
+ price.erc20TokenAmount
172
+ )
173
+ );
174
+ }
175
+ }
176
+
177
+ /**
178
+ * @notice Remove a blox implementation from the whitelist. Only callable by the controller (macro + _validateExecuteBySelf).
179
+ * @param bloxImplementation Address to remove from the whitelist.
180
+ */
181
+ function removeFromWhitelist(address bloxImplementation) external nonReentrant {
182
+ _validateExecuteBySelf();
183
+ if (_clonesWhitelist.remove(bloxImplementation)) {
184
+ // Clear any configured price for the removed implementation
185
+ delete _clonePrices[bloxImplementation];
186
+ _logComponentEvent(abi.encode(bloxImplementation));
187
+ }
188
+ }
189
+
190
+ /**
191
+ * @dev Validates that an address is a deployed contract implementing IBaseStateMachine.
192
+ */
193
+ function _validateBloxImplementation(address bloxAddress) internal view {
194
+ if (!bloxAddress.supportsInterface(type(IBaseStateMachine).interfaceId)) {
195
+ revert SharedValidation.InvalidOperation(bloxAddress);
196
+ }
197
+ }
198
+
199
+ function _initializeClone(
200
+ address cloneAddress,
201
+ address initialOwner,
202
+ address broadcaster,
203
+ address recovery,
204
+ uint256 timeLockPeriodSec,
205
+ address eventForwarder
206
+ ) internal returns (bool) {
207
+ (bool success, ) = cloneAddress.call(
208
+ abi.encodeWithSignature(
209
+ "initialize(address,address,address,uint256,address)",
210
+ initialOwner,
211
+ broadcaster,
212
+ recovery,
213
+ timeLockPeriodSec,
214
+ eventForwarder
215
+ )
216
+ );
217
+ return success;
218
+ }
219
+
220
+ function _initializeCloneWithData(
221
+ address cloneAddress,
222
+ address initialOwner,
223
+ address broadcaster,
224
+ address recovery,
225
+ uint256 timeLockPeriodSec,
226
+ address eventForwarder,
227
+ bytes calldata initData
228
+ ) internal returns (bool) {
229
+ (bool success, ) = cloneAddress.call(
230
+ abi.encodeWithSignature(
231
+ "initializeWithData(address,address,address,uint256,address,bytes)",
232
+ initialOwner,
233
+ broadcaster,
234
+ recovery,
235
+ timeLockPeriodSec,
236
+ eventForwarder,
237
+ initData
238
+ )
239
+ );
240
+ return success;
241
+ }
242
+
243
+ function getCloneAtIndex(uint256 index) external view returns (address) {
244
+ return _clones.at(index);
245
+ }
246
+
247
+ function isClone(address cloneAddress) external view returns (bool) {
248
+ return _clones.contains(cloneAddress);
249
+ }
250
+
251
+ function getClonesCount() external view returns (uint256) {
252
+ return _clones.length();
253
+ }
254
+
255
+ function getWhitelistCount() external view returns (uint256) {
256
+ return _clonesWhitelist.length();
257
+ }
258
+
259
+ // function getWhitelistAtIndex(uint256 index) external view returns (address) {
260
+ // return _clonesWhitelist.at(index);
261
+ // }
262
+
263
+ function isWhitelisted(address bloxImplementation) external view returns (bool) {
264
+ return _clonesWhitelist.contains(bloxImplementation);
265
+ }
266
+
267
+ // /**
268
+ // * @notice Returns the configured clone price for a given blox implementation.
269
+ // * @dev Zero-valued fields indicate no explicit pricing (free clone) for this implementation.
270
+ // */
271
+ // function getClonePrice(address bloxImplementation) external view returns (EngineBlox.PaymentDetails memory) {
272
+ // return _clonePrices[bloxImplementation];
273
+ // }
274
+
275
+ /**
276
+ * @notice Returns whitelist entry (implementation + price) at a given index.
277
+ */
278
+ function getWhitelistEntryAtIndex(uint256 index) external view returns (CloneWhitelistEntry memory) {
279
+ address bloxImplementation = _clonesWhitelist.at(index);
280
+ return CloneWhitelistEntry({ bloxImplementation: bloxImplementation, price: _clonePrices[bloxImplementation] });
281
+ }
282
+
283
+ /**
284
+ * @dev Post-action hook: when cloneBlox was requested with payment, verify the tx payment matches
285
+ * the configured per-implementation clone price. Ensures the requestor did not alter the payment
286
+ * (e.g. recipient or amounts). Only runs when TxStatus is COMPLETED (e.g. not on cancellation
287
+ * or failed execution).
288
+ */
289
+ function _postActionHook(EngineBlox.TxRecord memory txRecord) internal virtual override {
290
+ if (txRecord.params.executionSelector != FactoryBloxDefinitions.CLONE_BLOX_SELECTOR) {
291
+ return;
292
+ }
293
+ if (txRecord.status != EngineBlox.TxStatus.COMPLETED) {
294
+ return;
295
+ }
296
+
297
+ // Decode the blox implementation address from execution params:
298
+ // cloneBlox(address bloxAddress, address initialOwner, address broadcaster, address recovery, uint256 timeLockPeriodSec, bytes initData)
299
+ (address bloxImplementation, , , , , ) = abi.decode(
300
+ txRecord.params.executionParams,
301
+ (address, address, address, address, uint256, bytes)
302
+ );
303
+
304
+ EngineBlox.PaymentDetails memory expectedPrice = _clonePrices[bloxImplementation];
305
+ EngineBlox.PaymentDetails memory payment = txRecord.payment;
306
+
307
+ if (
308
+ payment.recipient != expectedPrice.recipient
309
+ || payment.nativeTokenAmount != expectedPrice.nativeTokenAmount
310
+ || payment.erc20TokenAddress != expectedPrice.erc20TokenAddress
311
+ || payment.erc20TokenAmount != expectedPrice.erc20TokenAmount
312
+ ) {
313
+ revert SharedValidation.InvalidPayment();
314
+ }
315
+ }
316
+
317
+ function forwardTxEvent(
318
+ uint256 txId,
319
+ bytes4 functionSelector,
320
+ EngineBlox.TxStatus status,
321
+ address requester,
322
+ address target,
323
+ bytes32 operationType
324
+ ) external override {
325
+ if (!_clones.contains(msg.sender)) revert SharedValidation.NoPermission(msg.sender);
326
+
327
+ _logComponentEvent(abi.encode(msg.sender, txId, functionSelector, status, requester, target, operationType));
328
+
329
+ address eventForwarder = _secureState.eventForwarder;
330
+ if (eventForwarder != address(0) && eventForwarder != address(this)) {
331
+ try IEventForwarder(eventForwarder).forwardTxEvent(
332
+ txId, functionSelector, status, requester, target, operationType
333
+ ) {} catch {}
334
+ }
335
+ }
336
+
337
+ receive() external payable {}
338
+
339
+ fallback() external payable {
340
+ revert SharedValidation.NotSupported();
341
+ }
342
+
343
+ uint256[50] private __gap;
344
+ }