// File: contracts/base/AccessControl/LibAccessControl.sol



// SPDX-License-Identifier: MIT
/**
 * Copyright (C) 2024 uSmart
 */
pragma solidity ^0.8.9;

import "./LibAccessControlStorage.sol";

import { IERC173 } from "../../interfaces/IERC173.sol";

import { EnumerableSet } from "../../utils/EnumerableSet.sol";
import { UintUtils } from "../../utils/UintUtils.sol";
import { AddressUtils } from "../../utils/AddressUtils.sol";

library LibAccessControl {
	using EnumerableSet for EnumerableSet.AddressSet;
	using UintUtils for uint256;
	using AddressUtils for address;

	error Ownable__NotOwner();
	error Ownable__NotTransitiveOwner();

	error AccessDenied(bytes32 role, address account);

	event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

	event RoleAdminChanged(address indexed owner, bytes32 role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
	event RoleGranted(address indexed owner, bytes32 role, address indexed account, address indexed sender);
	event RoleRevoked(address indexed owner, bytes32 role, address indexed account, address indexed sender);

	bytes32 internal constant DEFAULT_ADMIN_ROLE = 0x00;

	function _setOwner(address _newOwner) internal {
		AccessControllStorage storage acls = LibAccessControlStorage.layout();
		address previousOwner = acls.owner;
		acls.owner = _newOwner;

		//Init DEFAULT_ADMIN_ROLE to _newOwner
		LibAccessControl._grantRole(LibAccessControl.DEFAULT_ADMIN_ROLE, _newOwner);

		emit OwnershipTransferred(previousOwner, _newOwner);
	}

	function _owner() internal view returns (address owner_) {
		AccessControllStorage storage acls = LibAccessControlStorage.layout();
		owner_ = acls.owner;
	}

	function _transitiveOwner() internal view returns (address owner_) {
		owner_ = LibAccessControl._owner();

		while (owner_.isContract()) {
			try IERC173(owner_).owner() returns (address transitiveOwner) {
				owner_ = transitiveOwner;
			} catch {
				break;
			}
		}
	}

	function _enforceIsOwner() internal view {
		//require(msg.sender == _owner(), "Not owner!");
		if (msg.sender != _owner()) {
			revert Ownable__NotOwner();
		}
	}

	function _enforceIsTransitiveOwner() internal view {
		//require(msg.sender == _transitiveOwner(), "Not transitive owner!");
		if (msg.sender != _transitiveOwner()) {
			revert Ownable__NotTransitiveOwner();
		}
	}

	/**
	 * @notice assign role to given account
	 * @param _role role to assign
	 * @param _account recipient of role assignment
	 */
	function _grantRole(bytes32 _role, address _account) internal {
		AccessControllStorage storage acls = LibAccessControlStorage.layout();
		if (!_hasRole(_role, _account)) {
			acls.roles[acls.owner][_role].members.add(_account);
			emit RoleGranted(acls.owner, _role, _account, msg.sender);
		}
	}

	/**
	 * @notice unassign role from given account
	 * @param _role role to unassign
	 * @param _account account to revokeAccessControlStorage
	 */
	function _revokeRole(bytes32 _role, address _account) internal {
		AccessControllStorage storage acls = LibAccessControlStorage.layout();
		// require(_role != LibAccessControl.DEFAULT_ADMIN_ROLE && _account != acls.owner);
		acls.roles[acls.owner][_role].members.remove(_account);
		emit RoleRevoked(acls.owner, _role, _account, msg.sender);
	}

	/**
	 * @notice relinquish role
	 * @param _role role to relinquish
	 */
	function _renounceRole(bytes32 _role) internal {
		_revokeRole(_role, msg.sender);
	}

	/**
	 * @notice Query one of the accounts that have role of the project
	 * @dev WARNING: When using _getProjectRoleMember and _getProjectRoleMemberCount, make sure you perform all queries on the same block.
	 * @param _role role to query
	 * @param _index index of role member
	 */
	function _getRoleMember(bytes32 _role, uint256 _index) internal view returns (address) {
		AccessControllStorage storage acls = LibAccessControlStorage.layout();
		return acls.roles[acls.owner][_role].members.at(_index);
	}

	/**
	 * @notice Query the number of accounts that have role.
	 * @dev WARNING: When using _getRoleMember and _getRoleMemberCount, make sure you perform all queries on the same block.
	 * @param _role role to query
	 */
	function _getRoleMemberCount(address, bytes32 _role) internal view returns (uint256) {
		AccessControllStorage storage acls = LibAccessControlStorage.layout();
		return acls.roles[acls.owner][_role].members.length();
	}

	/**
	 * @notice query whether role is assigned to account
	 * @param _role role to query
	 * @param _account account to query
	 * @return bool whether role is assigned to account
	 */
	function _hasRole(bytes32 _role, address _account) internal view returns (bool) {
		AccessControllStorage storage acls = LibAccessControlStorage.layout();
		return acls.roles[acls.owner][_role].members.contains(_account);
	}

	/**
	 * @notice revert if sender does not have given role
	 * @param _role role to query
	 */
	function _checkRole(bytes32 _role) internal view {
		_checkRole(_role, msg.sender);
	}

	/**
	 * @notice revert if given account does not have given role
	 * @param _role role to query
	 * @param _account to query
	 */
	function _checkRole(bytes32 _role, address _account) internal view {
		if (!_hasRole(_role, _account)) {
			revert AccessDenied({ role: _role, account: _account });
		}
	}

	/**
	 * @notice query admin role for given role
	 * @param _role role to query
	 * @return admin role
	 */
	function _getRoleAdmin(bytes32 _role) internal view returns (bytes32) {
		AccessControllStorage storage acls = LibAccessControlStorage.layout();
		return acls.roles[acls.owner][_role].adminRole;
	}

	/**
	 * @notice set role as admin role
	 * @param _role role to set
	 * @param _adminRole admin role to set
	 */
	function _setRoleAdmin(bytes32 _role, bytes32 _adminRole) internal {
		AccessControllStorage storage acls = LibAccessControlStorage.layout();
		bytes32 previousAdminRole = _getRoleAdmin(_role);
		acls.roles[acls.owner][_role].adminRole = _adminRole;
		emit RoleAdminChanged(acls.owner, _role, previousAdminRole, _adminRole);
	}
}

// File: contracts/base/AccessControl/LibAccessControlStorage.sol



// SPDX-License-Identifier: MIT
/**
 * Copyright (C) 2024 uSmart
 */
pragma solidity ^0.8.9;

import { EnumerableSet } from "../../utils/EnumerableSet.sol";
import "./RoleData.sol";

struct AccessControllStorage {
	//owner => role => adminRole, members mapping
	address owner;
	mapping(address => mapping(bytes32 => RoleData)) roles;
}

library LibAccessControlStorage {
	bytes32 internal constant STORAGE_SLOT = keccak256("usmart.common.access-control.storage.v1");

	function layout() internal pure returns (AccessControllStorage storage acls_) {
		bytes32 position = STORAGE_SLOT;
		assembly {
			acls_.slot := position
		}
	}
}

// File: contracts/base/AccessControl/RoleData.sol



// SPDX-License-Identifier: MIT
/**
 * Copyright (C) 2024 uSmart
 */
pragma solidity ^0.8.9;

import { EnumerableSet } from "../../utils/EnumerableSet.sol";

struct RoleData {
	bytes32 adminRole;
	EnumerableSet.AddressSet members;
}

// File: contracts/base/Diamond/LibDiamond.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/
import "./LibDiamondStorage.sol";
import { IDiamondCut } from "../../interfaces/IDiamondCut.sol";

import { LibAccessControl } from "../AccessControl/LibAccessControl.sol";

// Remember to add the loupe functions from DiamondLoupeFacet to the diamond.
// The loupe functions are required by the EIP2535 Diamonds standard

error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata);

