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
1 change: 0 additions & 1 deletion contracts/protocol/utils/common/Structs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ struct TriggerParams {
}
// timeout:
struct TimeoutRequest {
bytes32 timeoutId;
address target;
uint256 delayInSeconds;
uint256 executeAt;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {Ownable} from "solady/auth/Ownable.sol";
import "../../interfaces/IWatcherPrecompileConfig.sol";
import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol";
import {InvalidWatcherSignature, NonceUsed} from "../utils/common/Errors.sol";
import "./core/WatcherPrecompileUtils.sol";
import "./core/WatcherIdUtils.sol";

/// @title WatcherPrecompileConfig
/// @notice Configuration contract for the Watcher Precompile system
Expand All @@ -16,8 +16,7 @@ contract WatcherPrecompileConfig is
IWatcherPrecompileConfig,
Initializable,
Ownable,
AddressResolverUtil,
WatcherPrecompileUtils
AddressResolverUtil
{
// slots 0-50 (51) reserved for addr resolver util

Expand Down Expand Up @@ -201,7 +200,7 @@ contract WatcherPrecompileConfig is
) return;

(bytes32 appGatewayId, address switchboard) = getPlugConfigs(chainSlug_, target_);
if (appGatewayId != _encodeAppGatewayId(appGateway_)) revert InvalidGateway();
if (appGatewayId != WatcherIdUtils.encodeAppGatewayId(appGateway_)) revert InvalidGateway();
if (switchboard != switchboard_) revert InvalidSwitchboard();
}

