From 607229f1145f0a0ff7cdc60b555f154fa07d9e8b Mon Sep 17 00:00:00 2001 From: Alex Forshtat Date: Thu, 18 Dec 2025 19:27:47 +0100 Subject: [PATCH] WIP: Make some core functions owner-protected for testing on real networks --- src/L1AtomicSwapStakeManager.sol | 23 +++++++++++++++++-- .../DestinationSwapDisputeManager.sol | 19 +++++++++++---- src/interfaces/IL1AtomicSwapStakeManager.sol | 3 ++- src/origin/OriginationSwapDisputeManager.sol | 17 ++++++++------ 4 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/L1AtomicSwapStakeManager.sol b/src/L1AtomicSwapStakeManager.sol index 4a4cfde..47771a3 100644 --- a/src/L1AtomicSwapStakeManager.sol +++ b/src/L1AtomicSwapStakeManager.sol @@ -1,6 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.28; + import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import "./bridges/IL1Bridge.sol"; import "./common/Errors.sol"; @@ -17,6 +20,9 @@ import "./types/StakeManagerStructs.sol"; import "./types/XlpInsolvencyPool.sol"; contract L1AtomicSwapStakeManager is IL1AtomicSwapStakeManager, Ownable { + using MessageHashUtils for bytes; + using ECDSA for bytes32; + struct Config { uint256 claimDelay; uint256 destBeforeOriginMinGap; @@ -94,7 +100,8 @@ contract L1AtomicSwapStakeManager is IL1AtomicSwapStakeManager, Ownable { } /// @inheritdoc IL1AtomicSwapStakeManager - function addChainsInfo(uint256[] calldata chainIds, ChainInfo[] calldata chainsInfo) public payable { + function addChainsInfo(uint256[] calldata chainIds, ChainInfo[] calldata chainsInfo, bytes calldata ownerSignature) public payable { + _verifyOwnerSignature(chainIds, chainsInfo, ownerSignature); require( chainIds.length == chainsInfo.length, InvalidLength("chainIds/chainsInfo", chainIds.length, chainsInfo.length) @@ -113,6 +120,18 @@ contract L1AtomicSwapStakeManager is IL1AtomicSwapStakeManager, Ownable { emit StakeLocked(msg.sender, chainIds, msg.value); } + function _verifyOwnerSignature(uint256[] calldata chainIds, ChainInfo[] calldata chainsInfo, bytes calldata ownerSignature) internal { + address _owner = owner(); + if (_owner == address(0)){ + return; + } + bytes memory rawMessage = abi.encode(chainIds, chainsInfo); + bytes32 messageHash = rawMessage.toEthSignedMessageHash(); + (address recovered, ECDSA.RecoverError err, bytes32 errArg) = messageHash.tryRecoverCalldata(ownerSignature); + require(err == ECDSA.RecoverError.NoError, "Owner signature recovery error!"); + require(recovered == _owner, "Invalid owner address signature!"); + } + /// @inheritdoc IL1AtomicSwapStakeManager function getStakeInfo( address xlp, @@ -336,7 +355,7 @@ contract L1AtomicSwapStakeManager is IL1AtomicSwapStakeManager, Ownable { uint256 destinationChainId, DisputeType disputeType, SlashShareRole role - ) external { + ) external onlyOwner { if (disputeType == DisputeType.INSOLVENT_XLP) { _claimInsolvencyShare(l1XlpAddress, originationChainId, destinationChainId, role); return; diff --git a/src/destination/DestinationSwapDisputeManager.sol b/src/destination/DestinationSwapDisputeManager.sol index 30a2797..06e8fcc 100644 --- a/src/destination/DestinationSwapDisputeManager.sol +++ b/src/destination/DestinationSwapDisputeManager.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.28; +import "@openzeppelin/contracts/access/Ownable.sol"; + import "./DestinationSwapBase.sol"; import "../common/utils/AtomicSwapUtils.sol"; import "../interfaces/IL1AtomicSwapStakeManager.sol"; @@ -15,7 +17,7 @@ import "../AtomicSwapStorage.sol"; import "../common/utils/BridgeMessengerLib.sol"; import "../common/utils/ChunkReportLib.sol"; -contract DestinationSwapDisputeManager is DestinationSwapBase, IL2XlpPenalizer { +contract DestinationSwapDisputeManager is DestinationSwapBase, IL2XlpPenalizer, Ownable { using AtomicSwapUtils for AtomicSwapVoucher; using EnumerableMap for EnumerableMap.AddressToAddressMap; @@ -23,15 +25,22 @@ contract DestinationSwapDisputeManager is DestinationSwapBase, IL2XlpPenalizer { return keccak256(abi.encode(l2XlpAddressToSlash, origChainId, destChainId, disputeType)); } - constructor (address _l2Connector, address _l1Connector, address _entryPoint, uint256 l1SlashGasLimit) + constructor ( + address _l2Connector, + address _l1Connector, + address _entryPoint, + uint256 l1SlashGasLimit, + address owner + ) DestinationSwapBase(_entryPoint, l1SlashGasLimit) + Ownable(owner) { l2Connector = IL2Bridge(_l2Connector); l1Connector = _l1Connector; } /// @inheritdoc IL2XlpPenalizer - function accuseFalseVoucherOverride(AtomicSwapVoucherRequest[] calldata voucherRequests, AtomicSwapVoucher[] calldata voucherOverrides, address payable l1Beneficiary) public override { + function accuseFalseVoucherOverride(AtomicSwapVoucherRequest[] calldata voucherRequests, AtomicSwapVoucher[] calldata voucherOverrides, address payable l1Beneficiary) public override onlyOwner { require( voucherRequests.length == voucherOverrides.length && voucherRequests.length > 0, InvalidLength("requests/overrides", voucherRequests.length, voucherOverrides.length) @@ -87,7 +96,7 @@ contract DestinationSwapDisputeManager is DestinationSwapBase, IL2XlpPenalizer { } /// @inheritdoc IL2XlpPenalizer - function proveVoucherSpent(AtomicSwapVoucherRequest[] calldata voucherRequests, AtomicSwapVoucher[] calldata vouchers, address payable l1Beneficiary, address l2XlpAddressToSlash) public override { + function proveVoucherSpent(AtomicSwapVoucherRequest[] calldata voucherRequests, AtomicSwapVoucher[] calldata vouchers, address payable l1Beneficiary, address l2XlpAddressToSlash) public override onlyOwner { require( voucherRequests.length == vouchers.length && voucherRequests.length > 0, InvalidLength("requests/vouchers", voucherRequests.length, vouchers.length) @@ -145,7 +154,7 @@ contract DestinationSwapDisputeManager is DestinationSwapBase, IL2XlpPenalizer { uint256 nonce, bytes32 committedRequestIdsHash, uint256 committedVoucherCount - ) public override { + ) public override onlyOwner { require(voucherRequests.length == vouchers.length && voucherRequests.length > 0, InvalidLength("requests/vouchers", voucherRequests.length, vouchers.length)); address l2XlpAddressToSlash = vouchers[0].originationXlpAddress; diff --git a/src/interfaces/IL1AtomicSwapStakeManager.sol b/src/interfaces/IL1AtomicSwapStakeManager.sol index 8db31fd..0418f5f 100644 --- a/src/interfaces/IL1AtomicSwapStakeManager.sol +++ b/src/interfaces/IL1AtomicSwapStakeManager.sol @@ -131,7 +131,8 @@ interface IL1AtomicSwapStakeManager { /// @notice Adds chain information for supported chains. /// @param chainIds Array of chain IDs to add. /// @param chainsInfo Array of chain information corresponding to each chain ID. - function addChainsInfo(uint256[] calldata chainIds, ChainInfo[] calldata chainsInfo) external payable; + /// @param ownerSignature Signature of the L1 admin account if it is set to a non-zero address. + function addChainsInfo(uint256[] calldata chainIds, ChainInfo[] calldata chainsInfo, bytes calldata ownerSignature) external payable; /// @notice Retrieves stake information for a xlp. /// @param xlp The xlp's address. diff --git a/src/origin/OriginationSwapDisputeManager.sol b/src/origin/OriginationSwapDisputeManager.sol index d5d44ad..335b7b7 100644 --- a/src/origin/OriginationSwapDisputeManager.sol +++ b/src/origin/OriginationSwapDisputeManager.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.28; import "@account-abstraction/contracts/core/Helpers.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../AtomicSwapStorage.sol"; @@ -19,7 +20,7 @@ import "../types/ReportLegs.sol"; import "../types/SlashOutput.sol"; import "./OriginSwapBase.sol"; -contract OriginationSwapDisputeManager is OriginSwapBase, IL2XlpDisputeManager { +contract OriginationSwapDisputeManager is OriginSwapBase, IL2XlpDisputeManager, Ownable { using AssetUtils for Asset; using AtomicSwapUtils for AtomicSwapVoucher; using AtomicSwapUtils for AtomicSwapVoucherRequest; @@ -34,7 +35,8 @@ contract OriginationSwapDisputeManager is OriginSwapBase, IL2XlpDisputeManager { uint256 _voucherMinExpirationTime, uint256 _disputeBondPercent, uint256 _flatNativeBond, - uint256 l1DisputeGasLimit + uint256 l1DisputeGasLimit, + address owner ) OriginSwapBase( _voucherUnlockDelay, @@ -45,6 +47,7 @@ contract OriginationSwapDisputeManager is OriginSwapBase, IL2XlpDisputeManager { _flatNativeBond, l1DisputeGasLimit ) + Ownable(owner) { l2Connector = IL2Bridge(_l2Connector); l1Connector = _l1Connector; @@ -83,7 +86,7 @@ contract OriginationSwapDisputeManager is OriginSwapBase, IL2XlpDisputeManager { uint256 nonce, bytes32 committedRequestIdsHash, uint256 committedVoucherCount - ) external payable override { + ) external payable override onlyOwner { bytes32 reportId = _processInsolvencyDisputeChunk( disputeVouchers, l2XlpAddressToSlash, @@ -107,12 +110,12 @@ contract OriginationSwapDisputeManager is OriginSwapBase, IL2XlpDisputeManager { } /// @inheritdoc IL2XlpDisputeManager - function disputeVoucherOverride(DisputeVoucher[] calldata disputeVouchers, address l2XlpAddressToSlash, address payable l1Beneficiary) external payable override { + function disputeVoucherOverride(DisputeVoucher[] calldata disputeVouchers, address l2XlpAddressToSlash, address payable l1Beneficiary) external payable override onlyOwner { _initiateDisputeWithBond(disputeVouchers, l2XlpAddressToSlash, l1Beneficiary, DisputeType.VOUCHER_OVERRIDE); } /// @inheritdoc IL2XlpDisputeManager - function disputeXlpUnspentVoucherClaim(DisputeVoucher[] calldata disputeVouchers, address payable l1Beneficiary) external payable override { + function disputeXlpUnspentVoucherClaim(DisputeVoucher[] calldata disputeVouchers, address payable l1Beneficiary) external payable override onlyOwner { require(disputeVouchers.length > 0, InvalidLength("No disputed vouchers", 0, disputeVouchers.length)); bytes32 firstId = disputeVouchers[0].voucherRequest.getVoucherRequestId(); AtomicSwapMetadata storage firstMetadata = outgoingAtomicSwaps[firstId]; @@ -361,7 +364,7 @@ contract OriginationSwapDisputeManager is OriginSwapBase, IL2XlpDisputeManager { } /// @inheritdoc IL2XlpDisputeManager - function reportJustifiedDisputeRequests(bytes32 reportId, bytes32[] calldata requestIds) external override { + function reportJustifiedDisputeRequests(bytes32 reportId, bytes32[] calldata requestIds) external override onlyOwner { require(requestIds.length > 0, InvalidLength("No penalized vouchers", 0, requestIds.length)); bytes32 requestIdsHash = _requestIdsHashByReportId[reportId]; require(requestIdsHash != bytes32(0), InvalidReportId(bytes32(0), reportId)); @@ -592,7 +595,7 @@ contract OriginationSwapDisputeManager is OriginSwapBase, IL2XlpDisputeManager { /// @notice After L1 slashing, a disputer can withdraw both the dispute bond and the override bond for vouchers it currently owns. /// @dev This implements: justified dispute → both bonds go to the disputer. - function withdrawDisputeBonds(bytes32[] calldata requestIds) external override { + function withdrawDisputeBonds(bytes32[] calldata requestIds) external override onlyOwner { for (uint256 i = 0; i < requestIds.length; i++) { AtomicSwapMetadata storage metadata = outgoingAtomicSwaps[requestIds[i]]; require(metadata.core.status == AtomicSwapStatus.PENALIZED, InvalidSwapStatus(requestIds[i], metadata.core.status, AtomicSwapStatus.PENALIZED));