library LibDiamond {
	event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);

	function setDiamondAddress(address _diamondAddress) internal {
		DiamondStorage storage ds = LibDiamondStorage.layout();
		require(ds.diamondAddress == address(0), "Already initialized!");
		ds.diamondAddress = _diamondAddress;
	}

	function getDiamondAddress() internal view returns (address) {
		DiamondStorage storage ds = LibDiamondStorage.layout();
		return ds.diamondAddress;
	}

	function setContractOwner(address _newOwner) internal {
		LibAccessControl._setOwner(_newOwner);
	}

	function enforceIsContractOwner() internal view {
		LibAccessControl._enforceIsOwner();
	}

	// Internal function version of diamondCut
	function diamondCut(IDiamondCut.FacetCut[] memory _diamondCut, address _init, bytes memory _calldata) internal {
		for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) {
			IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
			if (action == IDiamondCut.FacetCutAction.Add) {
				addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
			} else if (action == IDiamondCut.FacetCutAction.Replace) {
				replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
			} else if (action == IDiamondCut.FacetCutAction.Remove) {
				removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
			} else {
				revert("LibDiamondCut: Incorrect FacetCutAction");
			}
		}
		emit DiamondCut(_diamondCut, _init, _calldata);
		initializeDiamondCut(_init, _calldata);
	}

	function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
		require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut");
		DiamondStorage storage ds = LibDiamondStorage.layout();
		require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)");
		uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
		// add new facet address if it does not exist
		if (selectorPosition == 0) {
			addFacet(ds, _facetAddress);
		}
		for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
			bytes4 selector = _functionSelectors[selectorIndex];
			address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
			require(oldFacetAddress == address(0), "LibDiamondCut: Can't add function that already exists");
			addFunction(ds, selector, selectorPosition, _facetAddress);
			selectorPosition++;
		}
	}

	function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
		require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut");
		DiamondStorage storage ds = LibDiamondStorage.layout();
		require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)");
		uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
		// add new facet address if it does not exist
		if (selectorPosition == 0) {
			addFacet(ds, _facetAddress);
		}
		for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
			bytes4 selector = _functionSelectors[selectorIndex];
			address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
			require(oldFacetAddress != _facetAddress, "LibDiamondCut: Can't replace function with same function");
			removeFunction(ds, oldFacetAddress, selector);
			addFunction(ds, selector, selectorPosition, _facetAddress);
			selectorPosition++;
		}
	}

	function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal {
		require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut");
		DiamondStorage storage ds = LibDiamondStorage.layout();
		// if function does not exist then do nothing and return
		require(_facetAddress == address(0), "LibDiamondCut: Remove facet address must be address(0)");
		for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) {
			bytes4 selector = _functionSelectors[selectorIndex];
			address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
			removeFunction(ds, oldFacetAddress, selector);
		}
	}

	function addFacet(DiamondStorage storage ds, address _facetAddress) internal {
		enforceHasContractCode(_facetAddress, "LibDiamondCut: New facet has no code");
		ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length;
		ds.facetAddresses.push(_facetAddress);
	}

	function addFunction(DiamondStorage storage ds, bytes4 _selector, uint96 _selectorPosition, address _facetAddress) internal {
		ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition;
		ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector);
		ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress;
	}

	function removeFunction(DiamondStorage storage ds, address _facetAddress, bytes4 _selector) internal {
		require(_facetAddress != address(0), "LibDiamondCut: Can't remove function that doesn't exist");
		// an immutable function is a function defined directly in a diamond
		require(_facetAddress != address(this), "LibDiamondCut: Can't remove immutable function");
		// replace selector with last selector, then delete last selector
		uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition;
		uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1;
		// if not the same then replace _selector with lastSelector
		if (selectorPosition != lastSelectorPosition) {
			bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition];
			ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector;
			ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition);
		}
		// delete the last selector
		ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
		delete ds.selectorToFacetAndPosition[_selector];

		// if no more selectors for facet address then delete the facet address
		if (lastSelectorPosition == 0) {
			// replace facet address with last facet address and delete last facet address
			uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1;
			uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
			if (facetAddressPosition != lastFacetAddressPosition) {
				address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition];
				ds.facetAddresses[facetAddressPosition] = lastFacetAddress;
				ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition;
			}
			ds.facetAddresses.pop();
			delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
		}
	}

	function initializeDiamondCut(address _init, bytes memory _calldata) internal {
		if (_init == address(0)) {
			return;
		}
		enforceHasContractCode(_init, "LibDiamondCut: _init address has no code");
		(bool success, bytes memory error) = _init.delegatecall(_calldata);
		if (!success) {
			if (error.length > 0) {
				// bubble up error
				/// @solidity memory-safe-assembly
				assembly {
					let returndata_size := mload(error)
					revert(add(32, error), returndata_size)
				}
			} else {
				revert InitializationFunctionReverted(_init, _calldata);
			}
		}
	}

	function enforceHasContractCode(address _contract, string memory _errorMessage) internal view {
		uint256 contractSize;
		assembly {
			contractSize := extcodesize(_contract)
		}
		require(contractSize > 0, _errorMessage);
	}
}