Expand Down
26 changes: 12 additions & 14 deletions contracts/protocol/watcherPrecompile/core/RequestHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ abstract contract RequestHandler is WatcherPrecompileCore {
/// @dev This function processes a batch of payload requests and assigns them to batches
/// @dev It also consumes limits for the app gateway based on the number of reads and writes
function submitRequest(
PayloadSubmitParams[] calldata payloadSubmitParams_
PayloadSubmitParams[] memory payloadSubmitParams_
) public returns (uint40 requestCount) {
address appGateway = _checkAppGateways(payloadSubmitParams_);

Expand Down Expand Up @@ -50,7 +50,7 @@ abstract contract RequestHandler is WatcherPrecompileCore {
}

uint40 localPayloadCount = payloadCounter++;
bytes32 payloadId = _createPayloadId(
bytes32 payloadId = WatcherIdUtils.createPayloadId(
requestCount,
batchCount,
localPayloadCount,
Expand All @@ -59,14 +59,15 @@ abstract contract RequestHandler is WatcherPrecompileCore {
);
batchPayloadIds[batchCount].push(payloadId);

bytes32 payloadHeader;
payloadHeader = payloadHeader.setRequestCount(requestCount);
payloadHeader = payloadHeader.setBatchCount(batchCount);
payloadHeader = payloadHeader.setPayloadCount(localPayloadCount);
payloadHeader = payloadHeader.setChainSlug(p.chainSlug);
payloadHeader = payloadHeader.setCallType(p.callType);
payloadHeader = payloadHeader.setIsParallel(p.isParallel);
payloadHeader = payloadHeader.setWriteFinality(p.writeFinality);
bytes32 payloadHeader = PayloadHeaderDecoder.createPayloadHeader(
requestCount,
batchCount,
localPayloadCount,
p.chainSlug,
p.callType,
p.isParallel,
p.writeFinality
);

payloads[payloadId].payloadHeader = payloadHeader;
payloads[payloadId].asyncPromise = p.asyncPromise;
Expand All @@ -89,9 +90,6 @@ abstract contract RequestHandler is WatcherPrecompileCore {
// This is needed because the last batch in the loop above doesn't get added since there's no next level to trigger it
requestBatchIds[requestCount].push(nextBatchCount++);

watcherPrecompileLimits__.consumeLimit(appGateway, QUERY, readCount);
watcherPrecompileLimits__.consumeLimit(appGateway, FINALIZE, writeCount);

requestParams[requestCount].queryCount = readCount;
requestParams[requestCount].finalizeCount = writeCount;

Expand All @@ -111,7 +109,7 @@ abstract contract RequestHandler is WatcherPrecompileCore {
/// @param payloadSubmitParams Array of payload submit parameters
/// @return appGateway The core app gateway address
function _checkAppGateways(
PayloadSubmitParams[] calldata payloadSubmitParams
PayloadSubmitParams[] memory payloadSubmitParams
) internal view returns (address appGateway) {
bool isDeliveryHelper = msg.sender == addressResolver__.deliveryHelper();

Expand Down
32 changes: 32 additions & 0 deletions contracts/protocol/watcherPrecompile/core/WatcherIdUtils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

library WatcherIdUtils {
function encodeAppGatewayId(address appGateway_) internal pure returns (bytes32) {
return bytes32(uint256(uint160(appGateway_)));
}

function decodeAppGatewayId(bytes32 appGatewayId_) internal pure returns (address) {
return address(uint160(uint256(appGatewayId_)));
}

/// @notice Creates a payload ID from the given parameters
/// @param requestCount_ The request count
/// @param batchCount_ The batch count
/// @param payloadCount_ The payload count
/// @param switchboard_ The switchboard address
/// @param chainSlug_ The chain slug
/// @return The created payload ID
function createPayloadId(
uint40 requestCount_,
uint40 batchCount_,
uint40 payloadCount_,
address switchboard_,
uint32 chainSlug_
) internal pure returns (bytes32) {
return
keccak256(
abi.encode(requestCount_, batchCount_, payloadCount_, switchboard_, chainSlug_)
);
}
}
115 changes: 61 additions & 54 deletions contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ contract WatcherPrecompile is RequestHandler {
watcherPrecompileConfig__ = IWatcherPrecompileConfig(watcherPrecompileConfig_);
maxTimeoutDelayInSeconds = 24 * 60 * 60; // 24 hours
expiryTime = expiryTime_;

evmxSlug = evmxSlug_;

timeoutIdPrefix = (uint256(evmxSlug_) << 224) | (uint256(uint160(address(this))) << 64);
}

// ================== Timeout functions ==================
Expand All @@ -53,10 +54,7 @@ contract WatcherPrecompile is RequestHandler {
/// @param delayInSeconds_ The delay in seconds before the timeout executes
/// @param payload_ The payload data to be executed after the timeout
/// @return The unique identifier for the timeout request
function setTimeout(
uint256 delayInSeconds_,
bytes calldata payload_
) external returns (bytes32) {
function setTimeout(uint256 delayInSeconds_, bytes memory payload_) external returns (bytes32) {
return _setTimeout(delayInSeconds_, payload_);
}

Expand All @@ -68,7 +66,7 @@ contract WatcherPrecompile is RequestHandler {
function resolveTimeout(
bytes32 timeoutId_,
uint256 signatureNonce_,
bytes calldata signature_
bytes memory signature_
) external {
_isWatcherSignatureValid(
abi.encode(this.resolveTimeout.selector, timeoutId_),
Expand Down Expand Up @@ -135,9 +133,9 @@ contract WatcherPrecompile is RequestHandler {
/// @dev keccak256(abi.encode(switchboard, digest))
function finalized(
bytes32 payloadId_,
bytes calldata proof_,
bytes memory proof_,
uint256 signatureNonce_,
bytes calldata signature_
bytes memory signature_
) external {
_isWatcherSignatureValid(
abi.encode(this.finalized.selector, payloadId_, proof_),
Expand Down Expand Up @@ -186,9 +184,9 @@ contract WatcherPrecompile is RequestHandler {
/// @dev It verifies that the signature is valid
/// @dev It also processes the next batch if the current batch is complete
function resolvePromises(
ResolvedPromises[] calldata resolvedPromises_,
ResolvedPromises[] memory resolvedPromises_,
uint256 signatureNonce_,
bytes calldata signature_
bytes memory signature_
) external {
_isWatcherSignatureValid(
abi.encode(this.resolvePromises.selector, resolvedPromises_),
Expand All @@ -197,45 +195,13 @@ contract WatcherPrecompile is RequestHandler {
);

for (uint256 i = 0; i < resolvedPromises_.length; i++) {
// Get the array of promise addresses for this payload
PayloadParams memory payloadParams = payloads[resolvedPromises_[i].payloadId];
address asyncPromise = payloadParams.asyncPromise;

uint40 requestCount = payloadParams.payloadHeader.getRequestCount();

// todo: non trusted call
if (asyncPromise != address(0)) {
// todo: limit the gas used for promise resolution
// Resolve each promise with its corresponding return data
bool success = IPromise(asyncPromise).markResolved(
requestCount,
resolvedPromises_[i].payloadId,
resolvedPromises_[i].returnData
);

if (!success) {
emit PromiseNotResolved(resolvedPromises_[i].payloadId, asyncPromise);
continue;
}
}

isPromiseExecuted[resolvedPromises_[i].payloadId] = true;
uint40 requestCount = payloads[resolvedPromises_[i].payloadId]
.payloadHeader
.getRequestCount();
RequestParams storage requestParams_ = requestParams[requestCount];
requestParams_.currentBatchPayloadsLeft--;
requestParams_.payloadsRemaining--;

// if all payloads of a batch are executed, process the next batch
if (
requestParams_.currentBatchPayloadsLeft == 0 && requestParams_.payloadsRemaining > 0
) {
_processBatch(requestCount, ++requestParams_.currentBatch);
}

// if all payloads of a request are executed, finish the request
if (requestParams_.payloadsRemaining == 0) {
IMiddleware(requestParams_.middleware).finishRequest(requestCount);
}
emit PromiseResolved(resolvedPromises_[i].payloadId, asyncPromise);
_processPromiseResolution(resolvedPromises_[i], requestParams_);
_checkAndProcessBatch(requestParams_, requestCount);
}
}

Expand All @@ -251,7 +217,7 @@ contract WatcherPrecompile is RequestHandler {
bool isRevertingOnchain_,
bytes32 payloadId_,
uint256 signatureNonce_,
bytes calldata signature_
bytes memory signature_
) external {
_isWatcherSignatureValid(
abi.encode(this.markRevert.selector, isRevertingOnchain_, payloadId_),
Expand Down Expand Up @@ -289,9 +255,9 @@ contract WatcherPrecompile is RequestHandler {
/// @dev This function calls app gateways with the specified parameters
/// @dev It verifies that the signature is valid and that the app gateway hasn't been called yet
function callAppGateways(
TriggerParams[] calldata params_,
TriggerParams[] memory params_,
uint256 signatureNonce_,
bytes calldata signature_
bytes memory signature_
) external {
_isWatcherSignatureValid(
abi.encode(this.callAppGateways.selector, params_),
Expand All @@ -302,7 +268,7 @@ contract WatcherPrecompile is RequestHandler {
for (uint256 i = 0; i < params_.length; i++) {
if (appGatewayCalled[params_[i].triggerId]) revert AppGatewayAlreadyCalled();

address appGateway = _decodeAppGatewayId(params_[i].appGatewayId);
address appGateway = WatcherIdUtils.decodeAppGatewayId(params_[i].appGatewayId);
if (
!watcherPrecompileConfig__.isValidPlug(
appGateway,
Expand All @@ -312,9 +278,9 @@ contract WatcherPrecompile is RequestHandler {
) revert InvalidCallerTriggered();

IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileCreditsFromAddress(
watcherPrecompileLimits__.callBackFees(),
appGateway
);
watcherPrecompileLimits__.callBackFees(),
appGateway
);

appGatewayCaller = appGateway;
appGatewayCalled[params_[i].triggerId] = true;
Expand Down Expand Up @@ -379,4 +345,45 @@ contract WatcherPrecompile is RequestHandler {
function getRequestParams(uint40 requestCount) external view returns (RequestParams memory) {
return requestParams[requestCount];
}

function _processPromiseResolution(
ResolvedPromises memory resolvedPromise_,
RequestParams storage requestParams_
) internal {
PayloadParams memory payloadParams = payloads[resolvedPromise_.payloadId];
address asyncPromise = payloadParams.asyncPromise;
uint40 requestCount = payloadParams.payloadHeader.getRequestCount();

if (asyncPromise != address(0)) {
bool success = IPromise(asyncPromise).markResolved(
requestCount,
resolvedPromise_.payloadId,
resolvedPromise_.returnData
);

if (!success) {
emit PromiseNotResolved(resolvedPromise_.payloadId, asyncPromise);
return;
}
}

isPromiseExecuted[resolvedPromise_.payloadId] = true;
requestParams_.currentBatchPayloadsLeft--;
requestParams_.payloadsRemaining--;

emit PromiseResolved(resolvedPromise_.payloadId, asyncPromise);
}

function _checkAndProcessBatch(
RequestParams storage requestParams_,
uint40 requestCount
) internal {
if (requestParams_.currentBatchPayloadsLeft == 0 && requestParams_.payloadsRemaining > 0) {
_processBatch(requestCount, ++requestParams_.currentBatch);
}

if (requestParams_.payloadsRemaining == 0) {
IMiddleware(requestParams_.middleware).finishRequest(requestCount);
}
}
}
Loading