From 2fba849892b54d436232241ebea3a24dc68a9634 Mon Sep 17 00:00:00 2001 From: akash Date: Mon, 3 Nov 2025 19:50:09 +0530 Subject: [PATCH 01/10] feat: updated payloadId implementation --- FunctionSignatures.md | 6 +- PAYLOAD_ID_ARCHITECTURE.md | 144 ++++++++++++++++++ contracts/evmx/watcher/Watcher.sol | 10 +- contracts/protocol/Socket.sol | 76 +++++---- contracts/protocol/interfaces/ISocket.sol | 18 ++- .../protocol/interfaces/ISwitchboard.sol | 13 +- .../protocol/switchboard/FastSwitchboard.sol | 64 +++++++- .../switchboard/MessageSwitchboard.sol | 60 +++++--- contracts/utils/common/IdUtils.sol | 74 +++++++-- contracts/utils/common/Structs.sol | 2 +- deprecated/test/mock/MockFastSwitchboard.sol | 2 +- .../switchboards/FastSwitchboardTest.t.sol | 4 +- test/mocks/MockPlug.sol | 2 +- test/switchboard/MessageSwitchboard.t.sol | 12 +- 14 files changed, 395 insertions(+), 92 deletions(-) create mode 100644 PAYLOAD_ID_ARCHITECTURE.md diff --git a/FunctionSignatures.md b/FunctionSignatures.md index 85a28d04..4cb00a84 100644 --- a/FunctionSignatures.md +++ b/FunctionSignatures.md @@ -554,7 +554,7 @@ | `messageTransmitter` | `0x7b04c181` | | `owner` | `0x8da5cb5b` | | `ownershipHandoverExpiresAt` | `0xfee81cf4` | -| `processTrigger` | `0x7f3352bc` | +| `processPayload` | `0x7f3352bc` | | `proveRemoteExecutions` | `0x893289f8` | | `registerSwitchboard` | `0x74f5b1fc` | | `remoteExecutedDigests` | `0xecbf77d9` | @@ -583,7 +583,7 @@ | `isAttested` | `0xc13c2396` | | `owner` | `0x8da5cb5b` | | `ownershipHandoverExpiresAt` | `0xfee81cf4` | -| `processTrigger` | `0x7f3352bc` | +| `processPayload` | `0x7f3352bc` | | `registerSwitchboard` | `0x74f5b1fc` | | `renounceOwnership` | `0x715018a6` | | `requestOwnershipHandover` | `0x25692962` | @@ -610,7 +610,7 @@ | `owner` | `0x8da5cb5b` | | `ownershipHandoverExpiresAt` | `0xfee81cf4` | | `payloadCounter` | `0x550ce1d5` | -| `processTrigger` | `0x7f3352bc` | +| `processPayload` | `0x7f3352bc` | | `registerSibling` | `0x4f58b88c` | | `registerSwitchboard` | `0x74f5b1fc` | | `renounceOwnership` | `0x715018a6` | diff --git a/PAYLOAD_ID_ARCHITECTURE.md b/PAYLOAD_ID_ARCHITECTURE.md new file mode 100644 index 00000000..21e9a635 --- /dev/null +++ b/PAYLOAD_ID_ARCHITECTURE.md @@ -0,0 +1,144 @@ +# Payload ID Architecture - Unified Design + +## Overview +Unified payload ID structure for all three payload types: Write, Trigger, and Message. + +## Payload ID Structure + +### Bit Layout (256 bits total) +``` +[Origin: 64 bits][Verification: 64 bits][Pointer: 64 bits][Reserved: 64 bits] +``` + +Each component breakdown: +- **Origin (64 bits)**: `chainSlug (32 bits) | switchboardId/watcherId (32 bits)` +- **Verification (64 bits)**: `chainSlug (32 bits) | switchboardId/watcherId (32 bits)` +- **Pointer (64 bits)**: Counter value +- **Reserved (64 bits)**: For future extensibility + +## Payload Type Specifications + +### 1. Write Payloads (EVMX → On-chain) +- **Origin**: `evmxChainSlug (32) | watcherId (32)` + - Generated by: Watcher (on EVMX) + - Verified by: Watcher offchain (links source) +- **Verification**: `dstChainSlug (32) | dstSwitchboardId (32)` + - Generated by: Watcher (from config) + - Used by: Socket for routing +- **Pointer**: `payloadCounter (64)` + - Generated by: Watcher (switchboard-specific counter) + +**Where Created**: `Watcher.sol` → `getCurrentPayloadId()` + +### 2. Trigger Payloads (On-chain → EVMX) +- **Origin**: `srcChainSlug (32) | srcSwitchboardId (32)` + - Generated by: FastSwitchboard + - Verified by: Watcher offchain (verifies source) +- **Verification**: `evmxChainSlug (32) | watcherId (32)` + - Generated by: FastSwitchboard (from stored config) + - Used by: Socket for routing +- **Pointer**: `switchboardCounter (64)` + - Generated by: FastSwitchboard (switchboard-specific counter) + +**Where Created**: `FastSwitchboard.sol` → `processPayload()` + +### 3. Message Payloads (Plug → Plug) +- **Origin**: `srcChainSlug (32) | srcSwitchboardId (32)` + - Generated by: MessageSwitchboard + - Verified by: Destination switchboard (checks source) +- **Verification**: `dstChainSlug (32) | dstSwitchboardId (32)` + - Generated by: MessageSwitchboard + - Used by: Socket for routing +- **Pointer**: `switchboardCounter (64)` + - Generated by: MessageSwitchboard (switchboard-specific counter) + +**Where Created**: `MessageSwitchboard.sol` → `_createDigestAndPayloadId()` + +## Decoding and Verification + +### Socket Verification (Destination) +1. Decode `payloadId` using `getVerificationInfo(payloadId)` +2. Extract `verificationChainSlug` and `verificationSwitchboardId` +3. Verify against local config: + - `verificationChainSlug == local chainSlug` + - `verificationSwitchboardId == local switchboardId` + +### Source Verification (Off-chain Watcher) +1. Decode `payloadId` using `getOriginInfo(payloadId)` +2. Extract `originChainSlug` and `originId` +3. Verify source configuration matches expected values + +### Payload Type Detection +- Check if `originChainSlug` or `verificationChainSlug` matches `evmxChainSlug` + - If `originChainSlug == evmxChainSlug`: **Write Payload** + - If `verificationChainSlug == evmxChainSlug`: **Trigger Payload** + - If neither: **Message Payload** + +## Implementation Details + +### IdUtils.sol Functions + +#### Encoding +- `createPayloadId(originChainSlug, originId, verificationChainSlug, verificationId, pointer)` + - Creates new payload ID with all components + +#### Decoding +- `decodePayloadId(payloadId)` - Full decode +- `getVerificationInfo(payloadId)` - Gets verification components (for Socket routing) +- `getOriginInfo(payloadId)` - Gets origin components (for source verification) + +### Required Updates + +1. **Watcher.sol** + - Update `getCurrentPayloadId()` to use new format + - Use `evmxSlug` as origin chain slug + - Use hardcoded `watcherId = 1` for now + - Get `dstSwitchboardId` from `switchboards` mapping + +2. **FastSwitchboard.sol** + - Add state variables: `evmxChainSlug`, `watcherId` (with onlyOwner setters) + - Implement `processPayload()` to create payload ID + - Add counter: `uint64 public triggerPayloadCounter` + - Use: `origin = (chainSlug, switchboardId)`, `verification = (evmxChainSlug, watcherId)` + +3. **MessageSwitchboard.sol** + - Update `_createDigestAndPayloadId()` to use new format + - Use: `origin = (chainSlug, switchboardId)`, `verification = (dstChainSlug, dstSwitchboardId)` + +4. **Socket.sol** + - Update `execute()` to decode payload ID and verify verification components + - Remove old `createPayloadId` usage + - Use `getVerificationInfo()` to extract routing info + +5. **SocketConfig.sol** + - Update `plugSwitchboardIds` type from `uint64` to `uint32` if needed (or keep uint64 and cast) + +## Security Considerations + +### Verification Flow +1. **Destination (Socket)**: Verifies verification component matches local config +2. **Source (Watcher offchain)**: Verifies origin component matches expected source +3. **Pointer verification**: Skipped for now (to be added later) + +### Counter Management +- Each switchboard maintains its own counter +- Prevents cross-switchboard collisions +- Counters are monotonic (never decrease) + +### ID Uniqueness +- Guaranteed by switchboard-specific counters +- Origin + Verification provide additional context +- Reserved bits allow future expansion without breaking changes + +## Migration Notes + +- No production deployments yet, so no migration needed +- All existing test code will need updates +- Backward compatibility not required + +## Future Enhancements + +- Add pointer verification mechanism +- Use reserved bits for additional metadata (payload version, flags, etc.) +- Support multiple watchers (remove hardcoded watcherId = 1) + diff --git a/contracts/evmx/watcher/Watcher.sol b/contracts/evmx/watcher/Watcher.sol index 8c9e6452..b50a20d3 100644 --- a/contracts/evmx/watcher/Watcher.sol +++ b/contracts/evmx/watcher/Watcher.sol @@ -263,7 +263,15 @@ contract Watcher is Initializable, Configurations { bytes32 switchboardType_ ) public view returns (bytes32) { uint64 switchboardId = switchboards[chainSlug_][switchboardType_]; - return createPayloadId(nextPayloadCount, switchboardId, chainSlug_); + // Write payload: origin = (evmxChainSlug, watcherId), verification = (dstChainSlug, dstSwitchboardId) + // watcherId hardcoded as 1 for now + return createPayloadId( + evmxSlug, // origin chain slug (evmx) + 1, // origin id (watcher id, hardcoded) + chainSlug_, // verification chain slug (destination) + uint32(switchboardId), // verification id (destination switchboard) + uint64(nextPayloadCount) // pointer (counter) + ); } /// @notice Read a simple payload by id. diff --git a/contracts/protocol/Socket.sol b/contracts/protocol/Socket.sol index 2bb5147a..860e11f0 100644 --- a/contracts/protocol/Socket.sol +++ b/contracts/protocol/Socket.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.21; import "./SocketUtils.sol"; import {WRITE} from "../utils/common/Constants.sol"; -import {createPayloadId} from "../utils/common/IdUtils.sol"; +import {getVerificationInfo} from "../utils/common/IdUtils.sol"; /** * @title Socket @@ -31,7 +31,10 @@ contract Socket is SocketUtils { error LowGasLimit(); /// @notice Thrown when the message value is insufficient error InsufficientMsgValue(); - + /// @notice Thrown when the verification chain slug is invalid + error InvalidVerificationChainSlug(); + /// @notice Thrown when the verification switchboard id is invalid + error InvalidVerificationSwitchboardId(); /** * @notice Constructor for the Socket contract * @param chainSlug_ The chain slug @@ -70,11 +73,15 @@ contract Socket is SocketUtils { if (msg.value < executeParams_.value + transmissionParams_.socketFees) revert InsufficientMsgValue(); - bytes32 payloadId = createPayloadId( - executeParams_.payloadPointer, - switchboardId, - chainSlug - ); + // Get payloadId from executeParams + bytes32 payloadId = executeParams_.payloadId; + + // Verify payload ID matches destination + (uint32 verificationChainSlug, uint32 verificationSwitchboardId) = getVerificationInfo(payloadId); + if (verificationChainSlug != chainSlug) + revert InvalidVerificationChainSlug(); + if (verificationSwitchboardId != uint32(switchboardId)) + revert InvalidVerificationSwitchboardId(); // validate the execution status _validateExecutionStatus(payloadId); @@ -93,7 +100,7 @@ contract Socket is SocketUtils { * @notice Verifies the digest of the payload * @param payloadId_ The id of the payload * @param switchboardId_ The id of the switchboard - * @param executeParams_ The execution parameters (appGatewayId, value, payloadPointer, callType, gasLimit) + * @param executeParams_ The execution parameters (appGatewayId, value, payloadId, callType, gasLimit) * @param transmitterProof_ The transmitter proof */ function _verify( @@ -129,7 +136,7 @@ contract Socket is SocketUtils { /** * @notice Executes the payload * @param payloadId_ The id of the payload - * @param executeParams_ The execution parameters (appGatewayId, value, payloadPointer, callType, gasLimit) + * @param executeParams_ The execution parameters (appGatewayId, value, payloadId, callType, gasLimit) * @param transmissionParams_ The transmission parameters (socketFees, transmitterProof, refundAddress) */ function _execute( @@ -185,52 +192,43 @@ contract Socket is SocketUtils { } //////////////////////////////////////////////////////// - ////////////////////// Trigger ////////////////////// + ////////////////////// Outbound Payloads ////////////////////// //////////////////////////////////////////////////////// /** - * @notice To trigger to a connected remote chain. Should only be called by a plug. - * @param data_ The data to trigger the app gateway - * @return triggerId The id of the trigger + * @notice Sends a payload to a connected remote chain (used for both triggers and messages) + * @dev Should only be called by a plug. The switchboard will create the payload ID. + * @param data_ The payload data + * @return payloadId The created payload ID */ - function triggerAppGateway(bytes calldata data_) external payable returns (bytes32 triggerId) { - triggerId = _triggerAppGateway(msg.sender, msg.value, data_); + function sendPayload(bytes calldata data_) external payable returns (bytes32 payloadId) { + payloadId = _sendPayload(msg.sender, msg.value, data_); } /** - * @notice To trigger to a connected remote chain. Should only be called by a plug. + * @notice Internal function to send a payload to a connected remote chain * @param plug_ The address of the plug - * @param value_ The value to trigger the app gateway - * @param data_ The data to trigger the app gateway - * @return triggerId The id of the trigger + * @param value_ The value to send with the payload + * @param data_ The payload data + * @return payloadId The created payload ID from the switchboard */ - function _triggerAppGateway( + function _sendPayload( address plug_, uint256 value_, bytes calldata data_ - ) internal returns (bytes32 triggerId) { + ) internal returns (bytes32 payloadId) { (uint64 switchboardId, address switchboardAddress) = _verifyPlugSwitchboard(plug_); bytes memory plugOverrides = IPlug(plug_).overrides(); - triggerId = _encodeTriggerId(); - // todo: need gas limit? - ISwitchboard(switchboardAddress).processTrigger{value: value_}( + // Switchboard creates the payload ID and emits PayloadRequested event + payloadId = ISwitchboard(switchboardAddress).processPayload{value: value_}( plug_, - triggerId, data_, plugOverrides ); - - emit AppGatewayCallRequested( - triggerId, - bytes32(0), // TODO: clean this up - switchboardId, - toBytes32Format(plug_), - plugOverrides, - data_ - ); } + /** * @notice Increase fees for a pending payload * @param payloadId_ The payload ID to increase fees for @@ -256,13 +254,13 @@ contract Socket is SocketUtils { switchboardAddress = switchboardAddresses[switchboardId]; } /** - * @notice Fallback function that forwards all calls to Socket's callAppGateway - * @dev The calldata is passed as-is to the gateways - * @return The trigger id + * @notice Fallback function that forwards all calls to Socket's sendPayload + * @dev The calldata is passed as-is to the switchboard + * @return The payload ID */ fallback(bytes calldata) external payable returns (bytes memory) { - // return the trigger id - return abi.encode(_triggerAppGateway(msg.sender, msg.value, msg.data)); + // return the payload ID + return abi.encode(_sendPayload(msg.sender, msg.value, msg.data)); } /** diff --git a/contracts/protocol/interfaces/ISocket.sol b/contracts/protocol/interfaces/ISocket.sol index 5df5e2e4..96825aa2 100644 --- a/contracts/protocol/interfaces/ISocket.sol +++ b/contracts/protocol/interfaces/ISocket.sol @@ -55,6 +55,22 @@ interface ISocket { bytes payload ); + /** + * @notice Event emitted when a payload is requested (for both triggers and messages) + * @param payloadId The created payload ID + * @param plug The source plug address + * @param switchboardId The switchboard ID processing the request + * @param overrides The override parameters + * @param payload The payload data + */ + event PayloadRequested( + bytes32 indexed payloadId, + address indexed plug, + uint64 indexed switchboardId, + bytes overrides, + bytes payload + ); + /** * @notice Executes a payload * @param executeParams_ The execution parameters @@ -136,7 +152,7 @@ interface ISocket { */ function switchboardAddresses(uint64 switchboardId_) external view returns (address); - function triggerAppGateway(bytes calldata data_) external payable returns (bytes32 triggerId); + function sendPayload(bytes calldata data_) external payable returns (bytes32 payloadId); function increaseFeesForPayload(bytes32 payloadId_, bytes calldata feesData_) external payable; } diff --git a/contracts/protocol/interfaces/ISwitchboard.sol b/contracts/protocol/interfaces/ISwitchboard.sol index 24040195..baf1e84d 100644 --- a/contracts/protocol/interfaces/ISwitchboard.sol +++ b/contracts/protocol/interfaces/ISwitchboard.sol @@ -18,20 +18,19 @@ interface ISwitchboard { function allowPayload(bytes32 digest_, bytes32 payloadId_, address target_, bytes memory source_) external view returns (bool); /** - * @notice Processes a trigger and creates payload - * @dev This function is called by the socket to process a trigger + * @notice Processes a payload request and creates payload ID + * @dev This function is called by the socket to process a payload request * @dev sb can override this function to add additional logic - * @param triggerId_ Trigger ID from socket * @param plug_ Source plug address * @param payload_ Payload data - * @param overrides_ Overrides for the trigger + * @param overrides_ Overrides for the payload (e.g., destination chain, gas limit, fees) + * @return payloadId_ The created payload ID */ - function processTrigger( + function processPayload( address plug_, - bytes32 triggerId_, bytes calldata payload_, bytes calldata overrides_ - ) external payable; + ) external payable returns (bytes32 payloadId_); /** * @notice Gets the transmitter for a given payload diff --git a/contracts/protocol/switchboard/FastSwitchboard.sol b/contracts/protocol/switchboard/FastSwitchboard.sol index 7632fc6a..d916702d 100644 --- a/contracts/protocol/switchboard/FastSwitchboard.sol +++ b/contracts/protocol/switchboard/FastSwitchboard.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.21; import "./SwitchboardBase.sol"; import {WATCHER_ROLE} from "../../utils/common/AccessRoles.sol"; import {toBytes32Format} from "../../utils/common/Converters.sol"; +import {createPayloadId} from "../../utils/common/IdUtils.sol"; /** * @title FastSwitchboard contract @@ -17,18 +18,37 @@ contract FastSwitchboard is SwitchboardBase { // sibling mappings for outbound journey // chainSlug => address => siblingPlug mapping(address => bytes32) public plugAppGatewayIds; + + // EVMX configuration for trigger payloads + uint32 public evmxChainSlug; + uint32 public watcherId; + + // Counter for trigger payload IDs + uint64 public triggerPayloadCounter; // Error emitted when a payload is already attested by watcher. error AlreadyAttested(); // Error emitted when watcher is not valid error WatcherNotFound(); // Error emitted when source is invalid error InvalidSource(); + // Error emitted when EVMX config not set + error EvmxConfigNotSet(); // Event emitted when watcher attests a payload event Attested(bytes32 payloadId_, address watcher); /** * @notice Event emitted when plug configuration is updated */ event PlugConfigUpdated(address indexed plug, bytes32 appGatewayId); + // Event emitted when EVMX config is set + event EvmxConfigSet(uint32 evmxChainSlug, uint32 watcherId); + // Event emitted when payload is requested + event PayloadRequested( + bytes32 indexed payloadId, + address indexed plug, + uint64 indexed switchboardId, + bytes overrides, + bytes payload + ); /** * @dev Constructor function for the FastSwitchboard contract * @param chainSlug_ Chain slug of the chain where the contract is deployed @@ -69,15 +89,51 @@ contract FastSwitchboard is SwitchboardBase { return isAttested[digest_]; } + /** + * @notice Set EVMX configuration for trigger payloads + * @param evmxChainSlug_ The EVMX chain slug + * @param watcherId_ The watcher ID (hardcoded as 1 for now) + */ + function setEvmxConfig(uint32 evmxChainSlug_, uint32 watcherId_) external onlyOwner { + evmxChainSlug = evmxChainSlug_; + watcherId = watcherId_; + emit EvmxConfigSet(evmxChainSlug_, watcherId_); + } + /** * @inheritdoc ISwitchboard + * @dev Creates a trigger payload ID with origin=(srcChainSlug, srcSwitchboardId), + * verification=(evmxChainSlug, watcherId) + * @return payloadId The created payload ID */ - function processTrigger( + function processPayload( address plug_, - bytes32 triggerId_, bytes calldata payload_, bytes calldata overrides_ - ) external payable virtual {} + ) external payable override onlySocket returns (bytes32 payloadId) { + if (evmxChainSlug == 0 || watcherId == 0) revert EvmxConfigNotSet(); + + // Create trigger payload ID + // Origin: source chain and switchboard + // Verification: EVMX chain and watcher + // Pointer: switchboard counter + payloadId = createPayloadId( + chainSlug, // origin chain slug (source) + uint32(switchboardId), // origin id (source switchboard) + evmxChainSlug, // verification chain slug (evmx) + watcherId, // verification id (watcher id) + triggerPayloadCounter++ // pointer (counter) + ); + + // Emit PayloadRequested event + emit PayloadRequested( + payloadId, + plug_, + switchboardId, + overrides_, + payload_ + ); + } /** * @inheritdoc ISwitchboard @@ -86,7 +142,7 @@ contract FastSwitchboard is SwitchboardBase { bytes32 payloadId_, address, bytes calldata - ) external payable virtual {} + ) external payable override {} /** * @inheritdoc ISwitchboard diff --git a/contracts/protocol/switchboard/MessageSwitchboard.sol b/contracts/protocol/switchboard/MessageSwitchboard.sol index 1f18b1a8..e72a9168 100644 --- a/contracts/protocol/switchboard/MessageSwitchboard.sol +++ b/contracts/protocol/switchboard/MessageSwitchboard.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.21; import "./SwitchboardBase.sol"; import {WATCHER_ROLE, FEE_UPDATER_ROLE} from "../../utils/common/AccessRoles.sol"; import {toBytes32Format} from "../../utils/common/Converters.sol"; -import {createPayloadId} from "../../utils/common/IdUtils.sol"; +import {createPayloadId, getVerificationInfo} from "../../utils/common/IdUtils.sol"; import {DigestParams, MessageOverrides, PayloadFees, SponsoredPayloadFees} from "../../utils/common/Structs.sol"; import {WRITE } from "../../utils/common/Constants.sol"; import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; @@ -21,8 +21,10 @@ contract MessageSwitchboard is SwitchboardBase { // sibling mappings for outbound journey // chainSlug => siblingSocket mapping(uint32 => bytes32) public siblingSockets; - // chainSlug => siblingSwitchboard + // chainSlug => siblingSwitchboard address (bytes32 format) mapping(uint32 => bytes32) public siblingSwitchboards; + // chainSlug => siblingSwitchboard ID + mapping(uint32 => uint32) public siblingSwitchboardIds; // chainSlug => address => siblingPlug mapping(uint32 => mapping(address => bytes32)) public siblingPlugs; @@ -113,6 +115,14 @@ contract MessageSwitchboard is SwitchboardBase { event MinMsgValueFeesSet(uint32 indexed chainSlug, uint256 minFees, address indexed updater); // Event emitted when sponsored fees are increased event SponsoredFeesIncreased(bytes32 indexed payloadId, uint256 newMaxFees, address indexed plug); + // Event emitted when payload is requested + event PayloadRequested( + bytes32 indexed payloadId, + address indexed plug, + uint64 indexed switchboardId, + bytes overrides, + bytes payload + ); /** * @dev Constructor function for the MessageSwitchboard contract @@ -135,10 +145,12 @@ contract MessageSwitchboard is SwitchboardBase { function setSiblingConfig( uint32 chainSlug_, bytes32 socket_, - bytes32 switchboard_ + bytes32 switchboard_, + uint32 switchboardId_ ) external onlyOwner { siblingSockets[chainSlug_] = socket_; siblingSwitchboards[chainSlug_] = switchboard_; + siblingSwitchboardIds[chainSlug_] = switchboardId_; emit SiblingConfigSet(chainSlug_, socket_, switchboard_); } @@ -146,30 +158,29 @@ contract MessageSwitchboard is SwitchboardBase { /** - * @dev Function to process trigger and create payload + * @dev Function to process payload request and create payload ID * @param plug_ Source plug address - * @param triggerId_ Trigger ID from socket * @param payload_ Payload data * @param overrides_ Override parameters including dstChainSlug and gasLimit + * @return payloadId The created payload ID */ - function processTrigger( + function processPayload( address plug_, - bytes32 triggerId_, bytes calldata payload_, bytes calldata overrides_ - ) external payable override onlySocket { + ) external payable override onlySocket returns (bytes32 payloadId) { MessageOverrides memory overrides = _decodeOverrides(overrides_); _validateSibling(overrides.dstChainSlug, plug_); // Create digest and payload ID (common for both flows) - (DigestParams memory digestParams, bytes32 digest, bytes32 payloadId) = _createDigestAndPayloadId( + (DigestParams memory digestParams, bytes32 digest, bytes32 payloadId_) = _createDigestAndPayloadId( overrides.dstChainSlug, plug_, overrides.gasLimit, overrides.value, - triggerId_, payload_ ); + payloadId = payloadId_; if (overrides.isSponsored) { // Sponsored flow - check sponsor approval @@ -216,6 +227,15 @@ contract MessageSwitchboard is SwitchboardBase { address(0) // No sponsor for native flow ); } + + // Emit PayloadRequested event + emit PayloadRequested( + payloadId, + plug_, + switchboardId, + overrides_, + payload_ + ); } /** @@ -287,14 +307,20 @@ contract MessageSwitchboard is SwitchboardBase { address plug_, uint256 gasLimit_, uint256 value_, - bytes32 triggerId_, bytes calldata payload_ ) internal returns (DigestParams memory digestParams, bytes32 digest, bytes32 payloadId) { - uint160 payloadPointer = (uint160(chainSlug) << 120) | - (uint160(uint64(uint256(triggerId_))) << 80) | - payloadCounter++; - - payloadId = createPayloadId(payloadPointer, switchboardId, dstChainSlug_); + // Get destination switchboard ID from sibling config + uint32 dstSwitchboardId = siblingSwitchboardIds[dstChainSlug_]; + if (dstSwitchboardId == 0) revert SiblingSocketNotFound(); + + // Message payload: origin = (srcChainSlug, srcSwitchboardId), verification = (dstChainSlug, dstSwitchboardId) + payloadId = createPayloadId( + chainSlug, // origin chain slug (source) + uint32(switchboardId), // origin id (source switchboard) + dstChainSlug_, // verification chain slug (destination) + dstSwitchboardId, // verification id (destination switchboard) + payloadCounter++ // pointer (counter) + ); digestParams = DigestParams({ socket: siblingSockets[dstChainSlug_], @@ -307,7 +333,7 @@ contract MessageSwitchboard is SwitchboardBase { payload: payload_, target: siblingPlugs[dstChainSlug_][plug_], source: abi.encode(chainSlug, toBytes32Format(plug_)), - prevBatchDigestHash: triggerId_, + prevBatchDigestHash: bytes32(0), // No longer using triggerId extraData:"0x" }); digest = _createDigest(digestParams); diff --git a/contracts/utils/common/IdUtils.sol b/contracts/utils/common/IdUtils.sol index 7423d329..d1053294 100644 --- a/contracts/utils/common/IdUtils.sol +++ b/contracts/utils/common/IdUtils.sol @@ -1,16 +1,72 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -/// @notice Creates a payload ID from the given parameters -/// @param payloadPointer_ The payload pointer -/// @param switchboardId_ The switchboard id -/// @param chainSlug_ The chain slug +/// @notice Payload ID structure: +/// [Origin: 64 bits][Verification: 64 bits][Pointer: 64 bits][Reserved: 64 bits] +/// Origin = chainSlug (32 bits) | switchboardId/watcherId (32 bits) +/// Verification = chainSlug (32 bits) | switchboardId/watcherId (32 bits) +/// Pointer = counter (64 bits) +/// Reserved = 64 bits for future use + +/// @notice Creates a payload ID from origin, verification, and pointer components +/// @param originChainSlug_ Chain slug for origin (32 bits) +/// @param originId_ Switchboard ID or watcher ID for origin (32 bits) +/// @param verificationChainSlug_ Chain slug for verification (32 bits) +/// @param verificationId_ Switchboard ID or watcher ID for verification (32 bits) +/// @param pointer_ Counter/pointer value (64 bits) /// @return The created payload ID function createPayloadId( - uint256 payloadPointer_, - uint64 switchboardId_, - uint32 chainSlug_ + uint32 originChainSlug_, + uint32 originId_, + uint32 verificationChainSlug_, + uint32 verificationId_, + uint64 pointer_ ) pure returns (bytes32) { - return - bytes32((uint256(chainSlug_) << 224) | (uint256(switchboardId_) << 160) | payloadPointer_); + uint256 origin = (uint256(originChainSlug_) << 32) | uint256(originId_); + uint256 verification = (uint256(verificationChainSlug_) << 32) | uint256(verificationId_); + return bytes32((origin << 192) | (verification << 128) | (uint256(pointer_) << 64)); +} + +/// @notice Decodes payload ID into its components +/// @param payloadId_ The payload ID to decode +/// @return originChainSlug Chain slug for origin +/// @return originId Switchboard ID or watcher ID for origin +/// @return verificationChainSlug Chain slug for verification +/// @return verificationId Switchboard ID or watcher ID for verification +/// @return pointer Counter/pointer value +function decodePayloadId( + bytes32 payloadId_ +) pure returns ( + uint32 originChainSlug, + uint32 originId, + uint32 verificationChainSlug, + uint32 verificationId, + uint64 pointer +) { + uint256 id = uint256(payloadId_); + originChainSlug = uint32((id >> 224) & type(uint32).max); + originId = uint32((id >> 192) & type(uint32).max); + verificationChainSlug = uint32((id >> 160) & type(uint32).max); + verificationId = uint32((id >> 128) & type(uint32).max); + pointer = uint64((id >> 64) & type(uint64).max); +} + +/// @notice Gets verification chain slug and switchboard ID from payload ID +/// @param payloadId_ The payload ID to decode +/// @return chainSlug Verification chain slug +/// @return switchboardId Verification switchboard ID +function getVerificationInfo(bytes32 payloadId_) pure returns (uint32 chainSlug, uint32 switchboardId) { + uint256 id = uint256(payloadId_); + chainSlug = uint32((id >> 160) & type(uint32).max); + switchboardId = uint32((id >> 128) & type(uint32).max); +} + +/// @notice Gets origin chain slug and switchboard ID from payload ID +/// @param payloadId_ The payload ID to decode +/// @return chainSlug Origin chain slug +/// @return switchboardId Origin switchboard ID or watcher ID +function getOriginInfo(bytes32 payloadId_) pure returns (uint32 chainSlug, uint32 switchboardId) { + uint256 id = uint256(payloadId_); + chainSlug = uint32((id >> 224) & type(uint32).max); + switchboardId = uint32((id >> 192) & type(uint32).max); } diff --git a/contracts/utils/common/Structs.sol b/contracts/utils/common/Structs.sol index 8351eb34..38d47bdc 100644 --- a/contracts/utils/common/Structs.sol +++ b/contracts/utils/common/Structs.sol @@ -70,7 +70,7 @@ struct PromiseReturnData { // AM struct ExecuteParams { bytes4 callType; - uint160 payloadPointer; + bytes32 payloadId; uint256 deadline; uint256 gasLimit; uint256 value; diff --git a/deprecated/test/mock/MockFastSwitchboard.sol b/deprecated/test/mock/MockFastSwitchboard.sol index 6ed61324..72e3c669 100644 --- a/deprecated/test/mock/MockFastSwitchboard.sol +++ b/deprecated/test/mock/MockFastSwitchboard.sol @@ -45,7 +45,7 @@ contract MockFastSwitchboard is ISwitchboard { return switchboardId; } - function processTrigger( + function processPayload( address plug_, bytes32 triggerId_, bytes calldata payload_, diff --git a/deprecated/test/protocol/switchboards/FastSwitchboardTest.t.sol b/deprecated/test/protocol/switchboards/FastSwitchboardTest.t.sol index f27ab1fb..5f624033 100644 --- a/deprecated/test/protocol/switchboards/FastSwitchboardTest.t.sol +++ b/deprecated/test/protocol/switchboards/FastSwitchboardTest.t.sol @@ -21,7 +21,7 @@ contract FastSwitchboardExtended is FastSwitchboard { address owner_ ) FastSwitchboard(chainSlug_, socket_, owner_) {} - function processTrigger( + function processPayload( address plug_, bytes32 triggerId_, bytes calldata payload_, @@ -143,7 +143,7 @@ contract FastSwitchboardTest is AppGatewayBaseSetup { bytes("test payload"), bytes("test overrides") ); - fastSwitchboardExtended.processTrigger( + fastSwitchboardExtended.processPayload( address(0x123), bytes32(uint256(0x456)), bytes("test payload"), diff --git a/test/mocks/MockPlug.sol b/test/mocks/MockPlug.sol index 65ca5710..ba4fb90e 100644 --- a/test/mocks/MockPlug.sol +++ b/test/mocks/MockPlug.sol @@ -38,7 +38,7 @@ contract MockPlug is MessagePlugBase { // New method to trigger Socket's triggerAppGateway function triggerSocket(bytes memory data) external payable returns (bytes32) { - return socket__.triggerAppGateway{value: msg.value}(data); + return socket__.sendPayload{value: msg.value}(data); } // Method to connect to socket diff --git a/test/switchboard/MessageSwitchboard.t.sol b/test/switchboard/MessageSwitchboard.t.sol index 95dfba87..e7664136 100644 --- a/test/switchboard/MessageSwitchboard.t.sol +++ b/test/switchboard/MessageSwitchboard.t.sol @@ -398,7 +398,7 @@ contract MessageSwitchboardTest is Test, Utils { } // ============================================ - // CRITICAL TESTS - GROUP 2: processTrigger - Native Flow + // CRITICAL TESTS - GROUP 2: processPayload - Native Flow // ============================================ function test_processTrigger_Native_Success() public { @@ -499,7 +499,7 @@ contract MessageSwitchboardTest is Test, Utils { } // ============================================ - // CRITICAL TESTS - GROUP 3: processTrigger - Sponsored Flow + // CRITICAL TESTS - GROUP 3: processPayload - Sponsored Flow // ============================================ function test_processTrigger_Sponsored_Success() public { @@ -945,7 +945,7 @@ contract MessageSwitchboardTest is Test, Utils { uint256 additionalFees = 0.01 ether; uint256 initialFees = MIN_FEES + 0.001 ether; - // First create a payload via processTrigger + // First create a payload via processPayload bytes memory overrides = abi.encode( uint8(1), // version DST_CHAIN, // dstChainSlug @@ -994,7 +994,7 @@ contract MessageSwitchboardTest is Test, Utils { uint256 newMaxFees = 0.05 ether; bytes memory feesData = abi.encode(uint8(2), newMaxFees); // Sponsored fees type + new maxFees - // First create a sponsored payload via processTrigger + // First create a sponsored payload via processPayload bytes memory overrides = abi.encode( uint8(2), // version DST_CHAIN, // dstChainSlug @@ -1106,8 +1106,8 @@ contract MessageSwitchboardTest is Test, Utils { * * Test Coverage: * - Sibling management (setSiblingConfig, registerSibling) - * - processTrigger Native flow (version 1) with fee handling - * - processTrigger Sponsored flow (version 2) with approval checks + * - processPayload Native flow (version 1) with fee handling + * - processPayload Sponsored flow (version 2) with approval checks * - Version handling and decodeOverrides validation * - Enhanced attest with target verification * - Sponsor approvals and revocations (single and batch) From 4c55c6a11697749f4404cdd47e5bf6ca4f91bbda Mon Sep 17 00:00:00 2001 From: akash Date: Wed, 5 Nov 2025 17:14:39 +0530 Subject: [PATCH 02/10] feat: added payloadId tests, fixed old tests --- test/SetupTest.t.sol | 45 ++- test/SocketPayloadIdVerification.t.sol | 382 ++++++++++++++++++++++ test/switchboard/MessageSwitchboard.t.sol | 294 +++++++++-------- 3 files changed, 552 insertions(+), 169 deletions(-) create mode 100644 test/SocketPayloadIdVerification.t.sol diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 17edeaaa..84118bab 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -132,12 +132,14 @@ contract DeploySetup is SetupStore { arbConfig.messageSwitchboard.setSiblingConfig( optChainSlug, toBytes32Format(address(optConfig.socket)), - toBytes32Format(address(optConfig.messageSwitchboard)) + toBytes32Format(address(optConfig.messageSwitchboard)), + uint32(optConfig.messageSwitchboard.switchboardId()) ); optConfig.messageSwitchboard.setSiblingConfig( arbChainSlug, toBytes32Format(address(arbConfig.socket)), - toBytes32Format(address(arbConfig.messageSwitchboard)) + toBytes32Format(address(arbConfig.messageSwitchboard)), + uint32(arbConfig.messageSwitchboard.switchboardId()) ); vm.stopPrank(); _connectCorePlugs(); @@ -667,7 +669,7 @@ contract WatcherSetup is FeesSetup { value: digestParams.value, payload: digestParams.payload, target: fromBytes32Format(digestParams.target), - payloadPointer: uint160(payloadParams.payloadPointer), + payloadId: payloadParams.payloadId, prevBatchDigestHash: digestParams.prevBatchDigestHash, source: digestParams.source, extraData: digestParams.extraData @@ -830,19 +832,17 @@ contract MessageSwitchboardSetup is DeploySetup { SocketContracts memory srcSocketConfig_, SocketContracts memory dstSocketConfig_, bytes memory payload_ - ) internal view returns (uint160 payloadPointer, DigestParams memory digestParams) { - bytes32 triggerId = srcPlug_.getNextTriggerId(srcSocketConfig_.chainSlug); + ) internal view returns (bytes32 payloadId, DigestParams memory digestParams) { uint40 payloadCounter = srcSocketConfig_.messageSwitchboard.payloadCounter(); - - payloadPointer = - (uint160(srcSocketConfig_.chainSlug) << 120) | - (uint160(uint64(uint256(triggerId))) << 80) | - payloadCounter; - - bytes32 payloadId = createPayloadId( - payloadPointer, - dstSocketConfig_.messageSwitchboard.switchboardId(), - dstSocketConfig_.chainSlug + + // Calculate payload ID using new structure + // Message payload: origin = (srcChainSlug, srcSwitchboardId), verification = (dstChainSlug, dstSwitchboardId) + payloadId = createPayloadId( + srcSocketConfig_.chainSlug, // origin chain slug + uint32(srcSocketConfig_.messageSwitchboard.switchboardId()), // origin switchboard id + dstSocketConfig_.chainSlug, // verification chain slug + uint32(dstSocketConfig_.messageSwitchboard.switchboardId()), // verification switchboard id + uint64(payloadCounter) // pointer (counter) ); digestParams = _createDigestParams( @@ -851,17 +851,16 @@ contract MessageSwitchboardSetup is DeploySetup { address(dstPlug_), address(dstSocketConfig_.socket), payloadId, - triggerId, payload_ ); } function _executeOnDestination( DigestParams memory digestParams_, - uint160 payloadPointer_ + bytes32 payloadId_ ) internal { _attestPayload(digestParams_); - _execute(digestParams_, payloadPointer_); + _execute(digestParams_, payloadId_); } // Helper function to attest a payload @@ -884,10 +883,8 @@ contract MessageSwitchboardSetup is DeploySetup { address dstPlug_, address dstSocket_, bytes32 payloadId_, - bytes32 triggerId_, bytes memory payload_ ) internal view returns (DigestParams memory digestParams) { - bytes memory extraData = abi.encode(srcChainSlug_, toBytes32Format(srcPlug_)); digestParams = DigestParams({ socket: toBytes32Format(dstSocket_), transmitter: bytes32(0), @@ -899,8 +896,8 @@ contract MessageSwitchboardSetup is DeploySetup { payload: payload_, target: toBytes32Format(dstPlug_), source: abi.encode(srcChainSlug_, toBytes32Format(srcPlug_)), - prevBatchDigestHash: triggerId_, - extraData: extraData + prevBatchDigestHash: bytes32(0), // No longer using triggerId + extraData: "0x" // Contract now sets extraData to empty }); } @@ -925,7 +922,7 @@ contract MessageSwitchboardSetup is DeploySetup { } // Helper function to execute on destination chain - function _execute(DigestParams memory digestParams_, uint160 payloadPointer_) internal { + function _execute(DigestParams memory digestParams_, bytes32 payloadId_) internal { // this is a signature for the socket batcher (only used for EVM) ExecuteParams memory executeParams = ExecuteParams({ callType: digestParams_.callType, @@ -934,7 +931,7 @@ contract MessageSwitchboardSetup is DeploySetup { value: digestParams_.value, payload: digestParams_.payload, target: fromBytes32Format(digestParams_.target), - payloadPointer: payloadPointer_, + payloadId: payloadId_, prevBatchDigestHash: digestParams_.prevBatchDigestHash, source: digestParams_.source, extraData: digestParams_.extraData diff --git a/test/SocketPayloadIdVerification.t.sol b/test/SocketPayloadIdVerification.t.sol new file mode 100644 index 00000000..6572722a --- /dev/null +++ b/test/SocketPayloadIdVerification.t.sol @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; + +import "forge-std/Test.sol"; +import "../contracts/protocol/Socket.sol"; +import "../contracts/protocol/switchboard/FastSwitchboard.sol"; +import "../contracts/protocol/switchboard/MessageSwitchboard.sol"; +import "../contracts/utils/common/IdUtils.sol"; +import "../contracts/utils/common/Structs.sol"; +import "../contracts/utils/common/Constants.sol"; +import "../contracts/utils/common/Converters.sol"; +import "./mocks/MockPlug.sol"; + +/** + * @title SocketPayloadIdVerificationTest + * @dev Tests for payload ID verification in Socket.execute() and FastSwitchboard payload creation + */ +contract SocketPayloadIdVerificationTest is Test { + // Event declarations + event PayloadRequested( + bytes32 indexed payloadId, + address indexed plug, + uint64 indexed switchboardId, + bytes overrides, + bytes payload + ); + // Test constants + uint32 constant CHAIN_SLUG = 1; + uint32 constant OTHER_CHAIN_SLUG = 2; + uint32 constant EVMX_CHAIN_SLUG = 100; + uint32 constant WATCHER_ID = 1; + + address owner = address(0x1000); + address plugOwner = address(0x2000); + + Socket socket; + FastSwitchboard fastSwitchboard; + MessageSwitchboard messageSwitchboard; + MockPlug mockPlug; + + function setUp() public { + // Deploy Socket + socket = new Socket(CHAIN_SLUG, owner, "1.0.0"); + + // Deploy switchboards + fastSwitchboard = new FastSwitchboard(CHAIN_SLUG, socket, owner); + messageSwitchboard = new MessageSwitchboard(CHAIN_SLUG, socket, owner); + + // Register switchboards + vm.startPrank(owner); + fastSwitchboard.registerSwitchboard(); + messageSwitchboard.registerSwitchboard(); + vm.stopPrank(); + + // Create a mock plug + uint64 switchboardId = fastSwitchboard.switchboardId(); + mockPlug = new MockPlug(address(socket), switchboardId); + + // Connect plug to socket + vm.prank(plugOwner); + mockPlug.connectToSocket(address(socket), switchboardId); + } + + // ============================================ + // TESTS - Socket.execute() Payload ID Verification + // ============================================ + + function test_Execute_VerifiesPayloadId_CorrectDestination() public { + // Create a valid payload ID for this chain and switchboard + uint64 switchboardId = fastSwitchboard.switchboardId(); + bytes32 payloadId = createPayloadId( + OTHER_CHAIN_SLUG, // origin chain slug + 100, // origin switchboard id + CHAIN_SLUG, // verification chain slug (matches socket) + uint32(switchboardId), // verification switchboard id (matches plug's switchboard) + 12345 // pointer + ); + + // Create ExecuteParams with valid payload ID + ExecuteParams memory executeParams = ExecuteParams({ + callType: WRITE, + payloadId: payloadId, + deadline: block.timestamp + 3600, + gasLimit: 100000, + value: 0, + payload: abi.encode("test"), + target: address(mockPlug), + prevBatchDigestHash: bytes32(0), + source: abi.encode(OTHER_CHAIN_SLUG, toBytes32Format(address(0x1234))), + extraData: "0x" + }); + + TransmissionParams memory transmissionParams = TransmissionParams({ + socketFees: 0, + refundAddress: address(0), + extraData: bytes(""), + transmitterProof: bytes("") + }); + + // Verify that payload ID check passes (doesn't revert with InvalidVerificationChainSlug or InvalidVerificationSwitchboardId) + // The execution should proceed past payload ID verification to the switchboard's allowPayload check. + // It will fail with InvalidSource because the source doesn't match the plug's appGatewayId. + // This confirms payload ID verification passed - we reached allowPayload which comes after payload ID check. + vm.expectRevert(FastSwitchboard.InvalidSource.selector); + socket.execute{value: 0}(executeParams, transmissionParams); + + // If we get InvalidSource, it means: + // 1. ✅ Payload ID verification passed (didn't revert with InvalidVerificationChainSlug/InvalidVerificationSwitchboardId) + // 2. ✅ We reached the switchboard's allowPayload check (comes after payload ID verification) + // 3. ✅ allowPayload failed with InvalidSource (expected, since source doesn't match plug config) + } + + function test_Execute_WrongChainSlug_Reverts() public { + // Create payload ID with wrong verification chain slug + uint64 switchboardId = fastSwitchboard.switchboardId(); + bytes32 payloadId = createPayloadId( + OTHER_CHAIN_SLUG, + 100, + OTHER_CHAIN_SLUG, // Wrong chain slug (doesn't match socket's chainSlug) + uint32(switchboardId), + 12345 + ); + + ExecuteParams memory executeParams = ExecuteParams({ + callType: WRITE, + payloadId: payloadId, + deadline: block.timestamp + 3600, + gasLimit: 100000, + value: 0, + payload: abi.encode("test"), + target: address(mockPlug), + prevBatchDigestHash: bytes32(0), + source: abi.encode(OTHER_CHAIN_SLUG, toBytes32Format(address(0x1234))), + extraData: "0x" + }); + + TransmissionParams memory transmissionParams = TransmissionParams({ + socketFees: 0, + refundAddress: address(0), + extraData: bytes(""), + transmitterProof: bytes("") + }); + + vm.expectRevert(Socket.InvalidVerificationChainSlug.selector); + socket.execute{value: 0}(executeParams, transmissionParams); + } + + function test_Execute_WrongSwitchboardId_Reverts() public { + // Create payload ID with wrong verification switchboard ID + bytes32 payloadId = createPayloadId( + OTHER_CHAIN_SLUG, + 100, + CHAIN_SLUG, // Correct chain slug + 999, // Wrong switchboard ID (doesn't match plug's switchboard) + 12345 + ); + + ExecuteParams memory executeParams = ExecuteParams({ + callType: WRITE, + payloadId: payloadId, + deadline: block.timestamp + 3600, + gasLimit: 100000, + value: 0, + payload: abi.encode("test"), + target: address(mockPlug), + prevBatchDigestHash: bytes32(0), + source: abi.encode(OTHER_CHAIN_SLUG, toBytes32Format(address(0x1234))), + extraData: "0x" + }); + + TransmissionParams memory transmissionParams = TransmissionParams({ + socketFees: 0, + refundAddress: address(0), + extraData: bytes(""), + transmitterProof: bytes("") + }); + + vm.expectRevert(Socket.InvalidVerificationSwitchboardId.selector); + socket.execute{value: 0}(executeParams, transmissionParams); + } + + // ============================================ + // TESTS - FastSwitchboard Payload Creation + // ============================================ + + function test_FastSwitchboard_ProcessPayload_CreatesTriggerPayloadId() public { + // Set EVMX config + vm.prank(owner); + fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); + + // Create a mock plug + uint64 switchboardId = fastSwitchboard.switchboardId(); + MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); + vm.prank(plugOwner); + triggerPlug.connectToSocket(address(socket), switchboardId); + + bytes memory payload = abi.encode("test trigger"); + bytes memory overrides = bytes(""); + + // Get counter before + uint64 counterBefore = fastSwitchboard.triggerPayloadCounter(); + + // Call processPayload (must be called by socket) + vm.prank(address(socket)); + bytes32 payloadId = fastSwitchboard.processPayload{value: 0}( + address(triggerPlug), + payload, + overrides + ); + + // Verify counter incremented + assertEq(fastSwitchboard.triggerPayloadCounter(), counterBefore + 1); + + // Verify payload ID structure + ( + uint32 originChainSlug, + uint32 originId, + uint32 verificationChainSlug, + uint32 verificationId, + uint64 pointer + ) = decodePayloadId(payloadId); + + assertEq(originChainSlug, CHAIN_SLUG, "Origin chain slug should match source"); + assertEq(originId, uint32(switchboardId), "Origin ID should match switchboard ID"); + assertEq(verificationChainSlug, EVMX_CHAIN_SLUG, "Verification chain slug should be EVMX"); + assertEq(verificationId, WATCHER_ID, "Verification ID should be watcher ID"); + assertEq(pointer, counterBefore, "Pointer should match counter before increment"); + } + + function test_FastSwitchboard_ProcessPayload_EmitsPayloadRequested() public { + // Set EVMX config + vm.prank(owner); + fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); + + // Create a mock plug + uint64 switchboardId = fastSwitchboard.switchboardId(); + MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); + vm.prank(plugOwner); + triggerPlug.connectToSocket(address(socket), switchboardId); + + bytes memory payload = abi.encode("test trigger"); + bytes memory overrides = bytes(""); + + // Get counter before to calculate expected payload ID + uint64 counterBefore = fastSwitchboard.triggerPayloadCounter(); + bytes32 expectedPayloadId = createPayloadId( + CHAIN_SLUG, + uint32(switchboardId), + EVMX_CHAIN_SLUG, + WATCHER_ID, + counterBefore + ); + + // Expect PayloadRequested event + vm.expectEmit(true, true, true, false); + emit PayloadRequested( + expectedPayloadId, + address(triggerPlug), + switchboardId, + overrides, + payload + ); + + // Call processPayload + vm.prank(address(socket)); + fastSwitchboard.processPayload{value: 0}( + address(triggerPlug), + payload, + overrides + ); + } + + function test_FastSwitchboard_ProcessPayload_EvmxConfigNotSet_Reverts() public { + // Don't set EVMX config - should revert + uint64 switchboardId = fastSwitchboard.switchboardId(); + MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); + vm.prank(plugOwner); + triggerPlug.connectToSocket(address(socket), switchboardId); + + bytes memory payload = abi.encode("test trigger"); + bytes memory overrides = bytes(""); + + vm.prank(address(socket)); + vm.expectRevert(FastSwitchboard.EvmxConfigNotSet.selector); + fastSwitchboard.processPayload{value: 0}( + address(triggerPlug), + payload, + overrides + ); + } + + function test_FastSwitchboard_ProcessPayload_CounterIncrements() public { + // Set EVMX config + vm.prank(owner); + fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); + + uint64 switchboardId = fastSwitchboard.switchboardId(); + MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); + vm.prank(plugOwner); + triggerPlug.connectToSocket(address(socket), switchboardId); + + bytes memory payload = abi.encode("test"); + bytes memory overrides = bytes(""); + + uint64 counter1 = fastSwitchboard.triggerPayloadCounter(); + + vm.prank(address(socket)); + fastSwitchboard.processPayload{value: 0}(address(triggerPlug), payload, overrides); + + uint64 counter2 = fastSwitchboard.triggerPayloadCounter(); + + vm.prank(address(socket)); + fastSwitchboard.processPayload{value: 0}(address(triggerPlug), payload, overrides); + + uint64 counter3 = fastSwitchboard.triggerPayloadCounter(); + + assertEq(counter2, counter1 + 1, "Counter should increment"); + assertEq(counter3, counter2 + 1, "Counter should increment again"); + } + + function test_FastSwitchboard_ProcessPayload_MultiplePayloads_UniqueIds() public { + // Set EVMX config + vm.prank(owner); + fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); + + uint64 switchboardId = fastSwitchboard.switchboardId(); + MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); + vm.prank(plugOwner); + triggerPlug.connectToSocket(address(socket), switchboardId); + + bytes memory payload = abi.encode("test"); + bytes memory overrides = bytes(""); + + vm.prank(address(socket)); + bytes32 payloadId1 = fastSwitchboard.processPayload{value: 0}(address(triggerPlug), payload, overrides); + + vm.prank(address(socket)); + bytes32 payloadId2 = fastSwitchboard.processPayload{value: 0}(address(triggerPlug), payload, overrides); + + vm.prank(address(socket)); + bytes32 payloadId3 = fastSwitchboard.processPayload{value: 0}(address(triggerPlug), payload, overrides); + + // All should be unique + assertNotEq(payloadId1, payloadId2, "Payload IDs should be unique"); + assertNotEq(payloadId2, payloadId3, "Payload IDs should be unique"); + assertNotEq(payloadId1, payloadId3, "Payload IDs should be unique"); + + // Verify they only differ in pointer + (uint32 origin1, uint32 originId1, uint32 verif1, uint32 verifId1, uint64 pointer1) = decodePayloadId(payloadId1); + (uint32 origin2, uint32 originId2, uint32 verif2, uint32 verifId2, uint64 pointer2) = decodePayloadId(payloadId2); + (uint32 origin3, uint32 originId3, uint32 verif3, uint32 verifId3, uint64 pointer3) = decodePayloadId(payloadId3); + + assertEq(origin1, origin2); + assertEq(origin1, origin3); + assertEq(originId1, originId2); + assertEq(originId1, originId3); + assertEq(verif1, verif2); + assertEq(verif1, verif3); + assertEq(verifId1, verifId2); + assertEq(verifId1, verifId3); + + // Only pointers should differ + assertEq(pointer2, pointer1 + 1); + assertEq(pointer3, pointer2 + 1); + } + + function test_FastSwitchboard_SetEvmxConfig_OnlyOwner() public { + // Non-owner should not be able to set EVMX config + vm.prank(address(0x9999)); + vm.expectRevert(); + fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); + + // Owner should be able to set + vm.prank(owner); + fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); + + // Verify it was set + assertEq(fastSwitchboard.evmxChainSlug(), EVMX_CHAIN_SLUG); + assertEq(fastSwitchboard.watcherId(), WATCHER_ID); + } +} + diff --git a/test/switchboard/MessageSwitchboard.t.sol b/test/switchboard/MessageSwitchboard.t.sol index e7664136..19e0571b 100644 --- a/test/switchboard/MessageSwitchboard.t.sol +++ b/test/switchboard/MessageSwitchboard.t.sol @@ -57,6 +57,13 @@ contract MessageSwitchboardTest is Test, Utils { event MinMsgValueFeesSet(uint32 indexed chainSlug, uint256 minFees, address indexed updater); event SponsoredFeesIncreased(bytes32 indexed payloadId, uint256 newMaxFees, address indexed plug); event PlugConfigUpdated(address indexed plug, uint32 indexed chainSlug, bytes32 siblingPlug); + event PayloadRequested( + bytes32 indexed payloadId, + address indexed plug, + uint64 indexed switchboardId, + bytes overrides, + bytes payload + ); function setUp() public { // Deploy actual Socket contract @@ -87,40 +94,6 @@ contract MessageSwitchboardTest is Test, Utils { return vm.addr(0x1111111111111111111111111111111111111111111111111111111111111111); } - // Helper to create payload ID (matches createPayloadId from IdUtils) - function createTestPayloadId( - uint256 payloadPointer_, - uint64 switchboardId_, - uint32 chainSlug_ - ) public pure returns (bytes32) { - return bytes32((uint256(chainSlug_) << 224) | (uint256(switchboardId_) << 160) | payloadPointer_); - } - - /** - * @dev Calculate triggerId based on Socket's _encodeTriggerId logic - * @param socketAddress The socket contract address - * @param triggerCounter The current trigger counter value (before increment) - * @return triggerId The calculated trigger ID - */ - function calculateTriggerId(address socketAddress, uint64 triggerCounter) public pure returns (bytes32) { - uint256 triggerPrefix = (uint256(SRC_CHAIN) << 224) | (uint256(uint160(socketAddress)) << 64); - return bytes32(triggerPrefix | triggerCounter); - } - - /** - * @dev Calculate payloadId based on MessageSwitchboard's _createDigestAndPayloadId logic - * @param triggerId The trigger ID from socket - * @param payloadCounter The current payload counter value (before increment) - * @param dstChainSlug The destination chain slug - * @return payloadId The calculated payload ID - */ - function calculatePayloadId(bytes32 triggerId, uint40 payloadCounter, uint32 dstChainSlug) public view returns (bytes32) { - uint160 payloadPointer = (uint160(SRC_CHAIN) << 120) | - (uint160(uint64(uint256(triggerId))) << 80) | - payloadCounter; - - return createTestPayloadId(payloadPointer, messageSwitchboard.switchboardId(), dstChainSlug); - } /** * @dev Calculate digest based on MessageSwitchboard's _createDigest logic @@ -164,10 +137,11 @@ contract MessageSwitchboardTest is Test, Utils { // Setup sibling config BEFORE registering siblings bytes32 siblingSocket = toBytes32Format(address(0x1234)); bytes32 siblingSwitchboard = toBytes32Format(address(0x5678)); + uint32 siblingSwitchboardId = 1; // Mock switchboard ID for destination vm.startPrank(owner); - messageSwitchboard.setSiblingConfig(DST_CHAIN, siblingSocket, siblingSwitchboard); + messageSwitchboard.setSiblingConfig(DST_CHAIN, siblingSocket, siblingSwitchboard, siblingSwitchboardId); // Also set config for reverse direction - messageSwitchboard.setSiblingConfig(SRC_CHAIN, toBytes32Format(address(socket)), toBytes32Format(address(messageSwitchboard))); + messageSwitchboard.setSiblingConfig(SRC_CHAIN, toBytes32Format(address(socket)), toBytes32Format(address(messageSwitchboard)), uint32(messageSwitchboard.switchboardId())); vm.stopPrank(); } @@ -205,15 +179,16 @@ contract MessageSwitchboardTest is Test, Utils { bytes memory payload = abi.encode(payloadData); - // Get counters before the call - uint64 triggerCounterBefore = socket.triggerCounter(); + // Get counter before the call uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); - // Use MockPlug to trigger Socket + // Use MockPlug to trigger Socket - this returns the payloadId vm.deal(address(srcPlug), 10 ether); - srcPlug.triggerSocket{value: msgValue}(payload); + payloadId = srcPlug.triggerSocket{value: msgValue}(payload); - return _getLastPayloadId(triggerCounterBefore, payloadCounterBefore); + // Verify payloadId matches expected + bytes32 expectedPayloadId = _getLastPayloadId(payloadCounterBefore); + assertEq(payloadId, expectedPayloadId, "PayloadId mismatch"); } /** @@ -237,31 +212,29 @@ contract MessageSwitchboardTest is Test, Utils { bytes memory payload = abi.encode(payloadData); - // Get counters before the call - uint64 triggerCounterBefore = socket.triggerCounter(); + // Get counter before the call uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); - // Use MockPlug to trigger Socket - srcPlug.triggerSocket(payload); + // Use MockPlug to trigger Socket - this returns the payloadId + payloadId = srcPlug.triggerSocket(payload); - return _getLastPayloadId(triggerCounterBefore, payloadCounterBefore); + // Verify payloadId matches expected + bytes32 expectedPayloadId = _getLastPayloadId(payloadCounterBefore); + assertEq(payloadId, expectedPayloadId, "PayloadId mismatch"); } /** * @dev Create DigestParams for attestation with flexible parameters * @param payloadId The payload ID - * @param triggerId The trigger ID * @param payload The payload data - * @param target_ The target address (defaults to dstPlug) * @param gasLimit_ The gas limit (defaults to 100000) * @param value_ The value (defaults to 0) * @return digestParams The constructed DigestParams */ function _createDigestParams( bytes32 payloadId, - bytes32 triggerId, bytes memory payload, - address target_, + address, // Unused parameter, kept for compatibility uint256 gasLimit_, uint256 value_ ) internal view returns (DigestParams memory) { @@ -280,31 +253,37 @@ contract MessageSwitchboardTest is Test, Utils { payload: payload, target: siblingPlug, source: abi.encode(SRC_CHAIN, toBytes32Format(address(srcPlug))), - prevBatchDigestHash: triggerId, - extraData: abi.encode(SRC_CHAIN, toBytes32Format(address(srcPlug))) + prevBatchDigestHash: bytes32(0), // No longer using triggerId + extraData: "0x" // Contract now sets extraData to empty }); } /** * @dev Create DigestParams for attestation (simplified version with defaults) * @param payloadId The payload ID - * @param triggerId The trigger ID * @param payload The payload data * @return digestParams The constructed DigestParams */ - function _createDigestParams(bytes32 payloadId, bytes32 triggerId, bytes memory payload) internal view returns (DigestParams memory) { - return _createDigestParams(payloadId, triggerId, payload, address(dstPlug), 100000, 0); + function _createDigestParams(bytes32 payloadId, bytes memory payload) internal view returns (DigestParams memory) { + return _createDigestParams(payloadId, payload, address(dstPlug), 100000, 0); } /** - * @dev Get the last created payload ID by reading counters before trigger - * @param triggerCounterBefore The trigger counter before the call + * @dev Get the last created payload ID by reading payload counter before call * @param payloadCounterBefore The payload counter before the call * @return payloadId The calculated payload ID */ - function _getLastPayloadId(uint64 triggerCounterBefore, uint40 payloadCounterBefore) internal view returns (bytes32) { - bytes32 triggerId = calculateTriggerId(address(socket), triggerCounterBefore); - return calculatePayloadId(triggerId, payloadCounterBefore, DST_CHAIN); + function _getLastPayloadId(uint40 payloadCounterBefore) internal view returns (bytes32) { + // Calculate payload ID using new structure + // Message payload: origin = (srcChainSlug, srcSwitchboardId), verification = (dstChainSlug, dstSwitchboardId) + uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); + return createPayloadId( + SRC_CHAIN, + uint32(messageSwitchboard.switchboardId()), + DST_CHAIN, + dstSwitchboardId, + uint64(payloadCounterBefore) + ); } /** @@ -357,14 +336,17 @@ contract MessageSwitchboardTest is Test, Utils { bytes32 siblingSocket = toBytes32Format(address(0x1234)); bytes32 siblingSwitchboard = toBytes32Format(address(0x5678)); + uint32 siblingSwitchboardId = 1; // Mock switchboard ID + vm.expectEmit(true, true, true, false); emit SiblingConfigSet(DST_CHAIN, siblingSocket, siblingSwitchboard); vm.prank(owner); - messageSwitchboard.setSiblingConfig(DST_CHAIN, siblingSocket, siblingSwitchboard); + messageSwitchboard.setSiblingConfig(DST_CHAIN, siblingSocket, siblingSwitchboard, siblingSwitchboardId); assertEq(messageSwitchboard.siblingSockets(DST_CHAIN), siblingSocket); assertEq(messageSwitchboard.siblingSwitchboards(DST_CHAIN), siblingSwitchboard); + assertEq(messageSwitchboard.siblingSwitchboardIds(DST_CHAIN), siblingSwitchboardId); } function test_setSiblingConfig_NotOwner_Reverts() public { @@ -373,7 +355,8 @@ contract MessageSwitchboardTest is Test, Utils { messageSwitchboard.setSiblingConfig( DST_CHAIN, toBytes32Format(address(0x1234)), - toBytes32Format(address(0x5678)) + toBytes32Format(address(0x5678)), + 1 // switchboardId ); } @@ -419,40 +402,61 @@ contract MessageSwitchboardTest is Test, Utils { bytes memory payload = abi.encode("test data"); uint256 msgValue = MIN_FEES + 0.001 ether; - // Get counters before the call - uint64 triggerCounterBefore = socket.triggerCounter(); + // Get counter before the call uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); - // Calculate expected values - bytes32 expectedTriggerId = calculateTriggerId(address(socket), triggerCounterBefore); - bytes32 expectedPayloadId = calculatePayloadId(expectedTriggerId, payloadCounterBefore, DST_CHAIN); - - // Create digest params for the expected event - DigestParams memory expectedDigestParams = _createDigestParams( - expectedPayloadId, - expectedTriggerId, - payload + // Calculate expected payload ID using new structure + uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); + bytes32 expectedPayloadId = createPayloadId( + SRC_CHAIN, + uint32(messageSwitchboard.switchboardId()), + DST_CHAIN, + dstSwitchboardId, + uint64(payloadCounterBefore) ); - bytes32 expectedDigest = calculateDigest(expectedDigestParams); - // Expect the event with calculated values + // Expect MessageOutbound event first (contract emits this before PayloadRequested) + // Only check indexed fields (payloadId, dstChainSlug) - struct fields may differ due to deadline timing vm.expectEmit(true, true, false, false); emit MessageOutbound( expectedPayloadId, DST_CHAIN, - expectedDigest, - expectedDigestParams, + bytes32(0), // digest - not checked (might differ due to deadline timing) + DigestParams({ // Only structure matters, values not checked + socket: bytes32(0), + transmitter: bytes32(0), + payloadId: bytes32(0), + deadline: 0, + callType: bytes4(0), + gasLimit: 0, + value: 0, + payload: "", + target: bytes32(0), + source: "", + prevBatchDigestHash: bytes32(0), + extraData: "" + }), false, // isSponsored msgValue, 0, address(0) ); + // Expect PayloadRequested event second + vm.expectEmit(true, true, true, false); + emit PayloadRequested( + expectedPayloadId, + address(srcPlug), + messageSwitchboard.switchboardId(), + overrides, + payload + ); + vm.deal(address(srcPlug), 10 ether); - bytes32 actualTriggerId = srcPlug.triggerSocket{value: msgValue}(payload); + bytes32 actualPayloadId = srcPlug.triggerSocket{value: msgValue}(payload); - // Verify trigger ID matches - assertEq(actualTriggerId, expectedTriggerId); + // Verify payload ID matches + assertEq(actualPayloadId, expectedPayloadId); // Verify payload counter increased assertEq(messageSwitchboard.payloadCounter(), payloadCounterBefore + 1); @@ -521,17 +525,23 @@ contract MessageSwitchboardTest is Test, Utils { bytes memory payload = abi.encode("sponsored test"); - // Get counters before the call - uint64 triggerCounterBefore = socket.triggerCounter(); + // Get counter before the call uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); - // Calculate expected values - bytes32 expectedTriggerId = calculateTriggerId(address(socket), triggerCounterBefore); - bytes32 expectedPayloadId = calculatePayloadId(expectedTriggerId, payloadCounterBefore, DST_CHAIN); + // Calculate expected payload ID using new structure + uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); + bytes32 expectedPayloadId = createPayloadId( + SRC_CHAIN, + uint32(messageSwitchboard.switchboardId()), + DST_CHAIN, + dstSwitchboardId, + uint64(payloadCounterBefore) + ); // Set overrides on the plug srcPlug.setOverrides(overrides); + // Expect MessageOutbound event first (contract emits this before PayloadRequested) // Only check indexed fields (payloadId, dstChainSlug, sponsor) - skip data fields for struct comparison vm.expectEmit(true, true, false, false); emit MessageOutbound( @@ -558,11 +568,21 @@ contract MessageSwitchboardTest is Test, Utils { sponsor ); + // Expect PayloadRequested event second + vm.expectEmit(true, true, true, false); + emit PayloadRequested( + expectedPayloadId, + address(srcPlug), + messageSwitchboard.switchboardId(), + overrides, + payload + ); + vm.prank(address(srcPlug)); - bytes32 actualTriggerId = srcPlug.triggerSocket(payload); + bytes32 actualPayloadId = srcPlug.triggerSocket(payload); - // Verify trigger ID matches - assertEq(actualTriggerId, expectedTriggerId); + // Verify payload ID matches + assertEq(actualPayloadId, expectedPayloadId); // Verify sponsored fees were stored (uint256 maxFees,) = messageSwitchboard.sponsoredPayloadFees(expectedPayloadId); @@ -611,11 +631,10 @@ contract MessageSwitchboardTest is Test, Utils { _setupSiblingConfig(); // Create digest params (using any valid values since we're just testing attestation) - bytes32 triggerId = bytes32(uint256(0x1234)); bytes memory payload = abi.encode("test"); bytes32 payloadId = bytes32(uint256(0x5678)); - DigestParams memory digestParams = _createDigestParams(payloadId, triggerId, payload); + DigestParams memory digestParams = _createDigestParams(payloadId, payload); // Calculate the actual digest from digestParams (as done in MessageSwitchboard._createDigest) bytes32 digest = calculateDigest(digestParams); @@ -634,51 +653,15 @@ contract MessageSwitchboardTest is Test, Utils { assertTrue(messageSwitchboard.isAttested(digest)); } - function test_attest_InvalidTarget_Reverts() public { - // Setup sibling config - _setupSiblingConfig(); - - // Create digest with wrong target (address(0x9999) is not registered as a sibling plug) - bytes32 triggerId = bytes32(uint256(0x1234)); - bytes memory payload = abi.encode("test"); - bytes32 payloadId = bytes32(uint256(0x5678)); - - // Create digest params with invalid target - bytes32 siblingSocket = messageSwitchboard.siblingSockets(DST_CHAIN); - DigestParams memory digestParams = DigestParams({ - socket: siblingSocket, - transmitter: bytes32(0), - payloadId: payloadId, - deadline: block.timestamp + 3600, - callType: WRITE, - gasLimit: 100000, - value: 0, - payload: payload, - target: toBytes32Format(address(0x9999)), // Wrong target - not registered - source: abi.encode(SRC_CHAIN, toBytes32Format(address(srcPlug))), - prevBatchDigestHash: triggerId, - extraData: abi.encode(SRC_CHAIN, toBytes32Format(address(srcPlug))) - }); - - // Calculate the actual digest from digestParams (signature needs valid digest first) - bytes32 digest = calculateDigest(digestParams); - - // Create watcher signature with correct digest (this will pass watcher check) - bytes32 signatureDigest = keccak256(abi.encodePacked(toBytes32Format(address(messageSwitchboard)), SRC_CHAIN, digest)); - bytes memory signature = createSignature(signatureDigest, watcherPrivateKey); - - vm.prank(getWatcherAddress()); - vm.expectRevert(MessageSwitchboard.InvalidTargetVerification.selector); - messageSwitchboard.attest(digestParams, signature); - } + // NOTE: test_attest_InvalidTarget_Reverts() was removed because the attest() function + // no longer validates the target - target validation is now done during execution function test_attest_InvalidWatcher_Reverts() public { // Setup sibling config _setupSiblingConfig(); bytes32 payloadId = bytes32(uint256(0x5678)); - bytes32 triggerId = bytes32(uint256(0x1234)); - DigestParams memory digestParams = _createDigestParams(payloadId, triggerId, abi.encode("test")); + DigestParams memory digestParams = _createDigestParams(payloadId, abi.encode("test")); // Calculate the actual digest from digestParams bytes32 digest = calculateDigest(digestParams); @@ -697,8 +680,7 @@ contract MessageSwitchboardTest is Test, Utils { _setupSiblingConfig(); bytes32 payloadId = bytes32(uint256(0x5678)); - bytes32 triggerId = bytes32(uint256(0x1234)); - DigestParams memory digestParams = _createDigestParams(payloadId, triggerId, abi.encode("test")); + DigestParams memory digestParams = _createDigestParams(payloadId, abi.encode("test")); // Calculate the actual digest from digestParams bytes32 digest = calculateDigest(digestParams); @@ -960,16 +942,23 @@ contract MessageSwitchboardTest is Test, Utils { // Set overrides on the plug srcPlug.setOverrides(overrides); - // Get counters before creating payload - uint64 triggerCounterBefore = socket.triggerCounter(); + // Get counter before creating payload uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); vm.deal(address(srcPlug), 1 ether); vm.prank(address(srcPlug)); - bytes32 actualTriggerId = srcPlug.triggerSocket{value: initialFees}(abi.encode("payload")); + bytes32 payloadId = srcPlug.triggerSocket{value: initialFees}(abi.encode("payload")); - // Calculate the actual payloadId - bytes32 payloadId = calculatePayloadId(actualTriggerId, payloadCounterBefore, DST_CHAIN); + // Verify payloadId matches expected structure + uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); + bytes32 expectedPayloadId = createPayloadId( + SRC_CHAIN, + uint32(messageSwitchboard.switchboardId()), + DST_CHAIN, + dstSwitchboardId, + uint64(payloadCounterBefore) + ); + assertEq(payloadId, expectedPayloadId, "PayloadId mismatch"); // Verify initial fees were stored (uint256 nativeFeesBefore,,,,) = messageSwitchboard.payloadFees(payloadId); @@ -1007,15 +996,22 @@ contract MessageSwitchboardTest is Test, Utils { // Set overrides on the plug srcPlug.setOverrides(overrides); - // Get counters before creating payload - uint64 triggerCounterBefore = socket.triggerCounter(); + // Get counter before creating payload uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); vm.prank(address(srcPlug)); - bytes32 actualTriggerId = srcPlug.triggerSocket(abi.encode("payload")); + bytes32 payloadId = srcPlug.triggerSocket(abi.encode("payload")); - // Calculate the actual payloadId - bytes32 payloadId = calculatePayloadId(actualTriggerId, payloadCounterBefore, DST_CHAIN); + // Verify payloadId matches expected structure + uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); + bytes32 expectedPayloadId = createPayloadId( + SRC_CHAIN, + uint32(messageSwitchboard.switchboardId()), + DST_CHAIN, + dstSwitchboardId, + uint64(payloadCounterBefore) + ); + assertEq(payloadId, expectedPayloadId, "PayloadId mismatch"); // Verify initial maxFees were stored (uint256 maxFeesBefore,) = messageSwitchboard.sponsoredPayloadFees(payloadId); @@ -1056,15 +1052,23 @@ contract MessageSwitchboardTest is Test, Utils { // Set overrides on the plug srcPlug.setOverrides(overrides); - // Get counters before creating payload + // Get counter before creating payload uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); vm.deal(address(srcPlug), 1 ether); vm.prank(address(srcPlug)); - bytes32 actualTriggerId = srcPlug.triggerSocket{value: initialFees}(abi.encode("payload")); + bytes32 payloadId = srcPlug.triggerSocket{value: initialFees}(abi.encode("payload")); - // Calculate the actual payloadId - bytes32 payloadId = calculatePayloadId(actualTriggerId, payloadCounterBefore, DST_CHAIN); + // Verify payloadId matches expected structure + uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); + bytes32 expectedPayloadId = createPayloadId( + SRC_CHAIN, + uint32(messageSwitchboard.switchboardId()), + DST_CHAIN, + dstSwitchboardId, + uint64(payloadCounterBefore) + ); + assertEq(payloadId, expectedPayloadId, "PayloadId mismatch"); // Try to increase fees with different plug - should revert because plug doesn't match vm.deal(address(dstPlug), 1 ether); From 933900bc7e1a6162f37ce63f4c0e44315d45f0d4 Mon Sep 17 00:00:00 2001 From: akash Date: Wed, 5 Nov 2025 18:19:05 +0530 Subject: [PATCH 03/10] feat: updated deposit to trigger --- contracts/evmx/fees/Credit.sol | 23 +++++++++------------- contracts/evmx/interfaces/IFeesManager.sol | 8 +------- contracts/evmx/interfaces/IFeesPlug.sol | 3 ++- contracts/evmx/plugs/FeesPlug.sol | 12 ++++++++++- test/SetupTest.t.sol | 3 ++- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/contracts/evmx/fees/Credit.sol b/contracts/evmx/fees/Credit.sol index eb12043e..cdda8c39 100644 --- a/contracts/evmx/fees/Credit.sol +++ b/contracts/evmx/fees/Credit.sol @@ -121,18 +121,12 @@ abstract contract Credit is FeesManagerStorage, Initializable, Ownable, AppGatew } /// @notice Deposits credits and native tokens to a user - /// @param depositTo_ The address to deposit the credits to - /// @param chainSlug_ The chain slug - /// @param token_ The token address - /// @param nativeAmount_ The native amount - /// @param creditAmount_ The credit amount - function deposit( - uint32 chainSlug_, - address token_, - address depositTo_, - uint256 nativeAmount_, - uint256 creditAmount_ - ) external override onlyWatcher { + /// @param payload_ Encoded deposit parameters: (chainSlug, token, receiver, creditAmount, nativeAmount) + function deposit(bytes calldata payload_) external override onlyWatcher { + // Decode payload: (chainSlug, token, receiver, creditAmount, nativeAmount) + (uint32 chainSlug_, address token_, address depositTo_, uint256 creditAmount_, uint256 nativeAmount_) = + abi.decode(payload_, (uint32, address, address, uint256, uint256)); + tokenOnChainBalances[chainSlug_][token_] += creditAmount_ + nativeAmount_; // Mint tokens to the user @@ -142,9 +136,10 @@ abstract contract Credit is FeesManagerStorage, Initializable, Ownable, AppGatew bool success = feesPool.withdraw(depositTo_, nativeAmount_); if (!success) { - _mint(depositTo_, creditAmount_); - nativeAmount_ = 0; + // Convert failed native amount to credits + _mint(depositTo_, nativeAmount_); creditAmount_ += nativeAmount_; + nativeAmount_ = 0; } } diff --git a/contracts/evmx/interfaces/IFeesManager.sol b/contracts/evmx/interfaces/IFeesManager.sol index 4a96aa5a..97d20438 100644 --- a/contracts/evmx/interfaces/IFeesManager.sol +++ b/contracts/evmx/interfaces/IFeesManager.sol @@ -3,13 +3,7 @@ pragma solidity ^0.8.21; import {WriteFinality, AppGatewayApprovals, OverrideParams, Transaction, RawPayload, Payload} from "../../utils/common/Structs.sol"; interface IFeesManager { - function deposit( - uint32 chainSlug_, - address token_, - address depositTo_, - uint256 nativeAmount_, - uint256 creditAmount_ - ) external; + function deposit(bytes calldata payload_) external; function wrap(address receiver_) external payable; diff --git a/contracts/evmx/interfaces/IFeesPlug.sol b/contracts/evmx/interfaces/IFeesPlug.sol index 24cc719a..71b1134d 100644 --- a/contracts/evmx/interfaces/IFeesPlug.sol +++ b/contracts/evmx/interfaces/IFeesPlug.sol @@ -7,7 +7,8 @@ interface IFeesPlug { address token, address receiver, uint256 creditAmount, - uint256 nativeAmount + uint256 nativeAmount, + bytes32 payloadId ); /// @notice Event emitted when fees are withdrawn event FeesWithdrawn(address token, address receiver, uint256 amount); diff --git a/contracts/evmx/plugs/FeesPlug.sol b/contracts/evmx/plugs/FeesPlug.sol index 8320db52..a543ddd8 100644 --- a/contracts/evmx/plugs/FeesPlug.sol +++ b/contracts/evmx/plugs/FeesPlug.sol @@ -68,7 +68,17 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { ) internal { if (!whitelistedTokens[token_]) revert TokenNotWhitelisted(token_); token_.safeTransferFrom(msg.sender, address(this), creditAmount_ + nativeAmount_); - emit FeesDeposited(token_, receiver_, creditAmount_, nativeAmount_); + + // Get chain slug from socket + uint32 chainSlug_ = socket__.chainSlug(); + + // Encode deposit parameters: (chainSlug, token, receiver, creditAmount, nativeAmount) + bytes memory payload = abi.encode(chainSlug_, token_, receiver_, creditAmount_, nativeAmount_); + + // Create trigger via Socket to get unique payloadId + bytes32 payloadId = socket__.sendPayload(payload); + + emit FeesDeposited(token_, receiver_, creditAmount_, nativeAmount_, payloadId); } /// @notice Withdraws fees diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 84118bab..16f1fbca 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -218,6 +218,7 @@ contract DeploySetup is SetupStore { // switchboard switchboard.registerSwitchboard(); + switchboard.setEvmxConfig(evmxSlug, 1); // Set EVMX config for trigger payloads switchboard.grantRole(WATCHER_ROLE, watcherEOA); switchboard.grantRole(RESCUE_ROLE, address(socketOwner)); @@ -430,7 +431,7 @@ contract FeesSetup is DeploySetup { vm.expectEmit(true, true, true, false); emit Deposited(chainSlug_, address(token), user_, credits_, native_); hoax(watcherEOA); - feesManager.deposit(chainSlug_, address(token), user_, native_, credits_); + feesManager.deposit(abi.encode(chainSlug_, address(token), user_, native_, credits_)); assertEq( feesManager.balanceOf(user_), From e1e990be5176541b20de66fbeeea53dd2e0efe8c Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 7 Nov 2025 11:04:13 +0530 Subject: [PATCH 04/10] fix: lint --- contracts/evmx/fees/FeesManager.sol | 1 - contracts/evmx/fees/MessageResolver.sol | 60 +- .../evmx/helpers/solana-utils/Ed25519.sol | 2 +- .../evmx/helpers/solana-utils/Ed25519_pow.sol | 2 +- .../evmx/helpers/solana-utils/Sha512.sol | 2 +- .../evmx/helpers/solana-utils/SolanaPda.sol | 2 +- .../helpers/solana-utils/SolanaSignature.sol | 2 +- .../solana-utils/program-pda/FeesPlugPdas.sol | 2 +- contracts/evmx/interfaces/IFeesManager.sol | 6 +- .../evmx/watcher/borsh-serde/BorshDecoder.sol | 2 +- .../evmx/watcher/borsh-serde/BorshEncoder.sol | 2 +- .../evmx/watcher/borsh-serde/BorshUtils.sol | 2 +- .../watcher/precompiles/WritePrecompile.sol | 4 +- contracts/protocol/Socket.sol | 22 +- contracts/protocol/SocketConfig.sol | 20 +- .../protocol/interfaces/ISwitchboard.sol | 12 +- .../protocol/switchboard/FastSwitchboard.sol | 19 +- .../switchboard/MessageSwitchboard.sol | 86 +-- .../protocol/switchboard/SwitchboardBase.sol | 2 +- hardhat-scripts/deploy/6.connect.ts | 10 +- script/counter/IncrementCountersFromApp.s.sol | 1 - test/SetupTest.t.sol | 14 +- test/Utils.t.sol | 5 +- test/mocks/MockPlug.sol | 26 +- test/switchboard/MessageSwitchboard.t.sol | 644 ++++++++++-------- 25 files changed, 509 insertions(+), 441 deletions(-) diff --git a/contracts/evmx/fees/FeesManager.sol b/contracts/evmx/fees/FeesManager.sol index 79dbc389..a3476d53 100644 --- a/contracts/evmx/fees/FeesManager.sol +++ b/contracts/evmx/fees/FeesManager.sol @@ -72,7 +72,6 @@ contract FeesManager is Credit { susdcSolanaProgramId = susdcSolanaProgramId_; } - function setChainMaxFees( uint32[] calldata chainSlugs_, uint256[] calldata maxFees_ diff --git a/contracts/evmx/fees/MessageResolver.sol b/contracts/evmx/fees/MessageResolver.sol index 86047f92..3c38ba31 100644 --- a/contracts/evmx/fees/MessageResolver.sol +++ b/contracts/evmx/fees/MessageResolver.sol @@ -50,9 +50,9 @@ abstract contract MessageResolverStorage { // Execution status enum enum ExecutionStatus { - NotAdded, // Message not yet added - Pending, // Message added, awaiting execution - Executed // Payment completed + NotAdded, // Message not yet added + Pending, // Message added, awaiting execution + Executed // Payment completed } // slot 51 @@ -76,36 +76,41 @@ abstract contract MessageResolverStorage { * @dev Uses Credits (ERC20) from FeesManager for payment settlement * @dev Upgradeable proxy pattern with AddressResolverUtil */ -contract MessageResolver is MessageResolverStorage, Initializable, AccessControl, AddressResolverUtil { +contract MessageResolver is + MessageResolverStorage, + Initializable, + AccessControl, + AddressResolverUtil +{ //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// //////////////////////////////////////////////////////// - + /// @notice Thrown when watcher is not authorized error UnauthorizedWatcher(); - + /// @notice Thrown when nonce has already been used error NonceAlreadyUsed(); - + /// @notice Thrown when message is already added error MessageAlreadyExists(); - + /// @notice Thrown when message is not found error MessageNotFound(); - + /// @notice Thrown when message is not in pending status error MessageNotPending(); - + /// @notice Thrown when payment transfer fails error PaymentFailed(); - + /// @notice Thrown when sponsor has insufficient credits error InsufficientSponsorCredits(); //////////////////////////////////////////////////////// ////////////////////// EVENTS ////////////////////////// //////////////////////////////////////////////////////// - + /// @notice Emitted when message details are added event MessageDetailsAdded( bytes32 indexed payloadId, @@ -118,7 +123,7 @@ contract MessageResolver is MessageResolverStorage, Initializable, AccessControl uint256 feeAmount, uint256 deadline ); - + /// @notice Emitted when transmitter is paid event TransmitterPaid( bytes32 indexed payloadId, @@ -126,14 +131,14 @@ contract MessageResolver is MessageResolverStorage, Initializable, AccessControl address indexed transmitter, uint256 feeAmount ); - + /// @notice Emitted when message is marked as executed by watcher event MessageMarkedExecuted(bytes32 indexed payloadId, address indexed watcher); //////////////////////////////////////////////////////// ////////////////////// CONSTRUCTOR ///////////////////// //////////////////////////////////////////////////////// - + constructor() { _disableInitializers(); // disable for implementation } @@ -236,11 +241,7 @@ contract MessageResolver is MessageResolverStorage, Initializable, AccessControl * @param signature_ Watcher signature confirming execution * @param nonce_ Nonce to prevent replay attacks */ - function markExecuted( - bytes32 payloadId_, - uint256 nonce_, - bytes calldata signature_ - ) external { + function markExecuted(bytes32 payloadId_, uint256 nonce_, bytes calldata signature_) external { MessageDetails storage details = messageDetails[payloadId_]; // Verify message exists @@ -251,12 +252,7 @@ contract MessageResolver is MessageResolverStorage, Initializable, AccessControl // Create digest for signature verification bytes32 digest = keccak256( - abi.encodePacked( - toBytes32Format(address(this)), - evmxChainSlug, - payloadId_, - nonce_ - ) + abi.encodePacked(toBytes32Format(address(this)), evmxChainSlug, payloadId_, nonce_) ); // Recover signer from signature @@ -286,12 +282,7 @@ contract MessageResolver is MessageResolverStorage, Initializable, AccessControl if (!success) revert PaymentFailed(); emit MessageMarkedExecuted(payloadId_, watcher); - emit TransmitterPaid( - payloadId_, - details.sponsor, - details.transmitter, - details.feeAmount - ); + emit TransmitterPaid(payloadId_, details.sponsor, details.transmitter, details.feeAmount); } //////////////////////////////////////////////////////// @@ -323,9 +314,7 @@ contract MessageResolver is MessageResolverStorage, Initializable, AccessControl * @param payloadId_ Unique identifier for the payload * @return Message details struct */ - function getMessageDetails( - bytes32 payloadId_ - ) external view returns (MessageDetails memory) { + function getMessageDetails(bytes32 payloadId_) external view returns (MessageDetails memory) { return messageDetails[payloadId_]; } @@ -369,4 +358,3 @@ contract MessageResolver is MessageResolverStorage, Initializable, AccessControl return messageDetails[payloadId_].status; } } - diff --git a/contracts/evmx/helpers/solana-utils/Ed25519.sol b/contracts/evmx/helpers/solana-utils/Ed25519.sol index e4420b3d..9c81addc 100644 --- a/contracts/evmx/helpers/solana-utils/Ed25519.sol +++ b/contracts/evmx/helpers/solana-utils/Ed25519.sol @@ -903,4 +903,4 @@ library Ed25519 { return vr == r; } } -} \ No newline at end of file +} diff --git a/contracts/evmx/helpers/solana-utils/Ed25519_pow.sol b/contracts/evmx/helpers/solana-utils/Ed25519_pow.sol index 3af4725a..681efca6 100644 --- a/contracts/evmx/helpers/solana-utils/Ed25519_pow.sol +++ b/contracts/evmx/helpers/solana-utils/Ed25519_pow.sol @@ -326,4 +326,4 @@ library Ed25519_pow { 0x7fffffff_ffffffff_ffffffff_ffffffff_ffffffff_ffffffff_ffffffff_ffffffed ); } -} \ No newline at end of file +} diff --git a/contracts/evmx/helpers/solana-utils/Sha512.sol b/contracts/evmx/helpers/solana-utils/Sha512.sol index 7f04221a..fb0776af 100644 --- a/contracts/evmx/helpers/solana-utils/Sha512.sol +++ b/contracts/evmx/helpers/solana-utils/Sha512.sol @@ -275,4 +275,4 @@ library Sha512 { return H; } -} \ No newline at end of file +} diff --git a/contracts/evmx/helpers/solana-utils/SolanaPda.sol b/contracts/evmx/helpers/solana-utils/SolanaPda.sol index 1a276334..741f2b36 100644 --- a/contracts/evmx/helpers/solana-utils/SolanaPda.sol +++ b/contracts/evmx/helpers/solana-utils/SolanaPda.sol @@ -267,4 +267,4 @@ library SolanaPDA { } return true; } -} \ No newline at end of file +} diff --git a/contracts/evmx/helpers/solana-utils/SolanaSignature.sol b/contracts/evmx/helpers/solana-utils/SolanaSignature.sol index c143500b..882f1dc3 100644 --- a/contracts/evmx/helpers/solana-utils/SolanaSignature.sol +++ b/contracts/evmx/helpers/solana-utils/SolanaSignature.sol @@ -14,4 +14,4 @@ contract SolanaSignature { return valid; } -} \ No newline at end of file +} diff --git a/contracts/evmx/helpers/solana-utils/program-pda/FeesPlugPdas.sol b/contracts/evmx/helpers/solana-utils/program-pda/FeesPlugPdas.sol index 1480153e..439a3884 100644 --- a/contracts/evmx/helpers/solana-utils/program-pda/FeesPlugPdas.sol +++ b/contracts/evmx/helpers/solana-utils/program-pda/FeesPlugPdas.sol @@ -44,4 +44,4 @@ library FeesPlugProgramPda { return SolanaPDA.findProgramAddress(programId, seeds); } -} \ No newline at end of file +} diff --git a/contracts/evmx/interfaces/IFeesManager.sol b/contracts/evmx/interfaces/IFeesManager.sol index 4a96aa5a..fd587cb5 100644 --- a/contracts/evmx/interfaces/IFeesManager.sol +++ b/contracts/evmx/interfaces/IFeesManager.sol @@ -39,9 +39,5 @@ interface IFeesManager { function setMaxFees(uint256 fees_) external; - function transferFrom( - address from_, - address to_, - uint256 amount_ - ) external returns (bool); + function transferFrom(address from_, address to_, uint256 amount_) external returns (bool); } diff --git a/contracts/evmx/watcher/borsh-serde/BorshDecoder.sol b/contracts/evmx/watcher/borsh-serde/BorshDecoder.sol index 1c7671a3..cb22bb57 100644 --- a/contracts/evmx/watcher/borsh-serde/BorshDecoder.sol +++ b/contracts/evmx/watcher/borsh-serde/BorshDecoder.sol @@ -356,4 +356,4 @@ library BorshDecoder { return values; } -} \ No newline at end of file +} diff --git a/contracts/evmx/watcher/borsh-serde/BorshEncoder.sol b/contracts/evmx/watcher/borsh-serde/BorshEncoder.sol index 96240761..131242aa 100644 --- a/contracts/evmx/watcher/borsh-serde/BorshEncoder.sol +++ b/contracts/evmx/watcher/borsh-serde/BorshEncoder.sol @@ -314,4 +314,4 @@ library BorshEncoder { } return out; } -} \ No newline at end of file +} diff --git a/contracts/evmx/watcher/borsh-serde/BorshUtils.sol b/contracts/evmx/watcher/borsh-serde/BorshUtils.sol index 0bcd31c1..82f92ff7 100644 --- a/contracts/evmx/watcher/borsh-serde/BorshUtils.sol +++ b/contracts/evmx/watcher/borsh-serde/BorshUtils.sol @@ -129,4 +129,4 @@ library BorshUtils { require(foundSemicolon && foundDigit && length > 0, "Could not extract array length"); return length; } -} \ No newline at end of file +} diff --git a/contracts/evmx/watcher/precompiles/WritePrecompile.sol b/contracts/evmx/watcher/precompiles/WritePrecompile.sol index c70662de..9a01ff7b 100644 --- a/contracts/evmx/watcher/precompiles/WritePrecompile.sol +++ b/contracts/evmx/watcher/precompiles/WritePrecompile.sol @@ -92,7 +92,7 @@ contract WritePrecompile is WritePrecompileStorage, Initializable, Ownable { writeFees = writeFees_; expiryTime = expiryTime_; _initializeOwner(owner_); - + watcher__ = IWatcher(watcher_); // _initializeWatcher(watcher_); } @@ -260,7 +260,7 @@ contract WritePrecompile is WritePrecompileStorage, Initializable, Ownable { (SolanaInstruction) ); emit SolanaDecodedInstruction(instruction); - + bytes memory functionArgsPacked = BorshEncoder.encodeFunctionArgs(instruction); emit SolanaFunctionArgsPacked(functionArgsPacked); diff --git a/contracts/protocol/Socket.sol b/contracts/protocol/Socket.sol index 2bb5147a..a05486f5 100644 --- a/contracts/protocol/Socket.sol +++ b/contracts/protocol/Socket.sol @@ -104,16 +104,15 @@ contract Socket is SocketUtils { ) internal { (, address switchboardAddress) = _verifyPlugSwitchboard(executeParams_.target); // NOTE: the first un-trusted call in the system - address transmitter = ISwitchboard(switchboardAddress) - .getTransmitter(msg.sender, payloadId_, transmitterProof_); + address transmitter = ISwitchboard(switchboardAddress).getTransmitter( + msg.sender, + payloadId_, + transmitterProof_ + ); // create the digest // transmitter, payloadId, appGateway, executeParams_ and there contents are validated using digest verification from switchboard - bytes32 digest = _createDigest( - transmitter, - payloadId_, - executeParams_ - ); + bytes32 digest = _createDigest(transmitter, payloadId_, executeParams_); payloadIdToDigest[payloadId_] = digest; if ( @@ -236,10 +235,7 @@ contract Socket is SocketUtils { * @param payloadId_ The payload ID to increase fees for * @param feesData_ Encoded fees data (type + data) */ - function increaseFeesForPayload( - bytes32 payloadId_, - bytes calldata feesData_ - ) external payable { + function increaseFeesForPayload(bytes32 payloadId_, bytes calldata feesData_) external payable { (, address switchboardAddress) = _verifyPlugSwitchboard(msg.sender); ISwitchboard(switchboardAddress).increaseFeesForPayload{value: msg.value}( payloadId_, @@ -248,7 +244,9 @@ contract Socket is SocketUtils { ); } - function _verifyPlugSwitchboard(address plug_) internal view returns (uint64 switchboardId, address switchboardAddress) { + function _verifyPlugSwitchboard( + address plug_ + ) internal view returns (uint64 switchboardId, address switchboardAddress) { switchboardId = plugSwitchboardIds[plug_]; if (switchboardId == 0) revert PlugNotFound(); if (isValidSwitchboard[switchboardId] != SwitchboardStatus.REGISTERED) diff --git a/contracts/protocol/SocketConfig.sol b/contracts/protocol/SocketConfig.sol index 0401c792..d99fea7b 100644 --- a/contracts/protocol/SocketConfig.sol +++ b/contracts/protocol/SocketConfig.sol @@ -27,7 +27,7 @@ abstract contract SocketConfig is ISocket, AccessControl { // @notice mapping of plug address to switchboard address mapping(address => uint64) public plugSwitchboardIds; - // @notice max copy bytes for socket + // @notice max copy bytes for socket uint16 public maxCopyBytes = 2048; // 2KB // @notice counter for switchboard ids @@ -121,12 +121,17 @@ abstract contract SocketConfig is ISocket, AccessControl { * @param configData_ The configuration data for the switchboard */ function connect(uint64 switchboardId_, bytes memory configData_) external override { - if (switchboardId_ == 0 || isValidSwitchboard[switchboardId_] != SwitchboardStatus.REGISTERED) - revert InvalidSwitchboard(); + if ( + switchboardId_ == 0 || + isValidSwitchboard[switchboardId_] != SwitchboardStatus.REGISTERED + ) revert InvalidSwitchboard(); plugSwitchboardIds[msg.sender] = switchboardId_; if (configData_.length > 0) { - ISwitchboard(switchboardAddresses[switchboardId_]).updatePlugConfig(msg.sender, configData_); + ISwitchboard(switchboardAddresses[switchboardId_]).updatePlugConfig( + msg.sender, + configData_ + ); } emit PlugConnected(msg.sender, switchboardId_, configData_); } @@ -138,7 +143,7 @@ abstract contract SocketConfig is ISocket, AccessControl { function updatePlugConfig(bytes memory configData_) external { uint64 switchboardId = plugSwitchboardIds[msg.sender]; if (switchboardId == 0) revert PlugNotConnected(); - ISwitchboard(switchboardAddresses[switchboardId]).updatePlugConfig(msg.sender,configData_); + ISwitchboard(switchboardAddresses[switchboardId]).updatePlugConfig(msg.sender, configData_); } /** @@ -182,7 +187,10 @@ abstract contract SocketConfig is ISocket, AccessControl { bytes memory extraData_ ) external view returns (uint64 switchboardId, bytes memory configData) { switchboardId = plugSwitchboardIds[plugAddress_]; - configData = ISwitchboard(switchboardAddresses[switchboardId]).getPlugConfig(plugAddress_, extraData_); + configData = ISwitchboard(switchboardAddresses[switchboardId]).getPlugConfig( + plugAddress_, + extraData_ + ); } function getPlugSwitchboard( diff --git a/contracts/protocol/interfaces/ISwitchboard.sol b/contracts/protocol/interfaces/ISwitchboard.sol index 24040195..5ce1dcef 100644 --- a/contracts/protocol/interfaces/ISwitchboard.sol +++ b/contracts/protocol/interfaces/ISwitchboard.sol @@ -15,7 +15,12 @@ interface ISwitchboard { * @param source_ The source of the payload (chainSlug, plug). * @return A boolean indicating whether the payloads is allowed to go through the switchboard or not. */ - function allowPayload(bytes32 digest_, bytes32 payloadId_, address target_, bytes memory source_) external view returns (bool); + function allowPayload( + bytes32 digest_, + bytes32 payloadId_, + address target_, + bytes memory source_ + ) external view returns (bool); /** * @notice Processes a trigger and creates payload @@ -72,5 +77,8 @@ interface ISwitchboard { * @param extraData_ The extra data for the plug * @return configData_ The configuration data for the plug */ - function getPlugConfig(address plug_, bytes memory extraData_) external view returns (bytes memory configData_); + function getPlugConfig( + address plug_, + bytes memory extraData_ + ) external view returns (bytes memory configData_); } diff --git a/contracts/protocol/switchboard/FastSwitchboard.sol b/contracts/protocol/switchboard/FastSwitchboard.sol index cbb12a1c..9a5ebca9 100644 --- a/contracts/protocol/switchboard/FastSwitchboard.sol +++ b/contracts/protocol/switchboard/FastSwitchboard.sol @@ -64,8 +64,13 @@ contract FastSwitchboard is SwitchboardBase { /** * @inheritdoc ISwitchboard */ - function allowPayload(bytes32 digest_, bytes32, address target_, bytes memory source_ ) external view returns (bool) { - (bytes32 appGatewayId) = abi.decode(source_, (bytes32)); + function allowPayload( + bytes32 digest_, + bytes32, + address target_, + bytes memory source_ + ) external view returns (bool) { + bytes32 appGatewayId = abi.decode(source_, (bytes32)); if (plugAppGatewayIds[target_] != appGatewayId) revert InvalidSource(); return isAttested[digest_]; } @@ -79,7 +84,7 @@ contract FastSwitchboard is SwitchboardBase { bytes calldata payload_, bytes calldata overrides_ ) external payable virtual {} - + /** * @inheritdoc ISwitchboard */ @@ -93,7 +98,7 @@ contract FastSwitchboard is SwitchboardBase { * @inheritdoc ISwitchboard */ function updatePlugConfig(address plug_, bytes memory configData_) external virtual { - (bytes32 appGatewayId_) = abi.decode(configData_, ( bytes32)); + bytes32 appGatewayId_ = abi.decode(configData_, (bytes32)); plugAppGatewayIds[plug_] = appGatewayId_; emit PlugConfigUpdated(plug_, appGatewayId_); } @@ -101,8 +106,10 @@ contract FastSwitchboard is SwitchboardBase { /** * @inheritdoc ISwitchboard */ - function getPlugConfig(address plug_, bytes memory extraData_) external view override returns (bytes memory configData_) { + function getPlugConfig( + address plug_, + bytes memory extraData_ + ) external view override returns (bytes memory configData_) { configData_ = abi.encode(plugAppGatewayIds[plug_]); } - } diff --git a/contracts/protocol/switchboard/MessageSwitchboard.sol b/contracts/protocol/switchboard/MessageSwitchboard.sol index 1f18b1a8..d9746527 100644 --- a/contracts/protocol/switchboard/MessageSwitchboard.sol +++ b/contracts/protocol/switchboard/MessageSwitchboard.sol @@ -6,7 +6,7 @@ import {WATCHER_ROLE, FEE_UPDATER_ROLE} from "../../utils/common/AccessRoles.sol import {toBytes32Format} from "../../utils/common/Converters.sol"; import {createPayloadId} from "../../utils/common/IdUtils.sol"; import {DigestParams, MessageOverrides, PayloadFees, SponsoredPayloadFees} from "../../utils/common/Structs.sol"; -import {WRITE } from "../../utils/common/Constants.sol"; +import {WRITE} from "../../utils/common/Constants.sol"; import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; /** @@ -32,9 +32,8 @@ contract MessageSwitchboard is SwitchboardBase { // minimum message value fees: chainSlug => minimum fee amount mapping(uint32 => uint256) public minMsgValueFees; - mapping(bytes32 => PayloadFees) public payloadFees; - + // sponsored payload fee tracking mapping(bytes32 => SponsoredPayloadFees) public sponsoredPayloadFees; @@ -112,7 +111,11 @@ contract MessageSwitchboard is SwitchboardBase { // Event emitted when minimum message value fees are set event MinMsgValueFeesSet(uint32 indexed chainSlug, uint256 minFees, address indexed updater); // Event emitted when sponsored fees are increased - event SponsoredFeesIncreased(bytes32 indexed payloadId, uint256 newMaxFees, address indexed plug); + event SponsoredFeesIncreased( + bytes32 indexed payloadId, + uint256 newMaxFees, + address indexed plug + ); /** * @dev Constructor function for the MessageSwitchboard contract @@ -143,8 +146,6 @@ contract MessageSwitchboard is SwitchboardBase { emit SiblingConfigSet(chainSlug_, socket_, switchboard_); } - - /** * @dev Function to process trigger and create payload * @param plug_ Source plug address @@ -162,19 +163,23 @@ contract MessageSwitchboard is SwitchboardBase { _validateSibling(overrides.dstChainSlug, plug_); // Create digest and payload ID (common for both flows) - (DigestParams memory digestParams, bytes32 digest, bytes32 payloadId) = _createDigestAndPayloadId( - overrides.dstChainSlug, - plug_, - overrides.gasLimit, - overrides.value, - triggerId_, - payload_ - ); + ( + DigestParams memory digestParams, + bytes32 digest, + bytes32 payloadId + ) = _createDigestAndPayloadId( + overrides.dstChainSlug, + plug_, + overrides.gasLimit, + overrides.value, + triggerId_, + payload_ + ); if (overrides.isSponsored) { // Sponsored flow - check sponsor approval if (!sponsorApprovals[overrides.sponsor][plug_]) revert PlugNotApprovedBySponsor(); - + // Store sponsored fees sponsoredPayloadFees[payloadId] = SponsoredPayloadFees({ maxFees: overrides.maxFees, @@ -228,13 +233,8 @@ contract MessageSwitchboard is SwitchboardBase { if (version == 1) { // Version 1: Native flow - ( - , - uint32 dstChainSlug, - uint256 gasLimit, - uint256 value, - address refundAddress - ) = abi.decode(overrides_, (uint8, uint32, uint256, uint256, address)); + (, uint32 dstChainSlug, uint256 gasLimit, uint256 value, address refundAddress) = abi + .decode(overrides_, (uint8, uint32, uint256, uint256, address)); return MessageOverrides({ @@ -308,7 +308,7 @@ contract MessageSwitchboard is SwitchboardBase { target: siblingPlugs[dstChainSlug_][plug_], source: abi.encode(chainSlug, toBytes32Format(plug_)), prevBatchDigestHash: triggerId_, - extraData:"0x" + extraData: "0x" }); digest = _createDigest(digestParams); } @@ -522,7 +522,7 @@ contract MessageSwitchboard is SwitchboardBase { ) external payable override onlySocket { // Decode the fees type from feesData uint8 feesType = abi.decode(feesData_, (uint8)); - + if (feesType == 1) { // Native fees increase _increaseNativeFees(payloadId_, plug_, feesData_); @@ -533,7 +533,7 @@ contract MessageSwitchboard is SwitchboardBase { revert InvalidFeesType(); } } - + /** * @dev Internal function to increase native fees */ @@ -543,18 +543,18 @@ contract MessageSwitchboard is SwitchboardBase { bytes calldata feesData_ ) internal { PayloadFees storage fees = payloadFees[payloadId_]; - + // Validation: Only the plug that created this payload can increase fees if (fees.plug != plug_) revert UnauthorizedFeeIncrease(); - + // Update native fees if msg.value is provided if (msg.value > 0) { fees.nativeFees += msg.value; } - + emit FeesIncreased(payloadId_, msg.value, feesData_); } - + /** * @dev Internal function to increase sponsored fees */ @@ -564,22 +564,26 @@ contract MessageSwitchboard is SwitchboardBase { bytes calldata feesData_ ) internal { SponsoredPayloadFees storage fees = sponsoredPayloadFees[payloadId_]; - + // Validation: Only the plug that created this payload can increase fees if (fees.plug != plug_) revert UnauthorizedFeeIncrease(); - + // Decode new maxFees (skip first byte which is feesType) (, uint256 newMaxFees) = abi.decode(feesData_, (uint8, uint256)); fees.maxFees = newMaxFees; - + emit SponsoredFeesIncreased(payloadId_, newMaxFees, plug_); } /** * @inheritdoc ISwitchboard */ - function allowPayload(bytes32 digest_, bytes32, address target_, bytes memory source_ ) external view override returns (bool) { - + function allowPayload( + bytes32 digest_, + bytes32, + address target_, + bytes memory source_ + ) external view override returns (bool) { (uint32 srcChainSlug, bytes32 srcPlug) = abi.decode(source_, (uint32, bytes32)); if (siblingPlugs[srcChainSlug][target_] != srcPlug) revert InvalidSource(); // digest has enough attestations @@ -613,9 +617,12 @@ contract MessageSwitchboard is SwitchboardBase { * @notice Updates plug configuration * @param configData_ The configuration data for the plug */ - function updatePlugConfig(address plug_, bytes memory configData_) external override onlySocket { + function updatePlugConfig( + address plug_, + bytes memory configData_ + ) external override onlySocket { (uint32 chainSlug_, bytes32 siblingPlug_) = abi.decode(configData_, (uint32, bytes32)); - if ( + if ( siblingSockets[chainSlug_] == bytes32(0) || siblingSwitchboards[chainSlug_] == bytes32(0) ) { @@ -629,8 +636,11 @@ contract MessageSwitchboard is SwitchboardBase { /** * @inheritdoc ISwitchboard */ - function getPlugConfig(address plug_, bytes memory extraData_) external view override returns (bytes memory configData_) { - (uint32 chainSlug_) = abi.decode(extraData_, (uint32)); + function getPlugConfig( + address plug_, + bytes memory extraData_ + ) external view override returns (bytes memory configData_) { + uint32 chainSlug_ = abi.decode(extraData_, (uint32)); configData_ = abi.encode(siblingPlugs[chainSlug_][plug_]); } diff --git a/contracts/protocol/switchboard/SwitchboardBase.sol b/contracts/protocol/switchboard/SwitchboardBase.sol index 6d840f61..1f4e3a19 100644 --- a/contracts/protocol/switchboard/SwitchboardBase.sol +++ b/contracts/protocol/switchboard/SwitchboardBase.sol @@ -59,7 +59,7 @@ abstract contract SwitchboardBase is ISwitchboard, AccessControl { ) external view returns (address transmitter) { transmitter = transmitterSignature_.length > 0 ? _recoverSigner( - // TODO: use api encode packed + // TODO: use api encode packed keccak256(abi.encode(address(socket__), payloadId_)), transmitterSignature_ ) diff --git a/hardhat-scripts/deploy/6.connect.ts b/hardhat-scripts/deploy/6.connect.ts index f53ed75c..28e2e491 100644 --- a/hardhat-scripts/deploy/6.connect.ts +++ b/hardhat-scripts/deploy/6.connect.ts @@ -1,7 +1,11 @@ import { ethers, Wallet } from "ethers"; import { ChainAddressesObj, ChainSlug, Contracts } from "../../src"; import { chains, CONCURRENCY_LIMIT, EVMX_CHAIN_ID, mode } from "../config"; -import { AppGatewayConfig, DeploymentAddresses, WatcherMultiCallParams } from "../constants"; +import { + AppGatewayConfig, + DeploymentAddresses, + WatcherMultiCallParams, +} from "../constants"; import { checkIfAppGatewayIdExists, getAddresses, @@ -170,7 +174,9 @@ export const updateConfigEVMx = async () => { if (appConfigs.length > 0) { console.log({ appConfigs }); const calldata = ethers.utils.defaultAbiCoder.encode( - ["tuple(tuple(bytes32 appGatewayId,uint64 switchboardId) plugConfig,bytes32 plug,uint32 chainSlug)[]"], + [ + "tuple(tuple(bytes32 appGatewayId,uint64 switchboardId) plugConfig,bytes32 plug,uint32 chainSlug)[]", + ], [appConfigs] ); diff --git a/script/counter/IncrementCountersFromApp.s.sol b/script/counter/IncrementCountersFromApp.s.sol index 4c215a25..2014aa22 100644 --- a/script/counter/IncrementCountersFromApp.s.sol +++ b/script/counter/IncrementCountersFromApp.s.sol @@ -34,6 +34,5 @@ contract IncrementCounters is Script { } else { console.log("Arbitrum Sepolia forwarder not yet deployed"); } - } } diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 1270451d..18c3ce13 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -354,8 +354,6 @@ contract DeploySetup is SetupStore { return createSignature(digest, watcherPrivateKey); } - - function predictAsyncPromiseAddress( address invoker_, address forwarder_ @@ -714,7 +712,10 @@ contract WatcherSetup is FeesSetup { contractAddress: address(watcher), data: abi.encode(promiseReturnData, feesAmount), nonce: watcherNonce, - signature: _createWatcherSignature(address(watcher), abi.encode(promiseReturnData, feesAmount)) + signature: _createWatcherSignature( + address(watcher), + abi.encode(promiseReturnData, feesAmount) + ) }); watcherNonce++; watcher.resolvePayload(params); @@ -728,7 +729,10 @@ contract WatcherSetup is FeesSetup { contractAddress: address(watcher), data: abi.encode(promiseReturnData, isRevertingOnchain_), nonce: watcherNonce, - signature: _createWatcherSignature(address(watcher), abi.encode(promiseReturnData, isRevertingOnchain_)) + signature: _createWatcherSignature( + address(watcher), + abi.encode(promiseReturnData, isRevertingOnchain_) + ) }); watcherNonce++; watcher.markRevert(params); @@ -954,5 +958,3 @@ contract MessageSwitchboardSetup is DeploySetup { optConfig.socket.execute(executeParams, transmissionParams); } } - - diff --git a/test/Utils.t.sol b/test/Utils.t.sol index a6d0c975..bc816cdb 100644 --- a/test/Utils.t.sol +++ b/test/Utils.t.sol @@ -4,9 +4,6 @@ pragma solidity ^0.8.21; import "forge-std/Test.sol"; abstract contract Utils is Test { - - - function createSignature( bytes32 digest_, uint256 privateKey_ @@ -29,4 +26,4 @@ abstract contract Utils is Test { function bytes32ToAddress(bytes32 addrBytes32_) public pure returns (address) { return address(uint160(uint256(addrBytes32_))); } -} \ No newline at end of file +} diff --git a/test/mocks/MockPlug.sol b/test/mocks/MockPlug.sol index 65ca5710..1096e256 100644 --- a/test/mocks/MockPlug.sol +++ b/test/mocks/MockPlug.sol @@ -6,53 +6,49 @@ import "../../contracts/protocol/base/MessagePlugBase.sol"; contract MockPlug is MessagePlugBase { uint32 public chainSlug; bytes32 public triggerId; - - constructor(address socket_, uint64 switchboardId_) MessagePlugBase(socket_, switchboardId_) { - } - + + constructor(address socket_, uint64 switchboardId_) MessagePlugBase(socket_, switchboardId_) {} function setSocket(address socket_) external { _setSocket(socket_); } - + function setChainSlug(uint32 chainSlug_) external { chainSlug = chainSlug_; } - + function setOverrides(bytes memory overrides_) external { _setOverrides(overrides_); } - + function getOverrides() external view returns (bytes memory) { return overrides; } - + function trigger(bytes memory data) external { // Mock trigger function triggerId = keccak256(data); } - + function getTriggerId() external view returns (bytes32) { return triggerId; } - + // New method to trigger Socket's triggerAppGateway function triggerSocket(bytes memory data) external payable returns (bytes32) { return socket__.triggerAppGateway{value: msg.value}(data); } - + // Method to connect to socket - function connectToSocket(address socket_,uint64 switchboardId_) external { + function connectToSocket(address socket_, uint64 switchboardId_) external { _setSocket(socket_); switchboardId = switchboardId_; socket__.connect(switchboardId_, ""); switchboard = socket__.switchboardAddresses(switchboardId_); } - - + // Method to increase fees for payload function increaseFeesForPayload(bytes32 payloadId_, bytes memory feesData_) external payable { socket__.increaseFeesForPayload{value: msg.value}(payloadId_, feesData_); } } - diff --git a/test/switchboard/MessageSwitchboard.t.sol b/test/switchboard/MessageSwitchboard.t.sol index 95dfba87..5dfb2065 100644 --- a/test/switchboard/MessageSwitchboard.t.sol +++ b/test/switchboard/MessageSwitchboard.t.sol @@ -18,23 +18,23 @@ contract MessageSwitchboardTest is Test, Utils { uint32 constant SRC_CHAIN = 1; uint32 constant DST_CHAIN = 2; uint256 constant MIN_FEES = 0.001 ether; - + // Test addresses address owner = address(0x1000); address watcher = address(0x2000); address sponsor = address(0x3000); address refundAddress = address(0x4000); address feeUpdater = address(0x5000); - + // Private keys for signing uint256 watcherPrivateKey = 0x1111111111111111111111111111111111111111111111111111111111111111; - + // Contracts Socket socket; MessageSwitchboard messageSwitchboard; MockPlug srcPlug; MockPlug dstPlug; - + // Events event SiblingConfigSet(uint32 indexed chainSlug, bytes32 socket, bytes32 switchboard); event SiblingRegistered(uint32 chainSlug, address plugAddress, bytes32 siblingPlug); @@ -55,58 +55,69 @@ contract MessageSwitchboardTest is Test, Utils { event Refunded(bytes32 indexed payloadId, address indexed refundAddress, uint256 amount); event FeesIncreased(bytes32 indexed payloadId, uint256 additionalNativeFees, bytes feesData); event MinMsgValueFeesSet(uint32 indexed chainSlug, uint256 minFees, address indexed updater); - event SponsoredFeesIncreased(bytes32 indexed payloadId, uint256 newMaxFees, address indexed plug); + event SponsoredFeesIncreased( + bytes32 indexed payloadId, + uint256 newMaxFees, + address indexed plug + ); event PlugConfigUpdated(address indexed plug, uint32 indexed chainSlug, bytes32 siblingPlug); function setUp() public { // Deploy actual Socket contract socket = new Socket(SRC_CHAIN, owner, "1.0.0"); messageSwitchboard = new MessageSwitchboard(SRC_CHAIN, socket, owner); - + // Setup roles - grant watcher role to the address derived from watcherPrivateKey address actualWatcherAddress = getWatcherAddress(); vm.startPrank(owner); messageSwitchboard.grantRole(WATCHER_ROLE, actualWatcherAddress); messageSwitchboard.grantRole(FEE_UPDATER_ROLE, feeUpdater); - + // Register switchboard on Socket (switchboard calls Socket.registerSwitchboard()) messageSwitchboard.registerSwitchboard(); vm.stopPrank(); uint64 switchboardId = messageSwitchboard.switchboardId(); - + // Socket automatically stores switchboard address, no manual setting needed - + // Now create plugs with the registered switchboard ID srcPlug = new MockPlug(address(socket), switchboardId); dstPlug = new MockPlug(address(socket), switchboardId); } - + // Helper to get watcher address function getWatcherAddress() public pure returns (address) { return vm.addr(0x1111111111111111111111111111111111111111111111111111111111111111); } - + // Helper to create payload ID (matches createPayloadId from IdUtils) function createTestPayloadId( uint256 payloadPointer_, uint64 switchboardId_, uint32 chainSlug_ ) public pure returns (bytes32) { - return bytes32((uint256(chainSlug_) << 224) | (uint256(switchboardId_) << 160) | payloadPointer_); + return + bytes32( + (uint256(chainSlug_) << 224) | (uint256(switchboardId_) << 160) | payloadPointer_ + ); } - + /** * @dev Calculate triggerId based on Socket's _encodeTriggerId logic * @param socketAddress The socket contract address * @param triggerCounter The current trigger counter value (before increment) * @return triggerId The calculated trigger ID */ - function calculateTriggerId(address socketAddress, uint64 triggerCounter) public pure returns (bytes32) { - uint256 triggerPrefix = (uint256(SRC_CHAIN) << 224) | (uint256(uint160(socketAddress)) << 64); + function calculateTriggerId( + address socketAddress, + uint64 triggerCounter + ) public pure returns (bytes32) { + uint256 triggerPrefix = (uint256(SRC_CHAIN) << 224) | + (uint256(uint160(socketAddress)) << 64); return bytes32(triggerPrefix | triggerCounter); } - + /** * @dev Calculate payloadId based on MessageSwitchboard's _createDigestAndPayloadId logic * @param triggerId The trigger ID from socket @@ -114,50 +125,54 @@ contract MessageSwitchboardTest is Test, Utils { * @param dstChainSlug The destination chain slug * @return payloadId The calculated payload ID */ - function calculatePayloadId(bytes32 triggerId, uint40 payloadCounter, uint32 dstChainSlug) public view returns (bytes32) { + function calculatePayloadId( + bytes32 triggerId, + uint40 payloadCounter, + uint32 dstChainSlug + ) public view returns (bytes32) { uint160 payloadPointer = (uint160(SRC_CHAIN) << 120) | (uint160(uint64(uint256(triggerId))) << 80) | payloadCounter; - - return createTestPayloadId(payloadPointer, messageSwitchboard.switchboardId(), dstChainSlug); + + return + createTestPayloadId(payloadPointer, messageSwitchboard.switchboardId(), dstChainSlug); } - + /** * @dev Calculate digest based on MessageSwitchboard's _createDigest logic * @param digestParams The digest parameters * @return digest The calculated digest */ function calculateDigest(DigestParams memory digestParams) public pure returns (bytes32) { - return keccak256( - abi.encodePacked( - digestParams.socket, - digestParams.transmitter, - digestParams.payloadId, - digestParams.deadline, - digestParams.callType, - digestParams.gasLimit, - digestParams.value, - digestParams.payload, - digestParams.target, - digestParams.source, - digestParams.prevBatchDigestHash, - digestParams.extraData - ) - ); + return + keccak256( + abi.encodePacked( + digestParams.socket, + digestParams.transmitter, + digestParams.payloadId, + digestParams.deadline, + digestParams.callType, + digestParams.gasLimit, + digestParams.value, + digestParams.payload, + digestParams.target, + digestParams.source, + digestParams.prevBatchDigestHash, + digestParams.extraData + ) + ); } // ============================================ // HELPER FUNCTIONS FOR TEST OPTIMIZATION // ============================================ - + /** * @dev Setup sibling configuration (socket, switchboard, plug registration) */ function _setupSiblingConfig() internal { - _setupSiblingSocketConfig(); _setupSiblingPlugConfig(); - } function _setupSiblingSocketConfig() internal { @@ -167,7 +182,11 @@ contract MessageSwitchboardTest is Test, Utils { vm.startPrank(owner); messageSwitchboard.setSiblingConfig(DST_CHAIN, siblingSocket, siblingSwitchboard); // Also set config for reverse direction - messageSwitchboard.setSiblingConfig(SRC_CHAIN, toBytes32Format(address(socket)), toBytes32Format(address(messageSwitchboard))); + messageSwitchboard.setSiblingConfig( + SRC_CHAIN, + toBytes32Format(address(socket)), + toBytes32Format(address(messageSwitchboard)) + ); vm.stopPrank(); } @@ -176,7 +195,7 @@ contract MessageSwitchboardTest is Test, Utils { srcPlug.registerSibling(DST_CHAIN, address(dstPlug)); dstPlug.registerSibling(SRC_CHAIN, address(srcPlug)); } - + /** * @dev Setup minimum fees for destination chain */ @@ -184,14 +203,17 @@ contract MessageSwitchboardTest is Test, Utils { vm.prank(owner); messageSwitchboard.setMinMsgValueFeesOwner(DST_CHAIN, MIN_FEES); } - + /** * @dev Create a native payload via Socket's triggerAppGateway * @param payloadData The payload data to encode * @param msgValue The msg.value to send with the transaction * @return payloadId The generated payload ID */ - function _createNativePayload(bytes memory payloadData, uint256 msgValue) internal returns (bytes32 payloadId) { + function _createNativePayload( + bytes memory payloadData, + uint256 msgValue + ) internal returns (bytes32 payloadId) { bytes memory overrides = abi.encode( uint8(1), // version DST_CHAIN, @@ -199,30 +221,33 @@ contract MessageSwitchboardTest is Test, Utils { uint256(0), // value refundAddress // refundAddress ); - + // Set overrides on the plug srcPlug.setOverrides(overrides); - + bytes memory payload = abi.encode(payloadData); - + // Get counters before the call uint64 triggerCounterBefore = socket.triggerCounter(); uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); - + // Use MockPlug to trigger Socket vm.deal(address(srcPlug), 10 ether); srcPlug.triggerSocket{value: msgValue}(payload); - + return _getLastPayloadId(triggerCounterBefore, payloadCounterBefore); } - + /** * @dev Create a sponsored payload via Socket's triggerAppGateway * @param payloadData The payload data to encode * @param maxFees The maximum fees for the sponsored transaction * @return payloadId The generated payload ID */ - function _createSponsoredPayload(bytes memory payloadData, uint256 maxFees) internal returns (bytes32 payloadId) { + function _createSponsoredPayload( + bytes memory payloadData, + uint256 maxFees + ) internal returns (bytes32 payloadId) { bytes memory overrides = abi.encode( uint8(2), // version DST_CHAIN, @@ -231,22 +256,22 @@ contract MessageSwitchboardTest is Test, Utils { maxFees, // maxFees sponsor // sponsor ); - + // Set overrides on the plug srcPlug.setOverrides(overrides); - + bytes memory payload = abi.encode(payloadData); - + // Get counters before the call uint64 triggerCounterBefore = socket.triggerCounter(); uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); - + // Use MockPlug to trigger Socket srcPlug.triggerSocket(payload); - + return _getLastPayloadId(triggerCounterBefore, payloadCounterBefore); } - + /** * @dev Create DigestParams for attestation with flexible parameters * @param payloadId The payload ID @@ -258,8 +283,8 @@ contract MessageSwitchboardTest is Test, Utils { * @return digestParams The constructed DigestParams */ function _createDigestParams( - bytes32 payloadId, - bytes32 triggerId, + bytes32 payloadId, + bytes32 triggerId, bytes memory payload, address target_, uint256 gasLimit_, @@ -268,23 +293,24 @@ contract MessageSwitchboardTest is Test, Utils { // Get sibling socket from switchboard (matches what contract uses) bytes32 siblingSocket = messageSwitchboard.siblingSockets(DST_CHAIN); bytes32 siblingPlug = messageSwitchboard.siblingPlugs(DST_CHAIN, address(srcPlug)); - - return DigestParams({ - socket: siblingSocket, - transmitter: bytes32(0), - payloadId: payloadId, - deadline: block.timestamp + 3600, - callType: WRITE, - gasLimit: gasLimit_, - value: value_, - payload: payload, - target: siblingPlug, - source: abi.encode(SRC_CHAIN, toBytes32Format(address(srcPlug))), - prevBatchDigestHash: triggerId, - extraData: abi.encode(SRC_CHAIN, toBytes32Format(address(srcPlug))) - }); + + return + DigestParams({ + socket: siblingSocket, + transmitter: bytes32(0), + payloadId: payloadId, + deadline: block.timestamp + 3600, + callType: WRITE, + gasLimit: gasLimit_, + value: value_, + payload: payload, + target: siblingPlug, + source: abi.encode(SRC_CHAIN, toBytes32Format(address(srcPlug))), + prevBatchDigestHash: triggerId, + extraData: abi.encode(SRC_CHAIN, toBytes32Format(address(srcPlug))) + }); } - + /** * @dev Create DigestParams for attestation (simplified version with defaults) * @param payloadId The payload ID @@ -292,21 +318,28 @@ contract MessageSwitchboardTest is Test, Utils { * @param payload The payload data * @return digestParams The constructed DigestParams */ - function _createDigestParams(bytes32 payloadId, bytes32 triggerId, bytes memory payload) internal view returns (DigestParams memory) { + function _createDigestParams( + bytes32 payloadId, + bytes32 triggerId, + bytes memory payload + ) internal view returns (DigestParams memory) { return _createDigestParams(payloadId, triggerId, payload, address(dstPlug), 100000, 0); } - + /** * @dev Get the last created payload ID by reading counters before trigger * @param triggerCounterBefore The trigger counter before the call * @param payloadCounterBefore The payload counter before the call * @return payloadId The calculated payload ID */ - function _getLastPayloadId(uint64 triggerCounterBefore, uint40 payloadCounterBefore) internal view returns (bytes32) { + function _getLastPayloadId( + uint64 triggerCounterBefore, + uint40 payloadCounterBefore + ) internal view returns (bytes32) { bytes32 triggerId = calculateTriggerId(address(socket), triggerCounterBefore); return calculatePayloadId(triggerId, payloadCounterBefore, DST_CHAIN); } - + /** * @dev Create watcher signature for a given payload ID * @param payloadId The payload ID to sign @@ -314,10 +347,12 @@ contract MessageSwitchboardTest is Test, Utils { */ function _createWatcherSignature(bytes32 payloadId) internal view returns (bytes memory) { // markRefundEligible signs: keccak256(abi.encodePacked(switchboardAddress, chainSlug, payloadId)) - bytes32 digest = keccak256(abi.encodePacked(toBytes32Format(address(messageSwitchboard)), SRC_CHAIN, payloadId)); + bytes32 digest = keccak256( + abi.encodePacked(toBytes32Format(address(messageSwitchboard)), SRC_CHAIN, payloadId) + ); return createSignature(digest, watcherPrivateKey); } - + /** * @dev Approve plug for sponsor */ @@ -325,7 +360,7 @@ contract MessageSwitchboardTest is Test, Utils { vm.prank(sponsor); messageSwitchboard.approvePlug(address(srcPlug)); } - + /** * @dev Complete setup for most tests (sibling config + min fees) */ @@ -333,7 +368,7 @@ contract MessageSwitchboardTest is Test, Utils { _setupSiblingConfig(); _setupMinFees(); } - + /** * @dev Complete setup for sponsored tests (sibling config + sponsor approval) */ @@ -342,31 +377,30 @@ contract MessageSwitchboardTest is Test, Utils { _approvePlugForSponsor(); } - function test_setup_Success() public view { assertTrue(messageSwitchboard.chainSlug() == SRC_CHAIN); assertTrue(messageSwitchboard.switchboardId() > 0); assertTrue(messageSwitchboard.owner() == owner); } - + // ============================================ // CRITICAL TESTS - GROUP 1: Sibling Management // ============================================ - + function test_setSiblingConfig_Success() public { bytes32 siblingSocket = toBytes32Format(address(0x1234)); bytes32 siblingSwitchboard = toBytes32Format(address(0x5678)); - + vm.expectEmit(true, true, true, false); emit SiblingConfigSet(DST_CHAIN, siblingSocket, siblingSwitchboard); - + vm.prank(owner); messageSwitchboard.setSiblingConfig(DST_CHAIN, siblingSocket, siblingSwitchboard); - + assertEq(messageSwitchboard.siblingSockets(DST_CHAIN), siblingSocket); assertEq(messageSwitchboard.siblingSwitchboards(DST_CHAIN), siblingSwitchboard); } - + function test_setSiblingConfig_NotOwner_Reverts() public { vm.prank(address(0x9999)); vm.expectRevert(); @@ -376,57 +410,62 @@ contract MessageSwitchboardTest is Test, Utils { toBytes32Format(address(0x5678)) ); } - - function test_registerSibling_Success() public { - + function test_registerSibling_Success() public { _setupSiblingConfig(); vm.expectEmit(true, true, true, false); emit PlugConfigUpdated(address(srcPlug), DST_CHAIN, toBytes32Format(address(dstPlug))); srcPlug.registerSibling(DST_CHAIN, address(dstPlug)); - - (bytes memory configData) = messageSwitchboard.getPlugConfig(address(srcPlug), abi.encode(DST_CHAIN)); - (bytes32 siblingPlug) = abi.decode(configData, (bytes32)); + + bytes memory configData = messageSwitchboard.getPlugConfig( + address(srcPlug), + abi.encode(DST_CHAIN) + ); + bytes32 siblingPlug = abi.decode(configData, (bytes32)); assertEq(siblingPlug, toBytes32Format(address(dstPlug))); } - + function test_registerSibling_SiblingSocketNotFound_Reverts() public { _setupSiblingConfig(); vm.expectRevert(MessageSwitchboard.SiblingSocketNotFound.selector); srcPlug.registerSibling(999, address(0x9999)); } - + // ============================================ // CRITICAL TESTS - GROUP 2: processTrigger - Native Flow // ============================================ - + function test_processTrigger_Native_Success() public { // Setup sibling config _setupCompleteNative(); - + // Prepare overrides for version 1 (Native) bytes memory overrides = abi.encode( - uint8(1), // version + uint8(1), // version DST_CHAIN, - uint256(100000), // gasLimit - uint256(0), // value - refundAddress // refundAddress + uint256(100000), // gasLimit + uint256(0), // value + refundAddress // refundAddress ); // Set overrides on the plug srcPlug.setOverrides(overrides); - + bytes memory payload = abi.encode("test data"); uint256 msgValue = MIN_FEES + 0.001 ether; - + // Get counters before the call uint64 triggerCounterBefore = socket.triggerCounter(); uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); - + // Calculate expected values bytes32 expectedTriggerId = calculateTriggerId(address(socket), triggerCounterBefore); - bytes32 expectedPayloadId = calculatePayloadId(expectedTriggerId, payloadCounterBefore, DST_CHAIN); - + bytes32 expectedPayloadId = calculatePayloadId( + expectedTriggerId, + payloadCounterBefore, + DST_CHAIN + ); + // Create digest params for the expected event DigestParams memory expectedDigestParams = _createDigestParams( expectedPayloadId, @@ -434,7 +473,7 @@ contract MessageSwitchboardTest is Test, Utils { payload ); bytes32 expectedDigest = calculateDigest(expectedDigestParams); - + // Expect the event with calculated values vm.expectEmit(true, true, false, false); emit MessageOutbound( @@ -447,28 +486,28 @@ contract MessageSwitchboardTest is Test, Utils { 0, address(0) ); - + vm.deal(address(srcPlug), 10 ether); bytes32 actualTriggerId = srcPlug.triggerSocket{value: msgValue}(payload); - + // Verify trigger ID matches assertEq(actualTriggerId, expectedTriggerId); - + // Verify payload counter increased assertEq(messageSwitchboard.payloadCounter(), payloadCounterBefore + 1); - + // Verify fees stored - (, address storedRefundAddr,,,) = messageSwitchboard.payloadFees(expectedPayloadId); + (, address storedRefundAddr, , , ) = messageSwitchboard.payloadFees(expectedPayloadId); assertEq(storedRefundAddr, refundAddress); } - + function test_processTrigger_Native_InsufficientValue_Reverts() public { // Setup sibling config _setupSiblingConfig(); - + // Set minimum fees _setupMinFees(); - + // Try with insufficient value bytes memory overrides = abi.encode( uint8(1), // version @@ -477,68 +516,72 @@ contract MessageSwitchboardTest is Test, Utils { 0, refundAddress ); - + // Set overrides on the plug srcPlug.setOverrides(overrides); - + vm.deal(address(srcPlug), 10 ether); vm.prank(address(srcPlug)); vm.expectRevert(MessageSwitchboard.InsufficientMsgValue.selector); srcPlug.triggerSocket{value: MIN_FEES - 1}(abi.encode("test")); } - + function test_processTrigger_Native_SiblingSocketNotFound_Reverts() public { bytes memory overrides = abi.encode(uint8(1), DST_CHAIN, 100000, 0, refundAddress); - + // Set overrides on the plug srcPlug.setOverrides(overrides); - + vm.prank(address(srcPlug)); vm.expectRevert(MessageSwitchboard.SiblingSocketNotFound.selector); srcPlug.triggerSocket(abi.encode("test")); } - + // ============================================ // CRITICAL TESTS - GROUP 3: processTrigger - Sponsored Flow // ============================================ - + function test_processTrigger_Sponsored_Success() public { // Setup sibling config _setupSiblingConfig(); - + // Sponsor approves plug _approvePlugForSponsor(); - + // Prepare overrides for version 2 (Sponsored) bytes memory overrides = abi.encode( - uint8(2), // version + uint8(2), // version DST_CHAIN, - uint256(100000), // gasLimit - uint256(0), // value + uint256(100000), // gasLimit + uint256(0), // value uint256(10 ether), // maxFees - sponsor // sponsor + sponsor // sponsor ); - + bytes memory payload = abi.encode("sponsored test"); - + // Get counters before the call uint64 triggerCounterBefore = socket.triggerCounter(); uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); - + // Calculate expected values bytes32 expectedTriggerId = calculateTriggerId(address(socket), triggerCounterBefore); - bytes32 expectedPayloadId = calculatePayloadId(expectedTriggerId, payloadCounterBefore, DST_CHAIN); - + bytes32 expectedPayloadId = calculatePayloadId( + expectedTriggerId, + payloadCounterBefore, + DST_CHAIN + ); + // Set overrides on the plug srcPlug.setOverrides(overrides); - + // Only check indexed fields (payloadId, dstChainSlug, sponsor) - skip data fields for struct comparison vm.expectEmit(true, true, false, false); emit MessageOutbound( expectedPayloadId, DST_CHAIN, bytes32(0), // digest - not checked - DigestParams({ // Only structure matters, values not checked + DigestParams({ // Only structure matters, values not checked socket: bytes32(0), transmitter: bytes32(0), payloadId: bytes32(0), @@ -552,97 +595,92 @@ contract MessageSwitchboardTest is Test, Utils { prevBatchDigestHash: bytes32(0), extraData: "" }), - true, // isSponsored + true, // isSponsored 0, 10 ether, sponsor ); - + vm.prank(address(srcPlug)); bytes32 actualTriggerId = srcPlug.triggerSocket(payload); - + // Verify trigger ID matches assertEq(actualTriggerId, expectedTriggerId); - + // Verify sponsored fees were stored - (uint256 maxFees,) = messageSwitchboard.sponsoredPayloadFees(expectedPayloadId); + (uint256 maxFees, ) = messageSwitchboard.sponsoredPayloadFees(expectedPayloadId); assertEq(maxFees, 10 ether); } - + function test_processTrigger_Sponsored_NotApproved_Reverts() public { // Setup sibling config _setupSiblingConfig(); - + // Don't approve - try without approval - bytes memory overrides = abi.encode( - uint8(2), - DST_CHAIN, - 100000, - 0, - 10 ether, - sponsor - ); - + bytes memory overrides = abi.encode(uint8(2), DST_CHAIN, 100000, 0, 10 ether, sponsor); + // Set overrides on the plug srcPlug.setOverrides(overrides); - + vm.prank(address(srcPlug)); vm.expectRevert(MessageSwitchboard.PlugNotApprovedBySponsor.selector); srcPlug.triggerSocket(abi.encode("test")); } - + function test_processTrigger_UnsupportedVersion_Reverts() public { bytes memory overrides = abi.encode(uint8(99), DST_CHAIN, 100000, 0, refundAddress); - + // Set overrides on the plug srcPlug.setOverrides(overrides); - + vm.prank(address(srcPlug)); vm.expectRevert(MessageSwitchboard.UnsupportedOverrideVersion.selector); srcPlug.triggerSocket(abi.encode("test")); } - + // ============================================ // CRITICAL TESTS - GROUP 4: Enhanced Attest // ============================================ - + function test_attest_SuccessWithTargetVerification() public { // Setup sibling config _setupSiblingConfig(); - + // Create digest params (using any valid values since we're just testing attestation) bytes32 triggerId = bytes32(uint256(0x1234)); bytes memory payload = abi.encode("test"); bytes32 payloadId = bytes32(uint256(0x5678)); - + DigestParams memory digestParams = _createDigestParams(payloadId, triggerId, payload); - + // Calculate the actual digest from digestParams (as done in MessageSwitchboard._createDigest) bytes32 digest = calculateDigest(digestParams); - + // Create watcher signature - attest signs: keccak256(abi.encodePacked(switchboardAddress, chainSlug, digest)) - bytes32 signatureDigest = keccak256(abi.encodePacked(toBytes32Format(address(messageSwitchboard)), SRC_CHAIN, digest)); + bytes32 signatureDigest = keccak256( + abi.encodePacked(toBytes32Format(address(messageSwitchboard)), SRC_CHAIN, digest) + ); bytes memory signature = createSignature(signatureDigest, watcherPrivateKey); - + // Register this digest as attested (simulating the flow) vm.prank(getWatcherAddress()); vm.expectEmit(true, false, true, false); emit Attested(payloadId, digest, getWatcherAddress()); messageSwitchboard.attest(digestParams, signature); - + // Verify it's attested assertTrue(messageSwitchboard.isAttested(digest)); } - + function test_attest_InvalidTarget_Reverts() public { // Setup sibling config _setupSiblingConfig(); - + // Create digest with wrong target (address(0x9999) is not registered as a sibling plug) bytes32 triggerId = bytes32(uint256(0x1234)); bytes memory payload = abi.encode("test"); bytes32 payloadId = bytes32(uint256(0x5678)); - + // Create digest params with invalid target bytes32 siblingSocket = messageSwitchboard.siblingSockets(DST_CHAIN); DigestParams memory digestParams = DigestParams({ @@ -659,292 +697,309 @@ contract MessageSwitchboardTest is Test, Utils { prevBatchDigestHash: triggerId, extraData: abi.encode(SRC_CHAIN, toBytes32Format(address(srcPlug))) }); - + // Calculate the actual digest from digestParams (signature needs valid digest first) bytes32 digest = calculateDigest(digestParams); - + // Create watcher signature with correct digest (this will pass watcher check) - bytes32 signatureDigest = keccak256(abi.encodePacked(toBytes32Format(address(messageSwitchboard)), SRC_CHAIN, digest)); + bytes32 signatureDigest = keccak256( + abi.encodePacked(toBytes32Format(address(messageSwitchboard)), SRC_CHAIN, digest) + ); bytes memory signature = createSignature(signatureDigest, watcherPrivateKey); - + vm.prank(getWatcherAddress()); vm.expectRevert(MessageSwitchboard.InvalidTargetVerification.selector); messageSwitchboard.attest(digestParams, signature); } - + function test_attest_InvalidWatcher_Reverts() public { // Setup sibling config _setupSiblingConfig(); - + bytes32 payloadId = bytes32(uint256(0x5678)); bytes32 triggerId = bytes32(uint256(0x1234)); - DigestParams memory digestParams = _createDigestParams(payloadId, triggerId, abi.encode("test")); - + DigestParams memory digestParams = _createDigestParams( + payloadId, + triggerId, + abi.encode("test") + ); + // Calculate the actual digest from digestParams bytes32 digest = calculateDigest(digestParams); - + // Invalid signature from non-watcher (random private key) - bytes32 signatureDigest = keccak256(abi.encodePacked(toBytes32Format(address(messageSwitchboard)), SRC_CHAIN, digest)); - bytes memory signature = createSignature(signatureDigest, 0x2222222222222222222222222222222222222222222222222222222222222222); // Random key - + bytes32 signatureDigest = keccak256( + abi.encodePacked(toBytes32Format(address(messageSwitchboard)), SRC_CHAIN, digest) + ); + bytes memory signature = createSignature( + signatureDigest, + 0x2222222222222222222222222222222222222222222222222222222222222222 + ); // Random key + vm.prank(address(0x9999)); vm.expectRevert(MessageSwitchboard.WatcherNotFound.selector); messageSwitchboard.attest(digestParams, signature); } - + function test_attest_AlreadyAttested_Reverts() public { // Setup sibling config _setupSiblingConfig(); - + bytes32 payloadId = bytes32(uint256(0x5678)); bytes32 triggerId = bytes32(uint256(0x1234)); - DigestParams memory digestParams = _createDigestParams(payloadId, triggerId, abi.encode("test")); - + DigestParams memory digestParams = _createDigestParams( + payloadId, + triggerId, + abi.encode("test") + ); + // Calculate the actual digest from digestParams bytes32 digest = calculateDigest(digestParams); - + // Create watcher signature - bytes32 signatureDigest = keccak256(abi.encodePacked(toBytes32Format(address(messageSwitchboard)), SRC_CHAIN, digest)); + bytes32 signatureDigest = keccak256( + abi.encodePacked(toBytes32Format(address(messageSwitchboard)), SRC_CHAIN, digest) + ); bytes memory signature = createSignature(signatureDigest, watcherPrivateKey); - + // First attest - should succeed vm.prank(getWatcherAddress()); messageSwitchboard.attest(digestParams, signature); - + // Second attest - should revert vm.prank(getWatcherAddress()); vm.expectRevert(MessageSwitchboard.AlreadyAttested.selector); messageSwitchboard.attest(digestParams, signature); } - + // ============================================ // IMPORTANT TESTS - GROUP 5: Sponsor Approvals // ============================================ - + function test_approvePlug_Success() public { vm.expectEmit(true, true, false, false); emit PlugApproved(sponsor, address(srcPlug)); - + vm.prank(sponsor); messageSwitchboard.approvePlug(address(srcPlug)); - + assertTrue(messageSwitchboard.sponsorApprovals(sponsor, address(srcPlug))); } - + function test_approvePlugs_Batch_Success() public { address[] memory plugs = new address[](2); plugs[0] = address(srcPlug); plugs[1] = address(dstPlug); - + vm.startPrank(sponsor); vm.expectEmit(true, true, false, false); emit PlugApproved(sponsor, address(srcPlug)); - + vm.expectEmit(true, true, false, false); emit PlugApproved(sponsor, address(dstPlug)); - + messageSwitchboard.approvePlugs(plugs); - + assertTrue(messageSwitchboard.sponsorApprovals(sponsor, address(srcPlug))); assertTrue(messageSwitchboard.sponsorApprovals(sponsor, address(dstPlug))); - + vm.stopPrank(); } - + function test_revokePlug_Success() public { // First approve vm.prank(sponsor); messageSwitchboard.approvePlug(address(srcPlug)); assertTrue(messageSwitchboard.sponsorApprovals(sponsor, address(srcPlug))); - + // Now revoke vm.expectEmit(true, true, false, false); emit PlugRevoked(sponsor, address(srcPlug)); - + vm.prank(sponsor); messageSwitchboard.revokePlug(address(srcPlug)); - + assertFalse(messageSwitchboard.sponsorApprovals(sponsor, address(srcPlug))); } - + function test_revokePlugs_Batch_Success() public { address[] memory plugs = new address[](2); plugs[0] = address(srcPlug); plugs[1] = address(dstPlug); - + vm.startPrank(sponsor); messageSwitchboard.approvePlugs(plugs); vm.stopPrank(); - + // Now revoke batch vm.startPrank(sponsor); vm.expectEmit(true, true, false, false); emit PlugRevoked(sponsor, address(srcPlug)); - + vm.expectEmit(true, true, false, false); emit PlugRevoked(sponsor, address(dstPlug)); - + messageSwitchboard.revokePlugs(plugs); - + assertFalse(messageSwitchboard.sponsorApprovals(sponsor, address(srcPlug))); assertFalse(messageSwitchboard.sponsorApprovals(sponsor, address(dstPlug))); - + vm.stopPrank(); } - + // ============================================ // CRITICAL TESTS - GROUP 6: Refund Flow // ============================================ - + function test_markRefundEligible_Success() public { // Setup and create a payload _setupCompleteNative(); - + bytes32 payloadId = _createNativePayload("test", MIN_FEES); - + // Verify fees exist - (uint256 nativeFees,,,,) = messageSwitchboard.payloadFees(payloadId); + (uint256 nativeFees, , , , ) = messageSwitchboard.payloadFees(payloadId); assertEq(nativeFees, MIN_FEES); - + // Mark eligible bytes memory signature = _createWatcherSignature(payloadId); - + vm.expectEmit(true, true, false, false); emit RefundEligibilityMarked(payloadId, getWatcherAddress()); - + vm.prank(getWatcherAddress()); messageSwitchboard.markRefundEligible(payloadId, signature); - + // Verify marked eligible - (,, bool isEligible,,) = messageSwitchboard.payloadFees(payloadId); + (, , bool isEligible, , ) = messageSwitchboard.payloadFees(payloadId); assertTrue(isEligible); } - + function test_markRefundEligible_NoFeesToRefund_Reverts() public { // Create a non-existent payloadId (one that was never created) bytes32 payloadId = bytes32(uint256(0x9999)); - + // Create valid watcher signature (this will pass watcher check) bytes memory signature = _createWatcherSignature(payloadId); - + // Should revert with NoFeesToRefund because payload doesn't exist vm.prank(getWatcherAddress()); vm.expectRevert(MessageSwitchboard.NoFeesToRefund.selector); messageSwitchboard.markRefundEligible(payloadId, signature); } - + function test_refund_Success() public { // Setup and create payload _setupCompleteNative(); - + bytes32 payloadId = _createNativePayload("test", MIN_FEES); - + // Mark eligible bytes memory signature = _createWatcherSignature(payloadId); vm.prank(getWatcherAddress()); messageSwitchboard.markRefundEligible(payloadId, signature); - + // Refund uint256 balanceBefore = refundAddress.balance; vm.deal(address(messageSwitchboard), MIN_FEES); - + vm.expectEmit(true, true, false, false); emit Refunded(payloadId, refundAddress, MIN_FEES); - + vm.prank(refundAddress); messageSwitchboard.refund(payloadId); - + assertEq(refundAddress.balance, balanceBefore + MIN_FEES); - + // Verify marked as refunded - (,,, bool isRefunded,) = messageSwitchboard.payloadFees(payloadId); + (, , , bool isRefunded, ) = messageSwitchboard.payloadFees(payloadId); assertTrue(isRefunded); } - + function test_refund_NotEligible_Reverts() public { bytes32 payloadId = keccak256("test"); - + vm.prank(refundAddress); vm.expectRevert(MessageSwitchboard.RefundNotEligible.selector); messageSwitchboard.refund(payloadId); } - + function test_refund_UnauthorizedCaller_Reverts() public { _setupCompleteNative(); - + // Create a payload and get its ID bytes32 payloadId = _createNativePayload("test", MIN_FEES); - + // Mark eligible bytes memory signature = _createWatcherSignature(payloadId); vm.prank(getWatcherAddress()); messageSwitchboard.markRefundEligible(payloadId, signature); - + vm.deal(address(messageSwitchboard), MIN_FEES); - + // Try to refund from wrong address vm.prank(address(0x9999)); vm.expectRevert(MessageSwitchboard.UnauthorizedRefund.selector); messageSwitchboard.refund(payloadId); } - + // ============================================ // IMPORTANT TESTS - GROUP 7: Fee Updates // ============================================ - + function test_setMinMsgValueFeesOwner_Success() public { uint256 newFee = 0.002 ether; - + vm.expectEmit(true, true, true, false); emit MinMsgValueFeesSet(DST_CHAIN, newFee, owner); - + vm.prank(owner); messageSwitchboard.setMinMsgValueFeesOwner(DST_CHAIN, newFee); - + assertEq(messageSwitchboard.minMsgValueFees(DST_CHAIN), newFee); } - + function test_setMinMsgValueFeesBatchOwner_Success() public { uint32[] memory chainSlugs = new uint32[](2); chainSlugs[0] = DST_CHAIN; chainSlugs[1] = 3; - + uint256[] memory minFees = new uint256[](2); minFees[0] = 0.001 ether; minFees[1] = 0.002 ether; - + vm.prank(owner); messageSwitchboard.setMinMsgValueFeesBatchOwner(chainSlugs, minFees); - + assertEq(messageSwitchboard.minMsgValueFees(chainSlugs[0]), 0.001 ether); assertEq(messageSwitchboard.minMsgValueFees(chainSlugs[1]), 0.002 ether); } - + function test_setMinMsgValueFeesBatchOwner_ArrayLengthMismatch_Reverts() public { uint32[] memory chainSlugs = new uint32[](2); chainSlugs[0] = DST_CHAIN; chainSlugs[1] = 3; - + uint256[] memory minFees = new uint256[](1); // Length mismatch minFees[0] = 0.001 ether; - + vm.prank(owner); vm.expectRevert(MessageSwitchboard.ArrayLengthMismatch.selector); messageSwitchboard.setMinMsgValueFeesBatchOwner(chainSlugs, minFees); } - + // ============================================ // IMPORTANT TESTS - GROUP 8: increaseFeesForPayload // ============================================ - + function test_increaseFeesForPayload_Native_Success() public { // Setup sibling config and min fees _setupCompleteNative(); - + bytes memory feesData = abi.encode(uint8(1)); // Native fees type uint256 additionalFees = 0.01 ether; uint256 initialFees = MIN_FEES + 0.001 ether; - + // First create a payload via processTrigger bytes memory overrides = abi.encode( uint8(1), // version @@ -956,44 +1011,44 @@ contract MessageSwitchboardTest is Test, Utils { address(0), // sponsor false // isSponsored ); - + // Set overrides on the plug srcPlug.setOverrides(overrides); - + // Get counters before creating payload uint64 triggerCounterBefore = socket.triggerCounter(); uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); - + vm.deal(address(srcPlug), 1 ether); vm.prank(address(srcPlug)); bytes32 actualTriggerId = srcPlug.triggerSocket{value: initialFees}(abi.encode("payload")); - + // Calculate the actual payloadId bytes32 payloadId = calculatePayloadId(actualTriggerId, payloadCounterBefore, DST_CHAIN); - + // Verify initial fees were stored - (uint256 nativeFeesBefore,,,,) = messageSwitchboard.payloadFees(payloadId); + (uint256 nativeFeesBefore, , , , ) = messageSwitchboard.payloadFees(payloadId); assertEq(nativeFeesBefore, initialFees); - + // Now test fee increase vm.expectEmit(true, true, false, false); emit FeesIncreased(payloadId, additionalFees, feesData); - + vm.prank(address(srcPlug)); srcPlug.increaseFeesForPayload{value: additionalFees}(payloadId, feesData); - + // Verify fees increased - (uint256 nativeFeesAfter,,,,) = messageSwitchboard.payloadFees(payloadId); + (uint256 nativeFeesAfter, , , , ) = messageSwitchboard.payloadFees(payloadId); assertEq(nativeFeesAfter, initialFees + additionalFees); } - + function test_increaseFeesForPayload_Sponsored_Success() public { // Setup sibling config and sponsor approval _setupCompleteSponsored(); - + uint256 newMaxFees = 0.05 ether; bytes memory feesData = abi.encode(uint8(2), newMaxFees); // Sponsored fees type + new maxFees - + // First create a sponsored payload via processTrigger bytes memory overrides = abi.encode( uint8(2), // version @@ -1003,44 +1058,44 @@ contract MessageSwitchboardTest is Test, Utils { uint256(0.02 ether), // maxFees sponsor // sponsor ); - + // Set overrides on the plug srcPlug.setOverrides(overrides); - + // Get counters before creating payload uint64 triggerCounterBefore = socket.triggerCounter(); uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); - + vm.prank(address(srcPlug)); bytes32 actualTriggerId = srcPlug.triggerSocket(abi.encode("payload")); - + // Calculate the actual payloadId bytes32 payloadId = calculatePayloadId(actualTriggerId, payloadCounterBefore, DST_CHAIN); - + // Verify initial maxFees were stored - (uint256 maxFeesBefore,) = messageSwitchboard.sponsoredPayloadFees(payloadId); + (uint256 maxFeesBefore, ) = messageSwitchboard.sponsoredPayloadFees(payloadId); assertEq(maxFeesBefore, 0.02 ether); - + // Now test sponsored fee increase vm.expectEmit(true, true, false, false); emit SponsoredFeesIncreased(payloadId, newMaxFees, address(srcPlug)); - + vm.prank(address(srcPlug)); srcPlug.increaseFeesForPayload(payloadId, feesData); - + // Verify maxFees updated - (uint256 maxFeesAfter,) = messageSwitchboard.sponsoredPayloadFees(payloadId); + (uint256 maxFeesAfter, ) = messageSwitchboard.sponsoredPayloadFees(payloadId); assertEq(maxFeesAfter, newMaxFees); } - + function test_increaseFeesForPayload_UnauthorizedPlug_Reverts() public { // Setup sibling config and min fees _setupCompleteNative(); - + bytes memory feesData = abi.encode(uint8(1)); // Native fees type uint256 additionalFees = 0.01 ether; uint256 initialFees = MIN_FEES + 0.001 ether; - + // Create payload with srcPlug bytes memory overrides = abi.encode( uint8(1), // version @@ -1052,32 +1107,32 @@ contract MessageSwitchboardTest is Test, Utils { address(0), // sponsor false // isSponsored ); - + // Set overrides on the plug srcPlug.setOverrides(overrides); - + // Get counters before creating payload uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); - + vm.deal(address(srcPlug), 1 ether); vm.prank(address(srcPlug)); bytes32 actualTriggerId = srcPlug.triggerSocket{value: initialFees}(abi.encode("payload")); - + // Calculate the actual payloadId bytes32 payloadId = calculatePayloadId(actualTriggerId, payloadCounterBefore, DST_CHAIN); - + // Try to increase fees with different plug - should revert because plug doesn't match vm.deal(address(dstPlug), 1 ether); vm.expectRevert(MessageSwitchboard.UnauthorizedFeeIncrease.selector); vm.prank(address(dstPlug)); // Different plug (not the one that created the payload) dstPlug.increaseFeesForPayload{value: additionalFees}(payloadId, feesData); } - + function test_increaseFeesForPayload_InvalidFeesType_Reverts() public { bytes memory feesData = abi.encode(uint8(3)); // Invalid fees type uint256 additionalFees = 0.01 ether; bytes32 payloadId = bytes32(uint256(0x9999)); // Non-existent payloadId - + // Socket's increaseFeesForPayload calls switchboard's increaseFeesForPayload with plug as msg.sender // Switchboard will decode feesType and revert with InvalidFeesType before checking authorization vm.deal(address(srcPlug), 1 ether); @@ -1085,16 +1140,16 @@ contract MessageSwitchboardTest is Test, Utils { vm.expectRevert(MessageSwitchboard.InvalidFeesType.selector); srcPlug.increaseFeesForPayload{value: additionalFees}(payloadId, feesData); } - + function test_increaseFeesForPayload_NotSocket_Reverts() public { bytes32 payloadId = keccak256("payload"); bytes memory feesData = abi.encode(uint8(1)); // Native fees type uint256 additionalFees = 0.01 ether; - + vm.expectRevert(SwitchboardBase.NotSocket.selector); messageSwitchboard.increaseFeesForPayload{value: additionalFees}( - payloadId, - address(srcPlug), + payloadId, + address(srcPlug), feesData ); } @@ -1103,7 +1158,7 @@ contract MessageSwitchboardTest is Test, Utils { /** * @title MessageSwitchboard Test Suite * @notice Comprehensive tests for MessageSwitchboard unique functionality - * + * * Test Coverage: * - Sibling management (setSiblingConfig, registerSibling) * - processTrigger Native flow (version 1) with fee handling @@ -1114,8 +1169,7 @@ contract MessageSwitchboardTest is Test, Utils { * - Refund flow (markRefundEligible + refund) * - Fee updates (owner + batch) * - increaseFeesForPayload - * + * * Total Tests: ~40 * Coverage: All critical and important MessageSwitchboard functionality */ - From e7970bf354afeb11451d357e0748faf872d72cc9 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 7 Nov 2025 12:53:01 +0530 Subject: [PATCH 05/10] fix: trigger --- contracts/evmx/watcher/Watcher.sol | 3 ++ contracts/protocol/Socket.sol | 4 +- .../protocol/interfaces/ISwitchboard.sol | 2 +- .../protocol/switchboard/FastSwitchboard.sol | 21 ++++++++- .../switchboard/MessageSwitchboard.sol | 43 +++++++++++++++---- .../protocol/switchboard/SwitchboardBase.sol | 3 ++ contracts/utils/common/Structs.sol | 1 + 7 files changed, 64 insertions(+), 13 deletions(-) diff --git a/contracts/evmx/watcher/Watcher.sol b/contracts/evmx/watcher/Watcher.sol index 6d61e704..4a4934fd 100644 --- a/contracts/evmx/watcher/Watcher.sol +++ b/contracts/evmx/watcher/Watcher.sol @@ -213,6 +213,9 @@ contract Watcher is Initializable, Configurations { if (!isValidPlug[appGateway][params_.chainSlug][params_.plug]) revert InvalidCallerTriggered(); + uint256 deadline = abi.decode(params_.overrides, (uint256)); + if (deadline < block.timestamp) revert DeadlinePassed(); + IERC20(address(feesManager__())).transferFrom(appGateway, address(this), triggerFees); triggerFromChainSlug = params_.chainSlug; triggerFromPlug = params_.plug; diff --git a/contracts/protocol/Socket.sol b/contracts/protocol/Socket.sol index a05486f5..3e0ec307 100644 --- a/contracts/protocol/Socket.sol +++ b/contracts/protocol/Socket.sol @@ -213,7 +213,7 @@ contract Socket is SocketUtils { triggerId = _encodeTriggerId(); // todo: need gas limit? - ISwitchboard(switchboardAddress).processTrigger{value: value_}( + bytes memory overridesData = ISwitchboard(switchboardAddress).processTrigger{value: value_}( plug_, triggerId, data_, @@ -225,7 +225,7 @@ contract Socket is SocketUtils { bytes32(0), // TODO: clean this up switchboardId, toBytes32Format(plug_), - plugOverrides, + overridesData, data_ ); } diff --git a/contracts/protocol/interfaces/ISwitchboard.sol b/contracts/protocol/interfaces/ISwitchboard.sol index 5ce1dcef..0fe18a17 100644 --- a/contracts/protocol/interfaces/ISwitchboard.sol +++ b/contracts/protocol/interfaces/ISwitchboard.sol @@ -36,7 +36,7 @@ interface ISwitchboard { bytes32 triggerId_, bytes calldata payload_, bytes calldata overrides_ - ) external payable; + ) external payable returns (bytes memory overridesData); /** * @notice Gets the transmitter for a given payload diff --git a/contracts/protocol/switchboard/FastSwitchboard.sol b/contracts/protocol/switchboard/FastSwitchboard.sol index 9a5ebca9..8dcd7154 100644 --- a/contracts/protocol/switchboard/FastSwitchboard.sol +++ b/contracts/protocol/switchboard/FastSwitchboard.sol @@ -11,6 +11,7 @@ import {toBytes32Format} from "../../utils/common/Converters.sol"; * that enables payload attestations from watchers */ contract FastSwitchboard is SwitchboardBase { + uint256 public defaultDeadline = 1 days; // used to track if watcher have attested a payload // payloadId => isAttested mapping(bytes32 => bool) public isAttested; @@ -25,6 +26,10 @@ contract FastSwitchboard is SwitchboardBase { error InvalidSource(); // Event emitted when watcher attests a payload event Attested(bytes32 digest, address watcher); + // Event emitted when reverting trigger is set + event RevertingTriggerSet(bytes32 triggerId, bool isReverting); + // Event emitted when default deadline is set + event DefaultDeadlineSet(uint256 defaultDeadline); /** * @notice Event emitted when plug configuration is updated */ @@ -83,7 +88,11 @@ contract FastSwitchboard is SwitchboardBase { bytes32 triggerId_, bytes calldata payload_, bytes calldata overrides_ - ) external payable virtual {} + ) external payable virtual returns (bytes memory overridesData) { + uint256 deadline = abi.decode(overrides_, (uint256)); + if (deadline == 0) return abi.encode(block.timestamp + defaultDeadline); + return overrides_; + } /** * @inheritdoc ISwitchboard @@ -103,6 +112,16 @@ contract FastSwitchboard is SwitchboardBase { emit PlugConfigUpdated(plug_, appGatewayId_); } + function setRevertingTrigger(bytes32 triggerId_, bool isReverting_) external onlyOwner { + revertingTriggers[triggerId_] = isReverting_; + emit RevertingTriggerSet(triggerId_, isReverting_); + } + + function setDefaultDeadline(uint256 defaultDeadline_) external onlyOwner { + defaultDeadline = defaultDeadline_; + emit DefaultDeadlineSet(defaultDeadline_); + } + /** * @inheritdoc ISwitchboard */ diff --git a/contracts/protocol/switchboard/MessageSwitchboard.sol b/contracts/protocol/switchboard/MessageSwitchboard.sol index d9746527..cf6983a1 100644 --- a/contracts/protocol/switchboard/MessageSwitchboard.sol +++ b/contracts/protocol/switchboard/MessageSwitchboard.sol @@ -43,6 +43,8 @@ contract MessageSwitchboard is SwitchboardBase { // nonce tracking for fee updates: updater => nonce => used mapping(address => mapping(uint256 => bool)) public usedNonces; + uint256 public defaultDeadline = 1 days; + // Error emitted when a payload is already attested by watcher. error AlreadyAttested(); // Error emitted when watcher is not valid @@ -117,6 +119,9 @@ contract MessageSwitchboard is SwitchboardBase { address indexed plug ); + // Event emitted when reverting trigger is set + event RevertingTriggerSet(bytes32 triggerId, bool isReverting); + /** * @dev Constructor function for the MessageSwitchboard contract * @param chainSlug_ Chain slug of the chain where the contract is deployed @@ -146,6 +151,11 @@ contract MessageSwitchboard is SwitchboardBase { emit SiblingConfigSet(chainSlug_, socket_, switchboard_); } + function setRevertingTrigger(bytes32 triggerId_, bool isReverting_) external onlyOwner { + revertingTriggers[triggerId_] = isReverting_; + emit RevertingTriggerSet(triggerId_, isReverting_); + } + /** * @dev Function to process trigger and create payload * @param plug_ Source plug address @@ -158,9 +168,10 @@ contract MessageSwitchboard is SwitchboardBase { bytes32 triggerId_, bytes calldata payload_, bytes calldata overrides_ - ) external payable override onlySocket { + ) external payable override onlySocket returns (bytes memory overridesData) { MessageOverrides memory overrides = _decodeOverrides(overrides_); _validateSibling(overrides.dstChainSlug, plug_); + overridesData = abi.encode(overrides); // Create digest and payload ID (common for both flows) ( @@ -228,13 +239,20 @@ contract MessageSwitchboard is SwitchboardBase { */ function _decodeOverrides( bytes calldata overrides_ - ) internal pure returns (MessageOverrides memory) { + ) internal returns (MessageOverrides memory) { uint8 version = abi.decode(overrides_, (uint8)); if (version == 1) { - // Version 1: Native flow - (, uint32 dstChainSlug, uint256 gasLimit, uint256 value, address refundAddress) = abi - .decode(overrides_, (uint8, uint32, uint256, uint256, address)); + // Version 1: Native flow + ( + , + uint32 dstChainSlug, + uint256 gasLimit, + uint256 value, + address refundAddress, + uint256 deadline + ) = abi.decode(overrides_, (uint8, uint32, uint256, uint256, address, uint256)); + if(deadline == 0) deadline = block.timestamp + defaultDeadline; return MessageOverrides({ @@ -244,7 +262,8 @@ contract MessageSwitchboard is SwitchboardBase { refundAddress: refundAddress, maxFees: 0, sponsor: address(0), - isSponsored: false + isSponsored: false, + deadline: deadline }); } else if (version == 2) { // Version 2: Sponsored flow @@ -254,8 +273,13 @@ contract MessageSwitchboard is SwitchboardBase { uint256 gasLimit, uint256 value, uint256 maxFees, - address sponsor - ) = abi.decode(overrides_, (uint8, uint32, uint256, uint256, uint256, address)); + address sponsor, + uint256 deadline + ) = abi.decode( + overrides_, + (uint8, uint32, uint256, uint256, uint256, address, uint256) + ); + if(deadline == 0) deadline = block.timestamp + defaultDeadline; return MessageOverrides({ @@ -265,7 +289,8 @@ contract MessageSwitchboard is SwitchboardBase { refundAddress: address(0), maxFees: maxFees, sponsor: sponsor, - isSponsored: true + isSponsored: true, + deadline: deadline }); } else { revert UnsupportedOverrideVersion(); diff --git a/contracts/protocol/switchboard/SwitchboardBase.sol b/contracts/protocol/switchboard/SwitchboardBase.sol index 1f4e3a19..3c12cebc 100644 --- a/contracts/protocol/switchboard/SwitchboardBase.sol +++ b/contracts/protocol/switchboard/SwitchboardBase.sol @@ -20,6 +20,9 @@ abstract contract SwitchboardBase is ISwitchboard, AccessControl { // switchboard id uint64 public switchboardId; + // mapping of trigger id to isReverting + mapping(bytes32 => bool) public revertingTriggers; + error NotSocket(); /** * @dev Constructor of SwitchboardBase diff --git a/contracts/utils/common/Structs.sol b/contracts/utils/common/Structs.sol index b65d7775..24b214cc 100644 --- a/contracts/utils/common/Structs.sol +++ b/contracts/utils/common/Structs.sol @@ -245,4 +245,5 @@ struct MessageOverrides { uint256 maxFees; address sponsor; bool isSponsored; + uint256 deadline; } From 0ccbc5197842d67edb27417e8c4d5d09760689c0 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 7 Nov 2025 13:56:05 +0530 Subject: [PATCH 06/10] fix: watcher and transmitter fees --- contracts/evmx/fees/FeesManager.sol | 16 ++++++++-------- contracts/evmx/interfaces/IFeesManager.sol | 2 +- contracts/evmx/watcher/Watcher.sol | 21 +++++++++++++-------- contracts/utils/common/Structs.sol | 1 + 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/contracts/evmx/fees/FeesManager.sol b/contracts/evmx/fees/FeesManager.sol index a3476d53..a14156ac 100644 --- a/contracts/evmx/fees/FeesManager.sol +++ b/contracts/evmx/fees/FeesManager.sol @@ -122,24 +122,24 @@ contract FeesManager is Credit { /// @param assignTo_ The address of the transmitter function unblockAndAssignCredits( bytes32 payloadId_, - address assignTo_ + address assignTo_, + uint256 amount_ ) external override onlyWatcher { uint256 blockedCredits_ = blockedCredits[payloadId_]; if (blockedCredits_ == 0) return; - address consumeFrom = watcher__().getPayload(payloadId_).consumeFrom; + Payload memory payload = watcher__().getPayload(payloadId_); + address consumeFrom = payload.consumeFrom; // Unblock credits from the original user - userBlockedCredits[consumeFrom] -= blockedCredits_; + userBlockedCredits[consumeFrom] -= amount_; + blockedCredits[payloadId_] -= amount_; // Burn tokens from the original user - _burn(consumeFrom, blockedCredits_); - + _burn(consumeFrom, amount_); // Mint tokens to the transmitter - _mint(assignTo_, blockedCredits_); + _mint(assignTo_, amount_); - // Clean up storage - delete blockedCredits[payloadId_]; emit CreditsUnblockedAndAssigned(payloadId_, consumeFrom, assignTo_, blockedCredits_); } diff --git a/contracts/evmx/interfaces/IFeesManager.sol b/contracts/evmx/interfaces/IFeesManager.sol index fd587cb5..a60b1ece 100644 --- a/contracts/evmx/interfaces/IFeesManager.sol +++ b/contracts/evmx/interfaces/IFeesManager.sol @@ -31,7 +31,7 @@ interface IFeesManager { function blockCredits(bytes32 payloadId_, address consumeFrom_, uint256 credits_) external; - function unblockAndAssignCredits(bytes32 payloadId_, address assignTo_) external; + function unblockAndAssignCredits(bytes32 payloadId_, address assignTo_, uint256 amount_) external; function unblockCredits(bytes32 payloadId_) external; diff --git a/contracts/evmx/watcher/Watcher.sol b/contracts/evmx/watcher/Watcher.sol index 4a4934fd..49364e2e 100644 --- a/contracts/evmx/watcher/Watcher.sol +++ b/contracts/evmx/watcher/Watcher.sol @@ -101,6 +101,7 @@ contract Watcher is Initializable, Configurations { callType: payloadData.overrideParams.callType, isPayloadCancelled: false, isPayloadExecuted: false, + isTransmitterFeesSettled: false, payloadPointer: nextPayloadCount++, asyncPromise: asyncPromise, appGateway: latestAppGateway, @@ -122,7 +123,6 @@ contract Watcher is Initializable, Configurations { params_.data, (PromiseReturnData, uint256) ); - _resolvePayload(resolvedPromise, feesUsed); } @@ -135,6 +135,11 @@ contract Watcher is Initializable, Configurations { if (p.isPayloadExecuted) return; if (p.isPayloadCancelled) return; + if (!p.isTransmitterFeesSettled) { + p.isTransmitterFeesSettled = true; + feesManager__().unblockAndAssignCredits(p.payloadId, transmitter, feesUsed_); + } + p.isPayloadExecuted = true; p.resolvedAt = block.timestamp; @@ -142,7 +147,9 @@ contract Watcher is Initializable, Configurations { bool success = _markResolved(resolvedPromise_); if (!success) return; - _settlePayload(resolvedPromise_.payloadId, feesUsed_); + feesManager__().unblockAndAssignCredits(p.payloadId, address(this), p.watcherFees); + feesManager__().unblockCredits(p.payloadId); + emit PayloadSettled(p.payloadId); emit PayloadResolved(resolvedPromise_.payloadId); } @@ -265,13 +272,11 @@ contract Watcher is Initializable, Configurations { if (r.isPayloadCancelled) revert PayloadAlreadyCancelled(); r.isPayloadCancelled = true; - _settlePayload(payloadId_, r.maxFees); - emit PayloadCancelled(payloadId_); - } + r.isTransmitterFeesSettled = true; - function _settlePayload(bytes32 payloadId_, uint256 feesUsed_) internal { - feesManager__().unblockAndAssignCredits(payloadId_, transmitter); - emit PayloadSettled(payloadId_); + feesManager__().unblockAndAssignCredits(payloadId_, transmitter, r.maxFees - r.watcherFees); + feesManager__().unblockAndAssignCredits(payloadId_, address(this), r.watcherFees); + emit PayloadCancelled(payloadId_); } function watcherMultiCall(WatcherMultiCallParams[] memory params_) external payable { diff --git a/contracts/utils/common/Structs.sol b/contracts/utils/common/Structs.sol index 24b214cc..f043e87f 100644 --- a/contracts/utils/common/Structs.sol +++ b/contracts/utils/common/Structs.sol @@ -151,6 +151,7 @@ struct Payload { bytes4 callType; bool isPayloadCancelled; bool isPayloadExecuted; + bool isTransmitterFeesSettled; uint256 payloadPointer; address asyncPromise; address appGateway; From 8cbd5b7202267a384dcc5008504d359013f6987d Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 7 Nov 2025 14:04:49 +0530 Subject: [PATCH 07/10] fix: promise retryable with deadline --- contracts/evmx/helpers/AsyncDeployer.sol | 9 ++++++-- contracts/evmx/helpers/AsyncPromise.sol | 27 +++++++++++++----------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/contracts/evmx/helpers/AsyncDeployer.sol b/contracts/evmx/helpers/AsyncDeployer.sol index 3c32c16b..e5bf7cb6 100644 --- a/contracts/evmx/helpers/AsyncDeployer.sol +++ b/contracts/evmx/helpers/AsyncDeployer.sol @@ -31,6 +31,9 @@ abstract contract AsyncDeployerStorage is IAsyncDeployer { // slot 54 uint256 public asyncPromiseCounter; + // slot 55 + uint256 public defaultDeadline; + // slots [55-104] reserved for gap uint256[50] _gap_after; @@ -50,9 +53,10 @@ contract AsyncDeployer is AsyncDeployerStorage, Initializable, AddressResolverUt /// @dev it deploys the forwarder and async promise implementations and beacons for them /// @dev this contract is owner of the beacons for upgrading later /// @param owner_ The address of the contract owner - function initialize(address owner_, address addressResolver_) public reinitializer(1) { + function initialize(address owner_, address addressResolver_, uint256 defaultDeadline_) public reinitializer(1) { _initializeOwner(owner_); _setAddressResolver(addressResolver_); + defaultDeadline = defaultDeadline_; forwarderImplementation = address(new Forwarder()); asyncPromiseImplementation = address(new AsyncPromise()); @@ -140,7 +144,8 @@ contract AsyncDeployer is AsyncDeployerStorage, Initializable, AddressResolverUt AsyncPromise.initialize.selector, payloadId_, invoker_, - address(addressResolver__) + address(addressResolver__), + defaultDeadline ); // creates salt with a counter diff --git a/contracts/evmx/helpers/AsyncPromise.sol b/contracts/evmx/helpers/AsyncPromise.sol index cc396d30..64f6a440 100644 --- a/contracts/evmx/helpers/AsyncPromise.sol +++ b/contracts/evmx/helpers/AsyncPromise.sol @@ -7,7 +7,7 @@ import {AddressResolverUtil} from "./AddressResolverUtil.sol"; import {IAppGateway} from "../interfaces/IAppGateway.sol"; import "../interfaces/IPromise.sol"; import "../../utils/RescueFundsLib.sol"; -import {NotInvoker, PayloadCountMismatch} from "../../utils/common/Errors.sol"; +import {NotInvoker, PayloadCountMismatch, DeadlinePassed} from "../../utils/common/Errors.sol"; abstract contract AsyncPromiseStorage is IPromise { // slots [0-49] reserved for gap @@ -30,6 +30,9 @@ abstract contract AsyncPromiseStorage is IPromise { /// @dev The callback will be executed on this address address public override localInvoker; + /// @notice The flag to check if the transmitter fees are settled + uint256 public promiseDeadline; + // slot 51 /// @notice The return data of the promise bytes public override returnData; @@ -60,6 +63,10 @@ contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil /// @notice Error thrown when attempting to resolve an already resolved promise. error PromiseAlreadyResolved(); + + /// @notice Error thrown when attempting to resolve an already onchain reverted promise. + error PromiseAlreadyOnchainReverted(); + /// @notice Only the local invoker can set then's promise callback error OnlyInvoker(); /// @notice Error thrown when attempting to set an already existing promise @@ -80,11 +87,13 @@ contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil function initialize( bytes32 payloadId_, address invoker_, - address addressResolver_ + address addressResolver_, + uint256 deadline_ ) public reinitializer(1) { localInvoker = invoker_; payloadId = payloadId_; _setAddressResolver(addressResolver_); + promiseDeadline = deadline_ + block.timestamp; } /// @notice Marks the promise as resolved and executes the callback if set. @@ -93,11 +102,8 @@ contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil function markResolved( PromiseReturnData memory resolvedPromise_ ) external override onlyWatcher returns (bool success) { - if ( - state == AsyncPromiseState.CALLBACK_REVERTING || - state == AsyncPromiseState.ONCHAIN_REVERTING || - state == AsyncPromiseState.RESOLVED - ) revert PromiseAlreadyResolved(); + if (block.timestamp > promiseDeadline) revert DeadlinePassed(); + if (state == AsyncPromiseState.RESOLVED || state == AsyncPromiseState.ONCHAIN_REVERTING) revert PromiseAlreadyResolvedOrOnchainReverted(); state = AsyncPromiseState.RESOLVED; // Call callback to app gateway @@ -125,11 +131,8 @@ contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil function markOnchainRevert( PromiseReturnData memory resolvedPromise_ ) external override onlyWatcher { - if ( - state == AsyncPromiseState.CALLBACK_REVERTING || - state == AsyncPromiseState.ONCHAIN_REVERTING || - state == AsyncPromiseState.RESOLVED - ) revert PromiseAlreadyResolved(); + if (block.timestamp > promiseDeadline) revert DeadlinePassed(); + if (state == AsyncPromiseState.RESOLVED || state == AsyncPromiseState.ONCHAIN_REVERTING) revert PromiseAlreadyResolved(); // to update the state in case selector is bytes(0) but reverting onchain state = AsyncPromiseState.ONCHAIN_REVERTING; From 5b62e53247b923eb6c6020315580402a5e6ee329 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Sat, 8 Nov 2025 00:52:13 +0530 Subject: [PATCH 08/10] fix: tests --- contracts/evmx/helpers/AsyncPromise.sol | 5 +--- test/SetupTest.t.sol | 4 ++- test/switchboard/MessageSwitchboard.t.sol | 30 ++++++++++++++--------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/contracts/evmx/helpers/AsyncPromise.sol b/contracts/evmx/helpers/AsyncPromise.sol index 64f6a440..5932aeea 100644 --- a/contracts/evmx/helpers/AsyncPromise.sol +++ b/contracts/evmx/helpers/AsyncPromise.sol @@ -64,9 +64,6 @@ contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil /// @notice Error thrown when attempting to resolve an already resolved promise. error PromiseAlreadyResolved(); - /// @notice Error thrown when attempting to resolve an already onchain reverted promise. - error PromiseAlreadyOnchainReverted(); - /// @notice Only the local invoker can set then's promise callback error OnlyInvoker(); /// @notice Error thrown when attempting to set an already existing promise @@ -103,7 +100,7 @@ contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil PromiseReturnData memory resolvedPromise_ ) external override onlyWatcher returns (bool success) { if (block.timestamp > promiseDeadline) revert DeadlinePassed(); - if (state == AsyncPromiseState.RESOLVED || state == AsyncPromiseState.ONCHAIN_REVERTING) revert PromiseAlreadyResolvedOrOnchainReverted(); + if (state == AsyncPromiseState.RESOLVED || state == AsyncPromiseState.ONCHAIN_REVERTING) revert PromiseAlreadyResolved(); state = AsyncPromiseState.RESOLVED; // Call callback to app gateway diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 18c3ce13..9d936600 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -50,6 +50,7 @@ contract SetupStore is Test, Utils { uint256 expiryTime = 86400; uint256 bidTimeout = 86400; + uint256 defaultDeadline = 86400; uint256 maxReAuctionCount = 10; uint256 auctionEndDelaySeconds = 0; uint256 maxScheduleDelayInSeconds = 86500; @@ -287,7 +288,8 @@ contract DeploySetup is SetupStore { abi.encodeWithSelector( AsyncDeployer.initialize.selector, watcherEOA, - address(addressResolver) + address(addressResolver), + defaultDeadline ) ); asyncDeployer = AsyncDeployer(asyncDeployerProxy); diff --git a/test/switchboard/MessageSwitchboard.t.sol b/test/switchboard/MessageSwitchboard.t.sol index 5dfb2065..781cfd59 100644 --- a/test/switchboard/MessageSwitchboard.t.sol +++ b/test/switchboard/MessageSwitchboard.t.sol @@ -219,7 +219,8 @@ contract MessageSwitchboardTest is Test, Utils { DST_CHAIN, uint256(100000), // gasLimit uint256(0), // value - refundAddress // refundAddress + refundAddress, // refundAddress + 86400 // deadline ); // Set overrides on the plug @@ -254,7 +255,8 @@ contract MessageSwitchboardTest is Test, Utils { uint256(100000), // gasLimit uint256(0), // value maxFees, // maxFees - sponsor // sponsor + sponsor, // sponsor + 86400 // deadline ); // Set overrides on the plug @@ -446,7 +448,8 @@ contract MessageSwitchboardTest is Test, Utils { DST_CHAIN, uint256(100000), // gasLimit uint256(0), // value - refundAddress // refundAddress + refundAddress, // refundAddress + 86400 // deadline ); // Set overrides on the plug srcPlug.setOverrides(overrides); @@ -514,7 +517,8 @@ contract MessageSwitchboardTest is Test, Utils { DST_CHAIN, 100000, 0, - refundAddress + refundAddress, + 86400 // deadline ); // Set overrides on the plug @@ -527,7 +531,7 @@ contract MessageSwitchboardTest is Test, Utils { } function test_processTrigger_Native_SiblingSocketNotFound_Reverts() public { - bytes memory overrides = abi.encode(uint8(1), DST_CHAIN, 100000, 0, refundAddress); + bytes memory overrides = abi.encode(uint8(1), DST_CHAIN, 100000, 0, refundAddress, 86400); // Set overrides on the plug srcPlug.setOverrides(overrides); @@ -555,7 +559,8 @@ contract MessageSwitchboardTest is Test, Utils { uint256(100000), // gasLimit uint256(0), // value uint256(10 ether), // maxFees - sponsor // sponsor + sponsor, // sponsor + 86400 // deadline ); bytes memory payload = abi.encode("sponsored test"); @@ -617,7 +622,7 @@ contract MessageSwitchboardTest is Test, Utils { _setupSiblingConfig(); // Don't approve - try without approval - bytes memory overrides = abi.encode(uint8(2), DST_CHAIN, 100000, 0, 10 ether, sponsor); + bytes memory overrides = abi.encode(uint8(2), DST_CHAIN, 100000, 0, 10 ether, sponsor, 86400); // Set overrides on the plug srcPlug.setOverrides(overrides); @@ -628,7 +633,7 @@ contract MessageSwitchboardTest is Test, Utils { } function test_processTrigger_UnsupportedVersion_Reverts() public { - bytes memory overrides = abi.encode(uint8(99), DST_CHAIN, 100000, 0, refundAddress); + bytes memory overrides = abi.encode(uint8(99), DST_CHAIN, 100000, 0, refundAddress, 86400); // Set overrides on the plug srcPlug.setOverrides(overrides); @@ -1009,7 +1014,8 @@ contract MessageSwitchboardTest is Test, Utils { refundAddress, // refundAddress uint256(0), // maxFees address(0), // sponsor - false // isSponsored + false, // isSponsored + 86400 // deadline ); // Set overrides on the plug @@ -1056,7 +1062,8 @@ contract MessageSwitchboardTest is Test, Utils { uint256(100000), // gasLimit uint256(0), // value uint256(0.02 ether), // maxFees - sponsor // sponsor + sponsor, // sponsor + 86400 // deadline ); // Set overrides on the plug @@ -1105,7 +1112,8 @@ contract MessageSwitchboardTest is Test, Utils { refundAddress, // refundAddress uint256(0), // maxFees address(0), // sponsor - false // isSponsored + false, // isSponsored + 86400 // deadline ); // Set overrides on the plug From 8672361c4a7f06a7da09bfa27c1b4577ad31f31a Mon Sep 17 00:00:00 2001 From: akash Date: Sat, 8 Nov 2025 10:58:38 +0530 Subject: [PATCH 09/10] fix: switchboardId, counter type change. idUtils optimize --- contracts/evmx/interfaces/IConfigurations.sol | 6 +-- contracts/evmx/interfaces/IWatcher.sol | 2 +- contracts/evmx/plugs/FeesPlug.sol | 3 +- contracts/evmx/watcher/Configurations.sol | 10 ++-- contracts/evmx/watcher/Watcher.sol | 8 ++-- contracts/protocol/Socket.sol | 8 ++-- contracts/protocol/SocketBatcher.sol | 4 +- contracts/protocol/SocketConfig.sol | 32 ++++++------- contracts/protocol/base/MessagePlugBase.sol | 23 +--------- contracts/protocol/base/PlugBase.sol | 4 +- contracts/protocol/interfaces/IPlug.sol | 2 +- contracts/protocol/interfaces/ISocket.sol | 14 +++--- .../protocol/interfaces/ISocketBatcher.sol | 2 +- .../protocol/switchboard/FastSwitchboard.sol | 4 +- .../switchboard/MessageSwitchboard.sol | 8 ++-- .../protocol/switchboard/SwitchboardBase.sol | 2 +- contracts/utils/common/IdUtils.sol | 21 ++++----- contracts/utils/common/Structs.sol | 4 +- deprecated/AuctionManager.sol | 2 +- deprecated/test/SetupTest.t.sol | 2 +- deprecated/test/evmx/Watcher.t.sol | 4 +- test/SetupTest.t.sol | 22 ++++----- test/SocketPayloadIdVerification.t.sol | 24 +++++----- test/mocks/MockPlug.sol | 4 +- test/switchboard/MessageSwitchboard.t.sol | 46 +++++++++---------- 25 files changed, 119 insertions(+), 142 deletions(-) diff --git a/contracts/evmx/interfaces/IConfigurations.sol b/contracts/evmx/interfaces/IConfigurations.sol index f2f95daf..bb45e082 100644 --- a/contracts/evmx/interfaces/IConfigurations.sol +++ b/contracts/evmx/interfaces/IConfigurations.sol @@ -26,7 +26,7 @@ interface IConfigurations { function getPlugConfigs( uint32 chainSlug_, bytes32 plug_ - ) external view returns (bytes32, uint64); + ) external view returns (bytes32, uint32); /// @notice Maps chain slug to their associated socket /// @param chainSlug_ The chain slug @@ -36,10 +36,10 @@ interface IConfigurations { /// @notice Returns the socket for a given chain slug /// @param chainSlug_ The chain slug /// @return The socket - function switchboards(uint32 chainSlug_, bytes32 sbType_) external view returns (uint64); + function switchboards(uint32 chainSlug_, bytes32 sbType_) external view returns (uint32); /// @notice Sets the switchboard for a network - function setSwitchboard(uint32 chainSlug_, bytes32 sbType_, uint64 switchboardId_) external; + function setSwitchboard(uint32 chainSlug_, bytes32 sbType_, uint32 switchboardId_) external; /// @notice Sets valid plugs for each chain slug /// @dev This function is used to verify if a plug deployed on a chain slug is valid connection to the app gateway diff --git a/contracts/evmx/interfaces/IWatcher.sol b/contracts/evmx/interfaces/IWatcher.sol index cf732c05..95cf217a 100644 --- a/contracts/evmx/interfaces/IWatcher.sol +++ b/contracts/evmx/interfaces/IWatcher.sol @@ -21,7 +21,7 @@ interface IWatcher is IConfigurations { function evmxSlug() external view returns (uint32); - function nextPayloadCount() external view returns (uint256); + function nextPayloadCount() external view returns (uint64); function currentPayloadId() external view returns (bytes32); diff --git a/contracts/evmx/plugs/FeesPlug.sol b/contracts/evmx/plugs/FeesPlug.sol index 8320db52..8a04f541 100644 --- a/contracts/evmx/plugs/FeesPlug.sol +++ b/contracts/evmx/plugs/FeesPlug.sol @@ -33,7 +33,6 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { constructor(address socket_, address owner_) { _setSocket(socket_); _initializeOwner(owner_); - isSocketInitialized = 1; } @@ -114,7 +113,7 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { function connectSocket( bytes32 appGatewayId_, address socket_, - uint64 switchboardId_ + uint32 switchboardId_ ) external onlyOwner { _connectSocket(appGatewayId_, socket_, switchboardId_); } diff --git a/contracts/evmx/watcher/Configurations.sol b/contracts/evmx/watcher/Configurations.sol index 6ea80ada..092eb7a5 100644 --- a/contracts/evmx/watcher/Configurations.sol +++ b/contracts/evmx/watcher/Configurations.sol @@ -22,7 +22,7 @@ abstract contract ConfigurationsStorage is IWatcher { // slot 51 /// @notice Maps chain slug to their associated switchboard /// @dev chainSlug => sb type => switchboard id - mapping(uint32 => mapping(bytes32 => uint64)) public switchboards; + mapping(uint32 => mapping(bytes32 => uint32)) public switchboards; // slot 52 /// @notice Maps chain slug to their associated socket @@ -57,7 +57,7 @@ abstract contract Configurations is ConfigurationsStorage, Ownable, AddressResol /// @param chainSlug The identifier of the network /// @param sbType The type of switchboard /// @param switchboardId The id of the switchboard - event SwitchboardSet(uint32 chainSlug, bytes32 sbType, uint64 switchboardId); + event SwitchboardSet(uint32 chainSlug, bytes32 sbType, uint32 switchboardId); /// @notice Emitted when socket is set for a network /// @param chainSlug The identifier of the network @@ -105,7 +105,7 @@ abstract contract Configurations is ConfigurationsStorage, Ownable, AddressResol function setSwitchboard( uint32 chainSlug_, bytes32 sbType_, - uint64 switchboardId_ + uint32 switchboardId_ ) external onlyOwner { switchboards[chainSlug_][sbType_] = switchboardId_; emit SwitchboardSet(chainSlug_, sbType_, switchboardId_); @@ -131,7 +131,7 @@ abstract contract Configurations is ConfigurationsStorage, Ownable, AddressResol function getPlugConfigs( uint32 chainSlug_, bytes32 plug_ - ) public view returns (bytes32, uint64) { + ) public view returns (bytes32, uint32) { return ( _plugConfigs[chainSlug_][plug_].appGatewayId, _plugConfigs[chainSlug_][plug_].switchboardId @@ -150,7 +150,7 @@ abstract contract Configurations is ConfigurationsStorage, Ownable, AddressResol address appGateway_, bytes32 switchboardType_ ) external view { - (bytes32 appGatewayId, uint64 switchboardId) = getPlugConfigs(chainSlug_, target_); + (bytes32 appGatewayId, uint32 switchboardId) = getPlugConfigs(chainSlug_, target_); if (appGatewayId != toBytes32Format(appGateway_)) revert InvalidGateway(); if (switchboardId != switchboards[chainSlug_][switchboardType_]) revert InvalidSwitchboard(); diff --git a/contracts/evmx/watcher/Watcher.sol b/contracts/evmx/watcher/Watcher.sol index 61ad4a19..f8d7a50e 100644 --- a/contracts/evmx/watcher/Watcher.sol +++ b/contracts/evmx/watcher/Watcher.sol @@ -16,7 +16,7 @@ import "solady/utils/LibCall.sol"; contract Watcher is Initializable, Configurations { using LibCall for address; - uint256 public nextPayloadCount; + uint64 public nextPayloadCount; mapping(bytes32 => Payload) internal _payloads; mapping(bytes4 => IPrecompile) public precompiles; @@ -284,15 +284,15 @@ contract Watcher is Initializable, Configurations { uint32 chainSlug_, bytes32 switchboardType_ ) public view returns (bytes32) { - uint64 switchboardId = switchboards[chainSlug_][switchboardType_]; + uint32 switchboardId = switchboards[chainSlug_][switchboardType_]; // Write payload: origin = (evmxChainSlug, watcherId), verification = (dstChainSlug, dstSwitchboardId) // watcherId hardcoded as 1 for now return createPayloadId( evmxSlug, // origin chain slug (evmx) 1, // origin id (watcher id, hardcoded) chainSlug_, // verification chain slug (destination) - uint32(switchboardId), // verification id (destination switchboard) - uint64(nextPayloadCount) // pointer (counter) + switchboardId, // verification id (destination switchboard) + nextPayloadCount // pointer (counter) ); } diff --git a/contracts/protocol/Socket.sol b/contracts/protocol/Socket.sol index 860e11f0..504e1f52 100644 --- a/contracts/protocol/Socket.sol +++ b/contracts/protocol/Socket.sol @@ -67,7 +67,7 @@ contract Socket is SocketUtils { if (executeParams_.callType != WRITE) revert InvalidCallType(); // check if the plug is connected - uint64 switchboardId = plugSwitchboardIds[executeParams_.target]; + uint32 switchboardId = plugSwitchboardIds[executeParams_.target]; // check if the message value is sufficient if (msg.value < executeParams_.value + transmissionParams_.socketFees) @@ -105,7 +105,7 @@ contract Socket is SocketUtils { */ function _verify( bytes32 payloadId_, - uint64 switchboardId_, + uint32 switchboardId_, ExecuteParams calldata executeParams_, bytes calldata transmitterProof_ ) internal { @@ -217,7 +217,7 @@ contract Socket is SocketUtils { uint256 value_, bytes calldata data_ ) internal returns (bytes32 payloadId) { - (uint64 switchboardId, address switchboardAddress) = _verifyPlugSwitchboard(plug_); + (uint32 switchboardId, address switchboardAddress) = _verifyPlugSwitchboard(plug_); bytes memory plugOverrides = IPlug(plug_).overrides(); // Switchboard creates the payload ID and emits PayloadRequested event @@ -246,7 +246,7 @@ contract Socket is SocketUtils { ); } - function _verifyPlugSwitchboard(address plug_) internal view returns (uint64 switchboardId, address switchboardAddress) { + function _verifyPlugSwitchboard(address plug_) internal view returns (uint32 switchboardId, address switchboardAddress) { switchboardId = plugSwitchboardIds[plug_]; if (switchboardId == 0) revert PlugNotFound(); if (isValidSwitchboard[switchboardId] != SwitchboardStatus.REGISTERED) diff --git a/contracts/protocol/SocketBatcher.sol b/contracts/protocol/SocketBatcher.sol index 7fc43529..6e8e3f20 100644 --- a/contracts/protocol/SocketBatcher.sol +++ b/contracts/protocol/SocketBatcher.sol @@ -44,7 +44,7 @@ contract SocketBatcher is ISocketBatcher, Ownable { */ function attestAndExecute( ExecuteParams calldata executeParams_, - uint64 switchboardId_, + uint32 switchboardId_, bytes32 digest_, bytes calldata proof_, bytes calldata transmitterProof_, @@ -74,7 +74,7 @@ contract SocketBatcher is ISocketBatcher, Ownable { // function attestCCTPAndProveAndExecute( // CCTPExecutionParams calldata execParams_, // CCTPBatchParams calldata cctpParams_, - // uint64 switchboardId_ + // uint32 switchboardId_ // ) external payable returns (bool, bytes memory) { // address switchboard = socket__.switchboardAddresses(switchboardId_); // bytes32 payloadId = createPayloadId( diff --git a/contracts/protocol/SocketConfig.sol b/contracts/protocol/SocketConfig.sol index 0401c792..ef63f6fc 100644 --- a/contracts/protocol/SocketConfig.sol +++ b/contracts/protocol/SocketConfig.sol @@ -22,22 +22,22 @@ abstract contract SocketConfig is ISocket, AccessControl { ISocketFeeManager public socketFeeManager; // @notice mapping of switchboard address to its status, helps socket to block invalid switchboards - mapping(uint64 => SwitchboardStatus) public isValidSwitchboard; + mapping(uint32 => SwitchboardStatus) public isValidSwitchboard; // @notice mapping of plug address to switchboard address - mapping(address => uint64) public plugSwitchboardIds; + mapping(address => uint32) public plugSwitchboardIds; // @notice max copy bytes for socket uint16 public maxCopyBytes = 2048; // 2KB // @notice counter for switchboard ids - uint64 public switchboardIdCounter = 1; + uint32 public switchboardIdCounter = 1; // @notice mapping of switchboard id to its address - mapping(uint64 => address) public switchboardAddresses; + mapping(uint32 => address) public switchboardAddresses; // @notice mapping of switchboard address to its id - mapping(address => uint64) public switchboardIds; + mapping(address => uint32) public switchboardIds; // @notice buffer to account for gas used by current contract execution uint256 public gasLimitBuffer; @@ -48,25 +48,25 @@ abstract contract SocketConfig is ISocket, AccessControl { error PlugNotConnected(); // @notice event triggered when a new switchboard is added - event SwitchboardAdded(address switchboard, uint64 switchboardId); + event SwitchboardAdded(address switchboard, uint32 switchboardId); // @notice event triggered when a switchboard is disabled - event SwitchboardDisabled(uint64 switchboardId); + event SwitchboardDisabled(uint32 switchboardId); // @notice event triggered when a switchboard is enabled - event SwitchboardEnabled(uint64 switchboardId); + event SwitchboardEnabled(uint32 switchboardId); // @notice event triggered when a socket fee manager is updated event SocketFeeManagerUpdated(address oldSocketFeeManager, address newSocketFeeManager); // @notice event triggered when the gas limit buffer is updated event GasLimitBufferUpdated(uint256 gasLimitBuffer); // @notice event triggered when the max copy bytes is updated event MaxCopyBytesUpdated(uint16 maxCopyBytes); - event PlugConfigUpdated(address plug, uint64 switchboardId, bytes configData); + event PlugConfigUpdated(address plug, uint32 switchboardId, bytes configData); /** * @notice Registers a switchboard on the socket * @dev This function is called by the switchboard to register itself on the socket * @dev This function will revert if the switchboard already exists * @return switchboardId The id of the switchboard */ - function registerSwitchboard() external returns (uint64 switchboardId) { + function registerSwitchboard() external returns (uint32 switchboardId) { switchboardId = switchboardIds[msg.sender]; if (switchboardId != 0) revert SwitchboardExists(); @@ -88,7 +88,7 @@ abstract contract SocketConfig is ISocket, AccessControl { * @param switchboardId_ The id of the switchboard to disable */ function disableSwitchboard( - uint64 switchboardId_ + uint32 switchboardId_ ) external onlyRole(SWITCHBOARD_DISABLER_ROLE) { isValidSwitchboard[switchboardId_] = SwitchboardStatus.DISABLED; emit SwitchboardDisabled(switchboardId_); @@ -99,7 +99,7 @@ abstract contract SocketConfig is ISocket, AccessControl { * @dev This function is called by the governance role to enable a switchboard * @param switchboardId_ The id of the switchboard to enable */ - function enableSwitchboard(uint64 switchboardId_) external onlyRole(GOVERNANCE_ROLE) { + function enableSwitchboard(uint32 switchboardId_) external onlyRole(GOVERNANCE_ROLE) { isValidSwitchboard[switchboardId_] = SwitchboardStatus.REGISTERED; emit SwitchboardEnabled(switchboardId_); } @@ -120,7 +120,7 @@ abstract contract SocketConfig is ISocket, AccessControl { * @param switchboardId_ The switchboard id * @param configData_ The configuration data for the switchboard */ - function connect(uint64 switchboardId_, bytes memory configData_) external override { + function connect(uint32 switchboardId_, bytes memory configData_) external override { if (switchboardId_ == 0 || isValidSwitchboard[switchboardId_] != SwitchboardStatus.REGISTERED) revert InvalidSwitchboard(); plugSwitchboardIds[msg.sender] = switchboardId_; @@ -136,7 +136,7 @@ abstract contract SocketConfig is ISocket, AccessControl { * @param configData_ The configuration data for the switchboard */ function updatePlugConfig(bytes memory configData_) external { - uint64 switchboardId = plugSwitchboardIds[msg.sender]; + uint32 switchboardId = plugSwitchboardIds[msg.sender]; if (switchboardId == 0) revert PlugNotConnected(); ISwitchboard(switchboardAddresses[switchboardId]).updatePlugConfig(msg.sender,configData_); } @@ -180,14 +180,14 @@ abstract contract SocketConfig is ISocket, AccessControl { function getPlugConfig( address plugAddress_, bytes memory extraData_ - ) external view returns (uint64 switchboardId, bytes memory configData) { + ) external view returns (uint32 switchboardId, bytes memory configData) { switchboardId = plugSwitchboardIds[plugAddress_]; configData = ISwitchboard(switchboardAddresses[switchboardId]).getPlugConfig(plugAddress_, extraData_); } function getPlugSwitchboard( address plugAddress_ - ) external view returns (uint64 switchboardId, address switchboardAddress) { + ) external view returns (uint32 switchboardId, address switchboardAddress) { switchboardId = plugSwitchboardIds[plugAddress_]; switchboardAddress = switchboardAddresses[switchboardId]; } diff --git a/contracts/protocol/base/MessagePlugBase.sol b/contracts/protocol/base/MessagePlugBase.sol index 01adc49d..e2eeabf3 100644 --- a/contracts/protocol/base/MessagePlugBase.sol +++ b/contracts/protocol/base/MessagePlugBase.sol @@ -11,23 +11,13 @@ import {toBytes32Format} from "../../utils/common/Converters.sol"; /// Uses constant appGatewayId (0xaaaaa) for all chains abstract contract MessagePlugBase is PlugBase { address public switchboard; - uint64 public switchboardId; - uint256 public triggerPrefix; - error NotSupported(); + uint32 public switchboardId; - constructor(address socket_, uint64 switchboardId_) { + constructor(address socket_, uint32 switchboardId_) { _setSocket(socket_); switchboardId = switchboardId_; switchboard = socket__.switchboardAddresses(switchboardId_); - socket__.connect(switchboardId_, ""); - - triggerPrefix = (uint256(socket__.chainSlug()) << 224) | (uint256(uint160(socket_)) << 64); - } - - /// @notice Initializes the socket with the new protocol - function initSocket(bytes32, address, uint64) external override socketInitializer { - revert("Not Supported"); } /// @notice Registers a sibling plug for a specific chain @@ -43,13 +33,4 @@ abstract contract MessagePlugBase is PlugBase { registerSibling(chainSlugs_[i], siblingPlugs_[i]); } } - - function getNextTriggerId(uint32 chainSlug_) public view returns (bytes32) { - return - bytes32( - (uint256(chainSlug_) << 224) | - (uint256(uint160(address(socket__))) << 64) | - (uint256(socket__.triggerCounter()) << 16) - ); - } } diff --git a/contracts/protocol/base/PlugBase.sol b/contracts/protocol/base/PlugBase.sol index ef885077..105fa25a 100644 --- a/contracts/protocol/base/PlugBase.sol +++ b/contracts/protocol/base/PlugBase.sol @@ -45,7 +45,7 @@ abstract contract PlugBase is IPlug { function _connectSocket( bytes32 appGatewayId_, address socket_, - uint64 switchboardId_ + uint32 switchboardId_ ) internal { _setSocket(socket_); appGatewayId = appGatewayId_; @@ -82,7 +82,7 @@ abstract contract PlugBase is IPlug { function initSocket( bytes32 appGatewayId_, address socket_, - uint64 switchboardId_ + uint32 switchboardId_ ) external virtual socketInitializer { _connectSocket(appGatewayId_, socket_, switchboardId_); } diff --git a/contracts/protocol/interfaces/IPlug.sol b/contracts/protocol/interfaces/IPlug.sol index f67160a4..b5237d0f 100644 --- a/contracts/protocol/interfaces/IPlug.sol +++ b/contracts/protocol/interfaces/IPlug.sol @@ -10,7 +10,7 @@ interface IPlug { /// @param appGatewayId_ The app gateway id /// @param socket_ The socket address /// @param switchboardId_ The switchboard id - function initSocket(bytes32 appGatewayId_, address socket_, uint64 switchboardId_) external; + function initSocket(bytes32 appGatewayId_, address socket_, uint32 switchboardId_) external; /// @notice Gets the overrides /// @dev encoding format depends on the watcher system diff --git a/contracts/protocol/interfaces/ISocket.sol b/contracts/protocol/interfaces/ISocket.sol index 96825aa2..5432b7e3 100644 --- a/contracts/protocol/interfaces/ISocket.sol +++ b/contracts/protocol/interfaces/ISocket.sol @@ -30,7 +30,7 @@ interface ISocket { * @param configData The configuration data for the plug * @param switchboardId The outbound switchboard (select from registered options) */ - event PlugConnected(address plug, uint64 switchboardId, bytes configData); + event PlugConnected(address plug, uint32 switchboardId, bytes configData); /** * @notice emits the config set by a plug for a remoteChainSlug @@ -49,7 +49,7 @@ interface ISocket { event AppGatewayCallRequested( bytes32 triggerId, bytes32 appGatewayId, - uint64 switchboardId, + uint32 switchboardId, bytes32 plug, bytes overrides, bytes payload @@ -66,7 +66,7 @@ interface ISocket { event PayloadRequested( bytes32 indexed payloadId, address indexed plug, - uint64 indexed switchboardId, + uint32 indexed switchboardId, bytes overrides, bytes payload ); @@ -88,7 +88,7 @@ interface ISocket { * @param switchboardId_ The switchboard id * @param configData_ The configuration data for the switchboard */ - function connect(uint64 switchboardId_, bytes memory configData_) external; + function connect(uint32 switchboardId_, bytes memory configData_) external; /** * @notice Updates plug configuration on switchboard @@ -105,7 +105,7 @@ interface ISocket { * @notice Registers a switchboard for the socket * @return switchboardId The id of the switchboard */ - function registerSwitchboard() external returns (uint64); + function registerSwitchboard() external returns (uint32); /** * @notice Returns the config for given `plugAddress_` and `siblingChainSlug_` @@ -117,7 +117,7 @@ interface ISocket { function getPlugConfig( address plugAddress_, bytes memory extraData_ - ) external view returns (uint64, bytes memory); + ) external view returns (uint32, bytes memory); /** * @notice Returns the execution status of a payload @@ -150,7 +150,7 @@ interface ISocket { * @param switchboardId_ The switchboard id * @return switchboardAddress The switchboard address */ - function switchboardAddresses(uint64 switchboardId_) external view returns (address); + function switchboardAddresses(uint32 switchboardId_) external view returns (address); function sendPayload(bytes calldata data_) external payable returns (bytes32 payloadId); diff --git a/contracts/protocol/interfaces/ISocketBatcher.sol b/contracts/protocol/interfaces/ISocketBatcher.sol index f31782b1..f98d6546 100644 --- a/contracts/protocol/interfaces/ISocketBatcher.sol +++ b/contracts/protocol/interfaces/ISocketBatcher.sol @@ -20,7 +20,7 @@ interface ISocketBatcher { */ function attestAndExecute( ExecuteParams calldata executeParams_, - uint64 switchboardId_, + uint32 switchboardId_, bytes32 digest_, bytes calldata proof_, bytes calldata transmitterSignature_, diff --git a/contracts/protocol/switchboard/FastSwitchboard.sol b/contracts/protocol/switchboard/FastSwitchboard.sol index 801a41c0..9e1e7361 100644 --- a/contracts/protocol/switchboard/FastSwitchboard.sol +++ b/contracts/protocol/switchboard/FastSwitchboard.sol @@ -47,7 +47,7 @@ contract FastSwitchboard is SwitchboardBase { event PayloadRequested( bytes32 indexed payloadId, address indexed plug, - uint64 indexed switchboardId, + uint32 indexed switchboardId, bytes overrides, bytes payload ); @@ -122,7 +122,7 @@ contract FastSwitchboard is SwitchboardBase { // Pointer: switchboard counter payloadId = createPayloadId( chainSlug, // origin chain slug (source) - uint32(switchboardId), // origin id (source switchboard) + switchboardId, // origin id (source switchboard) evmxChainSlug, // verification chain slug (evmx) watcherId, // verification id (watcher id) triggerPayloadCounter++ // pointer (counter) diff --git a/contracts/protocol/switchboard/MessageSwitchboard.sol b/contracts/protocol/switchboard/MessageSwitchboard.sol index e72a9168..bb061174 100644 --- a/contracts/protocol/switchboard/MessageSwitchboard.sol +++ b/contracts/protocol/switchboard/MessageSwitchboard.sol @@ -29,7 +29,7 @@ contract MessageSwitchboard is SwitchboardBase { mapping(uint32 => mapping(address => bytes32)) public siblingPlugs; // payload counter for generating unique payload IDs - uint40 public payloadCounter; + uint64 public payloadCounter; // minimum message value fees: chainSlug => minimum fee amount mapping(uint32 => uint256) public minMsgValueFees; @@ -119,7 +119,7 @@ contract MessageSwitchboard is SwitchboardBase { event PayloadRequested( bytes32 indexed payloadId, address indexed plug, - uint64 indexed switchboardId, + uint32 indexed switchboardId, bytes overrides, bytes payload ); @@ -316,7 +316,7 @@ contract MessageSwitchboard is SwitchboardBase { // Message payload: origin = (srcChainSlug, srcSwitchboardId), verification = (dstChainSlug, dstSwitchboardId) payloadId = createPayloadId( chainSlug, // origin chain slug (source) - uint32(switchboardId), // origin id (source switchboard) + switchboardId, // origin id (source switchboard) dstChainSlug_, // verification chain slug (destination) dstSwitchboardId, // verification id (destination switchboard) payloadCounter++ // pointer (counter) @@ -334,7 +334,7 @@ contract MessageSwitchboard is SwitchboardBase { target: siblingPlugs[dstChainSlug_][plug_], source: abi.encode(chainSlug, toBytes32Format(plug_)), prevBatchDigestHash: bytes32(0), // No longer using triggerId - extraData:"0x" + extraData:bytes("") }); digest = _createDigest(digestParams); } diff --git a/contracts/protocol/switchboard/SwitchboardBase.sol b/contracts/protocol/switchboard/SwitchboardBase.sol index 00ecb9c7..8709726a 100644 --- a/contracts/protocol/switchboard/SwitchboardBase.sol +++ b/contracts/protocol/switchboard/SwitchboardBase.sol @@ -18,7 +18,7 @@ abstract contract SwitchboardBase is ISwitchboard, AccessControl { uint32 public immutable chainSlug; // switchboard id - uint64 public switchboardId; + uint32 public switchboardId; error NotSocket(); /** diff --git a/contracts/utils/common/IdUtils.sol b/contracts/utils/common/IdUtils.sol index d1053294..64f45694 100644 --- a/contracts/utils/common/IdUtils.sol +++ b/contracts/utils/common/IdUtils.sol @@ -43,12 +43,11 @@ function decodePayloadId( uint32 verificationId, uint64 pointer ) { - uint256 id = uint256(payloadId_); - originChainSlug = uint32((id >> 224) & type(uint32).max); - originId = uint32((id >> 192) & type(uint32).max); - verificationChainSlug = uint32((id >> 160) & type(uint32).max); - verificationId = uint32((id >> 128) & type(uint32).max); - pointer = uint64((id >> 64) & type(uint64).max); + originChainSlug = uint32(uint256(payloadId_) >> 224); + originId = uint32(uint256(payloadId_) >> 192); + verificationChainSlug = uint32(uint256(payloadId_) >> 160); + verificationId = uint32(uint256(payloadId_) >> 128); + pointer = uint64(uint256(payloadId_) >> 64); } /// @notice Gets verification chain slug and switchboard ID from payload ID @@ -56,9 +55,8 @@ function decodePayloadId( /// @return chainSlug Verification chain slug /// @return switchboardId Verification switchboard ID function getVerificationInfo(bytes32 payloadId_) pure returns (uint32 chainSlug, uint32 switchboardId) { - uint256 id = uint256(payloadId_); - chainSlug = uint32((id >> 160) & type(uint32).max); - switchboardId = uint32((id >> 128) & type(uint32).max); + chainSlug = uint32(uint256(payloadId_) >> 160); + switchboardId = uint32(uint256(payloadId_) >> 128); } /// @notice Gets origin chain slug and switchboard ID from payload ID @@ -66,7 +64,6 @@ function getVerificationInfo(bytes32 payloadId_) pure returns (uint32 chainSlug, /// @return chainSlug Origin chain slug /// @return switchboardId Origin switchboard ID or watcher ID function getOriginInfo(bytes32 payloadId_) pure returns (uint32 chainSlug, uint32 switchboardId) { - uint256 id = uint256(payloadId_); - chainSlug = uint32((id >> 224) & type(uint32).max); - switchboardId = uint32((id >> 192) & type(uint32).max); + chainSlug = uint32(uint256(payloadId_) >> 224); + switchboardId = uint32(uint256(payloadId_) >> 192); } diff --git a/contracts/utils/common/Structs.sol b/contracts/utils/common/Structs.sol index fec1fce9..dbdb202e 100644 --- a/contracts/utils/common/Structs.sol +++ b/contracts/utils/common/Structs.sol @@ -43,13 +43,13 @@ struct AppGatewayConfig { // Plug config: struct PlugConfigGeneric { bytes32 appGatewayId; - uint64 switchboardId; + uint32 switchboardId; } // Plug config: struct PlugConfigEvm { bytes32 appGatewayId; - uint64 switchboardId; + uint32 switchboardId; } //trigger: diff --git a/deprecated/AuctionManager.sol b/deprecated/AuctionManager.sol index 712a207c..839c3a39 100644 --- a/deprecated/AuctionManager.sol +++ b/deprecated/AuctionManager.sol @@ -233,7 +233,7 @@ contract AuctionManager is AuctionManagerStorage, Initializable, AppGatewayBase, watcher__().requestHandler__().assignTransmitter( requestCount, - Bid({fee: 0, transmitter: address(0), extraData: ""}) + Bid({fee: 0, transmitter: address(0), extraData: bytes("")}) ); emit AuctionRestarted(requestCount); } diff --git a/deprecated/test/SetupTest.t.sol b/deprecated/test/SetupTest.t.sol index 7c808510..bb0e18ed 100644 --- a/deprecated/test/SetupTest.t.sol +++ b/deprecated/test/SetupTest.t.sol @@ -732,7 +732,7 @@ contract AuctionSetup is FeesSetup { vm.expectEmit(true, true, true, true); emit AuctionEnded( requestCount_, - Bid({fee: bidAmount, transmitter: transmitterEOA, extraData: ""}) + Bid({fee: bidAmount, transmitter: transmitterEOA, extraData: bytes("")}) ); // promiseResolver.resolvePromises(); diff --git a/deprecated/test/evmx/Watcher.t.sol b/deprecated/test/evmx/Watcher.t.sol index 90e563e8..c31ad456 100644 --- a/deprecated/test/evmx/Watcher.t.sol +++ b/deprecated/test/evmx/Watcher.t.sol @@ -381,7 +381,7 @@ contract WatcherTest is AppGatewayBaseSetup { function testRequestHandlerAssignTransmitter() public { uint40 requestCount = 0; appGateway.deployContracts(arbConfig.chainSlug); - Bid memory bid = Bid({fee: 100, transmitter: transmitterEOA, extraData: ""}); + Bid memory bid = Bid({fee: 100, transmitter: transmitterEOA, extraData: bytes("")}); hoax(nonOwner); vm.expectRevert(abi.encodeWithSelector(InvalidCaller.selector)); @@ -491,7 +491,7 @@ contract WatcherTest is AppGatewayBaseSetup { uint40[] memory batches = requestHandler.getRequestBatchIds(requestCount); bytes32[] memory payloadIds = requestHandler.getBatchPayloadIds(batches[0]); bytes32 payloadId = payloadIds[0]; - Bid memory bid = Bid({fee: 100, transmitter: transmitterEOA, extraData: ""}); + Bid memory bid = Bid({fee: 100, transmitter: transmitterEOA, extraData: bytes("")}); hoax(address(auctionManager)); requestHandler.assignTransmitter(requestCount, bid); diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 84118bab..59b2d842 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -564,7 +564,7 @@ contract WatcherSetup is FeesSetup { ( uint32 chainSlug, - uint64 switchboard, + uint32 switchboard, bytes32 digest, DigestParams memory digestParams ) = _validateAndGetDigest(payloadParams); @@ -583,7 +583,7 @@ contract WatcherSetup is FeesSetup { function _uploadProof( bytes32 payloadId, bytes32 digest, - uint64 switchboard, + uint32 switchboard, uint32 chainSlug ) internal returns (bytes memory proof) { address sbAddress = getSocketConfig(chainSlug).socket.switchboardAddresses(switchboard); @@ -607,7 +607,7 @@ contract WatcherSetup is FeesSetup { view returns ( uint32 chainSlug, - uint64 switchboard, + uint32 switchboard, bytes32 digest, DigestParams memory digestParams ) @@ -618,10 +618,10 @@ contract WatcherSetup is FeesSetup { , uint256 gasLimit, uint256 value, - uint64 switchboard_ + uint32 switchboard_ ) = abi.decode( payloadParams.precompileData, - (address, Transaction, WriteFinality, uint256, uint256, uint64) + (address, Transaction, WriteFinality, uint256, uint256, uint32) ); chainSlug = transaction.chainSlug; @@ -648,7 +648,7 @@ contract WatcherSetup is FeesSetup { function _executeWrite( uint32 chainSlug, - uint64 switchboard, + uint32 switchboard, bytes32 digest, DigestParams memory digestParams, Payload memory payloadParams, @@ -833,16 +833,16 @@ contract MessageSwitchboardSetup is DeploySetup { SocketContracts memory dstSocketConfig_, bytes memory payload_ ) internal view returns (bytes32 payloadId, DigestParams memory digestParams) { - uint40 payloadCounter = srcSocketConfig_.messageSwitchboard.payloadCounter(); + uint64 payloadCounter = srcSocketConfig_.messageSwitchboard.payloadCounter(); // Calculate payload ID using new structure // Message payload: origin = (srcChainSlug, srcSwitchboardId), verification = (dstChainSlug, dstSwitchboardId) payloadId = createPayloadId( srcSocketConfig_.chainSlug, // origin chain slug - uint32(srcSocketConfig_.messageSwitchboard.switchboardId()), // origin switchboard id + srcSocketConfig_.messageSwitchboard.switchboardId(), // origin switchboard id dstSocketConfig_.chainSlug, // verification chain slug - uint32(dstSocketConfig_.messageSwitchboard.switchboardId()), // verification switchboard id - uint64(payloadCounter) // pointer (counter) + dstSocketConfig_.messageSwitchboard.switchboardId(), // verification switchboard id + payloadCounter // pointer (counter) ); digestParams = _createDigestParams( @@ -897,7 +897,7 @@ contract MessageSwitchboardSetup is DeploySetup { target: toBytes32Format(dstPlug_), source: abi.encode(srcChainSlug_, toBytes32Format(srcPlug_)), prevBatchDigestHash: bytes32(0), // No longer using triggerId - extraData: "0x" // Contract now sets extraData to empty + extraData: bytes("") // Contract now sets extraData to empty }); } diff --git a/test/SocketPayloadIdVerification.t.sol b/test/SocketPayloadIdVerification.t.sol index 6572722a..19dec4a6 100644 --- a/test/SocketPayloadIdVerification.t.sol +++ b/test/SocketPayloadIdVerification.t.sol @@ -20,7 +20,7 @@ contract SocketPayloadIdVerificationTest is Test { event PayloadRequested( bytes32 indexed payloadId, address indexed plug, - uint64 indexed switchboardId, + uint32 indexed switchboardId, bytes overrides, bytes payload ); @@ -53,7 +53,7 @@ contract SocketPayloadIdVerificationTest is Test { vm.stopPrank(); // Create a mock plug - uint64 switchboardId = fastSwitchboard.switchboardId(); + uint32 switchboardId = fastSwitchboard.switchboardId(); mockPlug = new MockPlug(address(socket), switchboardId); // Connect plug to socket @@ -67,7 +67,7 @@ contract SocketPayloadIdVerificationTest is Test { function test_Execute_VerifiesPayloadId_CorrectDestination() public { // Create a valid payload ID for this chain and switchboard - uint64 switchboardId = fastSwitchboard.switchboardId(); + uint32 switchboardId = fastSwitchboard.switchboardId(); bytes32 payloadId = createPayloadId( OTHER_CHAIN_SLUG, // origin chain slug 100, // origin switchboard id @@ -87,7 +87,7 @@ contract SocketPayloadIdVerificationTest is Test { target: address(mockPlug), prevBatchDigestHash: bytes32(0), source: abi.encode(OTHER_CHAIN_SLUG, toBytes32Format(address(0x1234))), - extraData: "0x" + extraData: bytes("") }); TransmissionParams memory transmissionParams = TransmissionParams({ @@ -112,7 +112,7 @@ contract SocketPayloadIdVerificationTest is Test { function test_Execute_WrongChainSlug_Reverts() public { // Create payload ID with wrong verification chain slug - uint64 switchboardId = fastSwitchboard.switchboardId(); + uint32 switchboardId = fastSwitchboard.switchboardId(); bytes32 payloadId = createPayloadId( OTHER_CHAIN_SLUG, 100, @@ -131,7 +131,7 @@ contract SocketPayloadIdVerificationTest is Test { target: address(mockPlug), prevBatchDigestHash: bytes32(0), source: abi.encode(OTHER_CHAIN_SLUG, toBytes32Format(address(0x1234))), - extraData: "0x" + extraData: bytes("") }); TransmissionParams memory transmissionParams = TransmissionParams({ @@ -165,7 +165,7 @@ contract SocketPayloadIdVerificationTest is Test { target: address(mockPlug), prevBatchDigestHash: bytes32(0), source: abi.encode(OTHER_CHAIN_SLUG, toBytes32Format(address(0x1234))), - extraData: "0x" + extraData: bytes("") }); TransmissionParams memory transmissionParams = TransmissionParams({ @@ -189,7 +189,7 @@ contract SocketPayloadIdVerificationTest is Test { fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); // Create a mock plug - uint64 switchboardId = fastSwitchboard.switchboardId(); + uint32 switchboardId = fastSwitchboard.switchboardId(); MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); vm.prank(plugOwner); triggerPlug.connectToSocket(address(socket), switchboardId); @@ -233,7 +233,7 @@ contract SocketPayloadIdVerificationTest is Test { fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); // Create a mock plug - uint64 switchboardId = fastSwitchboard.switchboardId(); + uint32 switchboardId = fastSwitchboard.switchboardId(); MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); vm.prank(plugOwner); triggerPlug.connectToSocket(address(socket), switchboardId); @@ -272,7 +272,7 @@ contract SocketPayloadIdVerificationTest is Test { function test_FastSwitchboard_ProcessPayload_EvmxConfigNotSet_Reverts() public { // Don't set EVMX config - should revert - uint64 switchboardId = fastSwitchboard.switchboardId(); + uint32 switchboardId = fastSwitchboard.switchboardId(); MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); vm.prank(plugOwner); triggerPlug.connectToSocket(address(socket), switchboardId); @@ -294,7 +294,7 @@ contract SocketPayloadIdVerificationTest is Test { vm.prank(owner); fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); - uint64 switchboardId = fastSwitchboard.switchboardId(); + uint32 switchboardId = fastSwitchboard.switchboardId(); MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); vm.prank(plugOwner); triggerPlug.connectToSocket(address(socket), switchboardId); @@ -323,7 +323,7 @@ contract SocketPayloadIdVerificationTest is Test { vm.prank(owner); fastSwitchboard.setEvmxConfig(EVMX_CHAIN_SLUG, WATCHER_ID); - uint64 switchboardId = fastSwitchboard.switchboardId(); + uint32 switchboardId = fastSwitchboard.switchboardId(); MockPlug triggerPlug = new MockPlug(address(socket), switchboardId); vm.prank(plugOwner); triggerPlug.connectToSocket(address(socket), switchboardId); diff --git a/test/mocks/MockPlug.sol b/test/mocks/MockPlug.sol index ba4fb90e..907aea10 100644 --- a/test/mocks/MockPlug.sol +++ b/test/mocks/MockPlug.sol @@ -7,7 +7,7 @@ contract MockPlug is MessagePlugBase { uint32 public chainSlug; bytes32 public triggerId; - constructor(address socket_, uint64 switchboardId_) MessagePlugBase(socket_, switchboardId_) { + constructor(address socket_, uint32 switchboardId_) MessagePlugBase(socket_, switchboardId_) { } @@ -42,7 +42,7 @@ contract MockPlug is MessagePlugBase { } // Method to connect to socket - function connectToSocket(address socket_,uint64 switchboardId_) external { + function connectToSocket(address socket_,uint32 switchboardId_) external { _setSocket(socket_); switchboardId = switchboardId_; socket__.connect(switchboardId_, ""); diff --git a/test/switchboard/MessageSwitchboard.t.sol b/test/switchboard/MessageSwitchboard.t.sol index 19e0571b..d8b94d02 100644 --- a/test/switchboard/MessageSwitchboard.t.sol +++ b/test/switchboard/MessageSwitchboard.t.sol @@ -60,7 +60,7 @@ contract MessageSwitchboardTest is Test, Utils { event PayloadRequested( bytes32 indexed payloadId, address indexed plug, - uint64 indexed switchboardId, + uint32 indexed switchboardId, bytes overrides, bytes payload ); @@ -80,7 +80,7 @@ contract MessageSwitchboardTest is Test, Utils { messageSwitchboard.registerSwitchboard(); vm.stopPrank(); - uint64 switchboardId = messageSwitchboard.switchboardId(); + uint32 switchboardId = messageSwitchboard.switchboardId(); // Socket automatically stores switchboard address, no manual setting needed @@ -180,7 +180,7 @@ contract MessageSwitchboardTest is Test, Utils { bytes memory payload = abi.encode(payloadData); // Get counter before the call - uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); + uint64 payloadCounterBefore = messageSwitchboard.payloadCounter(); // Use MockPlug to trigger Socket - this returns the payloadId vm.deal(address(srcPlug), 10 ether); @@ -213,7 +213,7 @@ contract MessageSwitchboardTest is Test, Utils { bytes memory payload = abi.encode(payloadData); // Get counter before the call - uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); + uint64 payloadCounterBefore = messageSwitchboard.payloadCounter(); // Use MockPlug to trigger Socket - this returns the payloadId payloadId = srcPlug.triggerSocket(payload); @@ -254,7 +254,7 @@ contract MessageSwitchboardTest is Test, Utils { target: siblingPlug, source: abi.encode(SRC_CHAIN, toBytes32Format(address(srcPlug))), prevBatchDigestHash: bytes32(0), // No longer using triggerId - extraData: "0x" // Contract now sets extraData to empty + extraData: bytes("") // Contract now sets extraData to empty }); } @@ -273,7 +273,7 @@ contract MessageSwitchboardTest is Test, Utils { * @param payloadCounterBefore The payload counter before the call * @return payloadId The calculated payload ID */ - function _getLastPayloadId(uint40 payloadCounterBefore) internal view returns (bytes32) { + function _getLastPayloadId(uint64 payloadCounterBefore) internal view returns (bytes32) { // Calculate payload ID using new structure // Message payload: origin = (srcChainSlug, srcSwitchboardId), verification = (dstChainSlug, dstSwitchboardId) uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); @@ -282,7 +282,7 @@ contract MessageSwitchboardTest is Test, Utils { uint32(messageSwitchboard.switchboardId()), DST_CHAIN, dstSwitchboardId, - uint64(payloadCounterBefore) + payloadCounterBefore ); } @@ -403,7 +403,7 @@ contract MessageSwitchboardTest is Test, Utils { uint256 msgValue = MIN_FEES + 0.001 ether; // Get counter before the call - uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); + uint64 payloadCounterBefore = messageSwitchboard.payloadCounter(); // Calculate expected payload ID using new structure uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); @@ -412,7 +412,7 @@ contract MessageSwitchboardTest is Test, Utils { uint32(messageSwitchboard.switchboardId()), DST_CHAIN, dstSwitchboardId, - uint64(payloadCounterBefore) + payloadCounterBefore ); // Expect MessageOutbound event first (contract emits this before PayloadRequested) @@ -430,11 +430,11 @@ contract MessageSwitchboardTest is Test, Utils { callType: bytes4(0), gasLimit: 0, value: 0, - payload: "", + payload: bytes(""), target: bytes32(0), - source: "", + source: bytes(""), prevBatchDigestHash: bytes32(0), - extraData: "" + extraData: bytes("") }), false, // isSponsored msgValue, @@ -526,7 +526,7 @@ contract MessageSwitchboardTest is Test, Utils { bytes memory payload = abi.encode("sponsored test"); // Get counter before the call - uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); + uint64 payloadCounterBefore = messageSwitchboard.payloadCounter(); // Calculate expected payload ID using new structure uint32 dstSwitchboardId = messageSwitchboard.siblingSwitchboardIds(DST_CHAIN); @@ -535,7 +535,7 @@ contract MessageSwitchboardTest is Test, Utils { uint32(messageSwitchboard.switchboardId()), DST_CHAIN, dstSwitchboardId, - uint64(payloadCounterBefore) + payloadCounterBefore ); // Set overrides on the plug @@ -556,11 +556,11 @@ contract MessageSwitchboardTest is Test, Utils { callType: bytes4(0), gasLimit: 0, value: 0, - payload: "", + payload: bytes(""), target: bytes32(0), - source: "", + source: bytes(""), prevBatchDigestHash: bytes32(0), - extraData: "" + extraData: bytes("") }), true, // isSponsored 0, @@ -943,7 +943,7 @@ contract MessageSwitchboardTest is Test, Utils { srcPlug.setOverrides(overrides); // Get counter before creating payload - uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); + uint64 payloadCounterBefore = messageSwitchboard.payloadCounter(); vm.deal(address(srcPlug), 1 ether); vm.prank(address(srcPlug)); @@ -956,7 +956,7 @@ contract MessageSwitchboardTest is Test, Utils { uint32(messageSwitchboard.switchboardId()), DST_CHAIN, dstSwitchboardId, - uint64(payloadCounterBefore) + payloadCounterBefore ); assertEq(payloadId, expectedPayloadId, "PayloadId mismatch"); @@ -997,7 +997,7 @@ contract MessageSwitchboardTest is Test, Utils { srcPlug.setOverrides(overrides); // Get counter before creating payload - uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); + uint64 payloadCounterBefore = messageSwitchboard.payloadCounter(); vm.prank(address(srcPlug)); bytes32 payloadId = srcPlug.triggerSocket(abi.encode("payload")); @@ -1009,7 +1009,7 @@ contract MessageSwitchboardTest is Test, Utils { uint32(messageSwitchboard.switchboardId()), DST_CHAIN, dstSwitchboardId, - uint64(payloadCounterBefore) + payloadCounterBefore ); assertEq(payloadId, expectedPayloadId, "PayloadId mismatch"); @@ -1053,7 +1053,7 @@ contract MessageSwitchboardTest is Test, Utils { srcPlug.setOverrides(overrides); // Get counter before creating payload - uint40 payloadCounterBefore = messageSwitchboard.payloadCounter(); + uint64 payloadCounterBefore = messageSwitchboard.payloadCounter(); vm.deal(address(srcPlug), 1 ether); vm.prank(address(srcPlug)); @@ -1066,7 +1066,7 @@ contract MessageSwitchboardTest is Test, Utils { uint32(messageSwitchboard.switchboardId()), DST_CHAIN, dstSwitchboardId, - uint64(payloadCounterBefore) + payloadCounterBefore ); assertEq(payloadId, expectedPayloadId, "PayloadId mismatch"); From 73e6c782c69b9011756b460ddbf4b51dd37974d7 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 12 Nov 2025 15:38:04 +0530 Subject: [PATCH 10/10] fix: bugs --- contracts/evmx/fees/FeesManager.sol | 2 +- contracts/evmx/helpers/AsyncPromise.sol | 2 +- hardhat-scripts/constants/constants.ts | 2 ++ hardhat-scripts/deploy/1.deploy.ts | 3 ++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/contracts/evmx/fees/FeesManager.sol b/contracts/evmx/fees/FeesManager.sol index a14156ac..69ce7ee4 100644 --- a/contracts/evmx/fees/FeesManager.sol +++ b/contracts/evmx/fees/FeesManager.sol @@ -140,7 +140,7 @@ contract FeesManager is Credit { // Mint tokens to the transmitter _mint(assignTo_, amount_); - emit CreditsUnblockedAndAssigned(payloadId_, consumeFrom, assignTo_, blockedCredits_); + emit CreditsUnblockedAndAssigned(payloadId_, consumeFrom, assignTo_, amount_); } function unblockCredits(bytes32 payloadId_) external override onlyWatcher { diff --git a/contracts/evmx/helpers/AsyncPromise.sol b/contracts/evmx/helpers/AsyncPromise.sol index 5932aeea..96766440 100644 --- a/contracts/evmx/helpers/AsyncPromise.sol +++ b/contracts/evmx/helpers/AsyncPromise.sol @@ -30,7 +30,7 @@ abstract contract AsyncPromiseStorage is IPromise { /// @dev The callback will be executed on this address address public override localInvoker; - /// @notice The flag to check if the transmitter fees are settled + /// @notice The deadline timestamp (seconds since epoch) for promise resolution uint256 public promiseDeadline; // slot 51 diff --git a/hardhat-scripts/constants/constants.ts b/hardhat-scripts/constants/constants.ts index da2495ec..e04f3bc6 100644 --- a/hardhat-scripts/constants/constants.ts +++ b/hardhat-scripts/constants/constants.ts @@ -13,3 +13,5 @@ export const BYTES32_ZERO = ethers.constants.HashZero; export const MSG_SB_FEES = "100000000"; export const FEE_MANAGER_WRITE_MAX_FEES = ethers.utils.parseEther("10"); + +export const DEFAULT_DEADLINE = 86400; \ No newline at end of file diff --git a/hardhat-scripts/deploy/1.deploy.ts b/hardhat-scripts/deploy/1.deploy.ts index 3eb2c712..35e6792a 100644 --- a/hardhat-scripts/deploy/1.deploy.ts +++ b/hardhat-scripts/deploy/1.deploy.ts @@ -26,6 +26,7 @@ import { getFeePool, IMPLEMENTATION_SLOT, FEE_MANAGER_WRITE_MAX_FEES, + DEFAULT_DEADLINE, } from "../constants"; import { DeployParams, @@ -156,7 +157,7 @@ const deployEVMxContracts = async () => { deployUtils = await deployContractWithProxy( Contracts.AsyncDeployer, `contracts/evmx/helpers/AsyncDeployer.sol`, - [EVMxOwner, addressResolver.address], + [EVMxOwner, addressResolver.address, DEFAULT_DEADLINE], proxyFactory, deployUtils );