// File: contracts/base/Diamond/LibDiamondStorage.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import { EnumerableSet } from "../../utils/EnumerableSet.sol";

struct FacetAddressAndPosition {
	address facetAddress;
	uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array
}

struct FacetFunctionSelectors {
	bytes4[] functionSelectors;
	uint256 facetAddressPosition; // position of facetAddress in facetAddresses array
}

struct DiamondStorage {
	address diamondAddress;
	// maps function selector to the facet address and
	// the position of the selector in the facetFunctionSelectors.selectors array
	mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition;
	// maps facet addresses to function selectors
	mapping(address => FacetFunctionSelectors) facetFunctionSelectors;
	// facet addresses
	address[] facetAddresses;
	// Used to query if a contract implements an interface.
	// Used to implement ERC-165.
	mapping(bytes4 => bool) supportedInterfaces;
	//the whole diamond is paused or not
	bool paused;
}

library LibDiamondStorage {
	bytes32 internal constant STORAGE_SLOT = keccak256("usmart.contracts.diamond.storage.v1");

	function layout() internal pure returns (DiamondStorage storage layout_) {
		bytes32 position = STORAGE_SLOT;
		assembly {
			layout_.slot := position
		}
	}
}

// File: contracts/Diamond.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
*
* Implementation of a diamond.
/******************************************************************************/

