11// SPDX-License-Identifier: UNLICENSED
22pragma solidity ^ 0.8.20 ;
33
4+ import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol " ;
5+
46import "src/Permissions.sol " ;
57import "src/IBlueprintServiceManager.sol " ;
68
@@ -13,12 +15,22 @@ import "src/IBlueprintServiceManager.sol";
1315/// Each function serves as a hook for different lifecycle events, and reverting any
1416/// of these functions interrupts the process flow.
1517contract BlueprintServiceManagerBase is IBlueprintServiceManager , RootChainEnabled {
18+ using EnumerableSet for EnumerableSet.AddressSet;
19+
1620 /// @dev The Current Blueprint Id
1721 uint256 public currentBlueprintId;
1822
1923 /// @dev The address of the owner of the blueprint
2024 address public blueprintOwner;
2125
26+ /// @dev a mapping between service id and permitted payment assets.
27+ /// @dev serviceId => EnumerableSet of permitted payment assets.
28+ /// @notice This mapping is used to store the permitted payment assets for each service.
29+ mapping (uint64 => EnumerableSet.AddressSet) private _permittedPaymentAssets;
30+
31+ /// @dev The supplied address is not a valid asset address, it does not start with 0xFFFFFFFF.
32+ error InvalidAssetId (address assetAddress );
33+
2234 /// @inheritdoc IBlueprintServiceManager
2335 function onBlueprintCreated (uint64 blueprintId , address owner , address mbsm ) external virtual onlyFromRootChain {
2436 currentBlueprintId = blueprintId;
@@ -152,7 +164,243 @@ contract BlueprintServiceManagerBase is IBlueprintServiceManager, RootChainEnabl
152164 }
153165
154166 /// @inheritdoc IBlueprintServiceManager
155- function queryDeveloperPaymentAddress (uint64 ) external view virtual returns (address developerPaymentAddress ) {
167+ function queryDeveloperPaymentAddress (uint64 )
168+ external
169+ view
170+ virtual
171+ returns (address payable developerPaymentAddress )
172+ {
156173 return payable (blueprintOwner);
157174 }
175+
176+ /// @inheritdoc IBlueprintServiceManager
177+ function queryIsPaymentAssetAllowed (
178+ uint64 serviceId ,
179+ ServiceOperators.Asset calldata asset
180+ )
181+ external
182+ view
183+ virtual
184+ returns (bool isAllowed )
185+ {
186+ return _isAssetPermitted (serviceId, asset);
187+ }
188+
189+ /**
190+ * @notice Permits a specific asset for a given service.
191+ * @dev Adds the asset to the set of permitted payment assets based on its kind.
192+ * @param serviceId The ID of the service for which the asset is being permitted.
193+ * @param asset The asset to be permitted, defined by its kind and data.
194+ */
195+ function _permitAsset (
196+ uint64 serviceId ,
197+ ServiceOperators.Asset calldata asset
198+ )
199+ internal
200+ virtual
201+ returns (bool added )
202+ {
203+ if (asset.kind == ServiceOperators.AssetKind.Erc20) {
204+ address assetAddress = address (uint160 (uint256 (asset.data)));
205+ bool _added = _permittedPaymentAssets[serviceId].add (assetAddress);
206+ return _added;
207+ } else if (asset.kind == ServiceOperators.AssetKind.Custom) {
208+ address assetAddress = _assetIdToAddress (asset.data);
209+ bool _added = _permittedPaymentAssets[serviceId].add (assetAddress);
210+ return _added;
211+ } else {
212+ return false ;
213+ }
214+ }
215+
216+ /**
217+ * @notice Revokes a previously permitted asset for a given service.
218+ * @dev Removes the asset from the set of permitted payment assets based on its kind.
219+ * @param serviceId The ID of the service for which the asset is being revoked.
220+ * @param asset The asset to be revoked, defined by its kind and data.
221+ */
222+ function _revokeAsset (
223+ uint64 serviceId ,
224+ ServiceOperators.Asset calldata asset
225+ )
226+ internal
227+ virtual
228+ returns (bool removed )
229+ {
230+ if (asset.kind == ServiceOperators.AssetKind.Erc20) {
231+ address assetAddress = address (uint160 (uint256 (asset.data)));
232+ bool _removed = _permittedPaymentAssets[serviceId].remove (assetAddress);
233+ return _removed;
234+ } else if (asset.kind == ServiceOperators.AssetKind.Custom) {
235+ address assetAddress = _assetIdToAddress (asset.data);
236+ bool _removed = _permittedPaymentAssets[serviceId].remove (assetAddress);
237+ return _removed;
238+ } else {
239+ return false ;
240+ }
241+ }
242+
243+ /**
244+ * @notice Clears all permitted assets for a given service.
245+ * @dev Iterates through the set of permitted assets and removes each one.
246+ * @param serviceId The ID of the service for which permitted assets are being cleared.
247+ */
248+ function _clearPermittedAssets (uint64 serviceId ) internal virtual returns (bool cleared ) {
249+ EnumerableSet.AddressSet storage permittedAssets = _permittedPaymentAssets[serviceId];
250+ uint256 length = permittedAssets.length ();
251+ while (length > 0 ) {
252+ address assetAddress = permittedAssets.at (0 );
253+ permittedAssets.remove (assetAddress);
254+ length = permittedAssets.length ();
255+ }
256+
257+ // The set should be empty after clearing all permitted assets.
258+ return permittedAssets.length () == 0 ;
259+ }
260+
261+ /**
262+ * @notice Retrieves all permitted assets for a given service as an array of addresses.
263+ * @dev Converts the EnumerableSet of permitted assets to a dynamic array of addresses.
264+ * @param serviceId The ID of the service for which permitted assets are being retrieved.
265+ * @return assets An array of addresses representing the permitted assets.
266+ */
267+ function _getPermittedAssetsAsAddresses (uint64 serviceId ) internal view virtual returns (address [] memory ) {
268+ EnumerableSet.AddressSet storage permittedAssets = _permittedPaymentAssets[serviceId];
269+ address [] memory assets = new address [](permittedAssets.length ());
270+ for (uint256 i = 0 ; i < permittedAssets.length (); i++ ) {
271+ assets[i] = permittedAssets.at (i);
272+ }
273+ return assets;
274+ }
275+
276+ /**
277+ * @notice Retrieves all permitted assets for a given service as an array of Asset structs.
278+ * @dev Converts the EnumerableSet of permitted assets to a dynamic array of ServiceOperators.Asset.
279+ * @param serviceId The ID of the service for which permitted assets are being retrieved.
280+ * @return assets An array of ServiceOperators.Asset structs representing the permitted assets.
281+ */
282+ function _getPermittedAssets (uint64 serviceId ) internal view virtual returns (ServiceOperators.Asset[] memory ) {
283+ EnumerableSet.AddressSet storage permittedAssets = _permittedPaymentAssets[serviceId];
284+ ServiceOperators.Asset[] memory assets = new ServiceOperators.Asset [](permittedAssets.length ());
285+ for (uint256 i = 0 ; i < permittedAssets.length (); i++ ) {
286+ address assetAddress = permittedAssets.at (i);
287+ if (assetAddress == address (0 )) {
288+ continue ;
289+ }
290+ ServiceOperators.AssetKind kind;
291+ bytes32 data;
292+ if (_checkAddressIsAssetIdCompatible (assetAddress)) {
293+ kind = ServiceOperators.AssetKind.Custom;
294+ data = _addressToAssetId (assetAddress);
295+ } else {
296+ kind = ServiceOperators.AssetKind.Erc20;
297+ data = bytes32 (uint256 (uint160 (assetAddress)));
298+ }
299+ assets[i] = ServiceOperators.Asset (kind, data);
300+ }
301+ return assets;
302+ }
303+
304+ /**
305+ * @notice Checks if a specific asset is permitted for a given service.
306+ * @dev Determines if the asset is contained within the set of permitted payment assets based on its kind.
307+ * @param serviceId The ID of the service to check.
308+ * @param asset The asset to check, defined by its kind and data.
309+ * @return isAllowed Boolean indicating whether the asset is permitted.
310+ */
311+ function _isAssetPermitted (
312+ uint64 serviceId ,
313+ ServiceOperators.Asset calldata asset
314+ )
315+ internal
316+ view
317+ virtual
318+ returns (bool )
319+ {
320+ // Native assets are always permitted.
321+ if (_isNativeAsset (asset)) {
322+ return true ;
323+ } else if (asset.kind == ServiceOperators.AssetKind.Erc20) {
324+ address assetAddress = address (uint160 (uint256 (asset.data)));
325+ return _permittedPaymentAssets[serviceId].contains (assetAddress);
326+ } else if (asset.kind == ServiceOperators.AssetKind.Custom) {
327+ address assetAddress = _assetIdToAddress (asset.data);
328+ return _permittedPaymentAssets[serviceId].contains (assetAddress);
329+ } else {
330+ return false ;
331+ }
332+ }
333+
334+ /**
335+ * @notice Converts a given asset ID to its corresponding address representation.
336+ * @dev The conversion follows the pattern: 0xFFFFFFFF followed by the 16-byte asset ID.
337+ *
338+ * @param assetId The bytes32 asset ID to be converted.
339+ * @return The address representation of the asset ID.
340+ */
341+ function _assetIdToAddress (bytes32 assetId ) internal pure returns (address ) {
342+ // Construct the address by combining the prefix 0xFFFFFFFF00000000000000000000000000000000
343+ // with the lower 16 bytes of the assetId.
344+ // This ensures the address follows the designated asset address format.
345+ return address (uint160 (uint256 (0xFFFFFFFF << 128 ) | uint256 (assetId)));
346+ }
347+
348+ /**
349+ * @notice Converts an asset address back to its original asset ID.
350+ * @dev Validates that the address starts with the prefix 0xFFFFFFFF and extracts the 16-byte asset ID.
351+ *
352+ * @param assetAddress The address to be converted back to an asset ID.
353+ * @return The bytes32 representation of the original asset ID.
354+ */
355+ function _addressToAssetId (address assetAddress ) internal pure returns (bytes32 ) {
356+ // Convert the address to a uint256 for bit manipulation.
357+ uint256 addr = uint256 (uint160 (assetAddress));
358+
359+ // Ensure the upper 128 bits match the expected prefix 0xFFFFFFFF.
360+ if (! _checkAddressIsAssetIdCompatible (assetAddress)) {
361+ revert InvalidAssetId (assetAddress);
362+ }
363+
364+ // Extract the lower 128 bits which represent the original asset ID.
365+ uint128 assetIdUint = uint128 (addr);
366+
367+ // Convert the uint128 asset ID back to bytes32 format.
368+ return bytes32 (uint256 (assetIdUint));
369+ }
370+
371+ /**
372+ * @notice Checks if the given asset address is compatible by verifying it starts with the prefix 0xFFFFFFFF.
373+ * @dev This function converts the asset address to a uint256 and ensures the upper 128 bits match 0xFFFFFFFF.
374+ * @param assetAddress The address of the asset to check for compatibility.
375+ * @return bool Returns true if the asset address is compatible, false otherwise.
376+ */
377+ function _checkAddressIsAssetIdCompatible (address assetAddress ) internal pure returns (bool ) {
378+ // Convert the address to a uint256 for bit manipulation.
379+ uint256 addr = uint256 (uint160 (assetAddress));
380+
381+ // Ensure the upper 128 bits match the expected prefix 0xFFFFFFFF.
382+ if ((addr >> 128 ) != 0xFFFFFFFF ) {
383+ return false ;
384+ }
385+
386+ return true ;
387+ }
388+
389+ /**
390+ * @notice Determines if the provided asset is a native asset.
391+ * @dev This function checks the asset kind and verifies if the asset address or ID corresponds to a native asset.
392+ * @param asset The asset to be checked, defined by its kind and data.
393+ * @return bool Returns true if the asset is native, false otherwise.
394+ */
395+ function _isNativeAsset (ServiceOperators.Asset calldata asset ) internal pure returns (bool ) {
396+ if (asset.kind == ServiceOperators.AssetKind.Erc20) {
397+ address assetAddress = address (uint160 (uint256 (asset.data)));
398+ return (assetAddress == address (0 ));
399+ } else if (asset.kind == ServiceOperators.AssetKind.Custom) {
400+ uint256 assetId = uint256 (asset.data);
401+ return (assetId == 0 );
402+ } else {
403+ return false ;
404+ }
405+ }
158406}
0 commit comments