diff --git a/foundry.toml b/foundry.toml index f578850..8a8523c 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,10 +8,13 @@ libs = ["lib"] # optimizer = true (default) optimizer_runs = 200 fs_permissions = [{ access = "read-write", path = "./" }] -solc = "0.8.20" +auto_detect_solc = true remappings = [ - "@openzeppelin/=node_modules/@openzeppelin/" + "@openzeppelin/=node_modules/@openzeppelin/", + "glacis-contracts/=lib/v1-core/contracts/", + "glacis-test/=lib/v1-core/test/", + "core/=src/assets/" ] diff --git a/lib/v1-core b/lib/v1-core new file mode 160000 index 0000000..cb6a13a --- /dev/null +++ b/lib/v1-core @@ -0,0 +1 @@ +Subproject commit cb6a13aeba01be20a83f3fddfebad02934a30dfe diff --git a/src/BlueprintServiceManagerBase.sol b/src/BlueprintServiceManagerBase.sol index bf3f6d0..0855f53 100644 --- a/src/BlueprintServiceManagerBase.sol +++ b/src/BlueprintServiceManagerBase.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; import "./Permissions.sol"; import "./interfaces/IBlueprintServiceManager.sol"; diff --git a/src/Permissions.sol b/src/Permissions.sol index 50e8c04..a3689d0 100644 --- a/src/Permissions.sol +++ b/src/Permissions.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; contract Ownable { address internal _owner; @@ -55,7 +55,7 @@ contract RootChainEnabled { } } -contract RootChainEnabledOwnable is Ownable, RootChainEnabled { +abstract contract RootChainEnabledOwnable is Ownable, RootChainEnabled { constructor() Ownable() RootChainEnabled() { } modifier onlyOwnerOrRootChain() { diff --git a/src/assets/AssetDelegator.sol b/src/assets/AssetDelegator.sol index cb53764..e1b4ce6 100644 --- a/src/assets/AssetDelegator.sol +++ b/src/assets/AssetDelegator.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; import { MultiAssetDelegation } from "../precompiles/MultiAssetDelegation.sol"; @@ -40,38 +40,37 @@ abstract contract AssetDelegator { /// @param _asset The asset to operate on /// @param _amount The amount to operate with /// @param _operation The operation to perform - /// @return success Whether the operation was successful - function op(bytes32 _operator, address _asset, uint256 _amount, Operation _operation) public virtual returns (bool) { - uint8 result; + function op( + bytes32 _operator, + address _asset, + uint256 _amount, + uint8 _lockMultiplier, + uint64[] memory _blueprintSelection, + Operation _operation + ) + public + virtual + { uint256 assetId = uint256(uint160(_asset)); if (_operation == Operation.Deposit) { - result = DELEGATION.deposit(assetId, _amount); - if (result != 0) revert DelegationFailed(); + DELEGATION.deposit(assetId, _asset, _amount, _lockMultiplier); } else if (_operation == Operation.Delegate) { - result = DELEGATION.delegate(_operator, assetId, _amount); - if (result != 0) revert DelegationFailed(); + DELEGATION.delegate(_operator, assetId, _asset, _amount, _blueprintSelection); } else if (_operation == Operation.ScheduleUnstake) { - result = DELEGATION.scheduleDelegatorUnstake(_operator, assetId, _amount); - if (result != 0) revert UnstakeFailed(); + DELEGATION.scheduleDelegatorUnstake(_operator, assetId, _asset, _amount); } else if (_operation == Operation.CancelUnstake) { - result = DELEGATION.cancelDelegatorUnstake(_operator, assetId, _amount); - if (result != 0) revert UnstakeFailed(); + DELEGATION.cancelDelegatorUnstake(_operator, assetId, _asset, _amount); } else if (_operation == Operation.ExecuteUnstake) { - result = DELEGATION.executeDelegatorUnstake(); - if (result != 0) revert UnstakeFailed(); + DELEGATION.executeDelegatorUnstake(); } else if (_operation == Operation.ScheduleWithdraw) { - result = DELEGATION.scheduleWithdraw(assetId, _amount); - if (result != 0) revert WithdrawalFailed(); + DELEGATION.scheduleWithdraw(assetId, _asset, _amount); } else if (_operation == Operation.CancelWithdraw) { - result = DELEGATION.cancelWithdraw(assetId, _amount); - if (result != 0) revert WithdrawalFailed(); + DELEGATION.cancelWithdraw(assetId, _asset, _amount); } else if (_operation == Operation.ExecuteWithdraw) { - result = DELEGATION.executeWithdraw(); - if (result != 0) revert WithdrawalFailed(); + DELEGATION.executeWithdraw(); } emit OperationExecuted(_asset, _operator, _operation, _amount); - return true; } } diff --git a/src/assets/AssetManager.sol b/src/assets/AssetManager.sol deleted file mode 100644 index 06910ee..0000000 --- a/src/assets/AssetManager.sol +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; - -import "../precompiles/IAssets.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/access/Ownable.sol"; - -/// @title Asset Manager Contract -/// @dev This contract manages the bridging (not cross-chain bridging) between ERC20 tokens and native assets in the Tangle Network. -/// It allows users to deposit ERC20 tokens and receive corresponding native assets, maintaining a 1:1 relationship -/// between ERC20 tokens and native assets. -/// -/// Key features: -/// - Creates and tracks native assets corresponding to ERC20 tokens -/// - Handles deposits of ERC20 tokens and mints equivalent native assets -/// - Maintains a mapping between ERC20 tokens and their corresponding asset IDs -/// - Uses the Assets precompile for native asset operations -/// -/// Security considerations: -/// - Only the contract owner can manually set asset IDs -/// - Includes emergency recovery function for stuck ERC20 tokens -/// - Asset IDs are obtained from the precompile to ensure system-wide consistency -contract AssetManager { - // Interface to the Assets precompile that handles native asset operations - IAssets public immutable assetsPrecompile; - - // Maps ERC20 token addresses to their corresponding native asset IDs - mapping(address => uint256) public erc20ToAssetId; - - // Events for tracking asset creation and deposits - event AssetCreated(address indexed erc20Token, uint256 indexed assetId); - event Deposited(address indexed erc20Token, uint256 indexed assetId, address indexed user, uint256 amount); - - /// @dev Initializes the contract with the Assets precompile address - /// @param _assetsPrecompile The address of the Assets precompile contract - constructor(address _assetsPrecompile) { - assetsPrecompile = IAssets(_assetsPrecompile); - } - - /// @notice Deposits ERC20 tokens and mints corresponding native assets - /// @dev The function performs the following steps: - /// 1. Transfers ERC20 tokens from the user to this contract - /// 2. Gets or creates a native asset ID for the ERC20 token - /// 3. Mints an equivalent amount of native assets to the user - /// - /// @param erc20Token The address of the ERC20 token to deposit - /// @param amount The amount of tokens to deposit - /// @return assetId The ID of the native asset that was minted - /// - /// Requirements: - /// - Amount must be greater than 0 - /// - ERC20 token address must not be zero address - /// - User must have approved this contract to spend their tokens - function deposit(address erc20Token, uint256 amount) external returns (uint256) { - require(amount > 0, "Amount must be greater than 0"); - require(erc20Token != address(0), "Invalid token address"); - - // Transfer ERC20 tokens from user to this contract - require(IERC20(erc20Token).transferFrom(msg.sender, address(this), amount), "Token transfer failed"); - - // Get or create asset ID - uint256 assetId = getOrCreateAssetId(erc20Token); - - // Mint native assets to the user - require(assetsPrecompile.mint(assetId, address(this), amount), "Asset minting failed"); - - emit Deposited(erc20Token, assetId, msg.sender, amount); - return assetId; - } - - /// @dev Internal function to get existing or create new asset ID for an ERC20 token - /// @param erc20Token The ERC20 token address - /// @return assetId The asset ID (either existing or newly created) - /// - /// The function follows these steps: - /// 1. Checks if an asset ID already exists for the token - /// 2. If not, gets the next available asset ID from the precompile - /// 3. Creates a new native asset with this contract as admin - /// 4. Stores the ERC20 token to asset ID mapping - /// - /// Note: The minimum balance for new assets is set to 1 to prevent dust attacks - function getOrCreateAssetId(address erc20Token) internal returns (uint256) { - uint256 assetId = erc20ToAssetId[erc20Token]; - - // If asset doesn't exist, create it - if (assetId == 0) { - // Get the next available asset ID from the precompile - assetId = assetsPrecompile.next_asset_id(); - - // Create the asset with this contract as admin - require(assetsPrecompile.create(assetId, address(this), 1), "Asset creation failed"); - - // Store the mapping - erc20ToAssetId[erc20Token] = assetId; - - emit AssetCreated(erc20Token, assetId); - } - - return assetId; - } - - /// @notice Retrieves the native asset ID for a given ERC20 token - /// @dev Returns 0 if no asset ID exists for the token - /// @param erc20Token The ERC20 token address to query - /// @return The corresponding native asset ID, or 0 if none exists - function getAssetId(address erc20Token) external view returns (uint256) { - return erc20ToAssetId[erc20Token]; - } - - /// @notice Allows the owner to manually set an asset ID for an ERC20 token - /// @dev This function is restricted to the contract owner and can only be used - /// for tokens that don't already have an asset ID assigned - /// - /// @param erc20Token The ERC20 token address - /// @param assetId The corresponding asset ID to assign - /// - /// Requirements: - /// - Caller must be the contract owner - /// - Token must not already have an asset ID - /// - Asset ID must be greater than 0 - function setAssetId(address erc20Token, uint256 assetId) external onlyOwner { - require(erc20ToAssetId[erc20Token] == 0, "Asset ID already exists"); - require(assetId > 0, "Invalid asset ID"); - erc20ToAssetId[erc20Token] = assetId; - emit AssetCreated(erc20Token, assetId); - } - - /// @notice Emergency function to recover accidentally sent ERC20 tokens - /// @dev This function allows the owner to recover any ERC20 tokens that were - /// accidentally sent to this contract. This is a safety measure and should - /// only be used in emergency situations. - /// - /// @param token The ERC20 token address to recover - /// - /// Requirements: - /// - Caller must be the contract owner - /// - Contract must have a non-zero balance of the specified token - function recoverERC20(address token) external onlyOwner { - uint256 balance = IERC20(token).balanceOf(address(this)); - require(balance > 0, "No tokens to recover"); - require(IERC20(token).transfer(owner(), balance), "Token recovery failed"); - } -} diff --git a/src/assets/MasterVault.sol b/src/assets/MasterVault.sol index bb0a683..dfb7ffb 100644 --- a/src/assets/MasterVault.sol +++ b/src/assets/MasterVault.sol @@ -1,47 +1,27 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; import { IERC20 } from "node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IMasterVault } from "../interfaces/IMasterVault.sol"; -import { ICrossChainBridgeManager } from "../interfaces/ICrossChainBridgeManager.sol"; -import { ICrossChainReceiver } from "../interfaces/ICrossChainReceiver.sol"; +import { ISlashAccumulator } from "../interfaces/ISlashAccumulator.sol"; import { ICrossChainDelegatorMessage } from "../interfaces/ICrossChainDelegatorMessage.sol"; import { CrossChainDelegatorMessage } from "../libs/CrossChainDelegatorMessage.sol"; import { SyntheticRestakeAsset } from "./SyntheticRestakeAsset.sol"; import { UserVault } from "./UserVault.sol"; +import { XCBridge } from "../cross_chain/XCBridge.sol"; -contract MasterVault is IMasterVault, ICrossChainReceiver { +contract MasterVault is XCBridge, IMasterVault, ISlashAccumulator { using CrossChainDelegatorMessage for *; - ICrossChainBridgeManager public immutable bridgeManager; - mapping(uint32 => mapping(uint256 => address)) public syntheticAssets; mapping(address => bool) public authorizedAdapters; mapping(bytes32 => address) public userVaults; - - error UnauthorizedAdapter(address adapter); - error ZeroAddress(); - error InvalidMessage(); - error Unauthorized(bytes32 sender); - error InvalidUnlockTime(); - error InvalidRecipient(); - error VaultCreationFailed(); - error BridgeDispatchFailed(); + mapping(bytes32 => ICrossChainDelegatorMessage.Slash[]) public slashes; event SyntheticAssetCreated( address indexed syntheticAsset, uint32 indexed originChainId, uint256 indexed originAsset, uint256 bridgeId ); - modifier onlyAuthorizedAdapter() { - if (!authorizedAdapters[msg.sender]) revert UnauthorizedAdapter(msg.sender); - _; - } - - constructor(address _bridgeManager) { - require(_bridgeManager != address(0), "Invalid bridge manager"); - bridgeManager = ICrossChainBridgeManager(_bridgeManager); - } - function _getOrCreateUserVault(bytes32 sender) internal returns (address vault) { vault = userVaults[sender]; if (vault == address(0)) { @@ -51,41 +31,41 @@ contract MasterVault is IMasterVault, ICrossChainReceiver { return vault; } - function handleCrossChainMessage( - uint32 originChainId, - bytes32 sender, - bytes calldata message - ) - external - payable - onlyAuthorizedAdapter - returns (bytes memory) - { + fallback() external { + _receiveMessage(msg.sender, msg.data, _processMessage); + } + + function _processMessage(uint256 fromChainId, bytes calldata message) internal { uint8 messageType = CrossChainDelegatorMessage.getMessageType(message); bytes calldata payload = message[1:]; if (messageType == CrossChainDelegatorMessage.DEPOSIT_MESSAGE) { - return _handleDepositMessage(originChainId, sender, payload); - } else if (messageType == CrossChainDelegatorMessage.DELEGATION_MESSAGE) { - return _handleDelegationMessage(originChainId, sender, payload); - } else if (messageType == CrossChainDelegatorMessage.SCHEDULE_UNSTAKE_MESSAGE) { - return _handleScheduleUnstakeMessage(originChainId, sender, payload); - } else if (messageType == CrossChainDelegatorMessage.CANCEL_UNSTAKE_MESSAGE) { - return _handleCancelUnstakeMessage(originChainId, sender, payload); - } else if (messageType == CrossChainDelegatorMessage.EXECUTE_UNSTAKE_MESSAGE) { - return _handleExecuteUnstakeMessage(originChainId, sender, payload); - } else if (messageType == CrossChainDelegatorMessage.SCHEDULE_WITHDRAWAL_MESSAGE) { - return _handleScheduleWithdrawalMessage(originChainId, sender, payload); - } else if (messageType == CrossChainDelegatorMessage.CANCEL_WITHDRAWAL_MESSAGE) { - return _handleCancelWithdrawalMessage(originChainId, sender, payload); - } else if (messageType == CrossChainDelegatorMessage.EXECUTE_WITHDRAWAL_MESSAGE) { - return _handleExecuteWithdrawalMessage(originChainId, sender, payload); + _handleDepositMessage(uint32(fromChainId), payload); + } + if (messageType == CrossChainDelegatorMessage.DELEGATION_MESSAGE) { + _handleDelegationMessage(uint32(fromChainId), payload); + } + if (messageType == CrossChainDelegatorMessage.SCHEDULE_UNSTAKE_MESSAGE) { + _handleScheduleUnstakeMessage(uint32(fromChainId), payload); + } + if (messageType == CrossChainDelegatorMessage.CANCEL_UNSTAKE_MESSAGE) { + _handleCancelUnstakeMessage(uint32(fromChainId), payload); + } + if (messageType == CrossChainDelegatorMessage.EXECUTE_UNSTAKE_MESSAGE) { + _handleExecuteUnstakeMessage(uint32(fromChainId), payload); + } + if (messageType == CrossChainDelegatorMessage.SCHEDULE_WITHDRAWAL_MESSAGE) { + _handleScheduleWithdrawalMessage(uint32(fromChainId), payload); + } + if (messageType == CrossChainDelegatorMessage.CANCEL_WITHDRAWAL_MESSAGE) { + _handleCancelWithdrawalMessage(uint32(fromChainId), payload); + } + if (messageType == CrossChainDelegatorMessage.EXECUTE_WITHDRAWAL_MESSAGE) { + _handleExecuteWithdrawalMessage(uint32(fromChainId), payload); } - - revert InvalidMessage(); } - function _handleDepositMessage(uint32 originChainId, bytes32 sender, bytes calldata payload) internal returns (bytes memory) { + function _handleDepositMessage(uint32 originChainId, bytes calldata payload) internal returns (bytes memory) { ICrossChainDelegatorMessage.DepositMessage memory message = CrossChainDelegatorMessage.decodeDepositMessage(payload); address syntheticAsset = getOrCreateSyntheticAsset(originChainId, message.originAsset, message.bridgeId); @@ -93,38 +73,24 @@ contract MasterVault is IMasterVault, ICrossChainReceiver { // Mint directly to user vault SyntheticRestakeAsset(syntheticAsset).mint(userVault, message.amount); - UserVault(userVault).restakingDeposit(syntheticAsset, message.amount); + UserVault(userVault).restakingDeposit(syntheticAsset, message.amount, message.lockMultiplier); return abi.encode(true); } - function _handleDelegationMessage( - uint32 originChainId, - bytes32 sender, - bytes calldata payload - ) - internal - returns (bytes memory) - { + function _handleDelegationMessage(uint32 originChainId, bytes calldata payload) internal returns (bytes memory) { ICrossChainDelegatorMessage.DelegationMessage memory message = CrossChainDelegatorMessage.decodeDelegationMessage(payload); address syntheticAsset = syntheticAssets[originChainId][message.originAsset]; if (syntheticAsset == address(0)) revert InvalidAsset(address(0)); address userVault = _getOrCreateUserVault(message.sender); - UserVault(userVault).restakingDelegate(syntheticAsset, message.amount, message.operator); + UserVault(userVault).restakingDelegate(message.operator, syntheticAsset, message.amount, message.blueprintSelection); return abi.encode(true); } - function _handleScheduleUnstakeMessage( - uint32 originChainId, - bytes32 sender, - bytes calldata payload - ) - internal - returns (bytes memory) - { + function _handleScheduleUnstakeMessage(uint32 originChainId, bytes calldata payload) internal returns (bytes memory) { ICrossChainDelegatorMessage.ScheduleUnstakeMessage memory message = CrossChainDelegatorMessage.decodeScheduleUnstakeMessage(payload); @@ -132,19 +98,12 @@ contract MasterVault is IMasterVault, ICrossChainReceiver { if (syntheticAsset == address(0)) revert InvalidAsset(address(0)); address userVault = _getOrCreateUserVault(message.sender); - UserVault(userVault).restakingScheduleUnstake(syntheticAsset, message.amount, message.operator); + UserVault(userVault).restakingScheduleUnstake(message.operator, syntheticAsset, message.amount); return abi.encode(true); } - function _handleCancelUnstakeMessage( - uint32 originChainId, - bytes32 sender, - bytes calldata payload - ) - internal - returns (bytes memory) - { + function _handleCancelUnstakeMessage(uint32 originChainId, bytes calldata payload) internal returns (bytes memory) { ICrossChainDelegatorMessage.CancelUnstakeMessage memory message = CrossChainDelegatorMessage.decodeCancelUnstakeMessage(payload); @@ -152,19 +111,12 @@ contract MasterVault is IMasterVault, ICrossChainReceiver { if (syntheticAsset == address(0)) revert InvalidAsset(address(0)); address userVault = _getOrCreateUserVault(message.sender); - UserVault(userVault).restakingCancelUnstake(syntheticAsset, message.amount, message.operator); + UserVault(userVault).restakingCancelUnstake(message.operator, syntheticAsset, message.amount); return abi.encode(true); } - function _handleExecuteUnstakeMessage( - uint32 originChainId, - bytes32 sender, - bytes calldata payload - ) - internal - returns (bytes memory) - { + function _handleExecuteUnstakeMessage(uint32 originChainId, bytes calldata payload) internal returns (bytes memory) { ICrossChainDelegatorMessage.ExecuteUnstakeMessage memory message = CrossChainDelegatorMessage.decodeExecuteUnstakeMessage(payload); @@ -173,19 +125,12 @@ contract MasterVault is IMasterVault, ICrossChainReceiver { address userVault = _getOrCreateUserVault(message.sender); // Just execute unstake, keep assets in vault - UserVault(userVault).restakingExecuteUnstake(syntheticAsset, message.amount, message.operator); + UserVault(userVault).restakingExecuteUnstake(); return abi.encode(true); } - function _handleScheduleWithdrawalMessage( - uint32 originChainId, - bytes32 sender, - bytes calldata payload - ) - internal - returns (bytes memory) - { + function _handleScheduleWithdrawalMessage(uint32 originChainId, bytes calldata payload) internal returns (bytes memory) { ICrossChainDelegatorMessage.ScheduleWithdrawalMessage memory message = CrossChainDelegatorMessage.decodeScheduleWithdrawalMessage(payload); @@ -198,14 +143,7 @@ contract MasterVault is IMasterVault, ICrossChainReceiver { return abi.encode(true); } - function _handleCancelWithdrawalMessage( - uint32 originChainId, - bytes32 sender, - bytes calldata payload - ) - internal - returns (bytes memory) - { + function _handleCancelWithdrawalMessage(uint32 originChainId, bytes calldata payload) internal returns (bytes memory) { ICrossChainDelegatorMessage.CancelWithdrawalMessage memory message = CrossChainDelegatorMessage.decodeCancelWithdrawalMessage(payload); @@ -218,14 +156,7 @@ contract MasterVault is IMasterVault, ICrossChainReceiver { return abi.encode(true); } - function _handleExecuteWithdrawalMessage( - uint32 originChainId, - bytes32 sender, - bytes calldata payload - ) - internal - returns (bytes memory) - { + function _handleExecuteWithdrawalMessage(uint32 originChainId, bytes calldata payload) internal returns (bytes memory) { ICrossChainDelegatorMessage.ExecuteWithdrawalMessage memory message = CrossChainDelegatorMessage.decodeExecuteWithdrawalMessage(payload); @@ -235,7 +166,7 @@ contract MasterVault is IMasterVault, ICrossChainReceiver { address userVault = _getOrCreateUserVault(message.sender); // First execute withdrawal in user vault - UserVault(userVault).restakingExecuteWithdraw(syntheticAsset, message.amount); + UserVault(userVault).restakingExecuteWithdraw(); // Then dispatch message back to origin chain ICrossChainDelegatorMessage.WithdrawalExecutedMessage memory withdrawalMessage = ICrossChainDelegatorMessage @@ -248,12 +179,9 @@ contract MasterVault is IMasterVault, ICrossChainReceiver { }); // Only burn after successful message dispatch - try bridgeManager.dispatchMessage(withdrawalMessage.encode()) { - // If message dispatch succeeds, burn the synthetic asset - SyntheticRestakeAsset(syntheticAsset).burn(userVault, message.amount); - } catch { - revert BridgeDispatchFailed(); - } + _sendMessage(withdrawalMessage.encode(), message.bridgeId); + // If message dispatch succeeds, burn the synthetic asset + SyntheticRestakeAsset(syntheticAsset).burn(userVault, message.amount); return abi.encode(true); } @@ -274,14 +202,13 @@ contract MasterVault is IMasterVault, ICrossChainReceiver { return synthetic; } - function authorizeAdapter(address adapter) external { - if (adapter == address(0)) revert ZeroAddress(); - authorizedAdapters[adapter] = true; - emit AdapterAuthorized(adapter); + function slash(uint64 _blueprintId, uint64 _serviceId, bytes32 _operator, uint256 _slashAmount) external { + slashes[_operator].push( + ICrossChainDelegatorMessage.Slash({ blueprintId: _blueprintId, serviceId: _serviceId, slashAmount: _slashAmount }) + ); } - function unauthorizeAdapter(address adapter) external { - authorizedAdapters[adapter] = false; - emit AdapterUnauthorized(adapter); + function getSlashes(bytes32 _operator) external view returns (ICrossChainDelegatorMessage.Slash[] memory) { + return slashes[_operator]; } } diff --git a/src/assets/RemoteRestakeVault.sol b/src/assets/RemoteRestakeVault.sol index 97d54ef..4f2c2bc 100644 --- a/src/assets/RemoteRestakeVault.sol +++ b/src/assets/RemoteRestakeVault.sol @@ -1,27 +1,28 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import { IRemoteChainBridgeManager } from "../interfaces/IRemoteChainBridgeManager.sol"; import { ICrossChainDelegatorMessage } from "../interfaces/ICrossChainDelegatorMessage.sol"; import { CrossChainDelegatorMessage } from "../libs/CrossChainDelegatorMessage.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { XCBridge } from "../cross_chain/XCBridge.sol"; -contract RemoteRestakeVault { +contract RemoteRestakeVault is XCBridge { using SafeERC20 for IERC20; using CrossChainDelegatorMessage for *; using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.Bytes32Set; - IRemoteChainBridgeManager public immutable bridgeManager; - // Core state tracking mapping(address => mapping(address => uint256)) public userDeposits; mapping(address => mapping(address => mapping(bytes32 => uint256))) public userDelegations; mapping(address => mapping(address => mapping(bytes32 => uint256))) public userUnstaking; mapping(address => mapping(address => uint256)) public userWithdrawals; + // Blueprint selection tracking + mapping(address staker => mapping(bytes32 operator => mapping(uint64 blueprintId => uint256 amount))) public boundToBlueprint; + // Enumerable sets for efficient iteration EnumerableSet.AddressSet private knownTokens; EnumerableSet.AddressSet private knownDelegators; @@ -31,9 +32,6 @@ contract RemoteRestakeVault { mapping(bytes32 => mapping(address => bool)) public operatorTokens; mapping(bytes32 => mapping(address => mapping(address => bool))) public operatorDelegators; - // Trusted receivers for cross-chain messages - mapping(address => bool) public trustedReceivers; - error InvalidBridgeManager(); error InvalidAmount(); error InvalidToken(); @@ -55,7 +53,6 @@ contract RemoteRestakeVault { event WithdrawalCancelled(address indexed token, address indexed sender, uint256 amount); event WithdrawalExecuted(address indexed token, address indexed sender, address indexed recipient, uint256 amount); event TokensSlashed(address indexed token, bytes32 indexed operator, address indexed delegator, uint256 amount); - event TrustedReceiverUpdated(address indexed receiver, bool trusted); modifier validToken(address token) { if (token == address(0)) revert InvalidToken(); @@ -77,11 +74,6 @@ contract RemoteRestakeVault { _; } - modifier onlyTrustedReceiver() { - if (!trustedReceivers[msg.sender]) revert UnauthorizedReceiver(); - _; - } - modifier sufficientBalance(address token, uint256 amount) { if (userDeposits[msg.sender][token] < amount) revert InsufficientBalance(); _; @@ -97,12 +89,17 @@ contract RemoteRestakeVault { _; } - constructor(address _bridgeManager) { - if (_bridgeManager == address(0)) revert InvalidBridgeManager(); - bridgeManager = IRemoteChainBridgeManager(_bridgeManager); - } - - function deposit(address token, uint256 amount, uint256 bridgeId) external payable validToken(token) validAmount(amount) { + function deposit( + address token, + uint256 amount, + uint256 bridgeId, + uint8 lockMultiplier + ) + external + payable + validToken(token) + validAmount(amount) + { IERC20(token).safeTransferFrom(msg.sender, address(this), amount); userDeposits[msg.sender][token] += amount; @@ -114,10 +111,11 @@ contract RemoteRestakeVault { bridgeId: bridgeId, originAsset: uint256(uint160(token)), amount: amount, - sender: bytes32(uint256(uint160(msg.sender))) + sender: bytes32(uint256(uint160(msg.sender))), + lockMultiplier: lockMultiplier }); - _dispatchMessage(bridgeId, message.encode()); + _sendMessage(message.encode(), bridgeId); emit AssetDeposited(token, msg.sender, amount); } @@ -125,7 +123,8 @@ contract RemoteRestakeVault { address token, uint256 amount, uint256 bridgeId, - bytes32 operator + bytes32 operator, + uint64[] memory blueprintSelection ) external payable @@ -142,15 +141,25 @@ contract RemoteRestakeVault { operatorTokens[operator][token] = true; operatorDelegators[operator][token][msg.sender] = true; + // Update blueprint selection tracking + for (uint256 i = 0; i < blueprintSelection.length;) { + uint64 blueprintId = blueprintSelection[i]; + boundToBlueprint[msg.sender][operator][blueprintId] += amount; + unchecked { + ++i; + } + } + ICrossChainDelegatorMessage.DelegationMessage memory message = ICrossChainDelegatorMessage.DelegationMessage({ bridgeId: bridgeId, originAsset: uint256(uint160(token)), amount: amount, sender: bytes32(uint256(uint160(msg.sender))), - operator: operator + operator: operator, + blueprintSelection: blueprintSelection }); - _dispatchMessage(bridgeId, message.encode()); + _sendMessage(message.encode(), bridgeId); emit DelegationUpdated(token, msg.sender, amount, operator); } @@ -178,7 +187,7 @@ contract RemoteRestakeVault { operator: operator }); - _dispatchMessage(bridgeId, message.encode()); + _sendMessage(message.encode(), bridgeId); emit UnstakeScheduled(token, msg.sender, amount, operator); } @@ -206,7 +215,7 @@ contract RemoteRestakeVault { operator: operator }); - _dispatchMessage(bridgeId, message.encode()); + _sendMessage(message.encode(), bridgeId); emit UnstakeCancelled(token, msg.sender, amount, operator); } @@ -223,56 +232,174 @@ contract RemoteRestakeVault { validOperator(operator) sufficientUnstaking(token, operator, amount) { - userUnstaking[msg.sender][token][operator] -= amount; + ICrossChainDelegatorMessage.ExecuteUnstakeMessage memory message = ICrossChainDelegatorMessage.ExecuteUnstakeMessage({ + bridgeId: bridgeId, + originAsset: uint256(uint160(token)), + amount: amount, + sender: bytes32(uint256(uint160(msg.sender))), + operator: operator + }); + + _sendMessage(message.encode(), bridgeId); + } + + function scheduleWithdrawal( + address token, + uint256 amount, + uint256 bridgeId + ) + external + payable + validToken(token) + validAmount(amount) + { + userWithdrawals[msg.sender][token] += amount; + userDeposits[msg.sender][token] -= amount; + + ICrossChainDelegatorMessage.ScheduleWithdrawalMessage memory message = ICrossChainDelegatorMessage.ScheduleWithdrawalMessage({ + bridgeId: bridgeId, + originAsset: uint256(uint160(token)), + amount: amount, + sender: bytes32(uint256(uint160(msg.sender))) + }); + + _sendMessage(message.encode(), bridgeId); + emit WithdrawalScheduled(token, msg.sender, amount); + } + + function cancelWithdrawal( + address token, + uint256 amount, + uint256 bridgeId + ) + external + payable + validToken(token) + validAmount(amount) + { + userWithdrawals[msg.sender][token] -= amount; userDeposits[msg.sender][token] += amount; - // Clean up operator tracking if no more delegations - if (userDelegations[msg.sender][token][operator] == 0 && userUnstaking[msg.sender][token][operator] == 0) { - operatorDelegators[operator][token][msg.sender] = false; - _cleanupOperatorIfEmpty(operator, token); - } + ICrossChainDelegatorMessage.CancelWithdrawalMessage memory message = ICrossChainDelegatorMessage.CancelWithdrawalMessage({ + bridgeId: bridgeId, + originAsset: uint256(uint160(token)), + amount: amount, + sender: bytes32(uint256(uint160(msg.sender))) + }); - ICrossChainDelegatorMessage.ExecuteUnstakeMessage memory message = ICrossChainDelegatorMessage.ExecuteUnstakeMessage({ + _sendMessage(message.encode(), bridgeId); + emit WithdrawalCancelled(token, msg.sender, amount); + } + + function executeWithdrawal( + address token, + uint256 amount, + uint256 bridgeId, + address recipient + ) + external + payable + validToken(token) + validAmount(amount) + { + ICrossChainDelegatorMessage.ExecuteWithdrawalMessage memory message = ICrossChainDelegatorMessage.ExecuteWithdrawalMessage({ bridgeId: bridgeId, originAsset: uint256(uint160(token)), amount: amount, sender: bytes32(uint256(uint160(msg.sender))), - operator: operator + recipient: bytes32(uint256(uint160(recipient))) }); - _dispatchMessage(bridgeId, message.encode()); - emit UnstakeExecuted(token, msg.sender, amount, operator); + _sendMessage(message.encode(), bridgeId); + } + + fallback() external { + _receiveMessage(msg.sender, msg.data, _processMessage); + } + + function _processMessage(uint256, bytes calldata _message) internal { + uint8 messageType = CrossChainDelegatorMessage.getMessageType(_message); + bytes calldata payload = _message[1:]; + + if (messageType == CrossChainDelegatorMessage.WITHDRAWAL_EXECUTED_MESSAGE) { + _handleExecutedWithdrawalMessage(payload); + } else if (messageType == CrossChainDelegatorMessage.UNSTAKE_EXECUTED_MESSAGE) { + _handleExecutedUnstakeMessage(payload); + } + } + + function _handleExecutedWithdrawalMessage(bytes calldata _payload) internal { + ICrossChainDelegatorMessage.WithdrawalExecutedMessage memory message = + CrossChainDelegatorMessage.decodeWithdrawalExecutedMessage(_payload); + address token = address(uint160(message.originAsset)); + address sender = address(bytes20(message.sender)); + address recipient = address(bytes20(message.recipient)); + + userWithdrawals[sender][token] -= message.amount; + + IERC20(token).safeTransfer(recipient, message.amount); + + emit WithdrawalExecuted(token, sender, recipient, message.amount); + } + + function _handleExecutedUnstakeMessage(bytes calldata _payload) internal { + ICrossChainDelegatorMessage.UnstakeExecutedMessage memory message = + CrossChainDelegatorMessage.decodeUnstakeExecutedMessage(_payload); + address token = address(uint160(message.originAsset)); + address sender = address(bytes20(message.sender)); + + userUnstaking[sender][token][message.operator] -= message.amount; + userDeposits[sender][token] += message.amount; + + // Clean up operator tracking if no more delegations + if (userDelegations[sender][token][message.operator] == 0 && userUnstaking[sender][token][message.operator] == 0) { + operatorDelegators[message.operator][token][sender] = false; + _cleanupOperatorIfEmpty(message.operator, token); + } + + for (uint256 i = 0; i < message.slashes.length;) { + _handleSlash(message.operator, message.slashes[i].slashAmount, message.slashes[i].blueprintId); + unchecked { + ++i; + } + } + + emit UnstakeExecuted(token, sender, message.amount, message.operator); } - function handleSlashMessage(bytes32 operator, uint8 slashPercent) internal validOperator(operator) returns (bool) { + function _handleSlash(bytes32 operator, uint256 slashAmount, uint64 blueprintId) internal { uint256 length = knownTokens.length(); for (uint256 i = 0; i < length;) { address token = knownTokens.at(i); if (operatorTokens[operator][token]) { - _handleSlashForToken(operator, token, slashPercent); + _handleSlashForToken(operator, token, slashAmount, blueprintId); } unchecked { ++i; } } - return true; } - function _handleSlashForToken(bytes32 operator, address token, uint8 slashPercent) internal { + function _handleSlashForToken(bytes32 operator, address token, uint256 slashAmount, uint64 blueprintId) internal { + if (slashAmount == 0) return; + uint256 length = knownDelegators.length(); for (uint256 i = 0; i < length;) { address delegator = knownDelegators.at(i); - if (operatorDelegators[operator][token][delegator]) { - uint256 delegatedAmount = userDelegations[delegator][token][operator]; - if (delegatedAmount > 0) { - uint256 slashAmount = (delegatedAmount * slashPercent) / 100; - if (slashAmount > 0) { - userDelegations[delegator][token][operator] -= slashAmount; - IERC20(token).safeTransfer(address(0), slashAmount); - emit TokensSlashed(token, operator, delegator, slashAmount); - } - } - } + + if (!operatorDelegators[operator][token][delegator]) return; + + uint256 delegatedAmount = userDelegations[delegator][token][operator]; + if (delegatedAmount == 0) return; + + uint256 blueprintAmount = boundToBlueprint[delegator][operator][blueprintId]; + if (blueprintAmount == 0) return; + + userDelegations[delegator][token][operator] -= slashAmount; + boundToBlueprint[delegator][operator][blueprintId] -= slashAmount; + IERC20(token).safeTransfer(address(0), slashAmount); + emit TokensSlashed(token, operator, delegator, slashAmount); + unchecked { ++i; } @@ -318,16 +445,6 @@ contract RemoteRestakeVault { } } - function _dispatchMessage(uint256 bridgeId, bytes memory message) internal { - uint256 requiredFee = bridgeId != 0 ? bridgeManager.getMessageFee(bridgeId, message) : msg.value; - - try bridgeManager.dispatchMessage{ value: requiredFee }(message) { - // Success case handled by events - } catch { - revert BridgeDispatchFailed(); - } - } - // View functions function getOperatorTokens(bytes32 operator) external view returns (address[] memory) { uint256 length = knownTokens.length(); @@ -378,10 +495,4 @@ contract RemoteRestakeVault { return delegators; } - - // Admin functions - function setTrustedReceiver(address receiver, bool trusted) external validRecipient(receiver) { - trustedReceivers[receiver] = trusted; - emit TrustedReceiverUpdated(receiver, trusted); - } } diff --git a/src/assets/SyntheticRestakeAsset.sol b/src/assets/SyntheticRestakeAsset.sol index 8ca7200..f9279ac 100644 --- a/src/assets/SyntheticRestakeAsset.sol +++ b/src/assets/SyntheticRestakeAsset.sol @@ -1,12 +1,14 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; import { ERC20 } from "node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { ISyntheticRestakeAsset } from "../interfaces/ISyntheticRestakeAsset.sol"; +import { ISlashAlert } from "../interfaces/ISlashAlert.sol"; +import { ISlashAccumulator } from "../interfaces/ISlashAccumulator.sol"; /// @title SyntheticRestakeAsset /// @notice ERC20 token representing a cross-chain restaking position -contract SyntheticRestakeAsset is ERC20, ISyntheticRestakeAsset { +contract SyntheticRestakeAsset is ERC20, ISyntheticRestakeAsset, ISlashAlert { /// @notice Origin chain information uint32 public immutable originChainId; /// @notice Original asset address on origin chain @@ -52,4 +54,8 @@ contract SyntheticRestakeAsset is ERC20, ISyntheticRestakeAsset { require(msg.sender == vault, "Only vault can burn"); _burn(from, amount); } + + function onSlash(uint64 _blueprintId, uint64 _serviceId, bytes32 _operator, uint256 _slashAmount) external { + ISlashAccumulator(vault).slash(_blueprintId, _serviceId, _operator, _slashAmount); + } } diff --git a/src/assets/UserVault.sol b/src/assets/UserVault.sol index 3a354d1..6a6f2ec 100644 --- a/src/assets/UserVault.sol +++ b/src/assets/UserVault.sol @@ -1,13 +1,11 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; import { AssetDelegator } from "./AssetDelegator.sol"; -import { AssetManager } from "./AssetManager.sol"; contract UserVault is AssetDelegator { bytes32 public immutable owner; address public immutable masterVault; - address public immutable assetManager; error Unauthorized(); error InsufficientBalance(); @@ -22,59 +20,53 @@ contract UserVault is AssetDelegator { masterVault = _masterVault; } - function restakingDeposit(address syntheticAsset, uint256 amount) external onlyMasterVault returns (bool) { - return op(bytes32(0), syntheticAsset, amount, Operation.Deposit); + function restakingDeposit(address _asset, uint256 _amount, uint8 _lockMultiplier) external onlyMasterVault { + uint256 assetId = uint256(uint160(_asset)); + DELEGATION.deposit(assetId, _asset, _amount, _lockMultiplier); } - function restakingDelegate(address syntheticAsset, uint256 amount, bytes32 operator) external onlyMasterVault returns (bool) { - return op(operator, syntheticAsset, amount, Operation.Delegate); - } - - function restakingScheduleUnstake( - address syntheticAsset, - uint256 amount, - bytes32 operator + function restakingDelegate( + bytes32 _operator, + address _asset, + uint256 _amount, + uint64[] memory _blueprintSelection ) external onlyMasterVault - returns (bool) { - return op(operator, syntheticAsset, amount, Operation.ScheduleUnstake); + uint256 assetId = uint256(uint160(_asset)); + DELEGATION.delegate(_operator, assetId, _asset, _amount, _blueprintSelection); } - function restakingCancelUnstake( - address syntheticAsset, - uint256 amount, - bytes32 operator - ) - external - onlyMasterVault - returns (bool) - { - return op(operator, syntheticAsset, amount, Operation.CancelUnstake); + function restakingScheduleUnstake(bytes32 _operator, address _asset, uint256 _amount) external onlyMasterVault { + uint256 assetId = uint256(uint160(_asset)); + DELEGATION.scheduleDelegatorUnstake(_operator, assetId, _asset, _amount); } - function restakingExecuteUnstake( - address syntheticAsset, - uint256 amount, - bytes32 operator - ) - external - onlyMasterVault - returns (bool) - { - return op(operator, syntheticAsset, amount, Operation.ExecuteUnstake); + function restakingCancelUnstake(bytes32 _operator, address _asset, uint256 _amount) external onlyMasterVault { + uint256 assetId = uint256(uint160(_asset)); + DELEGATION.cancelDelegatorUnstake(_operator, assetId, _asset, _amount); + } + + function restakingExecuteUnstake() external onlyMasterVault { + DELEGATION.executeDelegatorUnstake(); + } + + function restakingScheduleWithdraw(address _asset, uint256 _amount) external onlyMasterVault { + uint256 assetId = uint256(uint160(_asset)); + DELEGATION.scheduleWithdraw(assetId, _asset, _amount); } - function restakingScheduleWithdraw(address syntheticAsset, uint256 amount) external onlyMasterVault returns (bool) { - return op(bytes32(0), syntheticAsset, amount, Operation.ScheduleWithdraw); + function restakingCancelWithdraw(address _asset, uint256 _amount) external onlyMasterVault { + uint256 assetId = uint256(uint160(_asset)); + DELEGATION.cancelWithdraw(assetId, _asset, _amount); } - function restakingCancelWithdraw(address syntheticAsset, uint256 amount) external onlyMasterVault returns (bool) { - return op(bytes32(0), syntheticAsset, amount, Operation.CancelWithdraw); + function restakingExecuteWithdraw() external onlyMasterVault { + DELEGATION.executeWithdraw(); } - function restakingExecuteWithdraw(address syntheticAsset, uint256 amount) external onlyMasterVault returns (bool) { - return op(bytes32(0), syntheticAsset, amount, Operation.ExecuteWithdraw); + function claim() external { + // TODO: Implement } } diff --git a/src/cross_chain/BaseBlueprintReceiver.sol b/src/cross_chain/BaseBlueprintReceiver.sol index 274a45d..107d1ce 100644 --- a/src/cross_chain/BaseBlueprintReceiver.sol +++ b/src/cross_chain/BaseBlueprintReceiver.sol @@ -1,100 +1,77 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import { ICrossChainReceiver } from "../interfaces/ICrossChainReceiver.sol"; - -/// @title BaseBlueprintReceiver -/// @notice Base contract for receiving cross-chain messages from Tangle Blueprints -/// This contract lives on the remote chain and accepts messages from Tangle Blueprints. -abstract contract BaseBlueprintReceiver is ICrossChainReceiver { - /// Event types - uint8 constant SLASH_EVENT = 1; - uint8 constant JOB_RESULT_EVENT = 2; - - /// Events for specific message types - event SlashEventReceived(uint64 serviceId, bytes offender, uint8 slashPercent, uint256 totalPayout); - event JobResultReceived(uint64 serviceId, uint8 job, uint64 jobCallId, bytes participant, bytes inputs, bytes outputs); - - /// Trusted senders mapping - mapping(uint32 => mapping(bytes32 => bool)) public trustedSenders; - - /// @dev Modifier to check if sender is trusted - modifier onlyTrustedSender(uint32 originChainId, bytes32 sender) { - require(trustedSenders[originChainId][sender], "Untrusted sender"); - _; - } - - /// @dev Add a trusted sender - /// TODO: Add access control - function addTrustedSender(uint32 chainId, bytes32 sender) external virtual { - trustedSenders[chainId][sender] = true; - } - - /// @dev Implementation of ICrossChainReceiver.handleCrossChainMessage - function handleCrossChainMessage( - uint32 originChainId, - bytes32 sender, - bytes calldata message - ) - external - payable - override - onlyTrustedSender(originChainId, sender) - returns (bytes memory) - { - emit MessageReceived(originChainId, sender, message); - - /// First byte indicates message type - require(message.length > 0, "Empty message"); - uint8 messageType = uint8(message[0]); - bytes memory messageData = message[1:]; - - if (messageType == SLASH_EVENT) { - return _handleSlashEvent(messageData); - } else if (messageType == JOB_RESULT_EVENT) { - return _handleJobResultEvent(messageData); - } - - revert("Unknown message type"); - } - - function _handleSlashEvent(bytes memory eventData) internal virtual returns (bytes memory) { - (uint64 serviceId, bytes memory offender, uint8 slashPercent, uint256 totalPayout) = - abi.decode(eventData, (uint64, bytes, uint8, uint256)); - - emit SlashEventReceived(serviceId, offender, slashPercent, totalPayout); - - return _processSlashEvent(serviceId, offender, slashPercent, totalPayout); - } - - function _handleJobResultEvent(bytes memory eventData) internal virtual returns (bytes memory) { - (uint64 serviceId, uint8 job, uint64 jobCallId, bytes memory participant, bytes memory inputs, bytes memory outputs) = - abi.decode(eventData, (uint64, uint8, uint64, bytes, bytes, bytes)); - - emit JobResultReceived(serviceId, job, jobCallId, participant, inputs, outputs); - - return _processJobResultEvent(serviceId, job, jobCallId, participant, inputs, outputs); - } - - function _processSlashEvent( - uint64 serviceId, - bytes memory offender, - uint8 slashPercent, - uint256 totalPayout - ) - internal - virtual - returns (bytes memory); - - function _processJobResultEvent( - uint64 serviceId, - uint8 job, - uint64 jobCallId, - bytes memory participant, - bytes memory inputs, - bytes memory outputs - ) - internal - virtual - returns (bytes memory); -} +// // SPDX-License-Identifier: UNLICENSED +// pragma solidity ^0.8.18; + +// import { GlacisClientCalldata } from "./GlacisClientCalldata.sol"; + +// /// @title BaseBlueprintReceiver +// /// @notice Base contract for receiving cross-chain messages from Tangle Blueprints +// /// This contract lives on the remote chain and accepts messages from Tangle Blueprints. +// abstract contract BaseBlueprintReceiver is GlacisClientCalldata { +// /// Event types +// uint8 constant SLASH_EVENT = 1; +// uint8 constant JOB_RESULT_EVENT = 2; + +// /// Events for specific message types +// event SlashEventReceived(uint64 serviceId, bytes offender, uint8 slashPercent, uint256 totalPayout); +// event JobResultReceived(uint64 serviceId, uint8 job, uint64 jobCallId, bytes participant, bytes inputs, bytes outputs); + +// constructor(address _glacisRouter, uint256 _quorum) GlacisClientCalldata(_glacisRouter, _quorum) { } + +// /// @dev Implementation of ICrossChainReceiver.handleCrossChainMessage +// function _receiveMessage(address[] memory, uint256, bytes32, bytes calldata payload) internal override { +// /// First byte indicates message type +// require(payload.length > 0, "Empty message"); +// uint8 messageType = uint8(payload[0]); +// bytes memory messageData = payload[1:]; + +// if (messageType == SLASH_EVENT) { +// _handleSlashEvent(messageData); +// return; +// } else if (messageType == JOB_RESULT_EVENT) { +// _handleJobResultEvent(messageData); +// return; +// } + +// revert("Unknown message type"); +// } + +// function _handleSlashEvent(bytes memory eventData) internal virtual { +// (uint64 serviceId, bytes memory offender, uint8 slashPercent, uint256 totalPayout) = +// abi.decode(eventData, (uint64, bytes, uint8, uint256)); + +// emit SlashEventReceived(serviceId, offender, slashPercent, totalPayout); + +// _processSlashEvent(serviceId, offender, slashPercent, totalPayout); +// } + +// function _handleJobResultEvent(bytes memory eventData) internal virtual returns (bytes memory) { +// (uint64 serviceId, uint8 job, uint64 jobCallId, bytes memory participant, bytes memory inputs, bytes memory outputs) = +// abi.decode(eventData, (uint64, uint8, uint64, bytes, bytes, bytes)); + +// emit JobResultReceived(serviceId, job, jobCallId, participant, inputs, outputs); + +// return _processJobResultEvent(serviceId, job, jobCallId, participant, inputs, outputs); +// } + +// function _processSlashEvent( +// uint64 serviceId, +// bytes memory offender, +// uint8 slashPercent, +// uint256 totalPayout +// ) +// internal +// virtual +// returns (bytes memory); + +// function _processJobResultEvent( +// uint64 serviceId, +// uint8 job, +// uint64 jobCallId, +// bytes memory participant, +// bytes memory inputs, +// bytes memory outputs +// ) +// internal +// virtual +// returns (bytes memory); +// } diff --git a/src/cross_chain/CrossChainBridgeManager.sol b/src/cross_chain/CrossChainBridgeManager.sol deleted file mode 100644 index d685450..0000000 --- a/src/cross_chain/CrossChainBridgeManager.sol +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import { RootChainEnabledOwnable } from "../Permissions.sol"; -import { ICrossChainMessenger } from "../interfaces/ICrossChainMessenger.sol"; -import { ICrossChainBridgeManager } from "../interfaces/ICrossChainBridgeManager.sol"; - -/// @dev Central manager for cross-chain bridge configurations and message dispatch -contract CrossChainBridgeManager is ICrossChainBridgeManager, RootChainEnabledOwnable { - // Mapping of bridge ID to bridge configuration - mapping(uint256 => BridgeConfig) public bridges; - - // Mapping of bridge ID to chain configurations - mapping(uint256 => ChainConfig[]) public bridgeChains; - - // Mapping of authorized blueprint contracts - mapping(address => bool) public authorizedBlueprints; - - modifier onlyAuthorizedBlueprint() { - require(authorizedBlueprints[msg.sender], "Unauthorized blueprint"); - _; - } - - /// @inheritdoc ICrossChainBridgeManager - function authorizeBlueprint(address blueprint) external onlyOwnerOrRootChain { - require(blueprint != address(0), "Invalid blueprint address"); - authorizedBlueprints[blueprint] = true; - emit BlueprintAuthorized(blueprint); - } - - /// @inheritdoc ICrossChainBridgeManager - function deauthorizeBlueprint(address blueprint) external onlyOwnerOrRootChain { - authorizedBlueprints[blueprint] = false; - emit BlueprintDeauthorized(blueprint); - } - - /// @inheritdoc ICrossChainBridgeManager - function addBridge(uint256 bridgeId, address messenger, string calldata bridgeName) external onlyOwnerOrRootChain { - require(messenger != address(0), "Invalid messenger address"); - require(!bridges[bridgeId].isActive, "Bridge already exists"); - require(bytes(bridgeName).length > 0, "Bridge name required"); - - bridges[bridgeId] = BridgeConfig({ messenger: ICrossChainMessenger(messenger), isActive: true, bridgeName: bridgeName }); - - emit BridgeAdded(bridgeId, messenger, bridgeName); - } - - /// @inheritdoc ICrossChainBridgeManager - function removeBridge(uint256 bridgeId) external onlyOwnerOrRootChain { - require(bridges[bridgeId].isActive, "Bridge not found"); - - delete bridges[bridgeId]; - delete bridgeChains[bridgeId]; - - emit BridgeRemoved(bridgeId); - } - - /// @inheritdoc ICrossChainBridgeManager - function addChain( - uint256 bridgeId, - uint32 chainId, - bytes32 recipient, - string calldata chainName - ) - external - onlyOwnerOrRootChain - { - require(bridges[bridgeId].isActive, "Bridge not found"); - require(recipient != bytes32(0), "Invalid recipient"); - require(bytes(chainName).length > 0, "Chain name required"); - - ChainConfig[] storage chains = bridgeChains[bridgeId]; - for (uint256 i = 0; i < chains.length; i++) { - require(chains[i].chainId != chainId, "Chain already exists"); - } - - chains.push(ChainConfig({ chainId: chainId, recipient: recipient, isActive: true, chainName: chainName })); - - emit ChainAdded(bridgeId, chainId, recipient, chainName); - } - - /// @inheritdoc ICrossChainBridgeManager - function removeChain(uint256 bridgeId, uint32 chainId) external onlyOwnerOrRootChain { - ChainConfig[] storage chains = bridgeChains[bridgeId]; - - for (uint256 i = 0; i < chains.length; i++) { - if (chains[i].chainId == chainId) { - chains[i] = chains[chains.length - 1]; - chains.pop(); - emit ChainRemoved(bridgeId, chainId); - return; - } - } - - revert("Chain not found"); - } - - /// @inheritdoc ICrossChainBridgeManager - function getActiveBridges() external view returns (uint256[] memory bridgeIds, string[] memory bridgeNames) { - uint256 count = 0; - for (uint256 i = 0; i < type(uint256).max; i++) { - if (bridges[i].isActive) count++; - } - - bridgeIds = new uint256[](count); - bridgeNames = new string[](count); - - uint256 index = 0; - for (uint256 i = 0; i < type(uint256).max; i++) { - if (bridges[i].isActive) { - bridgeIds[index] = i; - bridgeNames[index] = bridges[i].bridgeName; - index++; - } - } - } - - /// @inheritdoc ICrossChainBridgeManager - function getActiveChainsForBridge(uint256 bridgeId) - external - view - returns (uint32[] memory chainIds, string[] memory chainNames, bytes32[] memory recipients) - { - require(bridges[bridgeId].isActive, "Bridge not found"); - - ChainConfig[] storage chains = bridgeChains[bridgeId]; - uint256 count = 0; - for (uint256 i = 0; i < chains.length; i++) { - if (chains[i].isActive) count++; - } - - chainIds = new uint32[](count); - chainNames = new string[](count); - recipients = new bytes32[](count); - - uint256 index = 0; - for (uint256 i = 0; i < chains.length; i++) { - if (chains[i].isActive) { - chainIds[index] = chains[i].chainId; - chainNames[index] = chains[i].chainName; - recipients[index] = chains[i].recipient; - index++; - } - } - } - - /// @inheritdoc ICrossChainBridgeManager - function dispatchMessage(bytes memory message) external payable onlyAuthorizedBlueprint { - // Iterate through all bridges - for (uint256 bridgeId = 0; bridgeId < type(uint256).max; bridgeId++) { - BridgeConfig storage bridge = bridges[bridgeId]; - if (!bridge.isActive) continue; - - // Get chains for this bridge - ChainConfig[] storage chains = bridgeChains[bridgeId]; - - // Dispatch to all chains for this bridge - for (uint256 i = 0; i < chains.length; i++) { - if (!chains[i].isActive) continue; - - try bridge.messenger.quoteMessageFee(chains[i].chainId, chains[i].recipient, message) returns (uint256 fee) { - try bridge.messenger.sendMessage{ value: fee }(chains[i].chainId, chains[i].recipient, message) returns ( - bytes32 messageId - ) { - emit MessageDispatched(msg.sender, bridgeId, chains[i].chainId, chains[i].recipient, messageId); - } catch Error(string memory reason) { - emit DispatchError(msg.sender, bridgeId, chains[i].chainId, reason); - } - } catch Error(string memory reason) { - emit DispatchError(msg.sender, bridgeId, chains[i].chainId, reason); - } - } - } - } - - /// @inheritdoc ICrossChainBridgeManager - function get_bridges(uint256 bridgeId) external view override returns (BridgeConfig memory) { - return bridges[bridgeId]; - } - - /// @dev Function to receive ETH for bridge fees - receive() external payable { } -} diff --git a/src/cross_chain/RemoteChainBridgeManager.sol b/src/cross_chain/RemoteChainBridgeManager.sol deleted file mode 100644 index 1bd7f41..0000000 --- a/src/cross_chain/RemoteChainBridgeManager.sol +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import "../Permissions.sol"; -import { ICrossChainMessenger } from "../interfaces/ICrossChainMessenger.sol"; -import { IRemoteChainBridgeManager } from "../interfaces/IRemoteChainBridgeManager.sol"; - -/// @title RemoteChainBridgeManager -/// @notice Manages message dispatch to Tangle through multiple bridges -contract RemoteChainBridgeManager is IRemoteChainBridgeManager, RootChainEnabledOwnable { - /// @dev Maps bridge IDs to their configurations - mapping(uint256 => BridgeConfig) public bridges; - - error InvalidMessenger(); - error InvalidRecipient(); - error BridgeNotFound(); - error InactiveBridge(); - error InsufficientFee(); - - function configureBridge( - uint256 bridgeId, - address messenger, - uint32 tangleChainId, - bytes32 adapter - ) - external - onlyOwnerOrRootChain - { - if (messenger == address(0)) revert InvalidMessenger(); - if (adapter == bytes32(0)) revert InvalidRecipient(); - - bridges[bridgeId] = BridgeConfig({ - messenger: ICrossChainMessenger(messenger), - tangleChainId: tangleChainId, - adapter: adapter, - isActive: true - }); - - emit BridgeConfigured(bridgeId, messenger, tangleChainId, adapter); - } - - function dispatchMessage(bytes calldata message) external payable { - uint256 remainingValue = msg.value; - - // Try each configured bridge - for (uint256 bridgeId = 0; bridgeId < type(uint256).max; bridgeId++) { - BridgeConfig storage config = bridges[bridgeId]; - if (!config.isActive) continue; - - try config.messenger.quoteMessageFee(config.tangleChainId, config.adapter, message) returns (uint256 fee) { - if (fee > remainingValue) continue; - - try config.messenger.sendMessage{ value: fee }(config.tangleChainId, config.adapter, message) returns ( - bytes32 messageId - ) { - remainingValue -= fee; - emit MessageDispatched(bridgeId, messageId, message); - } catch Error(string memory reason) { - emit DispatchError(bridgeId, reason); - } - } catch Error(string memory reason) { - emit DispatchError(bridgeId, reason); - } - } - - // Return any unused fees - if (remainingValue > 0) { - (bool success,) = msg.sender.call{ value: remainingValue }(""); - require(success, "Fee return failed"); - } - } - - function getMessageFee(uint256 bridgeId, bytes calldata message) external view returns (uint256) { - BridgeConfig storage config = bridges[bridgeId]; - if (!config.isActive) revert InactiveBridge(); - - return config.messenger.quoteMessageFee(config.tangleChainId, config.adapter, message); - } - - receive() external payable { } -} diff --git a/src/cross_chain/XCBlueprintServiceManager.sol b/src/cross_chain/XCBlueprintServiceManager.sol index 534114b..75fd1cb 100644 --- a/src/cross_chain/XCBlueprintServiceManager.sol +++ b/src/cross_chain/XCBlueprintServiceManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; import { BlueprintServiceManagerBase } from "../BlueprintServiceManagerBase.sol"; import { ICrossChainBridgeManager } from "../interfaces/ICrossChainBridgeManager.sol"; diff --git a/src/cross_chain/XCBridge.sol b/src/cross_chain/XCBridge.sol new file mode 100644 index 0000000..ff805cf --- /dev/null +++ b/src/cross_chain/XCBridge.sol @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +import { IMessageHelper } from "./helpers/IMessageHelper.sol"; +import { RootChainEnabledOwnable } from "../Permissions.sol"; + +abstract contract XCBridge is RootChainEnabledOwnable { + error RouterNotAuthorized(); + + mapping(address => bool) public authorizedRouters; + mapping(address => address) public routerHandlers; + mapping(uint256 => address) public dispatchers; + mapping(address => bool) public initialized; + + receive() external payable { } + + /// @notice Function to allow owner to withdraw any excess ETH + function withdrawETH() external onlyOwnerOrRootChain { + uint256 balance = address(this).balance; + require(balance > 0, "No ETH to withdraw"); + + (bool success,) = msg.sender.call{ value: balance }(""); + require(success, "ETH transfer failed"); + } + + function _initialize(address _helper) internal { + if (!initialized[_helper]) { + (bool success, bytes memory data) = _helper.delegatecall(abi.encodeWithSelector(IMessageHelper.initialize.selector)); + if (!success) { + assembly { + revert(add(data, 32), mload(data)) + } + } + initialized[_helper] = true; + } + } + + function addDispatchers(address[] calldata _dispatchers, uint256[] calldata _bridgeIds) external onlyOwner { + for (uint256 i = 0; i < _dispatchers.length;) { + dispatchers[_bridgeIds[i]] = _dispatchers[i]; + _initialize(_dispatchers[i]); + unchecked { + ++i; + } + } + } + + function removeDispatchers(uint256[] calldata _bridgeIds) external onlyOwner { + for (uint256 i = 0; i < _bridgeIds.length;) { + delete dispatchers[_bridgeIds[i]]; + unchecked { + ++i; + } + } + } + + function addRouters(address[] calldata _routers, address[] calldata _implementations) external onlyOwner { + for (uint256 i = 0; i < _routers.length;) { + _initialize(_implementations[i]); + authorizedRouters[_routers[i]] = true; + routerHandlers[_routers[i]] = _implementations[i]; + unchecked { + ++i; + } + } + } + + function removeRouters(address[] calldata _routers) external onlyOwner { + for (uint256 i = 0; i < _routers.length;) { + authorizedRouters[_routers[i]] = false; + routerHandlers[_routers[i]] = address(0); + unchecked { + ++i; + } + } + } + + function _sendMessage(bytes memory message, uint256 _bridgeId) internal { + (bool success, bytes memory data) = + dispatchers[_bridgeId].delegatecall(abi.encodeWithSelector(IMessageHelper.sendMessage.selector, message, _bridgeId)); + if (!success) { + assembly { + revert(add(data, 32), mload(data)) + } + } + } + + function _receiveMessage( + address _sender, + bytes calldata _message, + function(uint256, bytes calldata) internal callback + ) + internal + { + if (!authorizedRouters[_sender]) { + revert RouterNotAuthorized(); + } + + (bool success, bytes memory data) = + routerHandlers[_sender].delegatecall(abi.encodeWithSelector(IMessageHelper.parseMessage.selector, _message)); + + if (!success) { + assembly { + revert(add(data, 32), mload(data)) + } + } + + (uint256 fromChainId, uint256 start, uint256 end) = abi.decode(data, (uint256, uint256, uint256)); + + callback(fromChainId, _message[start:end]); + } +} diff --git a/src/cross_chain/helpers/GlacisMessageReceiver.sol b/src/cross_chain/helpers/GlacisMessageReceiver.sol new file mode 100644 index 0000000..268f234 --- /dev/null +++ b/src/cross_chain/helpers/GlacisMessageReceiver.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +import { GlacisAccessControlClient, IGlacisClient } from "glacis-contracts/client/GlacisClient.sol"; + +contract GlacisMessageReceiver is GlacisAccessControlClient, IGlacisClient { + error GlacisClient__CanOnlyBeCalledByRouter(); + error GlacisClient__InvalidRouterAddress(); + + event GlacisClient__MessageRouted(bytes32 indexed messageId, uint256 toChainId, bytes32 to); + + event GlacisClient__MessageArrived(address[] fromAdapters, uint256 fromChainId, bytes32 fromAddress); + + address public immutable GLACIS_ROUTER; + + constructor(address _glacisRouter, uint256 _quorum) GlacisAccessControlClient() IGlacisClient(_quorum) { + if (_glacisRouter == address(0)) { + revert GlacisClient__InvalidRouterAddress(); + } + GLACIS_ROUTER = _glacisRouter; + } + + /// @notice Receives message from GMP(s) through GlacisRouter + /// @param fromAdapters addresses of the adapters sent this message (that reached quorum requirements) + /// @param fromChainId Source chain (Glacis chain ID) + /// @param fromAddress Source address on source chain + /// @param payload Routed payload + function receiveMessage( + address[] memory fromAdapters, + uint256 fromChainId, + bytes32 fromAddress, + bytes calldata payload + ) + external + override + { + if (msg.sender != GLACIS_ROUTER) { + revert GlacisClient__CanOnlyBeCalledByRouter(); + } + _receiveMessage(fromAdapters, fromChainId, fromAddress, payload); + emit GlacisClient__MessageArrived(fromAdapters, fromChainId, fromAddress); + } + + function _receiveMessage( + address[] memory fromAdapters, + uint256 fromChainId, + bytes32 fromAddress, + bytes calldata payload + ) + internal + virtual + { } +} diff --git a/src/cross_chain/helpers/GlacisMessageSender.sol b/src/cross_chain/helpers/GlacisMessageSender.sol new file mode 100644 index 0000000..0cad50f --- /dev/null +++ b/src/cross_chain/helpers/GlacisMessageSender.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +import { GlacisClient } from "glacis-contracts/client/GlacisClient.sol"; +import { Ownable } from "../../Permissions.sol"; + +/// @title GlacisMessageSender +/// @notice Manages message dispatch to Tangle through multiple bridges +contract GlacisMessageSender is GlacisClient, Ownable { + bytes32 immutable RECEIVER; + address immutable REFUND_ADDRESS; + uint256 immutable TANGLE_CHAIN_ID; + address[] gmps; + CrossChainGas[] fees; + + error InvalidMessenger(); + error InvalidRecipient(); + error BridgeNotFound(); + error InactiveBridge(); + error InsufficientFee(); + + constructor( + address _glacisRouter, + uint256 _quorum, + bytes32 _receiver, + address _refundAddress, + uint256 _tangleChainId + ) + GlacisClient(_glacisRouter, _quorum) + Ownable() + { + RECEIVER = _receiver; + REFUND_ADDRESS = _refundAddress; + TANGLE_CHAIN_ID = _tangleChainId; + } + + function configureGMPs(address[] memory _gmps, CrossChainGas[] memory _fees) external onlyOwner { + gmps = _gmps; + delete fees; + for (uint256 i = 0; i < _fees.length;) { + fees.push(_fees[i]); + unchecked { + i++; + } + } + } + + function _dispatchMessage(bytes calldata message) internal { + _route(TANGLE_CHAIN_ID, RECEIVER, message, gmps, fees, REFUND_ADDRESS, false, msg.value); + } + + function _calculateMessageFee(bytes calldata message) internal view virtual returns (uint256 fee) { } + + receive() external payable { } +} diff --git a/src/cross_chain/helpers/IMessageHelper.sol b/src/cross_chain/helpers/IMessageHelper.sol new file mode 100644 index 0000000..12c0804 --- /dev/null +++ b/src/cross_chain/helpers/IMessageHelper.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +interface IMessageHelper { + function initialize() external; + function update(bytes calldata) external; + function sendMessage(bytes calldata) external; + function parseMessage(bytes calldata) external; +} diff --git a/src/cross_chain/hyperlane/HyperlaneAdapter.sol b/src/cross_chain/hyperlane/HyperlaneAdapter.sol deleted file mode 100644 index f7c6093..0000000 --- a/src/cross_chain/hyperlane/HyperlaneAdapter.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import { IMailbox } from "../../vendored/hyperlane/IMailbox.sol"; -import { IMessageRecipient } from "../../vendored/hyperlane/IMessageRecipient.sol"; -import { ICrossChainReceiver } from "../../interfaces/ICrossChainReceiver.sol"; -import { ICrossChainDelegatorMessage } from "../../interfaces/ICrossChainDelegatorMessage.sol"; -import { CrossChainDelegatorMessage } from "../../libs/CrossChainDelegatorMessage.sol"; - -/// @title HyperlaneAdapter -/// @notice Adapts Hyperlane messages to standard cross-chain message format -contract HyperlaneAdapter is IMessageRecipient { - using CrossChainDelegatorMessage for ICrossChainDelegatorMessage.DepositMessage; - - ICrossChainReceiver public immutable receiver; - IMailbox public immutable mailbox; - - uint256 public constant BRIDGE_ID = 1; - - modifier onlyMailbox() { - require(msg.sender == address(mailbox), "Only mailbox can call"); - _; - } - - constructor(address _receiver, address _mailbox) { - require(_receiver != address(0), "Invalid receiver"); - require(_mailbox != address(0), "Invalid mailbox"); - receiver = ICrossChainReceiver(_receiver); - mailbox = IMailbox(_mailbox); - } - - /// @inheritdoc IMessageRecipient - function handle(uint32 _origin, bytes32 _sender, bytes calldata _message) external payable onlyMailbox returns (bytes memory) { - return receiver.handleCrossChainMessage(_origin, _sender, _message); - } -} diff --git a/src/cross_chain/hyperlane/HyperlaneBlueprintReceiver.sol b/src/cross_chain/hyperlane/HyperlaneBlueprintReceiver.sol deleted file mode 100644 index 4f2579a..0000000 --- a/src/cross_chain/hyperlane/HyperlaneBlueprintReceiver.sol +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import { IMailbox } from "../../vendored/hyperlane/IMailbox.sol"; -import { IMessageRecipient } from "../../vendored/hyperlane/IMessageRecipient.sol"; -import { TypeCasts } from "../../vendored/hyperlane/TypeCasts.sol"; -import { BaseBlueprintReceiver } from "../BaseBlueprintReceiver.sol"; - -/// @title HyperlaneBlueprintReceiver -/// @notice Blueprint receiver contract for Hyperlane -/// This contract is used to receive and process messages from Hyperlane on the -/// remote chain that accepts restaking assets for Tangle Blueprints. Every blueprint -/// sends messages to this contract to process job results and slash events. -contract HyperlaneBlueprintReceiver is BaseBlueprintReceiver, IMessageRecipient { - using TypeCasts for bytes32; - using TypeCasts for address; - - IMailbox public immutable mailbox; - - constructor(address _mailbox) { - require(_mailbox != address(0), "Invalid mailbox"); - mailbox = IMailbox(_mailbox); - } - - modifier onlyMailbox() { - require(msg.sender == address(mailbox), "Only mailbox can call"); - _; - } - - /** - * @notice Handle an interchain message - * @param _origin Domain ID of origin chain - * @param _sender Address of sender on origin chain as bytes32 - * @param _message Raw bytes content of message - */ - function handle( - uint32 _origin, - bytes32 _sender, - bytes calldata _message - ) - external - payable - override - onlyMailbox - returns (bytes memory) - { - return this.handleCrossChainMessage(_origin, _sender, _message); - } - - function _processSlashEvent( - uint64 serviceId, - bytes memory offender, - uint8 slashPercent, - uint256 totalPayout - ) - internal - virtual - override - returns (bytes memory) - { - // Implement Hyperlane-specific slash processing - return abi.encode(true); - } - - function _processJobResultEvent( - uint64 serviceId, - uint8 job, - uint64 jobCallId, - bytes memory participant, - bytes memory inputs, - bytes memory outputs - ) - internal - virtual - override - returns (bytes memory) - { - // Implement Hyperlane-specific job result processing - return abi.encode(true); - } -} diff --git a/src/cross_chain/hyperlane/HyperlaneMessenger.sol b/src/cross_chain/hyperlane/HyperlaneMessenger.sol deleted file mode 100644 index eb31674..0000000 --- a/src/cross_chain/hyperlane/HyperlaneMessenger.sol +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import { ICrossChainMessenger } from "../../interfaces/ICrossChainMessenger.sol"; -import { IMailbox } from "../../vendored/hyperlane/IMailbox.sol"; - -/// @title HyperlaneMessenger -/// @notice Blueprint messenger contract for Hyperlane -/// This contract is used to send messages from Tangle Blueprints to Hyperlane -/// for cross-chain communication. Every blueprint uses this contract to send job results -/// and slash events to the remote chain. - -contract HyperlaneMessenger is ICrossChainMessenger { - IMailbox public immutable mailbox; - - constructor(address _mailbox) { - require(_mailbox != address(0), "Invalid mailbox address"); - mailbox = IMailbox(_mailbox); - } - - function quoteMessageFee( - uint32 destinationChainId, - bytes32 recipient, - bytes calldata message - ) - external - view - override - returns (uint256) - { - return mailbox.quoteDispatch(destinationChainId, recipient, message); - } - - function sendMessage( - uint32 destinationChainId, - bytes32 recipient, - bytes calldata message - ) - external - payable - override - returns (bytes32) - { - return mailbox.dispatch{ value: msg.value }(destinationChainId, recipient, message); - } -} diff --git a/src/cross_chain/router_protocol/RouterAdapter.sol b/src/cross_chain/router_protocol/RouterAdapter.sol deleted file mode 100644 index a384c80..0000000 --- a/src/cross_chain/router_protocol/RouterAdapter.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import { IDapp } from "../../vendored/router_protocol/IDapp.sol"; -import { IGateway } from "../../vendored/router_protocol/IGateway.sol"; -import { ICrossChainReceiver } from "../../interfaces/ICrossChainReceiver.sol"; -import { ICrossChainDelegatorMessage } from "../../interfaces/ICrossChainDelegatorMessage.sol"; -import { CrossChainDelegatorMessage } from "../../libs/CrossChainDelegatorMessage.sol"; - -/// @title RouterAdapter -/// @notice Adapts Router Protocol messages to standard cross-chain message format -contract RouterAdapter is IDapp { - using CrossChainDelegatorMessage for ICrossChainDelegatorMessage.DepositMessage; - - ICrossChainReceiver public immutable receiver; - IGateway public immutable gateway; - - uint256 public constant BRIDGE_ID = 2; - - modifier onlyGateway() { - require(msg.sender == address(gateway), "Only gateway can call"); - _; - } - - constructor(address _receiver, address _gateway) { - require(_receiver != address(0), "Invalid receiver"); - receiver = ICrossChainReceiver(_receiver); - gateway = IGateway(_gateway); - } - - function iReceive( - string memory requestSender, - bytes memory packet, - string memory srcChainId - ) - external - onlyGateway - returns (bytes memory) - { - return receiver.handleCrossChainMessage(_parseChainId(srcChainId), _convertSender(requestSender), packet); - } - - function iAck(uint256 requestIdentifier, bool execFlag, bytes memory execData) external { } - - function _parseChainId(string memory chainId) internal pure returns (uint32) { - return uint32(_parseInt(chainId)); - } - - function _convertSender(string memory sender) internal pure returns (bytes32) { - return bytes32(bytes(sender)); - } - - function _parseInt(string memory value) internal pure returns (uint256) { - bytes memory b = bytes(value); - uint256 result = 0; - for (uint256 i = 0; i < b.length; i++) { - uint8 c = uint8(b[i]); - if (c >= 48 && c <= 57) { - result = result * 10 + (c - 48); - } - } - return result; - } -} diff --git a/src/cross_chain/router_protocol/RouterBlueprintReceiver.sol b/src/cross_chain/router_protocol/RouterBlueprintReceiver.sol deleted file mode 100644 index 638e35d..0000000 --- a/src/cross_chain/router_protocol/RouterBlueprintReceiver.sol +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import { IDapp } from "../../vendored/router_protocol/IDapp.sol"; -import { BaseBlueprintReceiver } from "../BaseBlueprintReceiver.sol"; - -/// @title RouterBlueprintReceiver -/// @notice Blueprint receiver contract for Router Protocol -/// This contract is used to receive and process messages from Router Protocol on the -/// remote chain that accepts restaking assets for Tangle Blueprints. Every blueprint -/// sends messages to this contract to process job results and slash events. -contract RouterBlueprintReceiver is BaseBlueprintReceiver, IDapp { - // Mapping to track pending requests - mapping(uint256 => bool) public pendingRequests; - - event AckReceived(uint256 indexed requestIdentifier, bool execFlag, bytes execData); - - function iReceive( - string memory requestSender, - bytes memory packet, - string memory srcChainId - ) - external - override - returns (bytes memory) - { - return this.handleCrossChainMessage(_stringToUint32(srcChainId), _stringToBytes32(requestSender), packet); - } - - function iAck(uint256 requestIdentifier, bool execFlag, bytes memory execData) external override { - require(pendingRequests[requestIdentifier], "Unknown request"); - delete pendingRequests[requestIdentifier]; - - emit AckReceived(requestIdentifier, execFlag, execData); - } - - function _processSlashEvent( - uint64 serviceId, - bytes memory offender, - uint8 slashPercent, - uint256 totalPayout - ) - internal - virtual - override - returns (bytes memory) - { - // Implement Router-specific slash processing - return abi.encode(true); - } - - function _processJobResultEvent( - uint64 serviceId, - uint8 job, - uint64 jobCallId, - bytes memory participant, - bytes memory inputs, - bytes memory outputs - ) - internal - virtual - override - returns (bytes memory) - { - // Implement Router-specific job result processing - return abi.encode(true); - } - - // Helper functions - function _stringToUint32(string memory str) internal pure returns (uint32) { - bytes memory b = bytes(str); - uint32 result = 0; - for (uint256 i = 0; i < b.length; i++) { - uint8 c = uint8(b[i]); - if (c >= 48 && c <= 57) { - result = result * 10 + (c - 48); - } - } - return result; - } - - function _stringToBytes32(string memory str) internal pure returns (bytes32) { - bytes memory b = bytes(str); - bytes32 result; - assembly { - result := mload(add(b, 32)) - } - return result; - } -} diff --git a/src/cross_chain/router_protocol/RouterProtocolMessenger.sol b/src/cross_chain/router_protocol/RouterProtocolMessenger.sol deleted file mode 100644 index ae5ebac..0000000 --- a/src/cross_chain/router_protocol/RouterProtocolMessenger.sol +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import { ICrossChainMessenger } from "../../interfaces/ICrossChainMessenger.sol"; -import { IGateway } from "../../vendored/router_protocol/IGateway.sol"; - -/// @title RouterProtocolMessenger -/// @notice Blueprint messenger contract for Router Protocol -/// This contract is used to send messages from Tangle Blueprints to Router Protocol -/// for cross-chain communication. Every blueprint uses this contract to send job results -/// and slash events to the remote chain. -contract RouterProtocolMessenger is ICrossChainMessenger { - IGateway public immutable gateway; - - struct RouterConfig { - uint64 destGasLimit; - uint64 destGasPrice; - uint64 ackGasLimit; - uint64 ackGasPrice; - uint128 relayerFees; - uint8 ackType; - bool isReadCall; - string asmAddress; - } - - // Default configuration per destination chain - mapping(uint32 => RouterConfig) public chainConfigs; - - event ConfigUpdated(uint32 chainId, RouterConfig config); - - constructor(address _gateway) { - require(_gateway != address(0), "Invalid router address"); - gateway = IGateway(_gateway); - } - - function setChainConfig(uint32 chainId, RouterConfig calldata config) external { - chainConfigs[chainId] = config; - emit ConfigUpdated(chainId, config); - } - - function getRequestMetadata(RouterConfig memory config) public pure returns (bytes memory) { - return abi.encodePacked( - config.destGasLimit, - config.destGasPrice, - config.ackGasLimit, - config.ackGasPrice, - config.relayerFees, - config.ackType, - config.isReadCall, - config.asmAddress - ); - } - - function getRequestPacket(string memory destAddress, bytes memory payload) public pure returns (bytes memory) { - return abi.encode(destAddress, payload); - } - - /// @inheritdoc ICrossChainMessenger - function quoteMessageFee( - uint32 destinationChainId, - bytes32 recipient, - bytes calldata message - ) - external - view - override - returns (uint256) - { - // TODO: Implement fee estimation - return 0; - } - - /// @inheritdoc ICrossChainMessenger - function sendMessage( - uint32 destinationChainId, - bytes32 recipient, - bytes calldata message - ) - external - payable - override - returns (bytes32) - { - RouterConfig memory config = chainConfigs[destinationChainId]; - require(config.destGasLimit != 0, "Chain config not set"); - - bytes memory metadata = getRequestMetadata(config); - bytes memory packet = getRequestPacket(_bytes32ToAddress(recipient), message); - - uint256 requestId = gateway.iSend{ value: msg.value }( - gateway.currentVersion(), - 0, // No ROUTE tokens - "", // No ROUTE recipient - _uint32ToString(destinationChainId), - metadata, - packet - ); - - // Convert requestId to bytes32 for compatibility with our interface - return bytes32(requestId); - } - - // Helper functions - function _uint32ToString(uint32 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 _bytes32ToAddress(bytes32 _input) internal pure returns (string memory) { - bytes memory alphabet = "0123456789abcdef"; - bytes memory str = new bytes(42); - str[0] = "0"; - str[1] = "x"; - for (uint256 i = 0; i < 20; i++) { - str[2 + i * 2] = alphabet[uint8(_input[i + 12] >> 4)]; - str[3 + i * 2] = alphabet[uint8(_input[i + 12] & 0x0f)]; - } - return string(str); - } -} diff --git a/src/interfaces/IBlueprintServiceManager.sol b/src/interfaces/IBlueprintServiceManager.sol index 695d625..77216f0 100644 --- a/src/interfaces/IBlueprintServiceManager.sol +++ b/src/interfaces/IBlueprintServiceManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; /// @title IBlueprintServiceManager /// @dev Interface for the BlueprintServiceManager contract, which acts as a manager for the lifecycle of a Blueprint Instance, diff --git a/src/interfaces/ICrossChainBridgeManager.sol b/src/interfaces/ICrossChainBridgeManager.sol index e930561..81a6802 100644 --- a/src/interfaces/ICrossChainBridgeManager.sol +++ b/src/interfaces/ICrossChainBridgeManager.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; import { ICrossChainMessenger } from "./ICrossChainMessenger.sol"; diff --git a/src/interfaces/ICrossChainDelegatorMessage.sol b/src/interfaces/ICrossChainDelegatorMessage.sol index 681a25f..5387d6a 100644 --- a/src/interfaces/ICrossChainDelegatorMessage.sol +++ b/src/interfaces/ICrossChainDelegatorMessage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; /// @title ICrossChainDelegatorMessage /// @notice Defines the structure for cross-chain delegator messages @@ -10,6 +10,7 @@ interface ICrossChainDelegatorMessage { uint256 originAsset; uint256 amount; bytes32 sender; + uint8 lockMultiplier; } /// @notice Structure for cross-chain delegation update messages @@ -19,6 +20,7 @@ interface ICrossChainDelegatorMessage { uint256 amount; bytes32 sender; bytes32 operator; + uint64[] blueprintSelection; } /// @notice Structure for scheduling an unstake operation @@ -73,6 +75,23 @@ interface ICrossChainDelegatorMessage { bytes32 recipient; } + /// @notice Structure for a slash + struct Slash { + uint64 blueprintId; + uint64 serviceId; + uint256 slashAmount; + } + + /// @notice Structure for an unstake executed message + struct UnstakeExecutedMessage { + uint256 bridgeId; + uint256 originAsset; + uint256 amount; + bytes32 sender; + bytes32 operator; + Slash[] slashes; + } + /// @notice Structure for cross-chain withdrawal executed messages /// This message is sent by the origin chain to the remote chain to /// inform about the successful withdrawal execution and before burning diff --git a/src/interfaces/ICrossChainMessenger.sol b/src/interfaces/ICrossChainMessenger.sol index 2ab240c..bfcba86 100644 --- a/src/interfaces/ICrossChainMessenger.sol +++ b/src/interfaces/ICrossChainMessenger.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; /// @title ICrossChainMessenger /// @dev Interface for cross-chain messaging implementations diff --git a/src/interfaces/IMasterVault.sol b/src/interfaces/IMasterVault.sol index 87d0753..2b5eb24 100644 --- a/src/interfaces/IMasterVault.sol +++ b/src/interfaces/IMasterVault.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; interface IMasterVault { struct CrossChainAsset { @@ -10,10 +10,4 @@ interface IMasterVault { } error InvalidAsset(address asset); - - event AdapterAuthorized(address indexed adapter); - event AdapterUnauthorized(address indexed adapter); - - function authorizeAdapter(address adapter) external; - function unauthorizeAdapter(address adapter) external; } diff --git a/src/interfaces/IRemoteChainBridgeManager.sol b/src/interfaces/IRemoteChainBridgeManager.sol index 61540df..2f70baa 100644 --- a/src/interfaces/IRemoteChainBridgeManager.sol +++ b/src/interfaces/IRemoteChainBridgeManager.sol @@ -1,19 +1,11 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; import { ICrossChainMessenger } from "./ICrossChainMessenger.sol"; /// @title IRemoteChainBridgeManager /// @notice Interface for managing cross-chain message dispatch to Tangle interface IRemoteChainBridgeManager { - /// @dev Configuration for a bridge to Tangle - struct BridgeConfig { - ICrossChainMessenger messenger; - uint32 tangleChainId; - bytes32 adapter; - bool isActive; - } - /// @dev Emitted when a message is dispatched to Tangle event MessageDispatched(uint256 indexed bridgeId, bytes32 indexed messageId, bytes message); @@ -23,20 +15,12 @@ interface IRemoteChainBridgeManager { /// @dev Emitted when a bridge is configured event BridgeConfigured(uint256 indexed bridgeId, address messenger, uint32 tangleChainId, bytes32 adapter); - /// @notice Configure a bridge for sending messages to Tangle - /// @param bridgeId The bridge identifier - /// @param messenger The messenger contract address - /// @param tangleChainId Tangle's chain ID - /// @param adapter Recipient address on Tangle - function configureBridge(uint256 bridgeId, address messenger, uint32 tangleChainId, bytes32 adapter) external; - /// @notice Dispatch a message to Tangle through all configured bridges /// @param message The message to dispatch function dispatchMessage(bytes calldata message) external payable; /// @notice Get the fee required to send a message through a specific bridge - /// @param bridgeId The bridge to query /// @param message The message to send /// @return fee The required fee - function getMessageFee(uint256 bridgeId, bytes calldata message) external view returns (uint256 fee); + function getMessageFee(bytes calldata message) external view returns (uint256 fee); } diff --git a/src/interfaces/ISlashAccumulator.sol b/src/interfaces/ISlashAccumulator.sol new file mode 100644 index 0000000..9cf0022 --- /dev/null +++ b/src/interfaces/ISlashAccumulator.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +interface ISlashAccumulator { + function slash(uint64 blueprintId, uint64 serviceId, bytes32 operator, uint256 slashAmount) external; +} diff --git a/src/interfaces/ISlashAlert.sol b/src/interfaces/ISlashAlert.sol new file mode 100644 index 0000000..7bd8733 --- /dev/null +++ b/src/interfaces/ISlashAlert.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +/// @title Slash Alert Interface +/// @notice Interface for handling slashing events in the re-staking protocol +/// @dev Implement this interface to handle slashing events for remote tokens +interface ISlashAlert { + /// @notice Called when a slashing event occurs + /// @param blueprintId The ID of the blueprint + /// @param serviceId The ID of the service + /// @param operator The address/account of the operator being slashed (32 bytes) + /// @param slashAmount The amount to slash in wei + function onSlash(uint64 blueprintId, uint64 serviceId, bytes32 operator, uint256 slashAmount) external; +} diff --git a/src/interfaces/ISyntheticRestakeAsset.sol b/src/interfaces/ISyntheticRestakeAsset.sol index 2b84791..38584bc 100644 --- a/src/interfaces/ISyntheticRestakeAsset.sol +++ b/src/interfaces/ISyntheticRestakeAsset.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; /// @title ISyntheticRestakeAsset /// @notice Interface for SyntheticRestakeAsset contract diff --git a/src/libs/CrossChainDelegatorMessage.sol b/src/libs/CrossChainDelegatorMessage.sol index 365143b..b7c0539 100644 --- a/src/libs/CrossChainDelegatorMessage.sol +++ b/src/libs/CrossChainDelegatorMessage.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; +pragma solidity ^0.8.18; import { ICrossChainDelegatorMessage } from "../interfaces/ICrossChainDelegatorMessage.sol"; @@ -23,6 +23,9 @@ library CrossChainDelegatorMessage { // Message type constants for withdrawal executed flow uint8 constant WITHDRAWAL_EXECUTED_MESSAGE = 9; + // Message type constants for unstake executed flow + uint8 constant UNSTAKE_EXECUTED_MESSAGE = 10; + error InvalidMessageType(); error EmptyMessage(); @@ -219,6 +222,27 @@ library CrossChainDelegatorMessage { return abi.decode(data[1:], (ICrossChainDelegatorMessage.WithdrawalExecutedMessage)); } + /// @notice Encode an unstake executed message + /// @param message The unstake executed message to encode + /// @return The encoded message with type prefix + function encode(ICrossChainDelegatorMessage.UnstakeExecutedMessage memory message) internal pure returns (bytes memory) { + bytes memory encoded = abi.encode(message); + return abi.encodePacked(UNSTAKE_EXECUTED_MESSAGE, encoded); + } + + /// @notice Decode an unstake executed message + /// @param data The encoded message + /// @return The decoded unstake executed message + function decodeUnstakeExecutedMessage(bytes calldata data) + internal + pure + returns (ICrossChainDelegatorMessage.UnstakeExecutedMessage memory) + { + if (data.length == 0) revert EmptyMessage(); + if (uint8(data[0]) != UNSTAKE_EXECUTED_MESSAGE) revert InvalidMessageType(); + return abi.decode(data[1:], (ICrossChainDelegatorMessage.UnstakeExecutedMessage)); + } + /// @notice Convert a bytes32 to an address /// @param _buf The bytes32 to convert /// @return The resulting address diff --git a/src/precompiles/IAssets.sol b/src/precompiles/IAssets.sol index 12fe034..024c895 100644 --- a/src/precompiles/IAssets.sol +++ b/src/precompiles/IAssets.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.20; +pragma solidity ^0.8.18; interface IAssets { /// @notice Get the next available asset ID diff --git a/src/precompiles/MultiAssetDelegation.sol b/src/precompiles/MultiAssetDelegation.sol index 15fd9d1..8a368c8 100644 --- a/src/precompiles/MultiAssetDelegation.sol +++ b/src/precompiles/MultiAssetDelegation.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-only -pragma solidity ^0.8.19; +pragma solidity >=0.8.3; -// /// @dev The MultiAssetDelegation contract's address. -// address constant MULTI_ASSET_DELEGATION = 0x0000000000000000000000000000000000000822; +/// @dev The MultiAssetDelegation contract's address. +address constant MULTI_ASSET_DELEGATION = 0x0000000000000000000000000000000000000822; -// /// @dev The MultiAssetDelegation contract's instance. -// MultiAssetDelegation constant MULTI_ASSET_DELEGATION_CONTRACT = MultiAssetDelegation(MULTI_ASSET_DELEGATION); +/// @dev The MultiAssetDelegation contract's instance. +MultiAssetDelegation constant MULTI_ASSET_DELEGATION_CONTRACT = MultiAssetDelegation(MULTI_ASSET_DELEGATION); /// @author The Tangle Team /// @title Pallet MultiAssetDelegation Interface @@ -14,73 +14,88 @@ pragma solidity ^0.8.19; interface MultiAssetDelegation { /// @dev Join as an operator with a bond amount. /// @param bondAmount The amount to bond as an operator. - function joinOperators(uint256 bondAmount) external returns (uint8); + function joinOperators(uint256 bondAmount) external; /// @dev Schedule to leave as an operator. - function scheduleLeaveOperators() external returns (uint8); + function scheduleLeaveOperators() external; /// @dev Cancel the scheduled leave as an operator. - function cancelLeaveOperators() external returns (uint8); + function cancelLeaveOperators() external; /// @dev Execute the leave as an operator. - function executeLeaveOperators() external returns (uint8); + function executeLeaveOperators() external; /// @dev Bond more as an operator. /// @param additionalBond The additional amount to bond. - function operatorBondMore(uint256 additionalBond) external returns (uint8); + function operatorBondMore(uint256 additionalBond) external; /// @dev Schedule to unstake as an operator. /// @param unstakeAmount The amount to unstake. - function scheduleOperatorUnstake(uint256 unstakeAmount) external returns (uint8); + function scheduleOperatorUnstake(uint256 unstakeAmount) external; /// @dev Execute the unstake as an operator. - function executeOperatorUnstake() external returns (uint8); + function executeOperatorUnstake() external; /// @dev Cancel the scheduled unstake as an operator. - function cancelOperatorUnstake() external returns (uint8); + function cancelOperatorUnstake() external; /// @dev Go offline as an operator. - function goOffline() external returns (uint8); + function goOffline() external; /// @dev Go online as an operator. - function goOnline() external returns (uint8); + function goOnline() external; /// @dev Deposit an amount of an asset. - /// @param assetId The ID of the asset. + /// @param assetId The ID of the asset (0 for ERC20). + /// @param tokenAddress The address of the ERC20 token (if assetId is 0). /// @param amount The amount to deposit. - function deposit(uint256 assetId, uint256 amount) external returns (uint8); + /// @param lockMultiplier The lock multiplier. + function deposit(uint256 assetId, address tokenAddress, uint256 amount, uint8 lockMultiplier) external; /// @dev Schedule a withdrawal of an amount of an asset. - /// @param assetId The ID of the asset. + /// @param assetId The ID of the asset (0 for ERC20). + /// @param tokenAddress The address of the ERC20 token (if assetId is 0). /// @param amount The amount to withdraw. - function scheduleWithdraw(uint256 assetId, uint256 amount) external returns (uint8); + function scheduleWithdraw(uint256 assetId, address tokenAddress, uint256 amount) external; /// @dev Execute the scheduled withdrawal. - function executeWithdraw() external returns (uint8); + function executeWithdraw() external; /// @dev Cancel the scheduled withdrawal. - /// @param assetId The ID of the asset. + /// @param assetId The ID of the asset (0 for ERC20). + /// @param tokenAddress The address of the ERC20 token (if assetId is 0). /// @param amount The amount to cancel withdrawal. - function cancelWithdraw(uint256 assetId, uint256 amount) external returns (uint8); + function cancelWithdraw(uint256 assetId, address tokenAddress, uint256 amount) external; /// @dev Delegate an amount of an asset to an operator. /// @param operator The address of the operator. - /// @param assetId The ID of the asset. + /// @param assetId The ID of the asset (0 for ERC20). + /// @param tokenAddress The address of the ERC20 token (if assetId is 0). /// @param amount The amount to delegate. - function delegate(bytes32 operator, uint256 assetId, uint256 amount) external returns (uint8); + /// @param blueprintSelection The blueprint selection. + function delegate( + bytes32 operator, + uint256 assetId, + address tokenAddress, + uint256 amount, + uint64[] memory blueprintSelection + ) + external; /// @dev Schedule an unstake of an amount of an asset as a delegator. /// @param operator The address of the operator. - /// @param assetId The ID of the asset. + /// @param assetId The ID of the asset (0 for ERC20). + /// @param tokenAddress The address of the ERC20 token (if assetId is 0). /// @param amount The amount to unstake. - function scheduleDelegatorUnstake(bytes32 operator, uint256 assetId, uint256 amount) external returns (uint8); + function scheduleDelegatorUnstake(bytes32 operator, uint256 assetId, address tokenAddress, uint256 amount) external; /// @dev Execute the scheduled unstake as a delegator. - function executeDelegatorUnstake() external returns (uint8); + function executeDelegatorUnstake() external; /// @dev Cancel the scheduled unstake as a delegator. /// @param operator The address of the operator. - /// @param assetId The ID of the asset. + /// @param assetId The ID of the asset (0 for ERC20). + /// @param tokenAddress The address of the ERC20 token (if assetId is 0). /// @param amount The amount to cancel unstake. - function cancelDelegatorUnstake(bytes32 operator, uint256 assetId, uint256 amount) external returns (uint8); + function cancelDelegatorUnstake(bytes32 operator, uint256 assetId, address tokenAddress, uint256 amount) external; } diff --git a/src/test/Blueprint.t.sol b/src/test/Blueprint.t.sol new file mode 100644 index 0000000..01e609e --- /dev/null +++ b/src/test/Blueprint.t.sol @@ -0,0 +1,23 @@ +// // SPDX-License-Identifier: UNLICENSED +// pragma solidity ^0.8.18; + +// import { LocalTestSetup, GlacisCommons } from "./mock/LocalTestSetup.sol"; +// import { Test } from "forge-std/Test.sol"; + +// contract BlueprintTest is LocalTestSetup, Test { +// address public immutable USER = makeAddr("USER"); + +// LocalTestSetup.Config config; +// GlacisCommons.CrossChainGas[] fees; + +// function setUp() public { +// config = setup(); +// createFees(fees, 100, 5); +// } + +// function test_blueprint_send() public { +// // config.xcConfig.remoteRestakeVault.deposit(100 ether); +// } + +// function test_blueprint_receive() public { } +// } diff --git a/src/test/MasterVault.t.sol b/src/test/MasterVault.t.sol new file mode 100644 index 0000000..cb75d16 --- /dev/null +++ b/src/test/MasterVault.t.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +import { Test } from "forge-std/Test.sol"; +import { MasterVault } from "../assets/MasterVault.sol"; +import { ICrossChainDelegatorMessage } from "../interfaces/ICrossChainDelegatorMessage.sol"; + +contract MasterVaultTest is Test { + MasterVault public masterVault; + bytes32 constant OPERATOR = bytes32(uint256(1)); + + function setUp() public { + masterVault = new MasterVault(); + } + + function testSingleSlash() public { + uint64 blueprintId = 1; + uint64 serviceId = 100; + uint256 slashAmount = 1000; + + masterVault.slash(blueprintId, serviceId, OPERATOR, slashAmount); + + ICrossChainDelegatorMessage.Slash[] memory slashes = masterVault.getSlashes(OPERATOR); + assertEq(slashes.length, 1, "Should have one slash record"); + assertEq(slashes[0].blueprintId, blueprintId, "Blueprint ID mismatch"); + assertEq(slashes[0].serviceId, serviceId, "Service ID mismatch"); + assertEq(slashes[0].slashAmount, slashAmount, "Slash amount mismatch"); + } + + function testMultipleSlashes() public { + // First slash + masterVault.slash(1, 100, OPERATOR, 1000); + + // Second slash + masterVault.slash(2, 200, OPERATOR, 2000); + + // Third slash + masterVault.slash(3, 300, OPERATOR, 3000); + + ICrossChainDelegatorMessage.Slash[] memory slashes = masterVault.getSlashes(OPERATOR); + assertEq(slashes.length, 3, "Should have three slash records"); + + // Verify first slash + assertEq(slashes[0].blueprintId, 1); + assertEq(slashes[0].serviceId, 100); + assertEq(slashes[0].slashAmount, 1000); + + // Verify second slash + assertEq(slashes[1].blueprintId, 2); + assertEq(slashes[1].serviceId, 200); + assertEq(slashes[1].slashAmount, 2000); + + // Verify third slash + assertEq(slashes[2].blueprintId, 3); + assertEq(slashes[2].serviceId, 300); + assertEq(slashes[2].slashAmount, 3000); + } + + function testDifferentOperatorSlashes() public { + bytes32 operator1 = bytes32(uint256(1)); + bytes32 operator2 = bytes32(uint256(2)); + + // Slash operator1 + masterVault.slash(1, 100, operator1, 1000); + + // Slash operator2 + masterVault.slash(2, 200, operator2, 2000); + + // Verify operator1 slashes + ICrossChainDelegatorMessage.Slash[] memory slashes1 = masterVault.getSlashes(operator1); + assertEq(slashes1.length, 1, "Operator1 should have one slash"); + assertEq(slashes1[0].blueprintId, 1); + assertEq(slashes1[0].slashAmount, 1000); + + // Verify operator2 slashes + ICrossChainDelegatorMessage.Slash[] memory slashes2 = masterVault.getSlashes(operator2); + assertEq(slashes2.length, 1, "Operator2 should have one slash"); + assertEq(slashes2[0].blueprintId, 2); + assertEq(slashes2[0].slashAmount, 2000); + } +} diff --git a/src/test/RouterProtocolMessengerSetup.sol b/src/test/RouterProtocolMessengerSetup.sol deleted file mode 100644 index 2f7df10..0000000 --- a/src/test/RouterProtocolMessengerSetup.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.19; - -import { RouterProtocolMessenger } from "../cross_chain/router_protocol/RouterProtocolMessenger.sol"; - -/// @title RouterProtocolMessengerSetup -/// @dev Helper contract to setup the RouterProtocolMessenger with default configurations -contract RouterProtocolMessengerSetup { - function setupArbitrumConfig(RouterProtocolMessenger messenger) external { - RouterProtocolMessenger.RouterConfig memory config = RouterProtocolMessenger.RouterConfig({ - destGasLimit: 500_000, // Example gas limit - destGasPrice: 0, // Let Router estimate - ackGasLimit: 300_000, // Example ack gas limit - ackGasPrice: 0, // Let Router estimate - relayerFees: 25_000_000_000_000_000, // 0.025 ROUTE (minimum) - ackType: 1, // Expect success acks - isReadCall: false, // This is a write operation - asmAddress: "" // No ASM module - }); - - messenger.setChainConfig(421_614, config); // Arbitrum Sepolia testnet - } -} diff --git a/src/test/mock/LocalTestSetup.sol b/src/test/mock/LocalTestSetup.sol new file mode 100644 index 0000000..f1b9cae --- /dev/null +++ b/src/test/mock/LocalTestSetup.sol @@ -0,0 +1,94 @@ +// // SPDX-License-Identifier: UNLICENSED +// pragma solidity ^0.8.18; + +// import { +// GlacisMockSetup, +// GlacisCommons, +// GlacisRouter, +// GlacisLayerZeroV2Adapter, +// GlacisHyperlaneAdapter +// } from "glacis-test/GlacisMockSetup.sol"; +// import { MasterVault } from "../../assets/MasterVault.sol"; +// import { RemoteRestakeVault } from "../../assets/RemoteRestakeVault.sol"; +// import { SyntheticRestakeAsset } from "../../assets/SyntheticRestakeAsset.sol"; +// import { RemoteChainBridgeManager } from "../../cross_chain/RemoteChainBridgeManager.sol"; +// import { XCBlueprintServiceManager } from "../../cross_chain/XCBlueprintServiceManager.sol"; + +// contract LocalTestSetup { +// address public constant TANGLE_SIDE = address(0x1337); +// address public constant XC_EVM_SIDE = address(0x9999); + +// struct GlacisConfig { +// GlacisRouter glacisRouter; +// GlacisLayerZeroV2Adapter LZAdapter; +// address LAYERZERO_GMP_ID; +// GlacisHyperlaneAdapter hyperlaneAdapter; +// address HYPERLANE_GMP_ID; +// } + +// struct XCConfig { +// MasterVault masterVault; +// RemoteRestakeVault remoteRestakeVault; +// SyntheticRestakeAsset syntheticRestakeAsset; +// RemoteChainBridgeManager remoteChainBridgeManagerTangle; +// RemoteChainBridgeManager remoteChainBridgeManagerXC; +// XCBlueprintServiceManager xcBlueprintServiceManager; +// } + +// struct Config { +// GlacisConfig glacisConfig; +// XCConfig xcConfig; +// } + +// function setup() internal returns (Config memory config) { +// GlacisMockSetup mock = new GlacisMockSetup(); +// config.glacisConfig.glacisRouter = mock.glacisRouter(); +// config.glacisConfig.LZAdapter = mock.setupLayerZero(); +// config.glacisConfig.hyperlaneAdapter = mock.setupHyperlane(); +// config.glacisConfig.LAYERZERO_GMP_ID = mock.LAYERZERO_GMP_ID(); +// config.glacisConfig.HYPERLANE_GMP_ID = mock.HYPERLANE_GMP_ID(); +// config.xcConfig.remoteChainBridgeManagerTangle = setupRemoteChainBridgeManager( +// address(config.glacisConfig.glacisRouter), 0, bytes32(uint256(uint160(XC_EVM_SIDE))), address(this), block.chainid +// ); +// config.xcConfig.remoteChainBridgeManagerXC = setupRemoteChainBridgeManager( +// address(config.glacisConfig.glacisRouter), 0, bytes32(uint256(uint160(TANGLE_SIDE))), address(this), block.chainid +// ); +// config.xcConfig.masterVault = +// setupMasterVault(address(config.xcConfig.remoteChainBridgeManagerTangle), address(config.glacisConfig.glacisRouter), +// 1); +// config.xcConfig.xcBlueprintServiceManager = setupXCBlueprintServiceManager(address(this)); + +// config.xcConfig.remoteRestakeVault = setupRemoteRestakeVault(address(config.xcConfig.remoteChainBridgeManagerXC)); +// } + +// function createFees(GlacisCommons.CrossChainGas[] storage fees, uint256 amount, uint256 size) internal { +// for (uint256 i; i < size; ++i) { +// fees.push(GlacisCommons.CrossChainGas({ gasLimit: 0, nativeCurrencyValue: uint128(amount) })); +// } +// } + +// function setupMasterVault(address _bridgeManager, address _glacisRouter, uint256 _quorum) internal returns (MasterVault) { +// return new MasterVault(_bridgeManager, _glacisRouter, _quorum); +// } + +// function setupRemoteRestakeVault(address _bridgeManager) internal returns (RemoteRestakeVault) { +// return new RemoteRestakeVault(_bridgeManager); +// } + +// function setupRemoteChainBridgeManager( +// address _glacisRouter, +// uint256 _quorum, +// bytes32 _receiver, +// address _refundAddress, +// uint256 _tangleChainId +// ) +// internal +// returns (RemoteChainBridgeManager) +// { +// return new RemoteChainBridgeManager(_glacisRouter, _quorum, _receiver, _refundAddress, _tangleChainId); +// } + +// function setupXCBlueprintServiceManager(address _bridgeManager) internal returns (XCBlueprintServiceManager) { +// return new XCBlueprintServiceManager(_bridgeManager); +// } +// } diff --git a/src/vendored/hyperlane/CallLib.sol b/src/vendored/hyperlane/CallLib.sol deleted file mode 100644 index 1ccb13f..0000000 --- a/src/vendored/hyperlane/CallLib.sol +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.20; - -import { Address } from "node_modules/@openzeppelin/contracts/utils/Address.sol"; - -import { TypeCasts } from "./TypeCasts.sol"; - -library CallLib { - struct StaticCall { - // supporting non EVM targets - bytes32 to; - bytes data; - } - - struct Call { - // supporting non EVM targets - bytes32 to; - uint256 value; - bytes data; - } - - struct StaticCallWithCallback { - StaticCall _call; - bytes callback; - } - - function call(Call memory _call) internal returns (bytes memory returnData) { - return Address.functionCallWithValue(TypeCasts.bytes32ToAddress(_call.to), _call.data, _call.value); - } - - function staticcall(StaticCall memory _call) private view returns (bytes memory) { - return Address.functionStaticCall(TypeCasts.bytes32ToAddress(_call.to), _call.data); - } - - function staticcall(StaticCallWithCallback memory _call) internal view returns (bytes memory callback) { - return bytes.concat(_call.callback, staticcall(_call._call)); - } - - function multicall(Call[] memory calls) internal { - uint256 i = 0; - uint256 len = calls.length; - while (i < len) { - call(calls[i]); - unchecked { - ++i; - } - } - } - - function multistaticcall(StaticCallWithCallback[] memory _calls) internal view returns (bytes[] memory) { - uint256 i = 0; - uint256 len = _calls.length; - bytes[] memory callbacks = new bytes[](len); - while (i < len) { - callbacks[i] = staticcall(_calls[i]); - unchecked { - ++i; - } - } - return callbacks; - } - - function multicallto(address to, bytes[] memory calls) internal { - uint256 i = 0; - uint256 len = calls.length; - while (i < len) { - Address.functionCall(to, calls[i]); - unchecked { - ++i; - } - } - } - - function build(bytes32 to, bytes memory data) internal pure returns (StaticCall memory) { - return StaticCall(to, data); - } - - function build(address to, bytes memory data) internal pure returns (StaticCall memory) { - return build(TypeCasts.addressToBytes32(to), data); - } - - function build(bytes32 to, uint256 value, bytes memory data) internal pure returns (Call memory) { - return Call(to, value, data); - } - - function build(address to, uint256 value, bytes memory data) internal pure returns (Call memory) { - return Call(TypeCasts.addressToBytes32(to), value, data); - } - - function build(bytes32 to, bytes memory data, bytes memory callback) internal pure returns (StaticCallWithCallback memory) { - return StaticCallWithCallback(build(to, data), callback); - } - - function build(address to, bytes memory data, bytes memory callback) internal pure returns (StaticCallWithCallback memory) { - return StaticCallWithCallback(build(to, data), callback); - } -} diff --git a/src/vendored/hyperlane/IInterchainAccountRouter.sol b/src/vendored/hyperlane/IInterchainAccountRouter.sol deleted file mode 100644 index cb5fb74..0000000 --- a/src/vendored/hyperlane/IInterchainAccountRouter.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.19; - -import { CallLib } from "./CallLib.sol"; - -interface IInterchainAccountRouter { - function callRemote(uint32 _destinationDomain, CallLib.Call[] calldata calls) external returns (bytes32); - - function getRemoteInterchainAccount(uint32 _destination, address _owner) external view returns (address); -} diff --git a/src/vendored/hyperlane/IInterchainSecurityModule.sol b/src/vendored/hyperlane/IInterchainSecurityModule.sol deleted file mode 100644 index 36538b6..0000000 --- a/src/vendored/hyperlane/IInterchainSecurityModule.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.19; - -interface IInterchainSecurityModule { - enum Types { - UNUSED, - ROUTING, - AGGREGATION, - LEGACY_MULTISIG, - MERKLE_ROOT_MULTISIG, - MESSAGE_ID_MULTISIG, - NULL, // used with relayer carrying no metadata - CCIP_READ, - ARB_L2_TO_L1, - WEIGHTED_MERKLE_ROOT_MULTISIG, - WEIGHTED_MESSAGE_ID_MULTISIG, - OP_L2_TO_L1 - } - - /** - * @notice Returns an enum that represents the type of security model - * encoded by this ISM. - * @dev Relayers infer how to fetch and format metadata. - */ - function moduleType() external view returns (uint8); - - /** - * @notice Defines a security model responsible for verifying interchain - * messages based on the provided metadata. - * @param _metadata Off-chain metadata provided by a relayer, specific to - * the security model encoded by the module (e.g. validator signatures) - * @param _message Hyperlane encoded interchain message - * @return True if the message was verified - */ - function verify(bytes calldata _metadata, bytes calldata _message) external returns (bool); -} - -interface ISpecifiesInterchainSecurityModule { - function interchainSecurityModule() external view returns (IInterchainSecurityModule); -} diff --git a/src/vendored/hyperlane/IMailbox.sol b/src/vendored/hyperlane/IMailbox.sol deleted file mode 100644 index 61add39..0000000 --- a/src/vendored/hyperlane/IMailbox.sol +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.19; - -import { IInterchainSecurityModule } from "./IInterchainSecurityModule.sol"; -import { IPostDispatchHook } from "./IPostDispatchHook.sol"; - -interface IMailbox { - // ============ Events ============ - /** - * @notice Emitted when a new message is dispatched via Hyperlane - * @param sender The address that dispatched the message - * @param destination The destination domain of the message - * @param recipient The message recipient address on `destination` - * @param message Raw bytes of message - */ - event Dispatch(address indexed sender, uint32 indexed destination, bytes32 indexed recipient, bytes message); - - /** - * @notice Emitted when a new message is dispatched via Hyperlane - * @param messageId The unique message identifier - */ - event DispatchId(bytes32 indexed messageId); - - /** - * @notice Emitted when a Hyperlane message is processed - * @param messageId The unique message identifier - */ - event ProcessId(bytes32 indexed messageId); - - /** - * @notice Emitted when a Hyperlane message is delivered - * @param origin The origin domain of the message - * @param sender The message sender address on `origin` - * @param recipient The address that handled the message - */ - event Process(uint32 indexed origin, bytes32 indexed sender, address indexed recipient); - - function localDomain() external view returns (uint32); - - function delivered(bytes32 messageId) external view returns (bool); - - function defaultIsm() external view returns (IInterchainSecurityModule); - - function defaultHook() external view returns (IPostDispatchHook); - - function requiredHook() external view returns (IPostDispatchHook); - - function latestDispatchedId() external view returns (bytes32); - - function dispatch( - uint32 destinationDomain, - bytes32 recipientAddress, - bytes calldata messageBody - ) - external - payable - returns (bytes32 messageId); - - function quoteDispatch( - uint32 destinationDomain, - bytes32 recipientAddress, - bytes calldata messageBody - ) - external - view - returns (uint256 fee); - - function dispatch( - uint32 destinationDomain, - bytes32 recipientAddress, - bytes calldata body, - bytes calldata defaultHookMetadata - ) - external - payable - returns (bytes32 messageId); - - function quoteDispatch( - uint32 destinationDomain, - bytes32 recipientAddress, - bytes calldata messageBody, - bytes calldata defaultHookMetadata - ) - external - view - returns (uint256 fee); - - function dispatch( - uint32 destinationDomain, - bytes32 recipientAddress, - bytes calldata body, - bytes calldata customHookMetadata, - IPostDispatchHook customHook - ) - external - payable - returns (bytes32 messageId); - - function quoteDispatch( - uint32 destinationDomain, - bytes32 recipientAddress, - bytes calldata messageBody, - bytes calldata customHookMetadata, - IPostDispatchHook customHook - ) - external - view - returns (uint256 fee); - - function process(bytes calldata metadata, bytes calldata message) external payable; - - function recipientIsm(address recipient) external view returns (IInterchainSecurityModule module); -} diff --git a/src/vendored/hyperlane/IMessageRecipient.sol b/src/vendored/hyperlane/IMessageRecipient.sol deleted file mode 100644 index c99c3fc..0000000 --- a/src/vendored/hyperlane/IMessageRecipient.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.19; - -/// @title IMessageRecipient Interface -/// @notice Interface for contracts that can receive interchain messages via Hyperlane -/// @dev Implement this interface to enable your contract to process messages from other chains -interface IMessageRecipient { - /// @notice Handle an interchain message - /// @param _origin Domain of origin chain - /// @param _sender Address of sender on origin chain as bytes32 - /// @param _message Raw bytes content of message body - /// @return bytes Response bytes - function handle(uint32 _origin, bytes32 _sender, bytes calldata _message) external payable returns (bytes memory); -} diff --git a/src/vendored/hyperlane/IPostDispatchHook.sol b/src/vendored/hyperlane/IPostDispatchHook.sol deleted file mode 100644 index c37e3f2..0000000 --- a/src/vendored/hyperlane/IPostDispatchHook.sol +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.19; - -/*@@@@@@@ @@@@@@@@@ - @@@@@@@@@ @@@@@@@@@ - @@@@@@@@@ @@@@@@@@@ - @@@@@@@@@ @@@@@@@@@ - @@@@@@@@@@@@@@@@@@@@@@@@@ - @@@@@ HYPERLANE @@@@@@@ - @@@@@@@@@@@@@@@@@@@@@@@@@ - @@@@@@@@@ @@@@@@@@@ - @@@@@@@@@ @@@@@@@@@ - @@@@@@@@@ @@@@@@@@@ -@@@@@@@@@ @@@@@@@@*/ - -interface IPostDispatchHook { - enum Types { - UNUSED, - ROUTING, - AGGREGATION, - MERKLE_TREE, - INTERCHAIN_GAS_PAYMASTER, - FALLBACK_ROUTING, - ID_AUTH_ISM, - PAUSABLE, - PROTOCOL_FEE, - LAYER_ZERO_V1, - RATE_LIMITED, - ARB_L2_TO_L1, - OP_L2_TO_L1 - } - - /** - * @notice Returns an enum that represents the type of hook - */ - function hookType() external view returns (uint8); - - /** - * @notice Returns whether the hook supports metadata - * @param metadata metadata - * @return Whether the hook supports metadata - */ - function supportsMetadata(bytes calldata metadata) external view returns (bool); - - /** - * @notice Post action after a message is dispatched via the Mailbox - * @param metadata The metadata required for the hook - * @param message The message passed from the Mailbox.dispatch() call - */ - function postDispatch(bytes calldata metadata, bytes calldata message) external payable; - - /** - * @notice Compute the payment required by the postDispatch call - * @param metadata The metadata required for the hook - * @param message The message passed from the Mailbox.dispatch() call - * @return Quoted payment for the postDispatch call - */ - function quoteDispatch(bytes calldata metadata, bytes calldata message) external view returns (uint256); -} diff --git a/src/vendored/hyperlane/TypeCasts.sol b/src/vendored/hyperlane/TypeCasts.sol deleted file mode 100644 index 926ed0c..0000000 --- a/src/vendored/hyperlane/TypeCasts.sol +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity ^0.8.19; - -library TypeCasts { - // alignment preserving cast - function addressToBytes32(address _addr) internal pure returns (bytes32) { - return bytes32(uint256(uint160(_addr))); - } - - // alignment preserving cast - function bytes32ToAddress(bytes32 _buf) internal pure returns (address) { - require(uint256(_buf) <= uint256(type(uint160).max), "TypeCasts: bytes32ToAddress overflow"); - return address(uint160(uint256(_buf))); - } -} diff --git a/src/vendored/router_protocol/IDApp.sol b/src/vendored/router_protocol/IDApp.sol deleted file mode 100644 index c307ce7..0000000 --- a/src/vendored/router_protocol/IDApp.sol +++ /dev/null @@ -1,10 +0,0 @@ -pragma solidity ^0.8.19; - -/** - * @dev Interface of the Gateway Self External Calls. - */ -interface IDapp { - function iReceive(string memory requestSender, bytes memory packet, string memory srcChainId) external returns (bytes memory); - - function iAck(uint256 requestIdentifier, bool execFlags, bytes memory execData) external; -} diff --git a/src/vendored/router_protocol/IGateway.sol b/src/vendored/router_protocol/IGateway.sol deleted file mode 100644 index 7a3f37f..0000000 --- a/src/vendored/router_protocol/IGateway.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -import "./Utils.sol"; - -/** - * @dev Interface of the Gateway Self External Calls. - */ -interface IGateway { - // requestMetadata = abi.encodePacked( - // uint256 destGasLimit; - // uint256 destGasPrice; - // uint256 ackGasLimit; - // uint256 ackGasPrice; - // uint256 relayerFees; - // uint8 ackType; - // bool isReadCall; - // bytes asmAddress; - // ) - - function iSend( - uint256 version, - uint256 routeAmount, - string calldata routeRecipient, - string calldata destChainId, - bytes calldata requestMetadata, - bytes calldata requestPacket - ) - external - payable - returns (uint256); - - function setDappMetadata(string memory feePayerAddress) external payable returns (uint256); - - function currentVersion() external view returns (uint256); -} diff --git a/src/vendored/router_protocol/Utils.sol b/src/vendored/router_protocol/Utils.sol deleted file mode 100644 index 2083adb..0000000 --- a/src/vendored/router_protocol/Utils.sol +++ /dev/null @@ -1,71 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; - -library Utils { - // This is used purely to avoid stack too deep errors - // represents everything about a given validator set - struct ValsetArgs { - // the validators in this set, represented by an Ethereum address - address[] validators; - // the powers of the given validators in the same order as above - uint64[] powers; - // the nonce of this validator set - uint256 valsetNonce; - } - - struct RequestPayload { - uint256 routeAmount; - uint256 requestIdentifier; - uint256 requestTimestamp; - string srcChainId; - address routeRecipient; - string destChainId; - address asmAddress; - string requestSender; - address handlerAddress; - bytes packet; - bool isReadCall; - } - - struct CrossChainAckPayload { - uint256 requestIdentifier; - uint256 ackRequestIdentifier; - string destChainId; - address requestSender; - bytes execData; - bool execFlag; - } - - enum AckType { - NO_ACK, - ACK_ON_SUCCESS, - ACK_ON_ERROR, - ACK_ON_BOTH - } - - error IncorrectCheckpoint(); - error C01(); - error C02(); - error C03(); - error C04(); - error C05(); - error C06(); - error C07(); - error WrongDestId(); - error InvalidRecipient(); - error InvalidValsetNonce(uint256 newNonce, uint256 currentNonce); - error MalformedNewValidatorSet(); - error MalformedCurrentValidatorSet(); - error InsufficientPower(uint64 cumulativePower, uint64 powerThreshold); - error InvalidSignature(); - error MessageExcecutionFailedWithLowGas(); - - // constants - string constant MSG_PREFIX = "\x19Ethereum Signed Message:\n32"; - // The number of 'votes' required to execute a valset - // update or batch execution, set to 2/3 of 2^32 - uint64 constant CONSTANT_POWER_THRESHOLD = 2_863_311_531; - // Upper Cap for I_DEFAULT_FEE 10 * 10^18 - uint256 constant I_DEFAULT_FEE_UPPER_LIMIT = 10_000_000_000_000_000_000; - uint256 constant MIN_GAS_THRESHHOLD = 75_000; -} diff --git a/yarn.lock b/yarn.lock index ad509e7..95c06b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4,10 +4,10 @@ "@openzeppelin/contracts-upgradeable@^5.1.0": version "5.1.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.1.0.tgz#4d37648b7402929c53e2ff6e45749ecff91eb2b6" + resolved "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.1.0.tgz" integrity sha512-AIElwP5Ck+cslNE+Hkemf5SxjJoF4wBvvjxc27Rp+9jaPs/CLIaUBMYe1FNzhdiN0cYuwGRmYaRHmmntuiju4Q== "@openzeppelin/contracts@^5.1.0": version "5.1.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-5.1.0.tgz#4e61162f2a2bf414c4e10c45eca98ce5f1aadbd4" + resolved "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.1.0.tgz" integrity sha512-p1ULhl7BXzjjbha5aqst+QMLY+4/LCWADXOCsmLHRM77AqiPjnd9vvUN9sosUfhL9JGKpZ0TjEGxgvnizmWGSA==