import "./base/Diamond/LibDiamondStorage.sol";
import { LibDiamond } from "./base/Diamond/LibDiamond.sol";
import { IDiamondCut } from "./interfaces/IDiamondCut.sol";

contract Diamond {
	constructor(address _contractOwner, address _diamondCutFacet) payable {
		LibDiamond.setDiamondAddress(address(this));
		LibDiamond.setContractOwner(_contractOwner);

		// Add the diamondCut external function from the diamondCutFacet
		IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](1);
		bytes4[] memory functionSelectors = new bytes4[](1);
		functionSelectors[0] = IDiamondCut.diamondCut.selector;
		cut[0] = IDiamondCut.FacetCut({
			facetAddress: _diamondCutFacet,
			action: IDiamondCut.FacetCutAction.Add,
			functionSelectors: functionSelectors
		});
		LibDiamond.diamondCut(cut, address(0), "");
	}

	// Find facet for function that is called and execute the
	// function if a facet is found and return any value.
	fallback() external payable {
		DiamondStorage storage ds = LibDiamondStorage.layout();
		// get facet from function selector
		address facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
		require(facet != address(0), "Diamond: Function does not exist");
		// Execute external function from facet using delegatecall and return any value.
		assembly {
			// copy function selector and any arguments
			calldatacopy(0, 0, calldatasize())
			// execute function call using the facet
			let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
			// get any return value
			returndatacopy(0, 0, returndatasize())
			// return any return value or error back to the caller
			switch result
			case 0 {
				revert(0, returndatasize())
			}
			default {
				return(0, returndatasize())
			}
		}
	}

	receive() external payable {}
}

