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
18 changes: 11 additions & 7 deletions contracts/protocol/AsyncPromise.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {AddressResolverUtil} from "./utils/AddressResolverUtil.sol";
import {IPromise} from "../interfaces/IPromise.sol";
import {IAppGateway} from "../interfaces/IAppGateway.sol";
import {Initializable} from "solady/utils/Initializable.sol";
import {AsyncPromiseState} from "../protocol/utils/common/Structs.sol";
import {AsyncPromiseState} from "./utils/common/Structs.sol";
import {MAX_COPY_BYTES} from "./utils/common/Constants.sol";
import {LibCall} from "solady/utils/LibCall.sol";

abstract contract AsyncPromiseStorage is IPromise {
// slots [0-49] reserved for gap
Expand Down Expand Up @@ -44,6 +46,7 @@ abstract contract AsyncPromiseStorage is IPromise {
/// @notice this contract stores the callback address and data to be executed once the previous call is executed
/// This promise expires once the callback is executed
contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil {
using LibCall for address;
/// @notice Error thrown when attempting to resolve an already resolved promise.
error PromiseAlreadyResolved();
/// @notice Only the forwarder or local invoker can set then's promise callback
Expand Down Expand Up @@ -92,7 +95,8 @@ contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil
callbackSelector,
abi.encode(callbackData, returnData_)
);
(success, ) = localInvoker.call(combinedCalldata);
// setting max_copy_bytes to 0 as not using returnData right now
(success, , ) = localInvoker.tryCall(0, gasleft(), 0, combinedCalldata);
if (success) return success;

_handleRevert(requestCount_, payloadId_, AsyncPromiseState.CALLBACK_REVERTING);
Expand All @@ -115,11 +119,11 @@ contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil
// to update the state in case selector is bytes(0) but reverting onchain
resolved = false;
state = state_;

(bool success, ) = localInvoker.call(
abi.encodeWithSelector(IAppGateway.handleRevert.selector, requestCount_, payloadId_)
);
if (!success) revert PromiseRevertFailed();
try IAppGateway(localInvoker).handleRevert(requestCount_, payloadId_) {
// Successfully handled revert
} catch {
revert PromiseRevertFailed();
}
}

/// @notice Sets the callback selector and data for the promise.
Expand Down
11 changes: 10 additions & 1 deletion contracts/protocol/payload-delivery/ContractFactoryPlug.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ import "../utils/RescueFundsLib.sol";
import {NotSocket} from "../utils/common/Errors.sol";
import "../../base/PlugBase.sol";
import "../../interfaces/IContractFactoryPlug.sol";
import {LibCall} from "solady/utils/LibCall.sol";
import {MAX_COPY_BYTES} from "../utils/common/Constants.sol";

/// @title ContractFactory
/// @notice Abstract contract for deploying contracts
contract ContractFactoryPlug is PlugBase, AccessControl, IContractFactoryPlug {
using LibCall for address;

event Deployed(address addr, bytes32 salt, bytes returnData);

/// @notice Error thrown if it failed to deploy the create2 contract
Expand Down Expand Up @@ -48,7 +52,12 @@ contract ContractFactoryPlug is PlugBase, AccessControl, IContractFactoryPlug {
bytes memory returnData;
if (initCallData_.length > 0) {
// Capture more detailed error information
(bool success, bytes memory returnData_) = addr.call(initCallData_);
(bool success, , bytes memory returnData_) = addr.tryCall(
0,
gasleft(),
MAX_COPY_BYTES,
initCallData_
);

if (!success) {
// Additional error logging
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ abstract contract DeliveryHelperStorage is IMiddleware {

mapping(uint40 => RequestMetadata) public requests;

mapping(uint32 => uint256) public chainMaxMsgValueLimit;
// slots [59-108] reserved for gap
uint256[50] _gap_after;
uint256[49] _gap_after;
}
19 changes: 19 additions & 0 deletions contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ abstract contract DeliveryUtils is
/// @notice Error thrown when a request contains only reads
error ReadOnlyRequests();

/// @notice Error thrown when a request contains more than 10 payloads
error RequestPayloadCountLimitExceeded();
/// @notice Error thrown when a maximum message value limit is exceeded
error MaxMsgValueLimitExceeded();

event CallBackReverted(uint40 requestCount_, bytes32 payloadId_);
event RequestCancelled(uint40 indexed requestCount);
event BidTimeoutUpdated(uint256 newBidTimeout);
Expand Down Expand Up @@ -71,4 +76,18 @@ abstract contract DeliveryUtils is
bidTimeout = newBidTimeout_;
emit BidTimeoutUpdated(newBidTimeout_);
}

/// @notice Updates the maximum message value limit for multiple chains
/// @param chainSlugs_ Array of chain identifiers
/// @param maxMsgValueLimits_ Array of corresponding maximum message value limits
function updateChainMaxMsgValueLimits(
uint32[] calldata chainSlugs_,
uint256[] calldata maxMsgValueLimits_
) external onlyOwner {
if (chainSlugs_.length != maxMsgValueLimits_.length) revert InvalidIndex();

for (uint256 i = 0; i < chainSlugs_.length; i++) {
chainMaxMsgValueLimit[chainSlugs_[i]] = maxMsgValueLimits_[i];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {Ownable} from "solady/auth/Ownable.sol";
import "solady/utils/Initializable.sol";
import {AddressResolverUtil} from "../../utils/AddressResolverUtil.sol";
import "./DeliveryUtils.sol";

import {PAYLOAD_SIZE_LIMIT, REQUEST_PAYLOAD_COUNT_LIMIT} from "../../utils/common/Constants.sol";
/// @notice Abstract contract for managing asynchronous payloads
abstract contract RequestQueue is DeliveryUtils {
// slots [0-108] reserved for delivery helper storage and [109-159] reserved for addr resolver util
Expand All @@ -20,6 +20,8 @@ abstract contract RequestQueue is DeliveryUtils {
/// @notice Queues a new payload
/// @param queuePayloadParams_ The call parameters
function queue(QueuePayloadParams memory queuePayloadParams_) external {
if (queuePayloadParams.length > REQUEST_PAYLOAD_COUNT_LIMIT)
revert RequestPayloadCountLimitExceeded();
queuePayloadParams.push(queuePayloadParams_);
}

Expand All @@ -43,7 +45,6 @@ abstract contract RequestQueue is DeliveryUtils {
bytes memory onCompleteData_
) internal returns (uint40 requestCount) {
if (queuePayloadParams.length == 0) return 0;

if (!IFeesManager(addressResolver__.feesManager()).isFeesEnough(appGateway_, fees_))
revert InsufficientFees();

Expand Down Expand Up @@ -144,10 +145,12 @@ abstract contract RequestQueue is DeliveryUtils {
queuePayloadParams_.initCallData
);

if (payload_.length > 24.5 * 1024) revert PayloadTooLarge();
target = getDeliveryHelperPlugAddress(queuePayloadParams_.chainSlug);
}

if (payload_.length > PAYLOAD_SIZE_LIMIT) revert PayloadTooLarge();
if (queuePayloadParams_.value > chainMaxMsgValueLimit[queuePayloadParams_.chainSlug])
revert MaxMsgValueLimitExceeded();
return
PayloadSubmitParams({
levelNumber: level_,
Expand Down
15 changes: 10 additions & 5 deletions contracts/protocol/socket/Socket.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
pragma solidity ^0.8.21;

import "./SocketUtils.sol";

import {LibCall} from "solady/utils/LibCall.sol";
import {IPlug} from "../../interfaces/IPlug.sol";
import {PlugDisconnected, InvalidAppGateway} from "../utils/common/Errors.sol";
import {MAX_COPY_BYTES} from "../utils/common/Constants.sol";

/**
* @title SocketDst
Expand All @@ -15,6 +16,8 @@ import {PlugDisconnected, InvalidAppGateway} from "../utils/common/Errors.sol";
* It also includes functions for payload execution and verification
*/
contract Socket is SocketUtils {
using LibCall for address;

////////////////////////////////////////////////////////
////////////////////// ERRORS //////////////////////////
////////////////////////////////////////////////////////
Expand Down Expand Up @@ -93,10 +96,12 @@ contract Socket is SocketUtils {
if (gasleft() < executeParams_.gasLimit) revert LowGasLimit();

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

if (!success) {
payloadExecuted[payloadId_] = ExecutionStatus.Reverted;
Expand Down
5 changes: 5 additions & 0 deletions contracts/protocol/socket/SocketConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ abstract contract SocketConfig is ISocket, AccessControl {
// plug => (appGateway, switchboard__)
mapping(address => PlugConfig) internal _plugConfigs;

uint16 public maxCopyBytes = 2048; // 2KB
// Error triggered when a connection is invalid
error InvalidConnection();
error InvalidSwitchboard();
Expand Down Expand Up @@ -58,6 +59,10 @@ abstract contract SocketConfig is ISocket, AccessControl {
emit PlugConnected(msg.sender, appGateway_, switchboard_);
}

function setMaxCopyBytes(uint16 maxCopyBytes_) external onlyRole(GOVERNANCE_ROLE) {
maxCopyBytes = maxCopyBytes_;
}

/**
* @notice returns the config for given `plugAddress_` and `siblingChainSlug_`
* @param plugAddress_ address of plug present at current chain
Expand Down
3 changes: 3 additions & 0 deletions contracts/protocol/utils/common/Constants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ bytes32 constant FAST = keccak256("FAST");

uint256 constant DEPLOY_GAS_LIMIT = 5_000_000;
uint256 constant CONFIGURE_GAS_LIMIT = 1_000_000;
uint256 constant PAYLOAD_SIZE_LIMIT = 24_500;
uint256 constant REQUEST_PAYLOAD_COUNT_LIMIT = 10;
uint16 constant MAX_COPY_BYTES = 2048; // 2KB
18 changes: 15 additions & 3 deletions contracts/protocol/watcherPrecompile/WatcherPrecompile.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
pragma solidity ^0.8.21;

import "./RequestHandler.sol";
import {LibCall} from "solady/utils/LibCall.sol";
import {MAX_COPY_BYTES} from "../utils/common/Constants.sol";

/// @title WatcherPrecompile
/// @notice Contract that handles payload verification, execution and app configurations
contract WatcherPrecompile is RequestHandler {
using DumpDecoder for bytes32;

using LibCall for address;
constructor() {
_disableInitializers(); // disable for implementation
}
Expand Down Expand Up @@ -63,7 +65,12 @@ contract WatcherPrecompile is RequestHandler {
if (timeoutRequest_.isResolved) revert TimeoutAlreadyResolved();
if (block.timestamp < timeoutRequest_.executeAt) revert ResolvingTimeoutTooEarly();

(bool success, ) = address(timeoutRequest_.target).call(timeoutRequest_.payload);
(bool success, , ) = timeoutRequest_.target.tryCall(
0,
gasleft(),
0, // setting max_copy_bytes to 0 as not using returnData right now
timeoutRequest_.payload
);
if (!success) revert CallFailed();

timeoutRequest_.isResolved = true;
Expand Down Expand Up @@ -264,7 +271,12 @@ contract WatcherPrecompile is RequestHandler {
appGatewayCaller = appGateway;
appGatewayCalled[params_[i].triggerId] = true;

(bool success, ) = address(appGateway).call(params_[i].payload);
(bool success, , ) = appGateway.tryCall(
0,
gasleft(),
0, // setting max_copy_bytes to 0 as not using returnData right now
params_[i].payload
);
if (!success) {
emit AppGatewayCallFailed(params_[i].triggerId);
} else {
Expand Down