Skip to content
4 changes: 2 additions & 2 deletions contracts/interfaces/ISocket.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.21;

import {ExecuteParams} from "../protocol/utils/common/Structs.sol";
import {ExecuteParams, TransmissionParams} from "../protocol/utils/common/Structs.sol";

/**
* @title ISocket
Expand Down Expand Up @@ -53,7 +53,7 @@ interface ISocket {
*/
function execute(
ExecuteParams memory executeParams_,
bytes memory transmitterSignature_
TransmissionParams memory transmissionParams_
) external payable returns (bytes memory);

/**
Expand Down
3 changes: 2 additions & 1 deletion contracts/interfaces/ISocketBatcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface ISocketBatcher {
ExecuteParams calldata executeParams_,
bytes32 digest_,
bytes calldata proof_,
bytes calldata transmitterSignature_
bytes calldata transmitterSignature_,
address refundAddress_
) external payable returns (bytes memory);
}
34 changes: 34 additions & 0 deletions contracts/interfaces/ISocketFeeManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.21;

import {ExecuteParams, TransmissionParams} from "../protocol/utils/common/Structs.sol";

interface ISocketFeeManager {
/**
* @notice Pays and validates fees for execution
* @param executeParams_ Execute params
* @param transmissionParams_ Transmission params
*/
function payAndCheckFees(
ExecuteParams memory executeParams_,
TransmissionParams memory transmissionParams_
) external payable;

/**
* @notice Gets minimum fees required for execution
* @return nativeFees Minimum native token fees required
*/
function getMinSocketFees() external view returns (uint256 nativeFees);

/**
* @notice Sets socket fees
* @param socketFees_ New socket fees amount
*/
function setSocketFees(uint256 socketFees_) external;