// File: contracts/interfaces/IDiamondCut.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/

interface IDiamondCut {
	enum FacetCutAction {
		Add,
		Replace,
		Remove
	}
	// Add=0, Replace=1, Remove=2

	struct FacetCut {
		address facetAddress;
		FacetCutAction action;
		bytes4[] functionSelectors;
	}

	/// @notice Add/replace/remove any number of functions and optionally execute
	///         a function with delegatecall
	/// @param _diamondCut Contains the facet addresses and function selectors
	/// @param _init The address of the contract or facet to execute _calldata
	/// @param _calldata A function call, including function selector and arguments
	///                  _calldata is executed with delegatecall on _init
	function diamondCut(FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata) external;

	event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
}

// File: contracts/interfaces/IERC173.sol



// SPDX-License-Identifier: MIT
/**
 * Copyright (C) 2024 uSmart
 */
pragma solidity ^0.8.9;

/// @title ERC-173 Contract Ownership Standard
///  Note: the ERC-165 identifier for this interface is 0x7f5828d0
interface IERC173 {
	/// @dev This emits when ownership of a contract changes.
	event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

	/// @notice Get the address of the owner
	/// @return owner_ The address of the owner.
	function owner() external view returns (address owner_);

	/// @notice Set the address of the new owner of the contract
	/// @dev Set _newOwner to address(0) to renounce any ownership.
	/// @param _newOwner The address of the new owner of the contract
	function transferOwnership(address _newOwner) external;
}

// File: contracts/utils/AddressUtils.sol



// SPDX-License-Identifier: MIT
/**
 * Copyright (C) 2024 uSmart
 */
pragma solidity ^0.8.9;

import { UintUtils } from "./UintUtils.sol";

library AddressUtils {
	using UintUtils for uint256;

	error AddressUtils__InsufficientBalance();
	error AddressUtils__NotContract();
	error AddressUtils__SendValueFailed();

	function toString(address account) internal pure returns (string memory) {
		return uint256(uint160(account)).toHexString(20);
	}

	function isContract(address account) internal view returns (bool) {
		uint256 size;
		assembly {
			size := extcodesize(account)
		}
		return size > 0;
	}

	function sendValue(address payable account, uint256 amount) internal {
		(bool success, ) = account.call{ value: amount }("");
		if (!success) revert AddressUtils__SendValueFailed();
	}

	function functionCall(address target, bytes memory data) internal returns (bytes memory) {
		return functionCall(target, data, "AddressUtils: failed low-level call");
	}

	function functionCall(address target, bytes memory data, string memory error) internal returns (bytes memory) {
		return _functionCallWithValue(target, data, 0, error);
	}

	function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
		return functionCallWithValue(target, data, value, "AddressUtils: failed low-level call with value");
	}

	function functionCallWithValue(address target, bytes memory data, uint256 value, string memory error) internal returns (bytes memory) {
		if (value > address(this).balance) revert AddressUtils__InsufficientBalance();
		return _functionCallWithValue(target, data, value, error);
	}

	/**
	 * @notice execute arbitrary external call with limited gas usage and amount of copied return data
	 * @dev derived from https://github.com/nomad-xyz/ExcessivelySafeCall (MIT License)
	 * @param target recipient of call
	 * @param gasAmount gas allowance for call
	 * @param value native token value to include in call
	 * @param maxCopy maximum number of bytes to copy from return data
	 * @param data encoded call data
	 * @return success whether call is successful
	 * @return returnData copied return data
	 */
	function excessivelySafeCall(
		address target,
		uint256 gasAmount,
		uint256 value,
		uint16 maxCopy,
		bytes memory data
	) internal returns (bool success, bytes memory returnData) {
		returnData = new bytes(maxCopy);

		assembly {
			// execute external call via assembly to avoid automatic copying of return data
			success := call(gasAmount, target, value, add(data, 0x20), mload(data), 0, 0)

			// determine whether to limit amount of data to copy
			let toCopy := returndatasize()

			if gt(toCopy, maxCopy) {
				toCopy := maxCopy
			}

			// store the length of the copied bytes
			mstore(returnData, toCopy)

			// copy the bytes from returndata[0:toCopy]
			returndatacopy(add(returnData, 0x20), 0, toCopy)
		}
	}

	function _functionCallWithValue(address target, bytes memory data, uint256 value, string memory error) private returns (bytes memory) {
		if (!isContract(target)) revert AddressUtils__NotContract();

		(bool success, bytes memory returnData) = target.call{ value: value }(data);

		if (success) {
			return returnData;
		} else if (returnData.length > 0) {
			assembly {
				let returnData_size := mload(returnData)
				revert(add(32, returnData), returnData_size)
			}
		} else {
			revert(error);
		}
	}
}

