// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/access/AccessControl.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "./interfaces/IBridgeVault138.sol"; import "./interfaces/IPolicyManager.sol"; import "./interfaces/IComplianceRegistry.sol"; import "./errors/BridgeErrors.sol"; /// @notice Placeholder for light client verification /// In production, this should integrate with an actual light client contract interface ILightClient { function verifyProof( bytes32 sourceChain, bytes32 sourceTx, bytes calldata proof ) external view returns (bool); } /** * @title BridgeVault138 * @notice Lock/unlock portal for cross-chain token representation * @dev Manages tokens locked for cross-chain transfers. Lock enforces liens via PolicyManager. * Unlock requires light client proof verification and compliance checks. */ contract BridgeVault138 is IBridgeVault138, AccessControl, ReentrancyGuard { bytes32 public constant BRIDGE_OPERATOR_ROLE = keccak256("BRIDGE_OPERATOR_ROLE"); using SafeERC20 for IERC20; IPolicyManager public immutable policyManager; IComplianceRegistry public immutable complianceRegistry; ILightClient public lightClient; // Can be set after deployment /** * @notice Initializes the bridge vault with registry addresses * @param admin Address that will receive DEFAULT_ADMIN_ROLE * @param policyManager_ Address of PolicyManager contract * @param complianceRegistry_ Address of ComplianceRegistry contract */ constructor(address admin, address policyManager_, address complianceRegistry_) { _grantRole(DEFAULT_ADMIN_ROLE, admin); policyManager = IPolicyManager(policyManager_); complianceRegistry = IComplianceRegistry(complianceRegistry_); } /** * @notice Sets the light client contract for proof verification * @dev Requires DEFAULT_ADMIN_ROLE * @param lightClient_ Address of the light client contract */ function setLightClient(address lightClient_) external onlyRole(DEFAULT_ADMIN_ROLE) { lightClient = ILightClient(lightClient_); } /** * @notice Locks tokens for cross-chain transfer * @dev Transfers tokens from user to vault. Enforces liens via PolicyManager.canTransfer. * @param token Token address to lock * @param amount Amount to lock * @param targetChain Target chain identifier * @param targetRecipient Recipient address on target chain */ function lock( address token, uint256 amount, bytes32 targetChain, address targetRecipient ) external override nonReentrant { if (token == address(0)) revert BridgeZeroToken(); if (amount == 0) revert BridgeZeroAmount(); if (targetRecipient == address(0)) revert BridgeZeroRecipient(); // Check if transfer would be allowed BEFORE transferring (checks liens, compliance, etc.) (bool allowed, ) = policyManager.canTransfer(token, msg.sender, address(this), amount); if (!allowed) revert BridgeTransferBlocked(token, msg.sender, address(this), amount); // Transfer tokens from user AFTER validation IERC20(token).safeTransferFrom(msg.sender, address(this), amount); emit Locked(token, msg.sender, amount, targetChain, targetRecipient); } /** * @notice Unlocks tokens from cross-chain transfer * @dev Requires BRIDGE_OPERATOR_ROLE. Verifies proof via light client and checks compliance. * Transfers tokens from vault to recipient. * @param token Token address to unlock * @param to Recipient address * @param amount Amount to unlock * @param sourceChain Source chain identifier * @param sourceTx Source transaction hash * @param proof Proof data for light client verification */ function unlock( address token, address to, uint256 amount, bytes32 sourceChain, bytes32 sourceTx, bytes calldata proof ) external override onlyRole(BRIDGE_OPERATOR_ROLE) nonReentrant { if (token == address(0)) revert BridgeZeroToken(); if (to == address(0)) revert BridgeZeroRecipient(); if (amount == 0) revert BridgeZeroAmount(); // Verify proof via light client if (address(lightClient) == address(0)) revert BridgeLightClientNotSet(); bool verified = lightClient.verifyProof(sourceChain, sourceTx, proof); if (!verified) revert BridgeProofVerificationFailed(sourceChain, sourceTx); // Check compliance if (!complianceRegistry.isAllowed(to)) revert BridgeRecipientNotCompliant(to); if (complianceRegistry.isFrozen(to)) revert BridgeRecipientFrozen(to); // Transfer tokens to recipient IERC20(token).safeTransfer(to, amount); emit Unlocked(token, to, amount, sourceChain, sourceTx); } }