Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions src/AssetsLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

/// @title Assets Library
/// @notice A library for handling asset types and conversions in a blockchain system
/// @dev Provides utilities for managing different asset types and converting between asset IDs and addresses
library Assets {
/// @dev Represents different types of assets that can be used in the system
enum Kind {
/// @notice Custom Asset Id
Custom,
/// @notice Standard ERC20 token asset type
Erc20
}

/// @dev Represents an asset with its type and associated data
struct Asset {
/// @notice The kind/type of the asset (Custom or ERC20)
Kind kind;
/// @notice The data associated with the asset, encoded as bytes32
/// @dev The data is encoded as follows:
/// - For Custom assets: The asset ID is stored as uint256
/// - For ERC20 assets: The token address is stored as address
bytes32 data;
}

/// @dev The asset kind is not supported by the library.
error UnsupportedAssetKind(uint256 kind);

/// @dev The supplied address is not a valid asset address, it does not start with 0xFFFFFFFF.
error InvalidAssetId(address assetAddress);

/// @notice Converts a given asset to its corresponding address representation.
/// @dev The conversion follows the pattern: 0xFFFFFFFF followed by the 16-byte asset ID or if the asset is an ERC20
/// token,
/// the address of the token contract.
///
/// @param asset The asset be converted.
/// @return The address representation of the asset.
function toAddress(Asset memory asset) internal pure returns (address) {
if (isErc20(asset)) {
return address(uint160(uint256(asset.data)));
} else if (isCustom(asset)) {
return toAddress(asset.data);
} else {
revert UnsupportedAssetKind(uint256(asset.kind));
}
}

/// @notice Converts a given asset ID to its corresponding address representation.
/// @dev The conversion follows the pattern: 0xFFFFFFFF followed by the 16-byte asset ID.
///
/// @param assetId The bytes32 asset ID to be converted.
/// @return The address representation of the asset ID.
function toAddress(bytes32 assetId) internal pure returns (address) {
// Construct the address by combining the prefix 0xFFFFFFFF00000000000000000000000000000000
// with the lower 16 bytes of the assetId.
// This ensures the address follows the designated asset address format.
return address(uint160(uint256(0xFFFFFFFF << 128) | uint256(assetId)));
}

/// @notice Converts an asset address back to its original asset ID.
/// @dev Validates that the address starts with the prefix 0xFFFFFFFF and extracts the 16-byte asset ID.
///
/// @param assetAddress The address to be converted back to an asset ID.
/// @return The bytes32 representation of the original asset ID.
function toAssetId(address assetAddress) internal pure returns (bytes32) {
// Convert the address to a uint256 for bit manipulation.
uint256 addr = uint256(uint160(assetAddress));

// Ensure the upper 128 bits match the expected prefix 0xFFFFFFFF.
if (!isAssetIdCompatible(assetAddress)) {
revert InvalidAssetId(assetAddress);
}

// Extract the lower 128 bits which represent the original asset ID.
uint128 assetIdUint = uint128(addr);

// Convert the uint128 asset ID back to bytes32 format.
return bytes32(uint256(assetIdUint));
}

/// @notice Converts an asset address to an asset representation.
/// @dev Converts the asset address to an asset ID and constructs an asset object.
/// @param assetAddress The address of the asset to convert.
/// @return Asset Returns the asset representation of the provided address.
function toAsset(address assetAddress) internal pure returns (Asset memory) {
if (isAssetIdCompatible(assetAddress)) {
return Asset(Kind.Custom, toAssetId(assetAddress));
} else {
return Asset(Kind.Erc20, bytes32(uint256(uint160(assetAddress))));
}
}

/// @notice Determines if the provided asset is an ERC20 token.
/// @dev Checks if the asset kind matches the ERC20 enum value.
/// @param asset The asset to check.
/// @return bool Returns true if the asset is an ERC20 token, false otherwise.
function isErc20(Asset memory asset) internal pure returns (bool) {
return asset.kind == Kind.Erc20;
}

/// @notice Checks i the given asset is a Cstom type.
/// @dev Verifies if the asset's kid property mathes he Custom type.
/// @param asset The asset to check, defned by its kind and data.
/// @return bool Returns true if the asset is Custom, false otherwise.
function isCustom(Asset memory asset) internal pure returns (bool) {
return asset.kind == Kind.Custom;
}

/// @notice Checks if the given asset address is compatible by verifying it starts with the prefix 0xFFFFFFFF.
/// @dev This function converts the asset address to a uint256 and ensures the upper 128 bits match 0xFFFFFFFF.
/// @param assetAddress The address of the asset to check for compatibility.
/// @return bool Returns true if the asset address is compatible, false otherwise.
function isAssetIdCompatible(address assetAddress) internal pure returns (bool) {
// Convert the address to a uint256 for bit manipulation.
uint256 addr = uint256(uint160(assetAddress));

// Ensure the upper 128 bits match the expected prefix 0xFFFFFFFF.
if ((addr >> 128) != 0xFFFFFFFF) {
return false;
}

return true;
}

/// @notice Determines if the provided asset is a native asset.
/// @dev This function checks the asset kind and verifies if the asset address or ID corresponds to a native asset.
/// @param asset The asset to be checked, defined by its kind and data.
/// @return bool Returns true if the asset is native, false otherwise.
function isNative(Asset memory asset) internal pure returns (bool) {
if (isErc20(asset)) {
address assetAddress = address(uint160(uint256(asset.data)));
return (assetAddress == address(0));
} else if (isCustom(asset)) {
uint256 assetId = uint256(asset.data);
return (assetId == 0);
} else {
return false;
}
}
}
163 changes: 19 additions & 144 deletions src/BlueprintServiceManagerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import "src/IBlueprintServiceManager.sol";
/// of these functions interrupts the process flow.
contract BlueprintServiceManagerBase is IBlueprintServiceManager, RootChainEnabled {
using EnumerableSet for EnumerableSet.AddressSet;
using Assets for Assets.Asset;
using Assets for address;
using Assets for bytes32;

/// @dev The Current Blueprint Id
uint256 public currentBlueprintId;
Expand All @@ -28,9 +31,6 @@ contract BlueprintServiceManagerBase is IBlueprintServiceManager, RootChainEnabl
/// @notice This mapping is used to store the permitted payment assets for each service.
mapping(uint64 => EnumerableSet.AddressSet) private _permittedPaymentAssets;

/// @dev The supplied address is not a valid asset address, it does not start with 0xFFFFFFFF.
error InvalidAssetId(address assetAddress);

/// @inheritdoc IBlueprintServiceManager
function onBlueprintCreated(uint64 blueprintId, address owner, address mbsm) external virtual onlyFromRootChain {
currentBlueprintId = blueprintId;
Expand Down Expand Up @@ -176,7 +176,7 @@ contract BlueprintServiceManagerBase is IBlueprintServiceManager, RootChainEnabl
/// @inheritdoc IBlueprintServiceManager
function queryIsPaymentAssetAllowed(
uint64 serviceId,
ServiceOperators.Asset calldata asset
Assets.Asset calldata asset
)
external
view
Expand All @@ -192,25 +192,10 @@ contract BlueprintServiceManagerBase is IBlueprintServiceManager, RootChainEnabl
* @param serviceId The ID of the service for which the asset is being permitted.
* @param asset The asset to be permitted, defined by its kind and data.
*/
function _permitAsset(
uint64 serviceId,
ServiceOperators.Asset calldata asset
)
internal
virtual
returns (bool added)
{
if (asset.kind == ServiceOperators.AssetKind.Erc20) {
address assetAddress = address(uint160(uint256(asset.data)));
bool _added = _permittedPaymentAssets[serviceId].add(assetAddress);
return _added;
} else if (asset.kind == ServiceOperators.AssetKind.Custom) {
address assetAddress = _assetIdToAddress(asset.data);
bool _added = _permittedPaymentAssets[serviceId].add(assetAddress);
return _added;
} else {
return false;
}
function _permitAsset(uint64 serviceId, Assets.Asset calldata asset) internal virtual returns (bool added) {
address assetAddress = asset.toAddress();
bool _added = _permittedPaymentAssets[serviceId].add(assetAddress);
return _added;
}

/**
Expand All @@ -219,25 +204,10 @@ contract BlueprintServiceManagerBase is IBlueprintServiceManager, RootChainEnabl
* @param serviceId The ID of the service for which the asset is being revoked.
* @param asset The asset to be revoked, defined by its kind and data.
*/
function _revokeAsset(
uint64 serviceId,
ServiceOperators.Asset calldata asset
)
internal
virtual
returns (bool removed)
{
if (asset.kind == ServiceOperators.AssetKind.Erc20) {
address assetAddress = address(uint160(uint256(asset.data)));
bool _removed = _permittedPaymentAssets[serviceId].remove(assetAddress);
return _removed;
} else if (asset.kind == ServiceOperators.AssetKind.Custom) {
address assetAddress = _assetIdToAddress(asset.data);
bool _removed = _permittedPaymentAssets[serviceId].remove(assetAddress);
return _removed;
} else {
return false;
}
function _revokeAsset(uint64 serviceId, Assets.Asset calldata asset) internal virtual returns (bool removed) {
address assetAddress = asset.toAddress();
bool _removed = _permittedPaymentAssets[serviceId].remove(assetAddress);
return _removed;
}

/**
Expand Down Expand Up @@ -279,24 +249,15 @@ contract BlueprintServiceManagerBase is IBlueprintServiceManager, RootChainEnabl
* @param serviceId The ID of the service for which permitted assets are being retrieved.
* @return assets An array of ServiceOperators.Asset structs representing the permitted assets.
*/
function _getPermittedAssets(uint64 serviceId) internal view virtual returns (ServiceOperators.Asset[] memory) {
function _getPermittedAssets(uint64 serviceId) internal view virtual returns (Assets.Asset[] memory) {
EnumerableSet.AddressSet storage permittedAssets = _permittedPaymentAssets[serviceId];
ServiceOperators.Asset[] memory assets = new ServiceOperators.Asset[](permittedAssets.length());
Assets.Asset[] memory assets = new Assets.Asset[](permittedAssets.length());
for (uint256 i = 0; i < permittedAssets.length(); i++) {
address assetAddress = permittedAssets.at(i);
if (assetAddress == address(0)) {
continue;
}
ServiceOperators.AssetKind kind;
bytes32 data;
if (_checkAddressIsAssetIdCompatible(assetAddress)) {
kind = ServiceOperators.AssetKind.Custom;
data = _addressToAssetId(assetAddress);
} else {
kind = ServiceOperators.AssetKind.Erc20;
data = bytes32(uint256(uint160(assetAddress)));
}
assets[i] = ServiceOperators.Asset(kind, data);
assets[i] = assetAddress.toAsset();
}
return assets;
}
Expand All @@ -308,99 +269,13 @@ contract BlueprintServiceManagerBase is IBlueprintServiceManager, RootChainEnabl
* @param asset The asset to check, defined by its kind and data.
* @return isAllowed Boolean indicating whether the asset is permitted.
*/
function _isAssetPermitted(
uint64 serviceId,
ServiceOperators.Asset calldata asset
)
internal
view
virtual
returns (bool)
{
function _isAssetPermitted(uint64 serviceId, Assets.Asset calldata asset) internal view virtual returns (bool) {
// Native assets are always permitted.
if (_isNativeAsset(asset)) {
if (asset.isNative()) {
return true;
} else if (asset.kind == ServiceOperators.AssetKind.Erc20) {
address assetAddress = address(uint160(uint256(asset.data)));
return _permittedPaymentAssets[serviceId].contains(assetAddress);
} else if (asset.kind == ServiceOperators.AssetKind.Custom) {
address assetAddress = _assetIdToAddress(asset.data);
return _permittedPaymentAssets[serviceId].contains(assetAddress);
} else {
return false;
}
}

/**
* @notice Converts a given asset ID to its corresponding address representation.
* @dev The conversion follows the pattern: 0xFFFFFFFF followed by the 16-byte asset ID.
*
* @param assetId The bytes32 asset ID to be converted.
* @return The address representation of the asset ID.
*/
function _assetIdToAddress(bytes32 assetId) internal pure returns (address) {
// Construct the address by combining the prefix 0xFFFFFFFF00000000000000000000000000000000
// with the lower 16 bytes of the assetId.
// This ensures the address follows the designated asset address format.
return address(uint160(uint256(0xFFFFFFFF << 128) | uint256(assetId)));
}

/**
* @notice Converts an asset address back to its original asset ID.
* @dev Validates that the address starts with the prefix 0xFFFFFFFF and extracts the 16-byte asset ID.
*
* @param assetAddress The address to be converted back to an asset ID.
* @return The bytes32 representation of the original asset ID.
*/
function _addressToAssetId(address assetAddress) internal pure returns (bytes32) {
// Convert the address to a uint256 for bit manipulation.
uint256 addr = uint256(uint160(assetAddress));

// Ensure the upper 128 bits match the expected prefix 0xFFFFFFFF.
if (!_checkAddressIsAssetIdCompatible(assetAddress)) {
revert InvalidAssetId(assetAddress);
}

// Extract the lower 128 bits which represent the original asset ID.
uint128 assetIdUint = uint128(addr);

// Convert the uint128 asset ID back to bytes32 format.
return bytes32(uint256(assetIdUint));
}

/**
* @notice Checks if the given asset address is compatible by verifying it starts with the prefix 0xFFFFFFFF.
* @dev This function converts the asset address to a uint256 and ensures the upper 128 bits match 0xFFFFFFFF.
* @param assetAddress The address of the asset to check for compatibility.
* @return bool Returns true if the asset address is compatible, false otherwise.
*/
function _checkAddressIsAssetIdCompatible(address assetAddress) internal pure returns (bool) {
// Convert the address to a uint256 for bit manipulation.
uint256 addr = uint256(uint160(assetAddress));

// Ensure the upper 128 bits match the expected prefix 0xFFFFFFFF.
if ((addr >> 128) != 0xFFFFFFFF) {
return false;
}

return true;
}

/**
* @notice Determines if the provided asset is a native asset.
* @dev This function checks the asset kind and verifies if the asset address or ID corresponds to a native asset.
* @param asset The asset to be checked, defined by its kind and data.
* @return bool Returns true if the asset is native, false otherwise.
*/
function _isNativeAsset(ServiceOperators.Asset calldata asset) internal pure returns (bool) {
if (asset.kind == ServiceOperators.AssetKind.Erc20) {
address assetAddress = address(uint160(uint256(asset.data)));
return (assetAddress == address(0));
} else if (asset.kind == ServiceOperators.AssetKind.Custom) {
uint256 assetId = uint256(asset.data);
return (assetId == 0);
} else {
return false;
address assetAddress = asset.toAddress();
return _permittedPaymentAssets[serviceId].contains(assetAddress);
}
}
}
Loading
Loading