// File: contracts/utils/EnumerableSet.sol



// SPDX-License-Identifier: MIT
/**
 * Copyright (C) 2024 uSmart
 */
pragma solidity ^0.8.9;

/**
 * @title Set implementation with enumeration functions
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts (MIT license)
 */
library EnumerableSet {
	error EnumerableSet__IndexOutOfBounds();

	struct Set {
		bytes32[] _values;
		// 1-indexed to allow 0 to signify nonexistence
		mapping(bytes32 => uint256) _indexes;
	}

	struct Bytes32Set {
		Set _inner;
	}

	struct AddressSet {
		Set _inner;
	}

	struct UintSet {
		Set _inner;
	}

	function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
		return _at(set._inner, index);
	}

	function at(AddressSet storage set, uint256 index) internal view returns (address) {
		return address(uint160(uint256(_at(set._inner, index))));
	}

	function at(UintSet storage set, uint256 index) internal view returns (uint256) {
		return uint256(_at(set._inner, index));
	}

	function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
		return _contains(set._inner, value);
	}

	function contains(AddressSet storage set, address value) internal view returns (bool) {
		return _contains(set._inner, bytes32(uint256(uint160(value))));
	}

	function contains(UintSet storage set, uint256 value) internal view returns (bool) {
		return _contains(set._inner, bytes32(value));
	}

	function indexOf(Bytes32Set storage set, bytes32 value) internal view returns (uint256) {
		return _indexOf(set._inner, value);
	}

	function indexOf(AddressSet storage set, address value) internal view returns (uint256) {
		return _indexOf(set._inner, bytes32(uint256(uint160(value))));
	}

	function indexOf(UintSet storage set, uint256 value) internal view returns (uint256) {
		return _indexOf(set._inner, bytes32(value));
	}

	function length(Bytes32Set storage set) internal view returns (uint256) {
		return _length(set._inner);
	}

	function length(AddressSet storage set) internal view returns (uint256) {
		return _length(set._inner);
	}

	function length(UintSet storage set) internal view returns (uint256) {
		return _length(set._inner);
	}

	function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
		return _add(set._inner, value);
	}

	function add(AddressSet storage set, address value) internal returns (bool) {
		return _add(set._inner, bytes32(uint256(uint160(value))));
	}

	function add(UintSet storage set, uint256 value) internal returns (bool) {
		return _add(set._inner, bytes32(value));
	}

	function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
		return _remove(set._inner, value);
	}

	function remove(AddressSet storage set, address value) internal returns (bool) {
		return _remove(set._inner, bytes32(uint256(uint160(value))));
	}

	function remove(UintSet storage set, uint256 value) internal returns (bool) {
		return _remove(set._inner, bytes32(value));
	}

	function toArray(Bytes32Set storage set) internal view returns (bytes32[] memory) {
		return set._inner._values;
	}

	function toArray(AddressSet storage set) internal view returns (address[] memory) {
		bytes32[] storage values = set._inner._values;
		address[] storage array;

		assembly {
			array.slot := values.slot
		}

		return array;
	}

	function toArray(UintSet storage set) internal view returns (uint256[] memory) {
		bytes32[] storage values = set._inner._values;
		uint256[] storage array;

		assembly {
			array.slot := values.slot
		}

		return array;
	}

	function _at(Set storage set, uint256 index) private view returns (bytes32) {
		if (index >= set._values.length) revert EnumerableSet__IndexOutOfBounds();
		return set._values[index];
	}

	function _contains(Set storage set, bytes32 value) private view returns (bool) {
		return set._indexes[value] != 0;
	}

	function _indexOf(Set storage set, bytes32 value) private view returns (uint256) {
		unchecked {
			return set._indexes[value] - 1;
		}
	}

	function _length(Set storage set) private view returns (uint256) {
		return set._values.length;
	}

	function _add(Set storage set, bytes32 value) private returns (bool status) {
		if (!_contains(set, value)) {
			set._values.push(value);
			set._indexes[value] = set._values.length;
			status = true;
		}
	}

	function _remove(Set storage set, bytes32 value) private returns (bool status) {
		uint256 valueIndex = set._indexes[value];

		if (valueIndex != 0) {
			unchecked {
				bytes32 last = set._values[set._values.length - 1];

				// move last value to now-vacant index

				set._values[valueIndex - 1] = last;
				set._indexes[last] = valueIndex;
			}
			// clear last index

			set._values.pop();
			delete set._indexes[value];

			status = true;
		}
	}
}