/**
* @notice Gets current socket fees
* @return Current socket fees amount
*/
function socketFees() external view returns (uint256);
}
40 changes: 24 additions & 16 deletions contracts/protocol/socket/Socket.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ contract Socket is SocketUtils {
*/
function execute(
ExecuteParams memory executeParams_,
bytes memory transmitterSignature_
TransmissionParams memory transmissionParams_
) external payable returns (bytes memory) {
// check if the deadline has passed
if (executeParams_.deadline < block.timestamp) revert DeadlinePassed();
Expand All @@ -66,17 +66,18 @@ contract Socket is SocketUtils {
// check if the plug is disconnected
if (plugConfig.appGatewayId == bytes32(0)) revert PlugDisconnected();

// check if the message value is insufficient
if (msg.value < executeParams_.value) revert InsufficientMsgValue();

// create the payload id
if (msg.value < executeParams_.value + transmissionParams_.socketFees)
revert InsufficientMsgValue();
bytes32 payloadId = _createPayloadId(plugConfig.switchboard, executeParams_);

// validate the execution status
_validateExecutionStatus(payloadId);

address transmitter = transmitterSignature_.length > 0
? _recoverSigner(keccak256(abi.encode(address(this), payloadId)), transmitterSignature_)
address transmitter = transmissionParams_.transmitterSignature.length > 0
? _recoverSigner(
keccak256(abi.encode(address(this), payloadId)),
transmissionParams_.transmitterSignature
)
: address(0);

// create the digest
Expand All @@ -90,9 +91,7 @@ contract Socket is SocketUtils {

// verify the digest
_verify(digest, payloadId, plugConfig.switchboard);

// execute the payload and return the data
return _execute(payloadId, executeParams_);
return _execute(payloadId, executeParams_, transmissionParams_);
}

////////////////////////////////////////////////////////
Expand All @@ -114,25 +113,34 @@ contract Socket is SocketUtils {
*/
function _execute(
bytes32 payloadId_,
ExecuteParams memory executeParams_
ExecuteParams memory executeParams_,
TransmissionParams memory transmissionParams_
) internal returns (bytes memory) {
// check if the gas limit is sufficient
if (gasleft() < executeParams_.gasLimit) revert LowGasLimit();

// NOTE: external un-trusted call
(bool success, , bytes memory returnData) = executeParams_.target.tryCall(
msg.value,
executeParams_.value,
executeParams_.gasLimit,
maxCopyBytes,
executeParams_.payload
);

// if the execution failed, set the execution status to reverted
if (!success) {
if (success) {
emit ExecutionSuccess(payloadId_, returnData);
if (address(socketFeeManager) != address(0)) {
socketFeeManager.payAndCheckFees{value: transmissionParams_.socketFees}(
executeParams_,
transmissionParams_
);
}
} else {
payloadExecuted[payloadId_] = ExecutionStatus.Reverted;
address receiver = transmissionParams_.refundAddress == address(0) ? msg.sender : transmissionParams_.refundAddress;
SafeTransferLib.forceSafeTransferETH(receiver, msg.value);

emit ExecutionFailed(payloadId_, returnData);
} else {
emit ExecutionSuccess(payloadId_, returnData);
}

return returnData;
Expand Down
18 changes: 14 additions & 4 deletions contracts/protocol/socket/SocketBatcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ pragma solidity ^0.8.21;
import "solady/auth/Ownable.sol";
import "../../interfaces/ISocket.sol";
import "../../interfaces/ISwitchboard.sol";
import "../../interfaces/ISocketBatcher.sol";
import "../utils/RescueFundsLib.sol";
import {ExecuteParams} from "../../protocol/utils/common/Structs.sol";
import {ExecuteParams, TransmissionParams} from "../../protocol/utils/common/Structs.sol";
import "../../interfaces/ISocketBatcher.sol";

/**
* @title SocketBatcher
Expand Down Expand Up @@ -38,10 +38,20 @@ contract SocketBatcher is ISocketBatcher, Ownable {
ExecuteParams calldata executeParams_,
bytes32 digest_,
bytes calldata proof_,
bytes calldata transmitterSignature_
bytes calldata transmitterSignature_,
address refundAddress_
) external payable returns (bytes memory) {
ISwitchboard(executeParams_.switchboard).attest(digest_, proof_);
return socket__.execute{value: msg.value}(executeParams_, transmitterSignature_);
return
socket__.execute{value: msg.value}(
executeParams_,
TransmissionParams({
transmitterSignature: transmitterSignature_,
socketFees: 0,
extraData: "",
refundAddress: refundAddress_
})
);
}

/**
Expand Down
14 changes: 14 additions & 0 deletions contracts/protocol/socket/SocketConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {IPlug} from "../../interfaces/IPlug.sol";
import "../utils/AccessControl.sol";
import {GOVERNANCE_ROLE, RESCUE_ROLE} from "../utils/common/AccessRoles.sol";
import {PlugConfig, SwitchboardStatus, ExecutionStatus} from "../utils/common/Structs.sol";
import "../../interfaces/ISocketFeeManager.sol";
import {PlugDisconnected, InvalidAppGateway, InvalidTransmitter} from "../utils/common/Errors.sol";
import {MAX_COPY_BYTES} from "../utils/common/Constants.sol";

Expand All @@ -18,6 +19,9 @@ import {MAX_COPY_BYTES} from "../utils/common/Constants.sol";
* @dev This contract is meant to be inherited by other contracts that require socket configuration functionality
*/
abstract contract SocketConfig is ISocket, AccessControl {
// socket fee manager
ISocketFeeManager public socketFeeManager;

// @notice mapping of switchboard address to its status, helps socket to block invalid switchboards
mapping(address => SwitchboardStatus) public isValidSwitchboard;

Expand All @@ -40,6 +44,7 @@ abstract contract SocketConfig is ISocket, AccessControl {
event SwitchboardAdded(address switchboard);
// @notice event triggered when a switchboard is disabled
event SwitchboardDisabled(address switchboard);
event SocketFeeManagerUpdated(address oldSocketFeeManager, address newSocketFeeManager);

// @notice function to register a switchboard
// @dev only callable by switchboards
Expand All @@ -58,6 +63,15 @@ abstract contract SocketConfig is ISocket, AccessControl {
emit SwitchboardDisabled(msg.sender);
}


function setSocketFeeManager(address socketFeeManager_) external onlyRole(GOVERNANCE_ROLE) {
emit SocketFeeManagerUpdated(address(socketFeeManager), socketFeeManager_);
socketFeeManager = ISocketFeeManager(socketFeeManager_);
}

/**
* @notice connects Plug to Socket and sets the config for given `siblingChainSlug_`
*/
// @notice function to connect a plug to a socket
// @dev only callable by plugs (msg.sender)
// @param appGatewayId_ The app gateway id
Expand Down
72 changes: 72 additions & 0 deletions contracts/protocol/socket/SocketFeeManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.21;

import "../utils/AccessControl.sol";
import {GOVERNANCE_ROLE, RESCUE_ROLE} from "../utils/common/AccessRoles.sol";
import {ExecuteParams, TransmissionParams} from "../../protocol/utils/common/Structs.sol";
import "../../interfaces/ISocketFeeManager.sol";
import "../utils/RescueFundsLib.sol";

/**
* @title SocketFeeManager
* @notice The SocketFeeManager contract is responsible for managing socket fees
*/
contract SocketFeeManager is ISocketFeeManager, AccessControl {
// Current socket fees in native tokens
uint256 public socketFees;

error InsufficientFees();
error FeeTooLow();

event SocketFeesUpdated(uint256 oldFees, uint256 newFees);

/**
* @notice Initializes the SocketFeeManager contract
* @param owner_ The owner of the contract with GOVERNANCE_ROLE
* @param socketFees_ Initial socket fees amount
*/
constructor(address owner_, uint256 socketFees_) {
emit SocketFeesUpdated(0, socketFees_);
socketFees = socketFees_;
_grantRole(GOVERNANCE_ROLE, owner_);
_grantRole(RESCUE_ROLE, owner_);
}

/**
* @notice Pays and validates fees for execution
*/
function payAndCheckFees(ExecuteParams memory, TransmissionParams memory) external payable {
if (msg.value < socketFees) revert InsufficientFees();
}

/**
* @notice Gets minimum fees required for execution
* @return nativeFees Minimum native token fees required
*/
function getMinSocketFees() external view returns (uint256 nativeFees) {
return socketFees;
}

/**
* @notice Sets socket fees
* @param socketFees_ New socket fees amount
*/
function setSocketFees(uint256 socketFees_) external onlyRole(GOVERNANCE_ROLE) {
emit SocketFeesUpdated(socketFees, socketFees_);
socketFees = socketFees_;
}

/**
* @notice Allows owner to rescue stuck funds
* @param token_ Token address (address(0) for native tokens)
* @param to_ Address to send funds to
* @param amount_ Amount of tokens to rescue
*/
function rescueFunds(
address token_,
address to_,
uint256 amount_
) external onlyRole(RESCUE_ROLE) {
RescueFundsLib._rescueFunds(token_, to_, amount_);
}
}
15 changes: 15 additions & 0 deletions contracts/protocol/utils/common/Structs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,21 @@ struct ExecuteParams {
address switchboard;
}

struct TransmissionParams {
uint256 socketFees;
address refundAddress;
bytes extraData;
bytes transmitterSignature;
}

struct PayloadIdParams {
uint40 requestCount;
uint40 batchCount;
uint40 payloadCount;
address switchboard;
uint32 chainSlug;
}

/// @notice Struct containing fee amounts and status
struct TokenBalance {
uint256 deposited; // Amount deposited
Expand Down
6 changes: 2 additions & 4 deletions test/Inbox.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,10 @@ contract TriggerTest is DeliveryHelperTest {
assertEq(gateway.counterVal(), incrementValue, "Gateway counter should be incremented");
}

function _encodeTriggerId(address appGateway_, uint256 chainSlug_) internal returns (bytes32) {
function _encodeTriggerId(address socket_, uint256 chainSlug_) internal returns (bytes32) {
return
bytes32(
(uint256(chainSlug_) << 224) |
(uint256(uint160(appGateway_)) << 64) |
triggerCounter++
(uint256(chainSlug_) << 224) | (uint256(uint160(socket_)) << 64) | triggerCounter++
);
}
}
Loading