Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions contracts/interfaces/ISocket.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ interface ISocket {
* @notice emits the status of payload after external call
* @param payloadId payload id which is executed
*/
event ExecutionSuccess(bytes32 payloadId, bytes returnData);
event ExecutionSuccess(bytes32 payloadId, bool exceededMaxCopy, bytes returnData);

/**
* @notice emits the status of payload after external call
* @param payloadId payload id which is executed
*/
event ExecutionFailed(bytes32 payloadId, bytes returnData);
event ExecutionFailed(bytes32 payloadId, bool exceededMaxCopy, bytes returnData);

/**
* @notice emits the config set by a plug for a remoteChainSlug
Expand All @@ -35,14 +35,14 @@ interface ISocket {
/**
* @notice emits the payload details when a new payload arrives at outbound
* @param triggerId trigger id
* @param chainSlug local chain slug
* @param switchboard switchboard address
* @param plug local plug address
* @param overrides params, for specifying details like fee pool chain, fee pool token and max fees if required
* @param payload the data which will be used by contracts on chain
*/
event AppGatewayCallRequested(
bytes32 triggerId,
uint32 chainSlug,
address switchboard,
address plug,
bytes overrides,
bytes payload
Expand All @@ -54,7 +54,7 @@ interface ISocket {
function execute(
ExecuteParams memory executeParams_,
TransmissionParams memory transmissionParams_
) external payable returns (bytes memory);
) external payable returns (bool, bool, bytes memory);

/**
* @notice sets the config specific to the plug
Expand Down
3 changes: 2 additions & 1 deletion contracts/interfaces/ISocketBatcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ interface ISocketBatcher {
*/
function attestAndExecute(
ExecuteParams calldata executeParams_,
address switchboard_,
bytes32 digest_,
bytes calldata proof_,
bytes calldata transmitterSignature_,
address refundAddress_
) external payable returns (bytes memory);
) external payable returns (bool, bool, bytes memory);
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ abstract contract RequestQueue is DeliveryUtils {
IContractFactoryPlug.deployContract.selector,
queuePayloadParams_.isPlug,
salt,
queuePayloadParams_.appGateway,
bytes32(uint256(uint160(queuePayloadParams_.appGateway))),
queuePayloadParams_.switchboard,
queuePayloadParams_.payload,
queuePayloadParams_.initCallData
Expand Down
77 changes: 48 additions & 29 deletions contracts/protocol/socket/Socket.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ import "./SocketUtils.sol";
contract Socket is SocketUtils {
using LibCall for address;

// @notice mapping of payload id to execution status
mapping(bytes32 => ExecutionStatus) public payloadExecuted;

// @notice mapping of payload id to execution status
mapping(bytes32 => bytes32) public payloadIdToDigest;

// @notice buffer to account for gas used by current contract execution
uint256 public constant GAS_LIMIT_BUFFER = 105;

////////////////////////////////////////////////////////
////////////////////// ERRORS //////////////////////////
////////////////////////////////////////////////////////
Expand Down Expand Up @@ -39,6 +48,10 @@ contract Socket is SocketUtils {
* @dev Error emitted when the message value is insufficient
*/
error InsufficientMsgValue();
/**
* @dev Error emitted when the call type is read
*/
error ReadOnlyCall();

/**
* @notice Constructor for the Socket contract
Expand All @@ -58,13 +71,15 @@ contract Socket is SocketUtils {
function execute(
ExecuteParams memory executeParams_,
TransmissionParams memory transmissionParams_
) external payable returns (bytes memory) {
) external payable returns (bool, bool, bytes memory) {
// check if the deadline has passed
if (executeParams_.deadline < block.timestamp) revert DeadlinePassed();
// check if the call type is valid
if (executeParams_.callType == CallType.READ) revert ReadOnlyCall();

PlugConfig memory plugConfig = _plugConfigs[executeParams_.target];
// check if the plug is disconnected
if (plugConfig.appGatewayId == bytes32(0)) revert PlugDisconnected();
if (plugConfig.appGatewayId == bytes32(0)) revert PlugNotFound();

if (msg.value < executeParams_.value + transmissionParams_.socketFees)
revert InsufficientMsgValue();
Expand All @@ -88,6 +103,7 @@ contract Socket is SocketUtils {
plugConfig.appGatewayId,
executeParams_
);
payloadIdToDigest[payloadId] = digest;

// verify the digest
_verify(digest, payloadId, plugConfig.switchboard);
Expand Down Expand Up @@ -115,20 +131,23 @@ contract Socket is SocketUtils {
bytes32 payloadId_,
ExecuteParams memory executeParams_,
TransmissionParams memory transmissionParams_
) internal returns (bytes memory) {
) internal returns (bool, bool, bytes memory) {
// check if the gas limit is sufficient
if (gasleft() < executeParams_.gasLimit) revert LowGasLimit();
// bump by 5% to account for gas used by current contract execution
if (gasleft() < (executeParams_.gasLimit * GAS_LIMIT_BUFFER) / 100) revert LowGasLimit();

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

if (success) {
emit ExecutionSuccess(payloadId_, returnData);
emit ExecutionSuccess(payloadId_, exceededMaxCopy, returnData);
if (address(socketFeeManager) != address(0)) {
socketFeeManager.payAndCheckFees{value: transmissionParams_.socketFees}(
executeParams_,
Expand All @@ -137,13 +156,15 @@ contract Socket is SocketUtils {
}
} else {
payloadExecuted[payloadId_] = ExecutionStatus.Reverted;
address receiver = transmissionParams_.refundAddress == address(0) ? msg.sender : transmissionParams_.refundAddress;
address receiver = transmissionParams_.refundAddress == address(0)
? msg.sender
: transmissionParams_.refundAddress;
SafeTransferLib.forceSafeTransferETH(receiver, msg.value);
emit ExecutionFailed(payloadId_, returnData);

emit ExecutionFailed(payloadId_, exceededMaxCopy, returnData);
}

return returnData;
return (success, exceededMaxCopy, returnData);
}

function _validateExecutionStatus(bytes32 payloadId_) internal {
Expand All @@ -157,33 +178,31 @@ contract Socket is SocketUtils {
////////////////////////////////////////////////////////
/**
* @notice To trigger to a connected remote chain. Should only be called by a plug.
* @param payload_ bytes to be delivered on EVMx
* @param overrides_ a bytes param to add details for execution, for eg: fees to be paid for execution
*/
function _triggerAppGateway(
address plug_,
bytes memory overrides_,
bytes memory payload_
) internal returns (bytes32 triggerId) {
PlugConfig memory plugConfig = _plugConfigs[plug_];
function _triggerAppGateway(address plug_) internal returns (bytes32 triggerId) {
PlugConfig storage plugConfig = _plugConfigs[plug_];

// if no sibling plug is found for the given chain slug, revert
// sends the trigger to connected app gateway
if (plugConfig.appGatewayId == bytes32(0)) revert PlugDisconnected();
if (plugConfig.appGatewayId == bytes32(0)) revert PlugNotFound();

// creates a unique ID for the message
triggerId = _encodeTriggerId();
emit AppGatewayCallRequested(triggerId, chainSlug, plug_, overrides_, payload_);
emit AppGatewayCallRequested(
triggerId,
plugConfig.switchboard,
plug_,
// gets the overrides from the plug
IPlug(plug_).overrides(),
msg.data
);
}

/// @notice Fallback function that forwards all calls to Socket's callAppGateway
/// @dev The calldata is passed as-is to the gateways
/// @dev if ETH sent with the call, it will revert
fallback(bytes calldata) external returns (bytes memory) {
// gets the overrides from the plug
bytes memory overrides = IPlug(msg.sender).overrides();

// return the trigger id
return abi.encode(_triggerAppGateway(msg.sender, overrides, msg.data));
return abi.encode(_triggerAppGateway(msg.sender));
}
}
5 changes: 3 additions & 2 deletions contracts/protocol/socket/SocketBatcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,13 @@ contract SocketBatcher is ISocketBatcher, Ownable {
*/
function attestAndExecute(
ExecuteParams calldata executeParams_,
address switchboard_,
bytes32 digest_,
bytes calldata proof_,
bytes calldata transmitterSignature_,
address refundAddress_
) external payable returns (bytes memory) {
ISwitchboard(executeParams_.switchboard).attest(digest_, proof_);
) external payable returns (bool, bool, bytes memory) {
ISwitchboard(switchboard_).attest(digest_, proof_);
return
socket__.execute{value: msg.value}(
executeParams_,
Expand Down
22 changes: 13 additions & 9 deletions contracts/protocol/socket/SocketConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import "../../interfaces/ISwitchboard.sol";
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 {GOVERNANCE_ROLE, RESCUE_ROLE, SWITCHBOARD_DISABLER_ROLE} from "../utils/common/AccessRoles.sol";
import {CallType, PlugConfig, SwitchboardStatus, ExecutionStatus} from "../utils/common/Structs.sol";
import {PlugNotFound, InvalidAppGateway, InvalidTransmitter} from "../utils/common/Errors.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 @@ -21,7 +21,7 @@ import {MAX_COPY_BYTES} from "../utils/common/Constants.sol";
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 @@ -44,6 +44,8 @@ abstract contract SocketConfig is ISocket, AccessControl {
event SwitchboardAdded(address switchboard);
// @notice event triggered when a switchboard is disabled
event SwitchboardDisabled(address switchboard);
// @notice event triggered when a switchboard is enabled
event SwitchboardEnabled(address switchboard);
event SocketFeeManagerUpdated(address oldSocketFeeManager, address newSocketFeeManager);

// @notice function to register a switchboard
Expand All @@ -58,11 +60,17 @@ abstract contract SocketConfig is ISocket, AccessControl {

// @notice function to disable a switchboard
// @dev only callable by governance role
function disableSwitchboard() external onlyRole(GOVERNANCE_ROLE) {
function disableSwitchboard() external onlyRole(SWITCHBOARD_DISABLER_ROLE) {
isValidSwitchboard[msg.sender] = SwitchboardStatus.DISABLED;
emit SwitchboardDisabled(msg.sender);
}

// @notice function to enable a switchboard
// @dev only callable by governance role
function enableSwitchboard() external onlyRole(GOVERNANCE_ROLE) {
isValidSwitchboard[msg.sender] = SwitchboardStatus.REGISTERED;
emit SwitchboardEnabled(msg.sender);
}

function setSocketFeeManager(address socketFeeManager_) external onlyRole(GOVERNANCE_ROLE) {
emit SocketFeeManagerUpdated(address(socketFeeManager), socketFeeManager_);
Expand All @@ -72,10 +80,6 @@ abstract contract SocketConfig is ISocket, AccessControl {
/**
* @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
// @param switchboard_ address of switchboard on sibling chain
function connect(bytes32 appGatewayId_, address switchboard_) external override {
if (isValidSwitchboard[switchboard_] != SwitchboardStatus.REGISTERED)
revert InvalidSwitchboard();
Expand Down
19 changes: 7 additions & 12 deletions contracts/protocol/socket/SocketUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,12 @@ abstract contract SocketUtils is SocketConfig {
bytes32 public immutable version;
// ChainSlug for this deployed socket instance
uint32 public immutable chainSlug;
// Prefix for trigger ID containing chain slug and address bits
uint256 private immutable triggerPrefix;

// @notice counter for trigger id
uint64 public triggerCounter;

// @notice mapping of payload id to execution status
mapping(bytes32 => ExecutionStatus) public payloadExecuted;

/*
* @notice constructor for creating a new Socket contract instance.
* @param chainSlug_ The unique identifier of the chain this socket is deployed on.
Expand All @@ -34,6 +33,8 @@ abstract contract SocketUtils is SocketConfig {
constructor(uint32 chainSlug_, address owner_, string memory version_) {
chainSlug = chainSlug_;
version = keccak256(bytes(version_));
triggerPrefix = (uint256(chainSlug_) << 224) | (uint256(uint160(address(this))) << 64);

_initializeOwner(owner_);
}

Expand All @@ -59,14 +60,13 @@ abstract contract SocketUtils is SocketConfig {
payloadId_,
executeParams_.deadline,
executeParams_.callType,
executeParams_.writeFinality,
executeParams_.gasLimit,
executeParams_.value,
executeParams_.readAt,
executeParams_.payload,
executeParams_.target,
appGatewayId_,
executeParams_.prevDigestsHash
executeParams_.prevDigestsHash,
executeParams_.extraData
)
);
}
Expand Down Expand Up @@ -112,12 +112,7 @@ abstract contract SocketUtils is SocketConfig {
* @return The trigger ID
*/
function _encodeTriggerId() internal returns (bytes32) {
return
bytes32(
(uint256(chainSlug) << 224) |
(uint256(uint160(address(this))) << 64) |
triggerCounter++
);
return bytes32(triggerPrefix | triggerCounter++);
}

//////////////////////////////////////////////
Expand Down
5 changes: 4 additions & 1 deletion contracts/protocol/socket/switchboard/FastSwitchboard.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ contract FastSwitchboard is SwitchboardBase {
function attest(bytes32 digest_, bytes calldata proof_) external {
if (isAttested[digest_]) revert AlreadyAttested();

address watcher = _recoverSigner(keccak256(abi.encode(address(this), digest_)), proof_);
address watcher = _recoverSigner(
keccak256(abi.encode(address(this), chainSlug, digest_)),
proof_
);
if (!_hasRole(WATCHER_ROLE, watcher)) revert WatcherNotFound();

isAttested[digest_] = true;
Expand Down
2 changes: 2 additions & 0 deletions contracts/protocol/utils/common/AccessRoles.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ bytes32 constant GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE");
bytes32 constant TRANSMITTER_ROLE = keccak256("TRANSMITTER_ROLE");
// used by switchboard watchers who work against transmitters
bytes32 constant WATCHER_ROLE = keccak256("WATCHER_ROLE");
// used to disable switchboard
bytes32 constant SWITCHBOARD_DISABLER_ROLE = keccak256("SWITCHBOARD_DISABLER_ROLE");
2 changes: 1 addition & 1 deletion contracts/protocol/utils/common/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ error LimitReached();
error FeesAlreadyPaid();
error NotAuctionManager();
error CallFailed();
error PlugDisconnected();
error PlugNotFound();
error InvalidAppGateway();
error AppGatewayAlreadyCalled();
error InvalidInboxCaller();
Expand Down
Loading