// File: contracts/utils/UintUtils.sol



// SPDX-License-Identifier: MIT
/**
 * Copyright (C) 2024 uSmart
 */
pragma solidity ^0.8.9;

/**
 * @title utility functions for uint256 operations
 * @dev derived from https://github.com/OpenZeppelin/openzeppelin-contracts/ (MIT license)
 */
library UintUtils {
	error UintUtils__InsufficientHexLength();

	bytes16 private constant HEX_SYMBOLS = "0123456789abcdef";

	function add(uint256 a, int256 b) internal pure returns (uint256) {
		return b < 0 ? sub(a, -b) : a + uint256(b);
	}

	function sub(uint256 a, int256 b) internal pure returns (uint256) {
		return b < 0 ? add(a, -b) : a - uint256(b);
	}

	function toString(uint256 value) internal pure returns (string memory) {
		if (value == 0) {
			return "0";
		}

		uint256 temp = value;
		uint256 digits;

		while (temp != 0) {
			digits++;
			temp /= 10;
		}

		bytes memory buffer = new bytes(digits);

		while (value != 0) {
			digits -= 1;
			buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
			value /= 10;
		}

		return string(buffer);
	}

	function toHexString(uint256 value) internal pure returns (string memory) {
		if (value == 0) {
			return "0x00";
		}

		uint256 length = 0;

		for (uint256 temp = value; temp != 0; temp >>= 8) {
			unchecked {
				length++;
			}
		}

		return toHexString(value, length);
	}

	function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
		bytes memory buffer = new bytes(2 * length + 2);
		buffer[0] = "0";
		buffer[1] = "x";

		unchecked {
			for (uint256 i = 2 * length + 1; i > 1; --i) {
				buffer[i] = HEX_SYMBOLS[value & 0xf];
				value >>= 4;
			}
		}

		if (value != 0) revert UintUtils__InsufficientHexLength();

		return string(buffer);
	}
}