From 8cded4e3823e4b5fe869eb0723c486c5aa9e7150 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 3 Apr 2025 09:24:28 +0530 Subject: [PATCH 001/108] fix: rearrange files --- .../{ => core}/RequestHandler.sol | 0 .../{ => core}/WatcherPrecompile.sol | 0 .../{ => core}/WatcherPrecompileCore.sol | 19 ++--- .../{ => core}/WatcherPrecompileStorage.sol | 80 ++++++++++--------- 4 files changed, 46 insertions(+), 53 deletions(-) rename contracts/protocol/watcherPrecompile/{ => core}/RequestHandler.sol (100%) rename contracts/protocol/watcherPrecompile/{ => core}/WatcherPrecompile.sol (100%) rename contracts/protocol/watcherPrecompile/{ => core}/WatcherPrecompileCore.sol (93%) rename contracts/protocol/watcherPrecompile/{ => core}/WatcherPrecompileStorage.sol (61%) diff --git a/contracts/protocol/watcherPrecompile/RequestHandler.sol b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol similarity index 100% rename from contracts/protocol/watcherPrecompile/RequestHandler.sol rename to contracts/protocol/watcherPrecompile/core/RequestHandler.sol diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol similarity index 100% rename from contracts/protocol/watcherPrecompile/WatcherPrecompile.sol rename to contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol similarity index 93% rename from contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol rename to contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 7950823e..bb45bb07 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -132,22 +132,13 @@ abstract contract WatcherPrecompileCore is ); } - function _getPreviousDigestsHash( - uint40 requestCount_, - uint40 batchCount_ - ) internal view returns (bytes32) { - RequestParams memory r = requestParams[requestCount_]; - - // If this is the first batch of the request, return 0 bytes - if (batchCount_ == r.payloadParamsArray[0].dump.getBatchCount()) { - return bytes32(0); - } - - PayloadParams[] memory previousPayloads = _getBatch(batchCount_ - 1); + function _getPreviousDigestsHash(uint40 batchCount_) internal view returns (bytes32) { + bytes32[] memory payloadIds = batchPayloadIds[batchCount]; bytes32 prevDigestsHash = bytes32(0); - for (uint40 i = 0; i < previousPayloads.length; i++) { - PayloadParams memory p = payloads[previousPayloads[i].payloadId]; + for (uint40 i = 0; i < payloadIds.length; i++) { + PayloadParams memory p = payloads[payloadIds[i]]; + DigestParams memory digestParams = DigestParams( p.finalizedTransmitter, p.payloadId, diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol similarity index 61% rename from contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol rename to contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol index 974a0b03..600e198c 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol @@ -1,16 +1,16 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "../../interfaces/IWatcherPrecompile.sol"; -import {IAppGateway} from "../../interfaces/IAppGateway.sol"; -import {IFeesManager} from "../../interfaces/IFeesManager.sol"; -import {IPromise} from "../../interfaces/IPromise.sol"; -import "./DumpDecoder.sol"; +import "../../../interfaces/IWatcherPrecompile.sol"; +import {IAppGateway} from "../../../interfaces/IAppGateway.sol"; +import {IFeesManager} from "../../../interfaces/IFeesManager.sol"; +import {IPromise} from "../../../interfaces/IPromise.sol"; +import "../DumpDecoder.sol"; -import {IMiddleware} from "../../interfaces/IMiddleware.sol"; -import {QUERY, FINALIZE, SCHEDULE} from "../utils/common/Constants.sol"; -import {TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidInboxCaller, ResolvingTimeoutTooEarly, CallFailed, AppGatewayAlreadyCalled, InvalidWatcherSignature, NonceUsed} from "../utils/common/Errors.sol"; -import {ResolvedPromises, AppGatewayConfig, LimitParams, WriteFinality, UpdateLimitParams, PlugConfig, DigestParams, TimeoutRequest, CallFromChainParams, QueuePayloadParams, PayloadParams, RequestParams} from "../utils/common/Structs.sol"; +import {IMiddleware} from "../../../interfaces/IMiddleware.sol"; +import {QUERY, FINALIZE, SCHEDULE} from "../../utils/common/Constants.sol"; +import {TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidInboxCaller, ResolvingTimeoutTooEarly, CallFailed, AppGatewayAlreadyCalled, InvalidWatcherSignature, NonceUsed} from "../../utils/common/Errors.sol"; +import {ResolvedPromises, AppGatewayConfig, LimitParams, WriteFinality, UpdateLimitParams, PlugConfig, DigestParams, TimeoutRequest, CallFromChainParams, QueuePayloadParams, PayloadParams, RequestParams} from "../../utils/common/Structs.sol"; abstract contract WatcherPrecompileStorage is IWatcherPrecompile { // slots [0-49]: gap for future storage variables @@ -20,63 +20,65 @@ abstract contract WatcherPrecompileStorage is IWatcherPrecompile { /// @notice The chain slug of the watcher precompile uint32 public evmxSlug; - // slot 51: isNonceUsed - /// @notice Maps nonce to whether it has been used - /// @dev signatureNonce => isValid - mapping(uint256 => bool) public isNonceUsed; - - // slot 52: maxTimeoutDelayInSeconds - uint256 public maxTimeoutDelayInSeconds; - - // slot 53: payloadCounter + // slot 51: payloadCounter /// @notice Counter for tracking payload requests uint40 public payloadCounter; - - // slot 54: timeoutCounter /// @notice Counter for tracking timeout requests uint40 public timeoutCounter; + /// @notice Counter for tracking request counts + uint40 public nextRequestCount; + /// @notice Counter for tracking batch counts + uint40 public nextBatchCount; - // slot 55: expiryTime - /// @notice The expiry time for the payload + // slot 52: expiryTime + /// @notice The time from finalize for the payload to be executed uint256 public expiryTime; - // slot 56: timeoutRequests + // slot 53: maxTimeoutDelayInSeconds + /// @notice The maximum delay for a timeout + uint256 public maxTimeoutDelayInSeconds; + + // slot 54: isNonceUsed + /// @notice Maps nonce to whether it has been used + /// @dev signatureNonce => isValid + mapping(uint256 => bool) public isNonceUsed; + + // slot 55: timeoutRequests /// @notice Mapping to store timeout requests /// @dev timeoutId => TimeoutRequest struct mapping(bytes32 => TimeoutRequest) public timeoutRequests; - // slot 57: watcherProofs + // slot 56: watcherProofs /// @notice Mapping to store watcher proofs /// @dev payloadId => proof bytes mapping(bytes32 => bytes) public watcherProofs; - // slot 58: appGatewayCalled + // slot 57: appGatewayCalled /// @notice Mapping to store if appGateway has been called with trigger from on-chain Inbox /// @dev callId => bool mapping(bytes32 => bool) public appGatewayCalled; - // slot 59: nextRequestCount - uint40 public nextRequestCount; - - // slot 60: nextBatchCount - uint40 public nextBatchCount; - - // slot 61: requestParams + // slot 58: requestParams mapping(uint40 => RequestParams) public requestParams; - // slot 62: batchPayloadIds + + // slot 59: batchPayloadIds mapping(uint40 => bytes32[]) public batchPayloadIds; - // slot 63: requestBatchIds + + // slot 60: requestBatchIds mapping(uint40 => uint40[]) public requestBatchIds; - // slot 64: payloads + + // slot 61: payloads mapping(bytes32 => PayloadParams) public payloads; - // slot 65: isPromiseExecuted + + // slot 62: isPromiseExecuted mapping(bytes32 => bool) public isPromiseExecuted; - // slot 66: watcherPrecompileLimits__ + // slot 63: watcherPrecompileLimits__ IWatcherPrecompileLimits public watcherPrecompileLimits__; - // slot 67: watcherPrecompileConfig__ + + // slot 64: watcherPrecompileConfig__ IWatcherPrecompileConfig public watcherPrecompileConfig__; - // slots [68-117]: gap for future storage variables + // slots [65-114]: gap for future storage variables uint256[50] _gap_after; } From 279f8276aaf2d4a56b37b94c690d8218c1db00b0 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 3 Apr 2025 11:09:17 +0530 Subject: [PATCH 002/108] fix: proxy comments and tests --- .../interfaces/IWatcherPrecompileLimits.sol | 2 +- contracts/protocol/AddressResolver.sol | 16 +- contracts/protocol/AsyncPromise.sol | 2 +- contracts/protocol/Forwarder.sol | 3 +- .../payload-delivery/AuctionManager.sol | 23 +- .../protocol/payload-delivery/FeesManager.sol | 2 +- .../app-gateway/DeliveryHelper.sol | 1 - .../app-gateway/DeliveryHelperStorage.sol | 8 +- .../app-gateway/DeliveryUtils.sol | 3 +- .../app-gateway/FeesHelpers.sol | 2 +- .../app-gateway/RequestQueue.sol | 3 +- .../WatcherPrecompileConfig.sol | 28 +- .../WatcherPrecompileLimits.sol | 38 +- .../watcherPrecompile/core/RequestHandler.sol | 3 + .../core/WatcherPrecompileCore.sol | 19 +- .../core/WatcherPrecompileStorage.sol | 35 +- script/admin/UpdateAppEVMxLimits.s.sol | 2 +- script/helpers/CheckAppEVMxLimits.s.sol | 2 +- test/Migration.t.sol | 590 ++++++++++-------- test/SetupTest.t.sol | 2 +- test/mock/MockWatcherPrecompileImpl.sol | 2 +- 21 files changed, 428 insertions(+), 358 deletions(-) diff --git a/contracts/interfaces/IWatcherPrecompileLimits.sol b/contracts/interfaces/IWatcherPrecompileLimits.sol index 70a0c01a..887c379f 100644 --- a/contracts/interfaces/IWatcherPrecompileLimits.sol +++ b/contracts/interfaces/IWatcherPrecompileLimits.sol @@ -38,7 +38,7 @@ interface IWatcherPrecompileLimits { function setDefaultRatePerSecond(uint256 defaultRatePerSecond_) external; /// @notice Number of decimals used in limit calculations - function LIMIT_DECIMALS() external view returns (uint256); + function limitDecimals() external view returns (uint256); /// @notice Default limit value for any app gateway function defaultLimit() external view returns (uint256); diff --git a/contracts/protocol/AddressResolver.sol b/contracts/protocol/AddressResolver.sol index df9bf3b6..f2347f5d 100644 --- a/contracts/protocol/AddressResolver.sol +++ b/contracts/protocol/AddressResolver.sol @@ -17,16 +17,16 @@ abstract contract AddressResolverStorage is IAddressResolver { IWatcherPrecompile public override watcherPrecompile__; // slot 51 - address public override deliveryHelper; + UpgradeableBeacon public forwarderBeacon; // slot 52 - address public override feesManager; + UpgradeableBeacon public asyncPromiseBeacon; // slot 53 - UpgradeableBeacon public forwarderBeacon; + address public override deliveryHelper; // slot 54 - UpgradeableBeacon public asyncPromiseBeacon; + address public override feesManager; // slot 55 address public forwarderImplementation; @@ -42,15 +42,13 @@ abstract contract AddressResolverStorage is IAddressResolver { // slot 59 uint64 public version; + address public override defaultAuctionManager; // slot 60 mapping(address => address) public override contractsToGateways; - // slot 61 - address public override defaultAuctionManager; - - // slots [62-110] reserved for gap - uint256[49] _gap_after; + // slots [61-110] reserved for gap + uint256[50] _gap_after; } /// @title AddressResolver Contract diff --git a/contracts/protocol/AsyncPromise.sol b/contracts/protocol/AsyncPromise.sol index ff81e4b7..3bcd95ec 100644 --- a/contracts/protocol/AsyncPromise.sol +++ b/contracts/protocol/AsyncPromise.sol @@ -37,7 +37,7 @@ abstract contract AsyncPromiseStorage is IPromise { // slots [53-102] reserved for gap uint256[50] _gap_after; - // slots 103-154 reserved for addr resolver util + // slots 103-154 (51) reserved for addr resolver util } /// @title AsyncPromise diff --git a/contracts/protocol/Forwarder.sol b/contracts/protocol/Forwarder.sol index d9231e1a..8426bc4d 100644 --- a/contracts/protocol/Forwarder.sol +++ b/contracts/protocol/Forwarder.sol @@ -17,7 +17,6 @@ abstract contract ForwarderStorage is IForwarder { // slot 50 /// @notice chain id uint32 public chainSlug; - /// @notice on-chain address associated with this forwarder address public onChainAddress; @@ -27,6 +26,8 @@ abstract contract ForwarderStorage is IForwarder { // slots [52-101] reserved for gap uint256[50] _gap_after; + + // slots 102-153 (51) reserved for addr resolver util } /// @title Forwarder Contract diff --git a/contracts/protocol/payload-delivery/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol index 860d527e..e9c97c68 100644 --- a/contracts/protocol/payload-delivery/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -22,31 +22,32 @@ abstract contract AuctionManagerStorage is IAuctionManager { uint32 public evmxSlug; // slot 51 - mapping(uint40 => Bid) public winningBids; + uint256 public maxReAuctionCount; // slot 52 - // requestCount => auction status - mapping(uint40 => bool) public override auctionClosed; + uint256 public auctionEndDelaySeconds; // slot 53 - mapping(uint40 => bool) public override auctionStarted; + mapping(uint40 => Bid) public winningBids; // slot 54 - uint256 public auctionEndDelaySeconds; + // requestCount => auction status + mapping(uint40 => bool) public override auctionClosed; // slot 55 - mapping(address => bool) public whitelistedTransmitters; + mapping(uint40 => bool) public override auctionStarted; // slot 56 - mapping(uint40 => uint256) public reAuctionCount; + mapping(address => bool) public whitelistedTransmitters; // slot 57 - uint256 public maxReAuctionCount; + mapping(uint40 => uint256) public reAuctionCount; - // slots [57-104] reserved for gap - uint256[48] _gap_after; + // slots [58-107] reserved for gap + uint256[50] _gap_after; - // slots 105-155 reserved for addr resolver util + // slots 108-158 (51) reserved for access control + // slots 159-209 (51) reserved for addr resolver util } /// @title AuctionManager diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 7b9804b5..7af43ca5 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -49,7 +49,7 @@ abstract contract FeesManagerStorage is IFeesManager { // slots [57-106] reserved for gap uint256[50] _gap_after; - // slots 107-157 reserved for addr resolver util + // slots 107-157 (51) reserved for addr resolver util } /// @title FeesManager diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol index 81d9e7b3..70588fd0 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol @@ -34,7 +34,6 @@ contract DeliveryHelper is FeesHelpers { RequestMetadata storage requestMetadata_ = requests[requestCount_]; bool isRestarted = requestMetadata_.winningBid.transmitter != address(0); - requestMetadata_.winningBid.transmitter = winningBid_.transmitter; if (!isRestarted) { diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol index d6daf1f4..b6fedb25 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol @@ -21,14 +21,20 @@ abstract contract DeliveryHelperStorage is IMiddleware { // slots [0-49] reserved for gap uint256[50] _gap_before; + // slot 50 uint128 public bidTimeout; + // slot 51 uint256 public saltCounter; + // slot 52 /// @notice The call parameters array QueuePayloadParams[] public queuePayloadParams; + // slot 53 mapping(uint40 => RequestMetadata) public requests; - // slots [59-108] reserved for gap + // slots [54-103] reserved for gap uint256[50] _gap_after; + + // slots 104-154 (51) reserved for addr resolver util } diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol index 6532aae7..3620b17b 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol @@ -14,8 +14,7 @@ abstract contract DeliveryUtils is Ownable, AddressResolverUtil { - // slots [0-108] reserved for delivery helper storage and [109-159] reserved for addr resolver util - // slots [160-209] reserved for gap + // slots [155-205] reserved for gap uint256[50] _gap_delivery_utils; /// @notice Error thrown when attempting to executed payloads after all have been executed diff --git a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol index 4655c8b8..2bf5cd46 100644 --- a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol +++ b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol @@ -6,7 +6,7 @@ import "./RequestQueue.sol"; /// @title RequestAsync /// @notice Abstract contract for managing asynchronous payload batches abstract contract FeesHelpers is RequestQueue { - // slots [210-259] reserved for gap + // slots [256-306] reserved for gap uint256[50] _gap_batch_async; function increaseFees(uint40 requestCount_, uint256 newMaxFees_) external override { diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index de8e7f36..5ec1f081 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -8,8 +8,7 @@ import "./DeliveryUtils.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 - // slots [160-209] reserved for gap + // slots [205-255] reserved for gap uint256[50] _gap_queue_async; /// @notice Clears the call parameters array diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index 881b3706..6a6a5a58 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -1,10 +1,11 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.21; -import "./WatcherPrecompileLimits.sol"; -import {ECDSA} from "solady/utils/ECDSA.sol"; import "solady/utils/Initializable.sol"; +import {ECDSA} from "solady/utils/ECDSA.sol"; +import {Ownable} from "solady/auth/Ownable.sol"; import "../../interfaces/IWatcherPrecompileConfig.sol"; +import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; /// @title WatcherPrecompileConfig /// @notice Configuration contract for the Watcher Precompile system @@ -12,44 +13,49 @@ import "../../interfaces/IWatcherPrecompileConfig.sol"; contract WatcherPrecompileConfig is IWatcherPrecompileConfig, Initializable, - AccessControl, + Ownable, AddressResolverUtil { - // slot 52: evmxSlug + // slots 0-50 (51) reserved for addr resolver util + + // slots [51-100]: gap for future storage variables + uint256[50] _gap_before; + + // slot 101: evmxSlug /// @notice The chain slug of the watcher precompile uint32 public evmxSlug; - // slot 55: _plugConfigs + // slot 102: _plugConfigs /// @notice Maps network and plug to their configuration /// @dev chainSlug => plug => PlugConfig mapping(uint32 => mapping(address => PlugConfig)) internal _plugConfigs; - // slot 56: switchboards + // slot 103: switchboards /// @notice Maps chain slug to their associated switchboard /// @dev chainSlug => sb type => switchboard address mapping(uint32 => mapping(bytes32 => address)) public switchboards; - // slot 57: sockets + // slot 104: sockets /// @notice Maps chain slug to their associated socket /// @dev chainSlug => socket address mapping(uint32 => address) public sockets; - // slot 58: contractFactoryPlug + // slot 105: contractFactoryPlug /// @notice Maps chain slug to their associated contract factory plug /// @dev chainSlug => contract factory plug address mapping(uint32 => address) public contractFactoryPlug; - // slot 59: feesPlug + // slot 106: feesPlug /// @notice Maps chain slug to their associated fees plug /// @dev chainSlug => fees plug address mapping(uint32 => address) public feesPlug; - // slot 60: isNonceUsed + // slot 107: isNonceUsed /// @notice Maps nonce to whether it has been used /// @dev signatureNonce => isValid mapping(uint256 => bool) public isNonceUsed; - // slot 61: isValidPlug + // slot 108: isValidPlug // appGateway => chainSlug => plug => isValid mapping(address => mapping(uint32 => mapping(address => bool))) public isValidPlug; diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index 42f1d2b4..4f78a84f 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -1,44 +1,43 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.21; -import {AccessControl} from "../utils/AccessControl.sol"; +import "solady/utils/Initializable.sol"; +import {Ownable} from "solady/auth/Ownable.sol"; import {Gauge} from "../utils/Gauge.sol"; import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; -import {WATCHER_ROLE} from "../utils/common/AccessRoles.sol"; import "../../interfaces/IWatcherPrecompileLimits.sol"; -import "solady/utils/Initializable.sol"; import {SCHEDULE, QUERY, FINALIZE} from "../utils/common/Constants.sol"; contract WatcherPrecompileLimits is IWatcherPrecompileLimits, Initializable, - AccessControl, + Ownable, Gauge, AddressResolverUtil { - // Slots from parent contracts: - // slot 0-118: watcher precompile storage - // 0 slots for initializable and ownable - // slots 119-169: access control (gap + 1) - // slots 170-219: gauge (gap) - // slots 220-270: address resolver util (gap + 1) - // slots 271-320: gap for future storage variables - uint256[50] _gap_watcher_precompile_limits; + // slots 0-49 (50) reserved for gauge + // slots 50-100 (51) reserved for addr resolver util + + // slots [101-150]: gap for future storage variables + uint256[50] _gap_before; + // slot 151: limitDecimals /// @notice Number of decimals used in limit calculations - uint256 public constant LIMIT_DECIMALS = 18; - // slot 50: defaultLimit + uint256 public limitDecimals; + + // slot 152: defaultLimit /// @notice Default limit value for any app gateway uint256 public defaultLimit; - // slot 51: defaultRatePerSecond + + // slot 153: defaultRatePerSecond /// @notice Rate at which limit replenishes per second uint256 public defaultRatePerSecond; - // slot 53: _limitParams + // slot 154: _limitParams // appGateway => limitType => receivingLimitParams mapping(address => mapping(bytes32 => LimitParams)) internal _limitParams; - // slot 54: _activeAppGateways + // slot 155: _activeAppGateways // Mapping to track active app gateways mapping(address => bool) internal _activeAppGateways; @@ -50,9 +49,10 @@ contract WatcherPrecompileLimits is ) public reinitializer(1) { _setAddressResolver(addressResolver_); _initializeOwner(owner_); + limitDecimals = 18; // limit per day - defaultLimit = defaultLimit_ * 10 ** LIMIT_DECIMALS; + defaultLimit = defaultLimit_ * 10 ** limitDecimals; // limit per second defaultRatePerSecond = defaultLimit / (24 * 60 * 60); } @@ -138,7 +138,7 @@ contract WatcherPrecompileLimits is } // Update the limit - _consumeFullLimit(consumeLimit_ * 10 ** LIMIT_DECIMALS, limitParams); + _consumeFullLimit(consumeLimit_ * 10 ** limitDecimals, limitParams); } /** diff --git a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol index da161179..f8296516 100644 --- a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol +++ b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol @@ -6,6 +6,9 @@ import "./WatcherPrecompileCore.sol"; abstract contract RequestHandler is WatcherPrecompileCore { using DumpDecoder for bytes32; + // slots [266-316] reserved for gap + uint256[50] _request_handler_gap; + function submitRequest( PayloadSubmitParams[] calldata payloadSubmitParams ) public returns (uint40 requestCount) { diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index bb45bb07..bb86cc1f 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -3,9 +3,9 @@ pragma solidity ^0.8.21; import "./WatcherPrecompileStorage.sol"; import {ECDSA} from "solady/utils/ECDSA.sol"; -import {AccessControl} from "../utils/AccessControl.sol"; +import {AccessControl} from "../../utils/AccessControl.sol"; import "solady/utils/Initializable.sol"; -import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; +import {AddressResolverUtil} from "../../utils/AddressResolverUtil.sol"; /// @title WatcherPrecompile /// @notice Contract that handles payload verification, execution and app configurations @@ -18,6 +18,9 @@ abstract contract WatcherPrecompileCore is { using DumpDecoder for bytes32; + // slots [215-265] reserved for gap + uint256[50] _core_gap; + // ================== Timeout functions ================== /// @notice Sets a timeout for a payload execution on app gateway @@ -61,10 +64,7 @@ abstract contract WatcherPrecompileCore is payloads[params_.payloadId].deadline = deadline; payloads[params_.payloadId].finalizedTransmitter = transmitter_; - bytes32 prevDigestsHash = _getPreviousDigestsHash( - params_.dump.getRequestCount(), - params_.dump.getBatchCount() - ); + bytes32 prevDigestsHash = _getPreviousDigestsHash(params_.dump.getBatchCount()); payloads[params_.payloadId].prevDigestsHash = prevDigestsHash; // Construct parameters for digest calculation @@ -102,10 +102,7 @@ abstract contract WatcherPrecompileCore is /// @notice Creates a new query request /// @param params_ The payload parameters function _query(PayloadParams memory params_) internal { - bytes32 prevDigestsHash = _getPreviousDigestsHash( - params_.dump.getRequestCount(), - params_.dump.getBatchCount() - ); + bytes32 prevDigestsHash = _getPreviousDigestsHash(params_.dump.getBatchCount()); payloads[params_.payloadId].prevDigestsHash = prevDigestsHash; emit QueryRequested(params_); } @@ -133,7 +130,7 @@ abstract contract WatcherPrecompileCore is } function _getPreviousDigestsHash(uint40 batchCount_) internal view returns (bytes32) { - bytes32[] memory payloadIds = batchPayloadIds[batchCount]; + bytes32[] memory payloadIds = batchPayloadIds[batchCount_]; bytes32 prevDigestsHash = bytes32(0); for (uint40 i = 0; i < payloadIds.length; i++) { diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol index 600e198c..37b13e50 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol @@ -16,11 +16,9 @@ abstract contract WatcherPrecompileStorage is IWatcherPrecompile { // slots [0-49]: gap for future storage variables uint256[50] _gap_before; - // slot 50: evmxSlug + // slot 50 /// @notice The chain slug of the watcher precompile uint32 public evmxSlug; - - // slot 51: payloadCounter /// @notice Counter for tracking payload requests uint40 public payloadCounter; /// @notice Counter for tracking timeout requests @@ -30,55 +28,58 @@ abstract contract WatcherPrecompileStorage is IWatcherPrecompile { /// @notice Counter for tracking batch counts uint40 public nextBatchCount; - // slot 52: expiryTime + // slot 51 /// @notice The time from finalize for the payload to be executed uint256 public expiryTime; - // slot 53: maxTimeoutDelayInSeconds + // slot 52 /// @notice The maximum delay for a timeout uint256 public maxTimeoutDelayInSeconds; - // slot 54: isNonceUsed + // slot 53 /// @notice Maps nonce to whether it has been used /// @dev signatureNonce => isValid mapping(uint256 => bool) public isNonceUsed; - // slot 55: timeoutRequests + // slot 54 /// @notice Mapping to store timeout requests /// @dev timeoutId => TimeoutRequest struct mapping(bytes32 => TimeoutRequest) public timeoutRequests; - // slot 56: watcherProofs + // slot 55 /// @notice Mapping to store watcher proofs /// @dev payloadId => proof bytes mapping(bytes32 => bytes) public watcherProofs; - // slot 57: appGatewayCalled + // slot 56 /// @notice Mapping to store if appGateway has been called with trigger from on-chain Inbox /// @dev callId => bool mapping(bytes32 => bool) public appGatewayCalled; - // slot 58: requestParams + // slot 57 mapping(uint40 => RequestParams) public requestParams; - // slot 59: batchPayloadIds + // slot 58 mapping(uint40 => bytes32[]) public batchPayloadIds; - // slot 60: requestBatchIds + // slot 59 mapping(uint40 => uint40[]) public requestBatchIds; - // slot 61: payloads + // slot 60 mapping(bytes32 => PayloadParams) public payloads; - // slot 62: isPromiseExecuted + // slot 61 mapping(bytes32 => bool) public isPromiseExecuted; - // slot 63: watcherPrecompileLimits__ + // slot 62 IWatcherPrecompileLimits public watcherPrecompileLimits__; - // slot 64: watcherPrecompileConfig__ + // slot 63 IWatcherPrecompileConfig public watcherPrecompileConfig__; - // slots [65-114]: gap for future storage variables + // slots [64-113]: gap for future storage variables uint256[50] _gap_after; + + // slots 114-164 (51) reserved for access control + // slots 165-215 (51) reserved for addr resolver util } diff --git a/script/admin/UpdateAppEVMxLimits.s.sol b/script/admin/UpdateAppEVMxLimits.s.sol index 4389c1be..7c650dd7 100644 --- a/script/admin/UpdateAppEVMxLimits.s.sol +++ b/script/admin/UpdateAppEVMxLimits.s.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.13; import "forge-std/Script.sol"; -import {WatcherPrecompile} from "../../contracts/protocol/watcherPrecompile/WatcherPrecompile.sol"; +import {WatcherPrecompile} from "../../contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol"; import {UpdateLimitParams} from "../../contracts/protocol/utils/common/Structs.sol"; import {SCHEDULE, QUERY, FINALIZE} from "../../contracts/protocol/utils/common/Constants.sol"; diff --git a/script/helpers/CheckAppEVMxLimits.s.sol b/script/helpers/CheckAppEVMxLimits.s.sol index 7c1f16a0..601dca82 100644 --- a/script/helpers/CheckAppEVMxLimits.s.sol +++ b/script/helpers/CheckAppEVMxLimits.s.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.13; import "forge-std/Script.sol"; -import {WatcherPrecompile} from "../../contracts/protocol/watcherPrecompile/WatcherPrecompile.sol"; +import {WatcherPrecompile} from "../../contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol"; import {LimitParams} from "../../contracts/protocol/utils/common/Structs.sol"; import {SCHEDULE, QUERY, FINALIZE} from "../../contracts/protocol/utils/common/Constants.sol"; diff --git a/test/Migration.t.sol b/test/Migration.t.sol index 0632d583..aed1bf3e 100644 --- a/test/Migration.t.sol +++ b/test/Migration.t.sol @@ -3,272 +3,332 @@ pragma solidity ^0.8.0; import "./SetupTest.t.sol"; import "../contracts/protocol/AddressResolver.sol"; -import "../contracts/protocol/watcherPrecompile/WatcherPrecompile.sol"; +import "../contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol"; +import "../contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol"; +import "../contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol"; import "../contracts/protocol/Forwarder.sol"; import "../contracts/protocol/AsyncPromise.sol"; import "./mock/MockWatcherPrecompileImpl.sol"; -// contract MigrationTest is SetupTest { -// // ERC1967Factory emits this event with both proxy and implementation addresses -// event Upgraded(address indexed proxy, address indexed implementation); -// event ImplementationUpdated(string contractName, address newImplementation); - -// // ERC1967 implementation slot -// bytes32 internal constant _IMPLEMENTATION_SLOT = -// 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - -// // Beacon implementation slot -// uint256 internal constant _BEACON_IMPLEMENTATION_SLOT = 0x911c5a209f08d5ec5e; - -// // Beacon slot in ERC1967 -// bytes32 internal constant _BEACON_SLOT = -// 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; - -// // Error selector for Unauthorized error -// bytes4 internal constant UNAUTHORIZED_SELECTOR = 0x82b42900; // bytes4(keccak256("Unauthorized()")) - -// function setUp() public { -// deployEVMxCore(); -// } - -// function getImplementation(address proxy) internal view returns (address) { -// bytes32 value = vm.load(proxy, _IMPLEMENTATION_SLOT); -// return address(uint160(uint256(value))); -// } - -// function getBeaconImplementation(address beacon) internal view returns (address) { -// bytes32 value = vm.load(beacon, bytes32(_BEACON_IMPLEMENTATION_SLOT)); -// return address(uint160(uint256(value))); -// } - -// function getBeacon(address proxy) internal view returns (address) { -// bytes32 value = vm.load(proxy, _BEACON_SLOT); -// return address(uint160(uint256(value))); -// } - -// function testAddressResolverUpgrade() public { -// // Deploy new implementation -// AddressResolver newImpl = new AddressResolver(); - -// // Store old implementation address -// address oldImpl = getImplementation(address(addressResolver)); - -// // Upgrade proxy to new implementation -// vm.startPrank(watcherEOA); -// vm.expectEmit(true, true, true, true, address(proxyFactory)); -// emit Upgraded(address(addressResolver), address(newImpl)); -// proxyFactory.upgradeAndCall(address(addressResolver), address(newImpl), ""); -// vm.stopPrank(); - -// // Verify upgrade was successful -// address newImplAddr = getImplementation(address(addressResolver)); -// assertNotEq(oldImpl, newImplAddr, "Implementation should have changed"); -// assertEq(newImplAddr, address(newImpl), "New implementation not set correctly"); - -// // Verify state is preserved -// assertEq(addressResolver.owner(), watcherEOA, "Owner should be preserved after upgrade"); -// assertEq( -// address(addressResolver.watcherPrecompile__()), -// address(watcherPrecompile), -// "WatcherPrecompile address should be preserved" -// ); -// } - -// function testWatcherPrecompileUpgrade() public { -// // Deploy new implementation -// WatcherPrecompile newImpl = new WatcherPrecompile(); - -// // Store old implementation address -// address oldImpl = getImplementation(address(watcherPrecompile)); - -// // Upgrade proxy to new implementation -// vm.startPrank(watcherEOA); -// vm.expectEmit(true, true, true, true, address(proxyFactory)); -// emit Upgraded(address(watcherPrecompile), address(newImpl)); -// proxyFactory.upgradeAndCall(address(watcherPrecompile), address(newImpl), ""); -// vm.stopPrank(); - -// // Verify upgrade was successful -// address newImplAddr = getImplementation(address(watcherPrecompile)); -// assertNotEq(oldImpl, newImplAddr, "Implementation should have changed"); -// assertEq(newImplAddr, address(newImpl), "New implementation not set correctly"); - -// // Verify state is preserved -// assertEq(watcherPrecompile.owner(), watcherEOA, "Owner should be preserved after upgrade"); -// assertEq( -// address(watcherPrecompile.addressResolver__()), -// address(addressResolver), -// "AddressResolver should be preserved" -// ); -// assertEq( -// watcherPrecompile.defaultLimit(), -// defaultLimit * 10 ** 18, -// "DefaultLimit should be preserved" -// ); -// } - -// function testUpgradeWithInitializationData() public { -// // Deploy new implementation -// MockWatcherPrecompileImpl newImpl = new MockWatcherPrecompileImpl(); - -// // Store old implementation address for verification -// address oldImpl = getImplementation(address(watcherPrecompile)); - -// // Prepare initialization data with new defaultLimit -// uint256 newDefaultLimit = 2000; -// bytes memory initData = abi.encodeWithSelector( -// MockWatcherPrecompileImpl.mockReinitialize.selector, -// watcherEOA, -// address(addressResolver), -// newDefaultLimit -// ); - -// // Upgrade proxy with initialization data -// vm.startPrank(watcherEOA); -// vm.expectEmit(true, true, true, true, address(proxyFactory)); -// emit Upgraded(address(watcherPrecompile), address(newImpl)); -// proxyFactory.upgradeAndCall(address(watcherPrecompile), address(newImpl), initData); -// vm.stopPrank(); - -// // Verify upgrade and initialization was successful -// address newImplAddr = getImplementation(address(watcherPrecompile)); -// assertNotEq(oldImpl, newImplAddr, "Implementation should have changed"); -// assertEq(newImplAddr, address(newImpl), "New implementation not set correctly"); -// assertEq( -// watcherPrecompile.defaultLimit(), -// newDefaultLimit * 10 ** 18, -// "DefaultLimit should be updated" -// ); -// } - -// function testUnauthorizedUpgrade() public { -// // Deploy new implementation -// WatcherPrecompile newImpl = new WatcherPrecompile(); - -// // Try to upgrade from unauthorized account -// address unauthorizedUser = address(0xBEEF); -// vm.startPrank(unauthorizedUser); -// vm.expectRevert(UNAUTHORIZED_SELECTOR); -// proxyFactory.upgradeAndCall(address(watcherPrecompile), address(newImpl), ""); -// vm.stopPrank(); - -// // Verify implementation was not changed -// assertEq( -// getImplementation(address(watcherPrecompile)), -// address(watcherPrecompileImpl), -// "Implementation should not have changed" -// ); -// } - -// function testForwarderBeaconUpgrade() public { -// // Deploy new implementation -// Forwarder newImpl = new Forwarder(); - -// // Get current implementation from beacon -// address oldImpl = getBeaconImplementation(address(addressResolver.forwarderBeacon())); - -// // Upgrade beacon to new implementation -// vm.startPrank(watcherEOA); -// vm.expectEmit(true, true, true, true, address(addressResolver)); -// emit ImplementationUpdated("Forwarder", address(newImpl)); -// addressResolver.setForwarderImplementation(address(newImpl)); -// vm.stopPrank(); - -// // Verify upgrade was successful -// address newImplAddr = getBeaconImplementation(address(addressResolver.forwarderBeacon())); -// assertNotEq(oldImpl, newImplAddr, "Implementation should have changed"); -// assertEq(newImplAddr, address(newImpl), "New implementation not set correctly"); - -// // Deploy a new forwarder and verify it uses the correct beacon -// address newForwarder = addressResolver.getOrDeployForwarderContract( -// address(this), -// address(0x123), -// 1 -// ); -// address beacon = getBeacon(newForwarder); -// assertEq( -// beacon, -// address(addressResolver.forwarderBeacon()), -// "Beacon address not set correctly" -// ); - -// // Get implementation from beacon and verify it matches -// address implFromBeacon = getBeaconImplementation(beacon); -// assertEq( -// implFromBeacon, -// address(newImpl), -// "Beacon implementation should match new implementation" -// ); -// } - -// function testAsyncPromiseBeaconUpgrade() public { -// // Deploy new implementation -// AsyncPromise newImpl = new AsyncPromise(); - -// // Get current implementation from beacon -// address oldImpl = getBeaconImplementation(address(addressResolver.asyncPromiseBeacon())); - -// // Upgrade beacon to new implementation -// vm.startPrank(watcherEOA); -// vm.expectEmit(true, true, true, true, address(addressResolver)); -// emit ImplementationUpdated("AsyncPromise", address(newImpl)); -// addressResolver.setAsyncPromiseImplementation(address(newImpl)); -// vm.stopPrank(); - -// // Verify upgrade was successful -// address newImplAddr = getBeaconImplementation( -// address(addressResolver.asyncPromiseBeacon()) -// ); -// assertNotEq(oldImpl, newImplAddr, "Implementation should have changed"); -// assertEq(newImplAddr, address(newImpl), "New implementation not set correctly"); - -// // Deploy a new async promise and verify it uses the correct beacon -// address newPromise = addressResolver.deployAsyncPromiseContract(address(this)); -// address beacon = getBeacon(newPromise); -// assertEq( -// beacon, -// address(addressResolver.asyncPromiseBeacon()), -// "Beacon address not set correctly" -// ); - -// // Get implementation from beacon and verify it matches -// address implFromBeacon = getBeaconImplementation(beacon); -// assertEq( -// implFromBeacon, -// address(newImpl), -// "Beacon implementation should match new implementation" -// ); -// } - -// function testUnauthorizedBeaconUpgrade() public { -// // Deploy new implementations -// Forwarder newForwarderImpl = new Forwarder(); -// AsyncPromise newAsyncPromiseImpl = new AsyncPromise(); - -// // Try to upgrade from unauthorized account -// address unauthorizedUser = address(0xBEEF); - -// vm.startPrank(unauthorizedUser); -// // Try upgrading forwarder beacon -// vm.expectRevert(abi.encodeWithSelector(Ownable.Unauthorized.selector)); -// addressResolver.setForwarderImplementation(address(newForwarderImpl)); - -// // Try upgrading async promise beacon -// vm.expectRevert(abi.encodeWithSelector(Ownable.Unauthorized.selector)); -// addressResolver.setAsyncPromiseImplementation(address(newAsyncPromiseImpl)); - -// vm.stopPrank(); - -// // Verify implementations were not changed -// assertNotEq( -// getBeaconImplementation(address(addressResolver.forwarderBeacon())), -// address(newForwarderImpl), -// "Forwarder implementation should not have changed" -// ); -// assertNotEq( -// getBeaconImplementation(address(addressResolver.asyncPromiseBeacon())), -// address(newAsyncPromiseImpl), -// "AsyncPromise implementation should not have changed" -// ); -// } -// } +contract MigrationTest is SetupTest { + // ERC1967Factory emits this event with both proxy and implementation addresses + event Upgraded(address indexed proxy, address indexed implementation); + event ImplementationUpdated(string contractName, address newImplementation); + + // ERC1967 implementation slot + bytes32 internal constant _IMPLEMENTATION_SLOT = + 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + // Beacon implementation slot + uint256 internal constant _BEACON_IMPLEMENTATION_SLOT = 0x911c5a209f08d5ec5e; + + // Beacon slot in ERC1967 + bytes32 internal constant _BEACON_SLOT = + 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; + + // Error selector for Unauthorized error + bytes4 internal constant UNAUTHORIZED_SELECTOR = 0x82b42900; // bytes4(keccak256("Unauthorized()")) + + function setUp() public { + deployEVMxCore(); + } + + function getImplementation(address proxy) internal view returns (address) { + bytes32 value = vm.load(proxy, _IMPLEMENTATION_SLOT); + return address(uint160(uint256(value))); + } + + function getBeaconImplementation(address beacon) internal view returns (address) { + bytes32 value = vm.load(beacon, bytes32(_BEACON_IMPLEMENTATION_SLOT)); + return address(uint160(uint256(value))); + } + + function getBeacon(address proxy) internal view returns (address) { + bytes32 value = vm.load(proxy, _BEACON_SLOT); + return address(uint160(uint256(value))); + } + + function testAddressResolverUpgrade() public { + // Deploy new implementation + AddressResolver newImpl = new AddressResolver(); + + // Store old implementation address + address oldImpl = getImplementation(address(addressResolver)); + + // Upgrade proxy to new implementation + vm.startPrank(watcherEOA); + vm.expectEmit(true, true, true, true, address(proxyFactory)); + emit Upgraded(address(addressResolver), address(newImpl)); + proxyFactory.upgradeAndCall(address(addressResolver), address(newImpl), ""); + vm.stopPrank(); + + // Verify upgrade was successful + address newImplAddr = getImplementation(address(addressResolver)); + assertNotEq(oldImpl, newImplAddr, "Implementation should have changed"); + assertEq(newImplAddr, address(newImpl), "New implementation not set correctly"); + + // Verify state is preserved + assertEq(addressResolver.owner(), watcherEOA, "Owner should be preserved after upgrade"); + assertEq( + address(addressResolver.watcherPrecompile__()), + address(watcherPrecompile), + "WatcherPrecompile address should be preserved" + ); + } + + function testWatcherPrecompileUpgrade() public { + // Deploy new implementation + WatcherPrecompile newImpl = new WatcherPrecompile(); + + // Store old implementation address + address oldImpl = getImplementation(address(watcherPrecompile)); + + // Upgrade proxy to new implementation + vm.startPrank(watcherEOA); + vm.expectEmit(true, true, true, true, address(proxyFactory)); + emit Upgraded(address(watcherPrecompile), address(newImpl)); + proxyFactory.upgradeAndCall(address(watcherPrecompile), address(newImpl), ""); + vm.stopPrank(); + + // Verify upgrade was successful + address newImplAddr = getImplementation(address(watcherPrecompile)); + assertNotEq(oldImpl, newImplAddr, "Implementation should have changed"); + assertEq(newImplAddr, address(newImpl), "New implementation not set correctly"); + + // Verify state is preserved + assertEq(watcherPrecompile.owner(), watcherEOA, "Owner should be preserved after upgrade"); + assertEq( + address(watcherPrecompile.watcherPrecompileConfig__()), + address(watcherPrecompileConfig), + "WatcherPrecompileConfig should be preserved" + ); + assertEq(watcherPrecompile.evmxSlug(), evmxSlug, "EvmxSlug should be preserved"); + } + + function testWatcherPrecompileLimitsUpgrade() public { + // Deploy new implementation + WatcherPrecompileLimits newImpl = new WatcherPrecompileLimits(); + + // Store old implementation address + address oldImpl = getImplementation(address(watcherPrecompileLimits)); + + // Upgrade proxy to new implementation + vm.startPrank(watcherEOA); + vm.expectEmit(true, true, true, true, address(proxyFactory)); + emit Upgraded(address(watcherPrecompileLimits), address(newImpl)); + proxyFactory.upgradeAndCall(address(watcherPrecompileLimits), address(newImpl), ""); + vm.stopPrank(); + + // Verify upgrade was successful + address newImplAddr = getImplementation(address(watcherPrecompileLimits)); + assertNotEq(oldImpl, newImplAddr, "Implementation should have changed"); + assertEq(newImplAddr, address(newImpl), "New implementation not set correctly"); + + // Verify state is preserved + assertEq(watcherPrecompile.owner(), watcherEOA, "Owner should be preserved after upgrade"); + assertEq( + address(watcherPrecompileLimits.addressResolver__()), + address(addressResolver), + "AddressResolver should be preserved" + ); + assertEq( + watcherPrecompileLimits.defaultLimit(), + defaultLimit * 10 ** 18, + "DefaultLimit should be preserved" + ); + } + + function testWatcherPrecompileConfigUpgrade() public { + // Deploy new implementation + WatcherPrecompileConfig newImpl = new WatcherPrecompileConfig(); + + // Store old implementation address + address oldImpl = getImplementation(address(watcherPrecompileConfig)); + + // Upgrade proxy to new implementation + vm.startPrank(watcherEOA); + vm.expectEmit(true, true, true, true, address(proxyFactory)); + emit Upgraded(address(watcherPrecompileConfig), address(newImpl)); + proxyFactory.upgradeAndCall(address(watcherPrecompileConfig), address(newImpl), ""); + vm.stopPrank(); + + // Verify upgrade was successful + address newImplAddr = getImplementation(address(watcherPrecompileConfig)); + assertNotEq(oldImpl, newImplAddr, "Implementation should have changed"); + assertEq(newImplAddr, address(newImpl), "New implementation not set correctly"); + + // Verify state is preserved + assertEq( + watcherPrecompileConfig.owner(), + watcherEOA, + "Owner should be preserved after upgrade" + ); + assertEq( + address(watcherPrecompileConfig.addressResolver__()), + address(addressResolver), + "AddressResolver should be preserved" + ); + assertEq(watcherPrecompileConfig.evmxSlug(), 1, "EvmxSlug should be preserved"); + } + + function testUpgradeWithInitializationData() public { + // Deploy new implementation + MockWatcherPrecompileImpl newImpl = new MockWatcherPrecompileImpl(); + + // Store old implementation address for verification + address oldImpl = getImplementation(address(watcherPrecompile)); + + // Prepare initialization data with new defaultLimit + uint256 newDefaultLimit = 2000; + bytes memory initData = abi.encodeWithSelector( + MockWatcherPrecompileImpl.mockReinitialize.selector, + watcherEOA, + address(addressResolver), + newDefaultLimit + ); + + // Upgrade proxy with initialization data + vm.startPrank(watcherEOA); + vm.expectEmit(true, true, true, true, address(proxyFactory)); + emit Upgraded(address(watcherPrecompile), address(newImpl)); + proxyFactory.upgradeAndCall(address(watcherPrecompile), address(newImpl), initData); + vm.stopPrank(); + + // Verify upgrade and initialization was successful + address newImplAddr = getImplementation(address(watcherPrecompile)); + assertNotEq(oldImpl, newImplAddr, "Implementation should have changed"); + assertEq(newImplAddr, address(newImpl), "New implementation not set correctly"); + assertEq(watcherPrecompile.evmxSlug(), evmxSlug, "EvmxSlug should be preserved"); + } + + function testUnauthorizedUpgrade() public { + // Deploy new implementation + WatcherPrecompile newImpl = new WatcherPrecompile(); + + // Try to upgrade from unauthorized account + address unauthorizedUser = address(0xBEEF); + vm.startPrank(unauthorizedUser); + vm.expectRevert(UNAUTHORIZED_SELECTOR); + proxyFactory.upgradeAndCall(address(watcherPrecompile), address(newImpl), ""); + vm.stopPrank(); + + // Verify implementation was not changed + assertEq( + getImplementation(address(watcherPrecompile)), + address(watcherPrecompileImpl), + "Implementation should not have changed" + ); + } + + function testForwarderBeaconUpgrade() public { + // Deploy new implementation + Forwarder newImpl = new Forwarder(); + + // Get current implementation from beacon + address oldImpl = getBeaconImplementation(address(addressResolver.forwarderBeacon())); + + // Upgrade beacon to new implementation + vm.startPrank(watcherEOA); + vm.expectEmit(true, true, true, true, address(addressResolver)); + emit ImplementationUpdated("Forwarder", address(newImpl)); + addressResolver.setForwarderImplementation(address(newImpl)); + vm.stopPrank(); + + // Verify upgrade was successful + address newImplAddr = getBeaconImplementation(address(addressResolver.forwarderBeacon())); + assertNotEq(oldImpl, newImplAddr, "Implementation should have changed"); + assertEq(newImplAddr, address(newImpl), "New implementation not set correctly"); + + // Deploy a new forwarder and verify it uses the correct beacon + address newForwarder = addressResolver.getOrDeployForwarderContract( + address(this), + address(0x123), + 1 + ); + address beacon = getBeacon(newForwarder); + assertEq( + beacon, + address(addressResolver.forwarderBeacon()), + "Beacon address not set correctly" + ); + + // Get implementation from beacon and verify it matches + address implFromBeacon = getBeaconImplementation(beacon); + assertEq( + implFromBeacon, + address(newImpl), + "Beacon implementation should match new implementation" + ); + } + + function testAsyncPromiseBeaconUpgrade() public { + // Deploy new implementation + AsyncPromise newImpl = new AsyncPromise(); + + // Get current implementation from beacon + address oldImpl = getBeaconImplementation(address(addressResolver.asyncPromiseBeacon())); + + // Upgrade beacon to new implementation + vm.startPrank(watcherEOA); + vm.expectEmit(true, true, true, true, address(addressResolver)); + emit ImplementationUpdated("AsyncPromise", address(newImpl)); + addressResolver.setAsyncPromiseImplementation(address(newImpl)); + vm.stopPrank(); + + // Verify upgrade was successful + address newImplAddr = getBeaconImplementation( + address(addressResolver.asyncPromiseBeacon()) + ); + assertNotEq(oldImpl, newImplAddr, "Implementation should have changed"); + assertEq(newImplAddr, address(newImpl), "New implementation not set correctly"); + + // Deploy a new async promise and verify it uses the correct beacon + address newPromise = addressResolver.deployAsyncPromiseContract(address(this)); + address beacon = getBeacon(newPromise); + assertEq( + beacon, + address(addressResolver.asyncPromiseBeacon()), + "Beacon address not set correctly" + ); + + // Get implementation from beacon and verify it matches + address implFromBeacon = getBeaconImplementation(beacon); + assertEq( + implFromBeacon, + address(newImpl), + "Beacon implementation should match new implementation" + ); + } + + function testUnauthorizedBeaconUpgrade() public { + // Deploy new implementations + Forwarder newForwarderImpl = new Forwarder(); + AsyncPromise newAsyncPromiseImpl = new AsyncPromise(); + + // Try to upgrade from unauthorized account + address unauthorizedUser = address(0xBEEF); + + vm.startPrank(unauthorizedUser); + // Try upgrading forwarder beacon + vm.expectRevert(abi.encodeWithSelector(Ownable.Unauthorized.selector)); + addressResolver.setForwarderImplementation(address(newForwarderImpl)); + + // Try upgrading async promise beacon + vm.expectRevert(abi.encodeWithSelector(Ownable.Unauthorized.selector)); + addressResolver.setAsyncPromiseImplementation(address(newAsyncPromiseImpl)); + + vm.stopPrank(); + + // Verify implementations were not changed + assertNotEq( + getBeaconImplementation(address(addressResolver.forwarderBeacon())), + address(newForwarderImpl), + "Forwarder implementation should not have changed" + ); + assertNotEq( + getBeaconImplementation(address(addressResolver.asyncPromiseBeacon())), + address(newAsyncPromiseImpl), + "AsyncPromise implementation should not have changed" + ); + } +} diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 8a6ee9a2..7486a047 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -5,7 +5,7 @@ import "forge-std/Test.sol"; import "../contracts/protocol/utils/common/Structs.sol"; import "../contracts/protocol/utils/common/Errors.sol"; import "../contracts/protocol/utils/common/Constants.sol"; -import "../contracts/protocol/watcherPrecompile/WatcherPrecompile.sol"; +import "../contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol"; import "../contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol"; import "../contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol"; import "../contracts/protocol/watcherPrecompile/DumpDecoder.sol"; diff --git a/test/mock/MockWatcherPrecompileImpl.sol b/test/mock/MockWatcherPrecompileImpl.sol index e888a9bb..567189f3 100644 --- a/test/mock/MockWatcherPrecompileImpl.sol +++ b/test/mock/MockWatcherPrecompileImpl.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "../../contracts/protocol/watcherPrecompile/WatcherPrecompile.sol"; +import "../../contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol"; contract MockWatcherPrecompileImpl is WatcherPrecompile { // Mock function to test reinitialization with version 2 From 86c0af221c9f77880e774201933e4e6bb3cc9a9f Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 3 Apr 2025 15:55:25 +0530 Subject: [PATCH 003/108] fix: diff counter for timeout --- .../protocol/watcherPrecompile/WatcherPrecompileCore.sol | 9 ++++----- test/DeliveryHelper.t.sol | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol index 7950823e..cdc88171 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol @@ -32,7 +32,7 @@ abstract contract WatcherPrecompileCore is // from auction manager watcherPrecompileLimits__.consumeLimit(_getCoreAppGateway(msg.sender), SCHEDULE, 1); uint256 executeAt = block.timestamp + delayInSeconds_; - timeoutId = _encodeId(evmxSlug, address(this)); + timeoutId = _encodeTimeoutId(evmxSlug, address(this)); timeoutRequests[timeoutId] = TimeoutRequest( timeoutId, msg.sender, @@ -192,18 +192,17 @@ abstract contract WatcherPrecompileCore is if (switchboard != switchboard_) revert InvalidSwitchboard(); } - // todo: revisit when we do timeout precompile - function _encodeId( + function _encodeTimeoutId( uint32 chainSlug_, address switchboardOrWatcher_ ) internal returns (bytes32) { - // Encode payload ID by bit-shifting and combining: + // Encode timeout ID by bit-shifting and combining: // chainSlug (32 bits) | switchboard or watcher precompile address (160 bits) | counter (64 bits) return bytes32( (uint256(chainSlug_) << 224) | (uint256(uint160(switchboardOrWatcher_)) << 64) | - payloadCounter++ + timeoutCounter++ ); } diff --git a/test/DeliveryHelper.t.sol b/test/DeliveryHelper.t.sol index 09ecc39d..ea346775 100644 --- a/test/DeliveryHelper.t.sol +++ b/test/DeliveryHelper.t.sol @@ -279,7 +279,7 @@ contract DeliveryHelperTest is SetupTest { function endAuction(uint40 requestCount_) internal { if (auctionEndDelaySeconds == 0) return; - bytes32 timeoutId = _encodeId(evmxSlug, address(watcherPrecompile), timeoutIdCounter++); + bytes32 timeoutId = _encodeTimeoutId(evmxSlug, address(watcherPrecompile), timeoutIdCounter++); bytes memory watcherSignature = _createWatcherSignature( address(watcherPrecompile), @@ -300,7 +300,7 @@ contract DeliveryHelperTest is SetupTest { } //////////////////////////////////// Utils /////////////////////////////////// - function _encodeId( + function _encodeTimeoutId( uint32 chainSlug_, address sbOrWatcher_, uint256 counter_ From d2ae720d1590d73b09943e97999a7bce28d43a87 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 3 Apr 2025 17:44:00 +0530 Subject: [PATCH 004/108] feat: fallback for inbox calls to socket --- contracts/base/PlugBase.sol | 7 ++- contracts/interfaces/IPlug.sol | 2 + contracts/interfaces/ISocket.sol | 17 ++---- contracts/protocol/socket/Socket.sol | 71 ++++++++++++++--------- contracts/protocol/socket/SocketUtils.sol | 10 ++-- 5 files changed, 58 insertions(+), 49 deletions(-) diff --git a/contracts/base/PlugBase.sol b/contracts/base/PlugBase.sol index 4707cff4..51bc6612 100644 --- a/contracts/base/PlugBase.sol +++ b/contracts/base/PlugBase.sol @@ -11,6 +11,7 @@ abstract contract PlugBase is IPlug { ISocket public socket__; address public appGateway; uint256 public isSocketInitialized; + bytes public inboxParams; error SocketAlreadyInitialized(); event ConnectorPlugDisconnected(); @@ -52,8 +53,10 @@ abstract contract PlugBase is IPlug { socket__ = ISocket(socket_); } - function _callAppGateway(bytes memory payload_, bytes32 params_) internal returns (bytes32) { - return socket__.callAppGateway(payload_, params_); + /// @notice Sets the inbox params + /// @param inboxParams_ The inbox params + function _setInboxParams(bytes memory inboxParams_) internal { + inboxParams = inboxParams_; } function initSocket( diff --git a/contracts/interfaces/IPlug.sol b/contracts/interfaces/IPlug.sol index 1e738f09..0d636ef6 100644 --- a/contracts/interfaces/IPlug.sol +++ b/contracts/interfaces/IPlug.sol @@ -7,4 +7,6 @@ pragma solidity ^0.8.21; */ interface IPlug { function initSocket(address appGateway_, address socket_, address switchboard_) external; + + function inboxParams() external view returns (bytes memory); } diff --git a/contracts/interfaces/ISocket.sol b/contracts/interfaces/ISocket.sol index 1d7e1a1e..d932806e 100644 --- a/contracts/interfaces/ISocket.sol +++ b/contracts/interfaces/ISocket.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.21; import {ExecuteParams} from "../protocol/utils/common/Structs.sol"; + /** * @title ISocket * @notice An interface for a Chain Abstraction contract @@ -33,7 +34,7 @@ interface ISocket { /** * @notice emits the message details when a new message arrives at outbound - * @param callId call id + * @param inboxId call id * @param chainSlug local chain slug * @param plug local plug address * @param appGateway appGateway address to trigger the call @@ -41,24 +42,14 @@ interface ISocket { * @param payload the data which will be used by contracts on chain */ event AppGatewayCallRequested( - bytes32 callId, + bytes32 inboxId, uint32 chainSlug, address plug, address appGateway, - bytes32 params, + bytes params, bytes payload ); - /** - * @notice To call the appGateway on EVMx. Should only be called by a plug. - * @param payload_ bytes to be delivered to the Plug on EVMx - * @param params_ a 32 bytes param to add details for execution. - */ - function callAppGateway( - bytes calldata payload_, - bytes32 params_ - ) external returns (bytes32 callId); - /** * @notice executes a payload */ diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 84b588af..57566613 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -2,6 +2,8 @@ pragma solidity ^0.8.21; import "./SocketUtils.sol"; + +import {IPlug} from "../../interfaces/IPlug.sol"; import {PlugDisconnected, InvalidAppGateway} from "../utils/common/Errors.sol"; /** @@ -38,35 +40,6 @@ contract Socket is SocketUtils { string memory version_ ) SocketUtils(chainSlug_, owner_, version_) {} - //////////////////////////////////////////////////////// - ////////////////////// OPERATIONS ////////////////////////// - //////////////////////////////////////////////////////// - /** - * @notice To send message to a connected remote chain. Should only be called by a plug. - * @param payload bytes to be delivered to the Plug on the siblingChainSlug_ - * @param params a 32 bytes param to add details for execution, for eg: fees to be paid for execution - */ - function callAppGateway( - bytes calldata payload, - bytes32 params - ) external returns (bytes32 callId) { - PlugConfig memory plugConfig = _plugConfigs[msg.sender]; - - // if no sibling plug is found for the given chain slug, revert - if (plugConfig.appGateway == address(0)) revert PlugDisconnected(); - - // creates a unique ID for the message - callId = _encodeCallId(plugConfig.appGateway); - emit AppGatewayCallRequested( - callId, - chainSlug, - msg.sender, - plugConfig.appGateway, - params, - payload - ); - } - /** * @notice Executes a payload that has been delivered by transmitters and authenticated by switchboards */ @@ -137,4 +110,44 @@ contract Socket is SocketUtils { revert PayloadAlreadyExecuted(payloadExecuted[payloadId_]); payloadExecuted[payloadId_] = ExecutionStatus.Executed; } + + //////////////////////////////////////////////////////// + ////////////////////// OPERATIONS ////////////////////////// + //////////////////////////////////////////////////////// + /** + * @notice To send message to a connected remote chain. Should only be called by a plug. + * @param payload_ bytes to be delivered to the Plug on the siblingChainSlug_ + * @param inboxParams_ a 32 bytes param to add details for execution, for eg: fees to be paid for execution + */ + function _callAppGateway( + address plug_, + bytes memory inboxParams_, + bytes memory payload_ + ) internal returns (bytes32 inboxId) { + PlugConfig memory plugConfig = _plugConfigs[plug_]; + + // if no sibling plug is found for the given chain slug, revert + if (plugConfig.appGateway == address(0)) revert PlugDisconnected(); + + // creates a unique ID for the message + inboxId = _encodeInboxId(plugConfig.appGateway); + emit AppGatewayCallRequested( + inboxId, + chainSlug, + plug_, + plugConfig.appGateway, + inboxParams_, + payload_ + ); + } + + /// @notice Fallback function that forwards all calls to Socket's callAppGateway + /// @dev The calldata is passed as-is to the gateways + fallback(bytes calldata) external payable returns (bytes memory) { + bytes memory inboxParams = IPlug(msg.sender).inboxParams(); + return abi.encode(_callAppGateway(msg.sender, inboxParams, msg.data)); + } + + /// @notice Receive function to accept ETH payments + receive() external payable {} } diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index 97aa46ba..52db1b43 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -20,7 +20,7 @@ abstract contract SocketUtils is SocketConfig { // ChainSlug for this deployed socket instance uint32 public immutable chainSlug; - uint64 public callCounter; + uint64 public inboxCounter; /** * @dev keeps track of whether a payload has been executed or not using payload id @@ -113,12 +113,12 @@ abstract contract SocketUtils is SocketConfig { } // Packs the local plug, local chain slug, remote chain slug and nonce - // callCount++ will take care of call id overflow as well - // callId(256) = localChainSlug(32) | appGateway_(160) | nonce(64) - function _encodeCallId(address appGateway_) internal returns (bytes32) { + // inboxCounter++ will take care of call id overflow as well + // inboxId(256) = localChainSlug(32) | appGateway_(160) | nonce(64) + function _encodeInboxId(address appGateway_) internal returns (bytes32) { return bytes32( - (uint256(chainSlug) << 224) | (uint256(uint160(appGateway_)) << 64) | callCounter++ + (uint256(chainSlug) << 224) | (uint256(uint160(appGateway_)) << 64) | inboxCounter++ ); } From 0d61a5d4eeae13e015bfeb27a0ef2c978d0da5da Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 3 Apr 2025 17:48:53 +0530 Subject: [PATCH 005/108] feat: inbox calls to app gateway --- contracts/base/AppGatewayBase.sol | 7 --- contracts/interfaces/IAppGateway.sol | 7 --- contracts/interfaces/IWatcherPrecompile.sol | 2 +- .../interfaces/IWatcherPrecompileConfig.sol | 1 + contracts/protocol/utils/common/Structs.sol | 2 +- .../watcherPrecompile/WatcherPrecompile.sol | 18 +++--- .../WatcherPrecompileStorage.sol | 60 +++++++++---------- 7 files changed, 42 insertions(+), 55 deletions(-) diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index 2b509aab..b0dd03fb 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -344,13 +344,6 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin } } - function callFromChain( - uint32 chainSlug_, - address plug_, - bytes32 params_, - bytes calldata payload_ - ) external virtual onlyWatcherPrecompile {} - /// @notice Initializes the contract /// @param chainSlug_ The chain slug function initialize(uint32 chainSlug_) public virtual {} diff --git a/contracts/interfaces/IAppGateway.sol b/contracts/interfaces/IAppGateway.sol index 9bd9a649..21200d0e 100644 --- a/contracts/interfaces/IAppGateway.sol +++ b/contracts/interfaces/IAppGateway.sol @@ -13,13 +13,6 @@ interface IAppGateway { function onRequestComplete(uint40 requestCount_, bytes calldata onCompleteData_) external; - function callFromChain( - uint32 chainSlug_, - address plug_, - bytes32 params_, - bytes calldata payload_ - ) external; - function handleRevert(uint40 requestCount_, bytes32 payloadId_) external; /// @notice initialize the contracts on chain diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 5ce28d64..06e4e5bc 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -10,7 +10,7 @@ import {IWatcherPrecompileConfig} from "./IWatcherPrecompileConfig.sol"; /// @dev Defines core functionality for payload processing and promise resolution interface IWatcherPrecompile { event CalledAppGateway( - bytes32 callId, + bytes32 inboxId, uint32 chainSlug, address plug, address appGateway, diff --git a/contracts/interfaces/IWatcherPrecompileConfig.sol b/contracts/interfaces/IWatcherPrecompileConfig.sol index d42c7294..85831eca 100644 --- a/contracts/interfaces/IWatcherPrecompileConfig.sol +++ b/contracts/interfaces/IWatcherPrecompileConfig.sol @@ -36,6 +36,7 @@ interface IWatcherPrecompileConfig { function setSwitchboard(uint32 chainSlug_, bytes32 sbType_, address switchboard_) 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 function setIsValidPlug(uint32 chainSlug_, address plug_, bool isValid_) external; /// @notice Retrieves the configuration for a specific plug on a network diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 2b7c1f55..67c4b763 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -77,7 +77,7 @@ struct PlugConfig { } //inbox: struct CallFromChainParams { - bytes32 callId; + bytes32 inboxId; bytes32 params; address plug; address appGateway; diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index c8bc8a76..18b742db 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -248,7 +248,7 @@ contract WatcherPrecompile is RequestHandler { ); for (uint256 i = 0; i < params_.length; i++) { - if (appGatewayCalled[params_[i].callId]) revert AppGatewayAlreadyCalled(); + if (appGatewayCalled[params_[i].inboxId]) revert AppGatewayAlreadyCalled(); if ( !watcherPrecompileConfig__.isValidPlug( params_[i].appGateway, @@ -257,16 +257,14 @@ contract WatcherPrecompile is RequestHandler { ) ) revert InvalidInboxCaller(); - appGatewayCalled[params_[i].callId] = true; - IAppGateway(params_[i].appGateway).callFromChain( - params_[i].chainSlug, - params_[i].plug, - params_[i].params, - params_[i].payload - ); + appGatewayCaller = params_[i].appGateway; + appGatewayCalled[params_[i].inboxId] = true; + + (bool success, ) = address(params_[i].appGateway).call(params_[i].payload); + if (!success) revert CallFailed(); emit CalledAppGateway( - params_[i].callId, + params_[i].inboxId, params_[i].chainSlug, params_[i].plug, params_[i].appGateway, @@ -274,6 +272,8 @@ contract WatcherPrecompile is RequestHandler { params_[i].payload ); } + + appGatewayCaller = address(0); } // ================== Helper functions ================== diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol index 974a0b03..0640d95d 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol @@ -16,67 +16,67 @@ abstract contract WatcherPrecompileStorage is IWatcherPrecompile { // slots [0-49]: gap for future storage variables uint256[50] _gap_before; - // slot 50: evmxSlug + // slot 50 /// @notice The chain slug of the watcher precompile uint32 public evmxSlug; - // slot 51: isNonceUsed - /// @notice Maps nonce to whether it has been used - /// @dev signatureNonce => isValid - mapping(uint256 => bool) public isNonceUsed; - - // slot 52: maxTimeoutDelayInSeconds - uint256 public maxTimeoutDelayInSeconds; - - // slot 53: payloadCounter /// @notice Counter for tracking payload requests uint40 public payloadCounter; - // slot 54: timeoutCounter /// @notice Counter for tracking timeout requests uint40 public timeoutCounter; - // slot 55: expiryTime + uint40 public nextRequestCount; + + uint40 public nextBatchCount; + + // slot 51: maxTimeoutDelayInSeconds + uint256 public maxTimeoutDelayInSeconds; + + // slot 52: expiryTime /// @notice The expiry time for the payload uint256 public expiryTime; - // slot 56: timeoutRequests + // slot 53: appGatewayCaller + /// @notice The address of the app gateway caller from a chain + address public appGatewayCaller; + + // slot 54: isNonceUsed + /// @notice Maps nonce to whether it has been used + /// @dev signatureNonce => isValid + mapping(uint256 => bool) public isNonceUsed; + + // slot 55: timeoutRequests /// @notice Mapping to store timeout requests /// @dev timeoutId => TimeoutRequest struct mapping(bytes32 => TimeoutRequest) public timeoutRequests; - // slot 57: watcherProofs + // slot 56: watcherProofs /// @notice Mapping to store watcher proofs /// @dev payloadId => proof bytes mapping(bytes32 => bytes) public watcherProofs; - // slot 58: appGatewayCalled + // slot 57: appGatewayCalled /// @notice Mapping to store if appGateway has been called with trigger from on-chain Inbox - /// @dev callId => bool + /// @dev inboxId => bool mapping(bytes32 => bool) public appGatewayCalled; - // slot 59: nextRequestCount - uint40 public nextRequestCount; - - // slot 60: nextBatchCount - uint40 public nextBatchCount; - - // slot 61: requestParams + // slot 58: requestParams mapping(uint40 => RequestParams) public requestParams; - // slot 62: batchPayloadIds + // slot 59: batchPayloadIds mapping(uint40 => bytes32[]) public batchPayloadIds; - // slot 63: requestBatchIds + // slot 60: requestBatchIds mapping(uint40 => uint40[]) public requestBatchIds; - // slot 64: payloads + // slot 61: payloads mapping(bytes32 => PayloadParams) public payloads; - // slot 65: isPromiseExecuted + // slot 62: isPromiseExecuted mapping(bytes32 => bool) public isPromiseExecuted; - // slot 66: watcherPrecompileLimits__ + // slot 63: watcherPrecompileLimits__ IWatcherPrecompileLimits public watcherPrecompileLimits__; - // slot 67: watcherPrecompileConfig__ + // slot 64: watcherPrecompileConfig__ IWatcherPrecompileConfig public watcherPrecompileConfig__; - // slots [68-117]: gap for future storage variables + // slots [65-114]: gap for future storage variables uint256[50] _gap_after; } From b1d2a26ec9d78339763e878a6bd1eb33bc9c5594 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 3 Apr 2025 17:49:45 +0530 Subject: [PATCH 006/108] fix: tests --- test/Inbox.t.sol | 39 +++++++++++++++++-- test/SetupTest.t.sol | 1 + test/apps/app-gateways/counter/Counter.sol | 8 +++- .../counter/CounterAppGateway.sol | 10 +---- test/mock/MockSocket.sol | 18 ++++----- test/mock/MockWatcherPrecompile.sol | 4 +- 6 files changed, 57 insertions(+), 23 deletions(-) diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index f45277fd..96360360 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -10,6 +10,15 @@ contract InboxTest is DeliveryHelperTest { CounterAppGateway public gateway; Counter public inbox; + event AppGatewayCallRequested( + bytes32 inboxId, + uint32 chainSlug, + address plug, + address appGateway, + bytes params, + bytes payload + ); + function setUp() public { // Setup core test infrastructure setUpDeliveryHelper(); @@ -54,15 +63,30 @@ contract InboxTest is DeliveryHelperTest { // Simulate a message from another chain through the watcher uint256 incrementValue = 5; + bytes32 inboxId = _encodeInboxId(address(gateway), arbChainSlug); + bytes memory payload = abi.encodeWithSelector( + CounterAppGateway.increase.selector, + incrementValue + ); + + vm.expectEmit(true, true, true, true); + emit AppGatewayCallRequested( + inboxId, + arbChainSlug, + address(inbox), + address(gateway), + bytes(""), + payload + ); + inbox.increaseOnGateway(incrementValue); - bytes32 callId = inbox.increaseOnGateway(incrementValue); CallFromChainParams[] memory params = new CallFromChainParams[](1); params[0] = CallFromChainParams({ - callId: callId, + inboxId: inboxId, chainSlug: arbChainSlug, appGateway: address(gateway), plug: address(inbox), - payload: abi.encode(incrementValue), + payload: payload, params: bytes32(0) }); @@ -74,4 +98,13 @@ contract InboxTest is DeliveryHelperTest { // Check counter was incremented assertEq(gateway.counterVal(), incrementValue, "Gateway counter should be incremented"); } + + function _encodeInboxId(address appGateway_, uint256 chainSlug_) internal returns (bytes32) { + return + bytes32( + (uint256(chainSlug_) << 224) | + (uint256(uint160(appGateway_)) << 64) | + inboxCounter++ + ); + } } diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 8a6ee9a2..7eb92676 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -44,6 +44,7 @@ contract SetupTest is Test { uint256 public signatureNonce; uint256 public payloadIdCounter; uint256 public timeoutIdCounter; + uint256 public inboxCounter; uint256 public defaultLimit = 1000; bytes public asyncPromiseBytecode = type(AsyncPromise).creationCode; diff --git a/test/apps/app-gateways/counter/Counter.sol b/test/apps/app-gateways/counter/Counter.sol index 9625635d..f17b55e6 100644 --- a/test/apps/app-gateways/counter/Counter.sol +++ b/test/apps/app-gateways/counter/Counter.sol @@ -4,9 +4,14 @@ pragma solidity >=0.7.0 <0.9.0; import "solady/auth/Ownable.sol"; import "../../../../contracts/base/PlugBase.sol"; +interface ICounterAppGateway { + function increase(uint256 value_) external returns (bytes32); +} + contract Counter is Ownable, PlugBase { uint256 public counter; event CounterIncreased(uint256 value); + function increase() external onlySocket { counter++; emit CounterIncreased(counter); @@ -17,6 +22,7 @@ contract Counter is Ownable, PlugBase { } function increaseOnGateway(uint256 value_) external returns (bytes32) { - return _callAppGateway(abi.encode(value_), bytes32(0)); + // can set inbox params here: _setInboxParams(params_); + return ICounterAppGateway(address(socket__)).increase(value_); } } diff --git a/test/apps/app-gateways/counter/CounterAppGateway.sol b/test/apps/app-gateways/counter/CounterAppGateway.sol index deef0e53..33bea3f6 100644 --- a/test/apps/app-gateways/counter/CounterAppGateway.sol +++ b/test/apps/app-gateways/counter/CounterAppGateway.sol @@ -102,14 +102,8 @@ contract CounterAppGateway is AppGatewayBase, Ownable { watcherPrecompileConfig().setIsValidPlug(chainSlug_, plug_, true); } - function callFromChain( - uint32, - address, - bytes32, - bytes calldata payload_ - ) external override onlyWatcherPrecompile { - uint256 value = abi.decode(payload_, (uint256)); - counterVal += value; + function increase(uint256 value_) external onlyWatcherPrecompile { + counterVal += value_; } // TIMEOUT diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index da1765a9..36a10f0c 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -65,7 +65,7 @@ contract MockSocket is ISocket { //////////////////////////////////////////////////////////// ////////////////////// State Vars ////////////////////////// //////////////////////////////////////////////////////////// - uint64 public callCounter; + uint64 public inboxCounter; uint32 public chainSlug; enum ExecutionStatus { @@ -94,13 +94,13 @@ contract MockSocket is ISocket { */ function callAppGateway( bytes calldata payload, - bytes32 params - ) external returns (bytes32 callId) { + bytes calldata params + ) external returns (bytes32 inboxId) { PlugConfig memory plugConfig = _plugConfigs[msg.sender]; // creates a unique ID for the message - callId = _encodeCallId(plugConfig.appGateway); + inboxId = _encodeInboxId(plugConfig.appGateway); emit AppGatewayCallRequested( - callId, + inboxId, chainSlug, msg.sender, plugConfig.appGateway, @@ -174,12 +174,12 @@ contract MockSocket is ISocket { } // Packs the local plug, local chain slug, remote chain slug and nonce - // callCount++ will take care of call id overflow as well - // callId(256) = localChainSlug(32) | appGateway_(160) | nonce(64) - function _encodeCallId(address appGateway_) internal returns (bytes32) { + // inboxCounter++ will take care of call id overflow as well + // inboxId(256) = localChainSlug(32) | appGateway_(160) | nonce(64) + function _encodeInboxId(address appGateway_) internal returns (bytes32) { return bytes32( - (uint256(chainSlug) << 224) | (uint256(uint160(appGateway_)) << 64) | callCounter++ + (uint256(chainSlug) << 224) | (uint256(uint160(appGateway_)) << 64) | inboxCounter++ ); } } diff --git a/test/mock/MockWatcherPrecompile.sol b/test/mock/MockWatcherPrecompile.sol index 42247124..47d43043 100644 --- a/test/mock/MockWatcherPrecompile.sol +++ b/test/mock/MockWatcherPrecompile.sol @@ -29,7 +29,7 @@ contract MockWatcherPrecompile { error InvalidTransmitter(); event CalledAppGateway( - bytes32 callId, + bytes32 inboxId, uint32 chainSlug, address plug, address appGateway, @@ -159,7 +159,7 @@ contract MockWatcherPrecompile { function callAppGateways(CallFromChainParams[] calldata params_) external { for (uint256 i = 0; i < params_.length; i++) { emit CalledAppGateway( - params_[i].callId, + params_[i].inboxId, params_[i].chainSlug, params_[i].plug, params_[i].appGateway, From cccc84ea83c879a41dddb99109af661baad41d5c Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 3 Apr 2025 21:38:06 +0530 Subject: [PATCH 007/108] fix: renames --- contracts/base/PlugBase.sol | 10 ++--- contracts/interfaces/IPlug.sol | 2 +- contracts/interfaces/ISocket.sol | 4 +- contracts/interfaces/IWatcherPrecompile.sol | 6 +-- contracts/protocol/socket/Socket.sol | 16 +++---- contracts/protocol/socket/SocketUtils.sol | 12 +++--- contracts/protocol/utils/common/Errors.sol | 2 +- contracts/protocol/utils/common/Structs.sol | 6 +-- .../watcherPrecompile/WatcherPrecompile.sol | 12 +++--- .../WatcherPrecompileStorage.sol | 8 ++-- test/Inbox.t.sol | 42 +++++++++---------- test/SetupTest.t.sol | 2 +- test/apps/app-gateways/counter/Counter.sol | 2 +- .../counter/CounterAppGateway.sol | 2 +- test/mock/MockSocket.sol | 18 ++++---- test/mock/MockWatcherPrecompile.sol | 12 +++--- 16 files changed, 80 insertions(+), 76 deletions(-) diff --git a/contracts/base/PlugBase.sol b/contracts/base/PlugBase.sol index 51bc6612..75fcec8d 100644 --- a/contracts/base/PlugBase.sol +++ b/contracts/base/PlugBase.sol @@ -11,7 +11,7 @@ abstract contract PlugBase is IPlug { ISocket public socket__; address public appGateway; uint256 public isSocketInitialized; - bytes public inboxParams; + bytes public triggerParams; error SocketAlreadyInitialized(); event ConnectorPlugDisconnected(); @@ -53,10 +53,10 @@ abstract contract PlugBase is IPlug { socket__ = ISocket(socket_); } - /// @notice Sets the inbox params - /// @param inboxParams_ The inbox params - function _setInboxParams(bytes memory inboxParams_) internal { - inboxParams = inboxParams_; + /// @notice Sets the trigger params + /// @param triggerParams_ The trigger params + function _setTriggerParams(bytes memory triggerParams_) internal { + triggerParams = triggerParams_; } function initSocket( diff --git a/contracts/interfaces/IPlug.sol b/contracts/interfaces/IPlug.sol index 0d636ef6..0d76f964 100644 --- a/contracts/interfaces/IPlug.sol +++ b/contracts/interfaces/IPlug.sol @@ -8,5 +8,5 @@ pragma solidity ^0.8.21; interface IPlug { function initSocket(address appGateway_, address socket_, address switchboard_) external; - function inboxParams() external view returns (bytes memory); + function triggerParams() external view returns (bytes memory); } diff --git a/contracts/interfaces/ISocket.sol b/contracts/interfaces/ISocket.sol index d932806e..c44b6ddf 100644 --- a/contracts/interfaces/ISocket.sol +++ b/contracts/interfaces/ISocket.sol @@ -34,7 +34,7 @@ interface ISocket { /** * @notice emits the message details when a new message arrives at outbound - * @param inboxId call id + * @param triggerId call id * @param chainSlug local chain slug * @param plug local plug address * @param appGateway appGateway address to trigger the call @@ -42,7 +42,7 @@ interface ISocket { * @param payload the data which will be used by contracts on chain */ event AppGatewayCallRequested( - bytes32 inboxId, + bytes32 triggerId, uint32 chainSlug, address plug, address appGateway, diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 06e4e5bc..64319f8d 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.21; -import {DigestParams, ResolvedPromises, PayloadParams, CallFromChainParams, PayloadSubmitParams, RequestParams} from "../protocol/utils/common/Structs.sol"; +import {DigestParams, ResolvedPromises, PayloadParams, TriggerParams, PayloadSubmitParams, RequestParams} from "../protocol/utils/common/Structs.sol"; import {IWatcherPrecompileLimits} from "./IWatcherPrecompileLimits.sol"; import {IWatcherPrecompileConfig} from "./IWatcherPrecompileConfig.sol"; @@ -10,7 +10,7 @@ import {IWatcherPrecompileConfig} from "./IWatcherPrecompileConfig.sol"; /// @dev Defines core functionality for payload processing and promise resolution interface IWatcherPrecompile { event CalledAppGateway( - bytes32 inboxId, + bytes32 triggerId, uint32 chainSlug, address plug, address appGateway, @@ -146,7 +146,7 @@ interface IWatcherPrecompile { function setMaxTimeoutDelayInSeconds(uint256 maxTimeoutDelayInSeconds_) external; function callAppGateways( - CallFromChainParams[] calldata params_, + TriggerParams[] calldata params_, uint256 signatureNonce_, bytes calldata signature_ ) external; diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 57566613..dd61bff5 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -117,26 +117,26 @@ contract Socket is SocketUtils { /** * @notice To send message to a connected remote chain. Should only be called by a plug. * @param payload_ bytes to be delivered to the Plug on the siblingChainSlug_ - * @param inboxParams_ a 32 bytes param to add details for execution, for eg: fees to be paid for execution + * @param triggerParams_ a 32 bytes param to add details for execution, for eg: fees to be paid for execution */ function _callAppGateway( address plug_, - bytes memory inboxParams_, + bytes memory triggerParams_, bytes memory payload_ - ) internal returns (bytes32 inboxId) { + ) internal returns (bytes32 triggerId) { PlugConfig memory plugConfig = _plugConfigs[plug_]; // if no sibling plug is found for the given chain slug, revert if (plugConfig.appGateway == address(0)) revert PlugDisconnected(); // creates a unique ID for the message - inboxId = _encodeInboxId(plugConfig.appGateway); + triggerId = _encodeTriggerId(plugConfig.appGateway); emit AppGatewayCallRequested( - inboxId, + triggerId, chainSlug, plug_, plugConfig.appGateway, - inboxParams_, + triggerParams_, payload_ ); } @@ -144,8 +144,8 @@ contract Socket is SocketUtils { /// @notice Fallback function that forwards all calls to Socket's callAppGateway /// @dev The calldata is passed as-is to the gateways fallback(bytes calldata) external payable returns (bytes memory) { - bytes memory inboxParams = IPlug(msg.sender).inboxParams(); - return abi.encode(_callAppGateway(msg.sender, inboxParams, msg.data)); + bytes memory triggerParams = IPlug(msg.sender).triggerParams(); + return abi.encode(_callAppGateway(msg.sender, triggerParams, msg.data)); } /// @notice Receive function to accept ETH payments diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index 52db1b43..32f5cfe1 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -20,7 +20,7 @@ abstract contract SocketUtils is SocketConfig { // ChainSlug for this deployed socket instance uint32 public immutable chainSlug; - uint64 public inboxCounter; + uint64 public triggerCounter; /** * @dev keeps track of whether a payload has been executed or not using payload id @@ -113,12 +113,14 @@ abstract contract SocketUtils is SocketConfig { } // Packs the local plug, local chain slug, remote chain slug and nonce - // inboxCounter++ will take care of call id overflow as well - // inboxId(256) = localChainSlug(32) | appGateway_(160) | nonce(64) - function _encodeInboxId(address appGateway_) internal returns (bytes32) { + // triggerCounter++ will take care of call id overflow as well + // triggerId(256) = localChainSlug(32) | appGateway_(160) | nonce(64) + function _encodeTriggerId(address appGateway_) internal returns (bytes32) { return bytes32( - (uint256(chainSlug) << 224) | (uint256(uint160(appGateway_)) << 64) | inboxCounter++ + (uint256(chainSlug) << 224) | + (uint256(uint160(appGateway_)) << 64) | + triggerCounter++ ); } diff --git a/contracts/protocol/utils/common/Errors.sol b/contracts/protocol/utils/common/Errors.sol index bdebefd1..da0a0764 100644 --- a/contracts/protocol/utils/common/Errors.sol +++ b/contracts/protocol/utils/common/Errors.sol @@ -22,7 +22,7 @@ error CallFailed(); error PlugDisconnected(); error InvalidAppGateway(); error AppGatewayAlreadyCalled(); -error InvalidInboxCaller(); +error InvalidCallerTriggered(); error PromisesNotResolved(); error InvalidPromise(); error InvalidIndex(); diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 67c4b763..91091792 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -75,9 +75,9 @@ struct PlugConfig { address appGateway; address switchboard; } -//inbox: -struct CallFromChainParams { - bytes32 inboxId; +//trigger: +struct TriggerParams { + bytes32 triggerId; bytes32 params; address plug; address appGateway; diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index 18b742db..f095c1f7 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -234,10 +234,10 @@ contract WatcherPrecompile is RequestHandler { maxTimeoutDelayInSeconds = maxTimeoutDelayInSeconds_; } - // ================== On-Chain Inbox ================== + // ================== On-Chain Trigger ================== function callAppGateways( - CallFromChainParams[] calldata params_, + TriggerParams[] calldata params_, uint256 signatureNonce_, bytes calldata signature_ ) external { @@ -248,23 +248,23 @@ contract WatcherPrecompile is RequestHandler { ); for (uint256 i = 0; i < params_.length; i++) { - if (appGatewayCalled[params_[i].inboxId]) revert AppGatewayAlreadyCalled(); + if (appGatewayCalled[params_[i].triggerId]) revert AppGatewayAlreadyCalled(); if ( !watcherPrecompileConfig__.isValidPlug( params_[i].appGateway, params_[i].chainSlug, params_[i].plug ) - ) revert InvalidInboxCaller(); + ) revert InvalidCallerTriggered(); appGatewayCaller = params_[i].appGateway; - appGatewayCalled[params_[i].inboxId] = true; + appGatewayCalled[params_[i].triggerId] = true; (bool success, ) = address(params_[i].appGateway).call(params_[i].payload); if (!success) revert CallFailed(); emit CalledAppGateway( - params_[i].inboxId, + params_[i].triggerId, params_[i].chainSlug, params_[i].plug, params_[i].appGateway, diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol index 0640d95d..1dff2b6f 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol @@ -9,8 +9,8 @@ import "./DumpDecoder.sol"; import {IMiddleware} from "../../interfaces/IMiddleware.sol"; import {QUERY, FINALIZE, SCHEDULE} from "../utils/common/Constants.sol"; -import {TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidInboxCaller, ResolvingTimeoutTooEarly, CallFailed, AppGatewayAlreadyCalled, InvalidWatcherSignature, NonceUsed} from "../utils/common/Errors.sol"; -import {ResolvedPromises, AppGatewayConfig, LimitParams, WriteFinality, UpdateLimitParams, PlugConfig, DigestParams, TimeoutRequest, CallFromChainParams, QueuePayloadParams, PayloadParams, RequestParams} from "../utils/common/Structs.sol"; +import {TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidCallerTriggered, ResolvingTimeoutTooEarly, CallFailed, AppGatewayAlreadyCalled, InvalidWatcherSignature, NonceUsed} from "../utils/common/Errors.sol"; +import {ResolvedPromises, AppGatewayConfig, LimitParams, WriteFinality, UpdateLimitParams, PlugConfig, DigestParams, TimeoutRequest, TriggerParams, QueuePayloadParams, PayloadParams, RequestParams} from "../utils/common/Structs.sol"; abstract contract WatcherPrecompileStorage is IWatcherPrecompile { // slots [0-49]: gap for future storage variables @@ -57,8 +57,8 @@ abstract contract WatcherPrecompileStorage is IWatcherPrecompile { mapping(bytes32 => bytes) public watcherProofs; // slot 57: appGatewayCalled - /// @notice Mapping to store if appGateway has been called with trigger from on-chain Inbox - /// @dev inboxId => bool + /// @notice Mapping to store if appGateway has been called with trigger from on-chain plug + /// @dev triggerId => bool mapping(bytes32 => bool) public appGatewayCalled; // slot 58: requestParams diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index 96360360..d01b8193 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -5,13 +5,13 @@ import {CounterAppGateway} from "./apps/app-gateways/counter/CounterAppGateway.s import {Counter} from "./apps/app-gateways/counter/Counter.sol"; import "./DeliveryHelper.t.sol"; -contract InboxTest is DeliveryHelperTest { +contract TriggerTest is DeliveryHelperTest { uint256 constant feesAmount = 0.01 ether; CounterAppGateway public gateway; - Counter public inbox; + Counter public counter; event AppGatewayCallRequested( - bytes32 inboxId, + bytes32 triggerId, uint32 chainSlug, address plug, address appGateway, @@ -23,15 +23,15 @@ contract InboxTest is DeliveryHelperTest { // Setup core test infrastructure setUpDeliveryHelper(); - // Deploy the inbox contract - inbox = new Counter(); + // Deploy the counter contract + counter = new Counter(); // Deploy the gateway with fees gateway = new CounterAppGateway(address(addressResolver), createFees(feesAmount)); - gateway.setIsValidPlug(arbChainSlug, address(inbox)); + gateway.setIsValidPlug(arbChainSlug, address(counter)); - // Connect the inbox to the gateway and socket - inbox.initSocket( + // Connect the counter to the gateway and socket + counter.initSocket( address(gateway), address(arbConfig.socket), address(arbConfig.switchboard) @@ -40,7 +40,7 @@ contract InboxTest is DeliveryHelperTest { // Setup gateway config for the watcher AppGatewayConfig[] memory gateways = new AppGatewayConfig[](1); gateways[0] = AppGatewayConfig({ - plug: address(inbox), + plug: address(counter), chainSlug: arbChainSlug, appGateway: address(gateway), switchboard: address(arbConfig.switchboard) @@ -54,16 +54,16 @@ contract InboxTest is DeliveryHelperTest { watcherPrecompileConfig.setAppGateways(gateways, signatureNonce++, watcherSignature); hoax(watcherEOA); - watcherPrecompileConfig.setIsValidPlug(arbChainSlug, address(inbox), true); + watcherPrecompileConfig.setIsValidPlug(arbChainSlug, address(counter), true); } - function testInboxIncrement() public { + function testIncrementAfterTrigger() public { // Initial counter value should be 0 assertEq(gateway.counterVal(), 0, "Initial gateway counter should be 0"); // Simulate a message from another chain through the watcher uint256 incrementValue = 5; - bytes32 inboxId = _encodeInboxId(address(gateway), arbChainSlug); + bytes32 triggerId = _encodeTriggerId(address(gateway), arbChainSlug); bytes memory payload = abi.encodeWithSelector( CounterAppGateway.increase.selector, incrementValue @@ -71,21 +71,21 @@ contract InboxTest is DeliveryHelperTest { vm.expectEmit(true, true, true, true); emit AppGatewayCallRequested( - inboxId, + triggerId, arbChainSlug, - address(inbox), + address(counter), address(gateway), bytes(""), payload ); - inbox.increaseOnGateway(incrementValue); + counter.increaseOnGateway(incrementValue); - CallFromChainParams[] memory params = new CallFromChainParams[](1); - params[0] = CallFromChainParams({ - inboxId: inboxId, + TriggerParams[] memory params = new TriggerParams[](1); + params[0] = TriggerParams({ + triggerId: triggerId, chainSlug: arbChainSlug, appGateway: address(gateway), - plug: address(inbox), + plug: address(counter), payload: payload, params: bytes32(0) }); @@ -99,12 +99,12 @@ contract InboxTest is DeliveryHelperTest { assertEq(gateway.counterVal(), incrementValue, "Gateway counter should be incremented"); } - function _encodeInboxId(address appGateway_, uint256 chainSlug_) internal returns (bytes32) { + function _encodeTriggerId(address appGateway_, uint256 chainSlug_) internal returns (bytes32) { return bytes32( (uint256(chainSlug_) << 224) | (uint256(uint160(appGateway_)) << 64) | - inboxCounter++ + triggerCounter++ ); } } diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 7eb92676..32112c65 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -44,7 +44,7 @@ contract SetupTest is Test { uint256 public signatureNonce; uint256 public payloadIdCounter; uint256 public timeoutIdCounter; - uint256 public inboxCounter; + uint256 public triggerCounter; uint256 public defaultLimit = 1000; bytes public asyncPromiseBytecode = type(AsyncPromise).creationCode; diff --git a/test/apps/app-gateways/counter/Counter.sol b/test/apps/app-gateways/counter/Counter.sol index f17b55e6..5885b0ad 100644 --- a/test/apps/app-gateways/counter/Counter.sol +++ b/test/apps/app-gateways/counter/Counter.sol @@ -22,7 +22,7 @@ contract Counter is Ownable, PlugBase { } function increaseOnGateway(uint256 value_) external returns (bytes32) { - // can set inbox params here: _setInboxParams(params_); + // can set trigger params here: _setTriggerParams(params_); return ICounterAppGateway(address(socket__)).increase(value_); } } diff --git a/test/apps/app-gateways/counter/CounterAppGateway.sol b/test/apps/app-gateways/counter/CounterAppGateway.sol index 33bea3f6..1911e0f2 100644 --- a/test/apps/app-gateways/counter/CounterAppGateway.sol +++ b/test/apps/app-gateways/counter/CounterAppGateway.sol @@ -97,7 +97,7 @@ contract CounterAppGateway is AppGatewayBase, Ownable { } } - // INBOX + // trigger from a chain function setIsValidPlug(uint32 chainSlug_, address plug_) public { watcherPrecompileConfig().setIsValidPlug(chainSlug_, plug_, true); } diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index 36a10f0c..9b9cb0f5 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -65,7 +65,7 @@ contract MockSocket is ISocket { //////////////////////////////////////////////////////////// ////////////////////// State Vars ////////////////////////// //////////////////////////////////////////////////////////// - uint64 public inboxCounter; + uint64 public triggerCounter; uint32 public chainSlug; enum ExecutionStatus { @@ -95,12 +95,12 @@ contract MockSocket is ISocket { function callAppGateway( bytes calldata payload, bytes calldata params - ) external returns (bytes32 inboxId) { + ) external returns (bytes32 triggerId) { PlugConfig memory plugConfig = _plugConfigs[msg.sender]; // creates a unique ID for the message - inboxId = _encodeInboxId(plugConfig.appGateway); + triggerId = _encodeTriggerId(plugConfig.appGateway); emit AppGatewayCallRequested( - inboxId, + triggerId, chainSlug, msg.sender, plugConfig.appGateway, @@ -174,12 +174,14 @@ contract MockSocket is ISocket { } // Packs the local plug, local chain slug, remote chain slug and nonce - // inboxCounter++ will take care of call id overflow as well - // inboxId(256) = localChainSlug(32) | appGateway_(160) | nonce(64) - function _encodeInboxId(address appGateway_) internal returns (bytes32) { + // triggerCounter++ will take care of call id overflow as well + // triggerId(256) = localChainSlug(32) | appGateway_(160) | nonce(64) + function _encodeTriggerId(address appGateway_) internal returns (bytes32) { return bytes32( - (uint256(chainSlug) << 224) | (uint256(uint160(appGateway_)) << 64) | inboxCounter++ + (uint256(chainSlug) << 224) | + (uint256(uint160(appGateway_)) << 64) | + triggerCounter++ ); } } diff --git a/test/mock/MockWatcherPrecompile.sol b/test/mock/MockWatcherPrecompile.sol index 47d43043..5993ac59 100644 --- a/test/mock/MockWatcherPrecompile.sol +++ b/test/mock/MockWatcherPrecompile.sol @@ -5,9 +5,9 @@ import "../../contracts/interfaces/IAppGateway.sol"; import "../../contracts/interfaces/IWatcherPrecompile.sol"; import "../../contracts/interfaces/IPromise.sol"; -import {TimeoutRequest, CallFromChainParams, PlugConfig, ResolvedPromises, AppGatewayConfig} from "../../contracts/protocol/utils/common/Structs.sol"; +import {TimeoutRequest, TriggerParams, PlugConfig, ResolvedPromises, AppGatewayConfig} from "../../contracts/protocol/utils/common/Structs.sol"; import {QUERY, FINALIZE, SCHEDULE} from "../../contracts/protocol/utils/common/Constants.sol"; -import {TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidInboxCaller, ResolvingTimeoutTooEarly, CallFailed, AppGatewayAlreadyCalled} from "../../contracts/protocol/utils/common/Errors.sol"; +import {TimeoutDelayTooLarge, TimeoutAlreadyResolved, ResolvingTimeoutTooEarly, CallFailed, AppGatewayAlreadyCalled} from "../../contracts/protocol/utils/common/Errors.sol"; import "solady/utils/ERC1967Factory.sol"; /// @title WatcherPrecompile @@ -29,7 +29,7 @@ contract MockWatcherPrecompile { error InvalidTransmitter(); event CalledAppGateway( - bytes32 inboxId, + bytes32 triggerId, uint32 chainSlug, address plug, address appGateway, @@ -154,12 +154,12 @@ contract MockWatcherPrecompile { } } - // ================== On-Chain Inbox ================== + // ================== On-Chain Trigger ================== - function callAppGateways(CallFromChainParams[] calldata params_) external { + function callAppGateways(TriggerParams[] calldata params_) external { for (uint256 i = 0; i < params_.length; i++) { emit CalledAppGateway( - params_[i].inboxId, + params_[i].triggerId, params_[i].chainSlug, params_[i].plug, params_[i].appGateway, From df32d7d7e340b814d3dfc2de5c6c5af539d4e8b6 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 3 Apr 2025 21:41:12 +0530 Subject: [PATCH 008/108] fix: rename --- .../protocol/watcherPrecompile/WatcherPrecompileCore.sol | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol index cdc88171..44f583ab 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol @@ -192,17 +192,12 @@ abstract contract WatcherPrecompileCore is if (switchboard != switchboard_) revert InvalidSwitchboard(); } - function _encodeTimeoutId( - uint32 chainSlug_, - address switchboardOrWatcher_ - ) internal returns (bytes32) { + function _encodeTimeoutId(uint32 chainSlug_, address watcher_) internal returns (bytes32) { // Encode timeout ID by bit-shifting and combining: // chainSlug (32 bits) | switchboard or watcher precompile address (160 bits) | counter (64 bits) return bytes32( - (uint256(chainSlug_) << 224) | - (uint256(uint160(switchboardOrWatcher_)) << 64) | - timeoutCounter++ + (uint256(chainSlug_) << 224) | (uint256(uint160(watcher_)) << 64) | timeoutCounter++ ); } From f203df3a1ddb9737329fc058dd9bced276a3b929 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 7 Apr 2025 14:03:12 +0530 Subject: [PATCH 009/108] fix: rename trigger params to overrides for plugs --- contracts/base/PlugBase.sol | 10 +++++----- contracts/interfaces/IPlug.sol | 2 +- contracts/protocol/socket/Socket.sol | 12 ++++++------ test/apps/app-gateways/counter/Counter.sol | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/contracts/base/PlugBase.sol b/contracts/base/PlugBase.sol index 75fcec8d..64dff39d 100644 --- a/contracts/base/PlugBase.sol +++ b/contracts/base/PlugBase.sol @@ -11,7 +11,7 @@ abstract contract PlugBase is IPlug { ISocket public socket__; address public appGateway; uint256 public isSocketInitialized; - bytes public triggerParams; + bytes public overrides; error SocketAlreadyInitialized(); event ConnectorPlugDisconnected(); @@ -53,10 +53,10 @@ abstract contract PlugBase is IPlug { socket__ = ISocket(socket_); } - /// @notice Sets the trigger params - /// @param triggerParams_ The trigger params - function _setTriggerParams(bytes memory triggerParams_) internal { - triggerParams = triggerParams_; + /// @notice Sets the overrides needed for the trigger + /// @param overrides_ The overrides + function _setOverrides(bytes memory overrides_) internal { + overrides = overrides_; } function initSocket( diff --git a/contracts/interfaces/IPlug.sol b/contracts/interfaces/IPlug.sol index 0d76f964..552ae8f0 100644 --- a/contracts/interfaces/IPlug.sol +++ b/contracts/interfaces/IPlug.sol @@ -8,5 +8,5 @@ pragma solidity ^0.8.21; interface IPlug { function initSocket(address appGateway_, address socket_, address switchboard_) external; - function triggerParams() external view returns (bytes memory); + function overrides() external view returns (bytes memory); } diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index dd61bff5..2613ad99 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -116,12 +116,12 @@ contract Socket is SocketUtils { //////////////////////////////////////////////////////// /** * @notice To send message to a connected remote chain. Should only be called by a plug. - * @param payload_ bytes to be delivered to the Plug on the siblingChainSlug_ - * @param triggerParams_ a 32 bytes param to add details for execution, for eg: fees to be paid for execution + * @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 _callAppGateway( address plug_, - bytes memory triggerParams_, + bytes memory overrides_, bytes memory payload_ ) internal returns (bytes32 triggerId) { PlugConfig memory plugConfig = _plugConfigs[plug_]; @@ -136,7 +136,7 @@ contract Socket is SocketUtils { chainSlug, plug_, plugConfig.appGateway, - triggerParams_, + overrides_, payload_ ); } @@ -144,8 +144,8 @@ contract Socket is SocketUtils { /// @notice Fallback function that forwards all calls to Socket's callAppGateway /// @dev The calldata is passed as-is to the gateways fallback(bytes calldata) external payable returns (bytes memory) { - bytes memory triggerParams = IPlug(msg.sender).triggerParams(); - return abi.encode(_callAppGateway(msg.sender, triggerParams, msg.data)); + bytes memory overrides = IPlug(msg.sender).overrides(); + return abi.encode(_callAppGateway(msg.sender, overrides, msg.data)); } /// @notice Receive function to accept ETH payments diff --git a/test/apps/app-gateways/counter/Counter.sol b/test/apps/app-gateways/counter/Counter.sol index 5885b0ad..94311326 100644 --- a/test/apps/app-gateways/counter/Counter.sol +++ b/test/apps/app-gateways/counter/Counter.sol @@ -22,7 +22,7 @@ contract Counter is Ownable, PlugBase { } function increaseOnGateway(uint256 value_) external returns (bytes32) { - // can set trigger params here: _setTriggerParams(params_); + // can set trigger params here: _setOverrides(params_); return ICounterAppGateway(address(socket__)).increase(value_); } } From b9257c2db5f95a2d95e5119761b3dac453e7c878 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 7 Apr 2025 14:07:32 +0530 Subject: [PATCH 010/108] fix: decode app gateway from trigger id --- contracts/interfaces/ISocket.sol | 2 -- contracts/interfaces/IWatcherPrecompile.sol | 1 - contracts/protocol/socket/Socket.sol | 1 - .../watcherPrecompile/WatcherPrecompile.sol | 13 +++++++++---- test/Inbox.t.sol | 2 -- test/mock/MockSocket.sol | 9 +-------- test/mock/MockWatcherPrecompile.sol | 2 -- 7 files changed, 10 insertions(+), 20 deletions(-) diff --git a/contracts/interfaces/ISocket.sol b/contracts/interfaces/ISocket.sol index c44b6ddf..68b57fdc 100644 --- a/contracts/interfaces/ISocket.sol +++ b/contracts/interfaces/ISocket.sol @@ -37,7 +37,6 @@ interface ISocket { * @param triggerId call id * @param chainSlug local chain slug * @param plug local plug address - * @param appGateway appGateway address to trigger the call * @param params 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 */ @@ -45,7 +44,6 @@ interface ISocket { bytes32 triggerId, uint32 chainSlug, address plug, - address appGateway, bytes params, bytes payload ); diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 64319f8d..2d397683 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -13,7 +13,6 @@ interface IWatcherPrecompile { bytes32 triggerId, uint32 chainSlug, address plug, - address appGateway, bytes32 params, bytes payload ); diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 2613ad99..a9d1c472 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -135,7 +135,6 @@ contract Socket is SocketUtils { triggerId, chainSlug, plug_, - plugConfig.appGateway, overrides_, payload_ ); diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index f095c1f7..ed79fead 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -249,25 +249,26 @@ contract WatcherPrecompile is RequestHandler { for (uint256 i = 0; i < params_.length; i++) { if (appGatewayCalled[params_[i].triggerId]) revert AppGatewayAlreadyCalled(); + + address appGateway = _decodeAppGateway(params_[i].triggerId); if ( !watcherPrecompileConfig__.isValidPlug( - params_[i].appGateway, + appGateway, params_[i].chainSlug, params_[i].plug ) ) revert InvalidCallerTriggered(); - appGatewayCaller = params_[i].appGateway; + appGatewayCaller = appGateway; appGatewayCalled[params_[i].triggerId] = true; - (bool success, ) = address(params_[i].appGateway).call(params_[i].payload); + (bool success, ) = address(appGateway).call(params_[i].payload); if (!success) revert CallFailed(); emit CalledAppGateway( params_[i].triggerId, params_[i].chainSlug, params_[i].plug, - params_[i].appGateway, params_[i].params, params_[i].payload ); @@ -285,4 +286,8 @@ contract WatcherPrecompile is RequestHandler { function getRequestParams(uint40 requestCount) external view returns (RequestParams memory) { return requestParams[requestCount]; } + + function _decodeAppGateway(bytes32 triggerId_) internal pure returns (address) { + return address(uint160(uint256(triggerId_) >> 64)); + } } diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index d01b8193..4c6e3f1d 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -14,7 +14,6 @@ contract TriggerTest is DeliveryHelperTest { bytes32 triggerId, uint32 chainSlug, address plug, - address appGateway, bytes params, bytes payload ); @@ -74,7 +73,6 @@ contract TriggerTest is DeliveryHelperTest { triggerId, arbChainSlug, address(counter), - address(gateway), bytes(""), payload ); diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index 9b9cb0f5..f5697a2b 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -99,14 +99,7 @@ contract MockSocket is ISocket { PlugConfig memory plugConfig = _plugConfigs[msg.sender]; // creates a unique ID for the message triggerId = _encodeTriggerId(plugConfig.appGateway); - emit AppGatewayCallRequested( - triggerId, - chainSlug, - msg.sender, - plugConfig.appGateway, - params, - payload - ); + emit AppGatewayCallRequested(triggerId, chainSlug, msg.sender, params, payload); } /** diff --git a/test/mock/MockWatcherPrecompile.sol b/test/mock/MockWatcherPrecompile.sol index 5993ac59..212a8684 100644 --- a/test/mock/MockWatcherPrecompile.sol +++ b/test/mock/MockWatcherPrecompile.sol @@ -32,7 +32,6 @@ contract MockWatcherPrecompile { bytes32 triggerId, uint32 chainSlug, address plug, - address appGateway, bytes32 params, bytes payload ); @@ -162,7 +161,6 @@ contract MockWatcherPrecompile { params_[i].triggerId, params_[i].chainSlug, params_[i].plug, - params_[i].appGateway, params_[i].params, params_[i].payload ); From 02401c8d080ac2fcb58aec25aa5843572ee2f354 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 7 Apr 2025 14:10:56 +0530 Subject: [PATCH 011/108] feat: emit event on trigger call failure --- contracts/interfaces/IWatcherPrecompile.sol | 2 ++ .../watcherPrecompile/WatcherPrecompile.sol | 20 ++++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 2d397683..8acf4142 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -17,6 +17,8 @@ interface IWatcherPrecompile { bytes payload ); + event AppGatewayCallFailed(bytes32 triggerId); + /// @notice Emitted when a new query is requested event QueryRequested(PayloadParams params); diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index ed79fead..e349a63d 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -263,15 +263,17 @@ contract WatcherPrecompile is RequestHandler { appGatewayCalled[params_[i].triggerId] = true; (bool success, ) = address(appGateway).call(params_[i].payload); - if (!success) revert CallFailed(); - - emit CalledAppGateway( - params_[i].triggerId, - params_[i].chainSlug, - params_[i].plug, - params_[i].params, - params_[i].payload - ); + if (!success) { + emit AppGatewayCallFailed(params_[i].triggerId); + } else { + emit CalledAppGateway( + params_[i].triggerId, + params_[i].chainSlug, + params_[i].plug, + params_[i].params, + params_[i].payload + ); + } } appGatewayCaller = address(0); From 69cac40fabb158e6abd38d9f87ce1be6635cea82 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 7 Apr 2025 14:25:56 +0530 Subject: [PATCH 012/108] feat: remove extra params from event --- contracts/interfaces/IWatcherPrecompile.sol | 8 +------- contracts/protocol/socket/Socket.sol | 8 +------- .../watcherPrecompile/WatcherPrecompile.sol | 8 +------- test/Inbox.t.sol | 8 +------- test/apps/app-gateways/counter/Counter.sol | 2 +- test/mock/MockWatcherPrecompile.sol | 16 ++-------------- 6 files changed, 7 insertions(+), 43 deletions(-) diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 8acf4142..54a1b25f 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -9,13 +9,7 @@ import {IWatcherPrecompileConfig} from "./IWatcherPrecompileConfig.sol"; /// @notice Interface for the Watcher Precompile system that handles payload verification and execution /// @dev Defines core functionality for payload processing and promise resolution interface IWatcherPrecompile { - event CalledAppGateway( - bytes32 triggerId, - uint32 chainSlug, - address plug, - bytes32 params, - bytes payload - ); + event CalledAppGateway(bytes32 triggerId); event AppGatewayCallFailed(bytes32 triggerId); diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index a9d1c472..b614808f 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -131,13 +131,7 @@ contract Socket is SocketUtils { // creates a unique ID for the message triggerId = _encodeTriggerId(plugConfig.appGateway); - emit AppGatewayCallRequested( - triggerId, - chainSlug, - plug_, - overrides_, - payload_ - ); + emit AppGatewayCallRequested(triggerId, chainSlug, plug_, overrides_, payload_); } /// @notice Fallback function that forwards all calls to Socket's callAppGateway diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index e349a63d..f7dc8789 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -266,13 +266,7 @@ contract WatcherPrecompile is RequestHandler { if (!success) { emit AppGatewayCallFailed(params_[i].triggerId); } else { - emit CalledAppGateway( - params_[i].triggerId, - params_[i].chainSlug, - params_[i].plug, - params_[i].params, - params_[i].payload - ); + emit CalledAppGateway(params_[i].triggerId); } } diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index 4c6e3f1d..6907325a 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -69,13 +69,7 @@ contract TriggerTest is DeliveryHelperTest { ); vm.expectEmit(true, true, true, true); - emit AppGatewayCallRequested( - triggerId, - arbChainSlug, - address(counter), - bytes(""), - payload - ); + emit AppGatewayCallRequested(triggerId, arbChainSlug, address(counter), bytes(""), payload); counter.increaseOnGateway(incrementValue); TriggerParams[] memory params = new TriggerParams[](1); diff --git a/test/apps/app-gateways/counter/Counter.sol b/test/apps/app-gateways/counter/Counter.sol index 94311326..b3bec2cb 100644 --- a/test/apps/app-gateways/counter/Counter.sol +++ b/test/apps/app-gateways/counter/Counter.sol @@ -22,7 +22,7 @@ contract Counter is Ownable, PlugBase { } function increaseOnGateway(uint256 value_) external returns (bytes32) { - // can set trigger params here: _setOverrides(params_); + // can set overrides here: _setOverrides(params_); return ICounterAppGateway(address(socket__)).increase(value_); } } diff --git a/test/mock/MockWatcherPrecompile.sol b/test/mock/MockWatcherPrecompile.sol index 212a8684..7d886e6c 100644 --- a/test/mock/MockWatcherPrecompile.sol +++ b/test/mock/MockWatcherPrecompile.sol @@ -28,13 +28,7 @@ contract MockWatcherPrecompile { error InvalidChainSlug(); error InvalidTransmitter(); - event CalledAppGateway( - bytes32 triggerId, - uint32 chainSlug, - address plug, - bytes32 params, - bytes payload - ); + event CalledAppGateway(bytes32 triggerId); /// @notice Emitted when a new query is requested /// @param chainSlug The identifier of the destination chain @@ -157,13 +151,7 @@ contract MockWatcherPrecompile { function callAppGateways(TriggerParams[] calldata params_) external { for (uint256 i = 0; i < params_.length; i++) { - emit CalledAppGateway( - params_[i].triggerId, - params_[i].chainSlug, - params_[i].plug, - params_[i].params, - params_[i].payload - ); + emit CalledAppGateway(params_[i].triggerId); } } From 6b1256141c7777dfebf1443f446eda555df60713 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 7 Apr 2025 15:03:41 +0530 Subject: [PATCH 013/108] fix: non payable fallback --- contracts/protocol/socket/Socket.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index b614808f..4f19540e 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -136,11 +136,8 @@ contract Socket is SocketUtils { /// @notice Fallback function that forwards all calls to Socket's callAppGateway /// @dev The calldata is passed as-is to the gateways - fallback(bytes calldata) external payable returns (bytes memory) { + fallback(bytes calldata) external returns (bytes memory) { bytes memory overrides = IPlug(msg.sender).overrides(); return abi.encode(_callAppGateway(msg.sender, overrides, msg.data)); } - - /// @notice Receive function to accept ETH payments - receive() external payable {} } From 4bc91b4ba41873aec2b2b9a79386e7ed53294b9a Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 7 Apr 2025 15:15:35 +0530 Subject: [PATCH 014/108] fix: rename --- contracts/interfaces/ISocket.sol | 4 ++-- test/Inbox.t.sol | 2 +- test/mock/MockSocket.sol | 6 ++---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/contracts/interfaces/ISocket.sol b/contracts/interfaces/ISocket.sol index 68b57fdc..42713610 100644 --- a/contracts/interfaces/ISocket.sol +++ b/contracts/interfaces/ISocket.sol @@ -37,14 +37,14 @@ interface ISocket { * @param triggerId call id * @param chainSlug local chain slug * @param plug local plug address - * @param params params, for specifying details like fee pool chain, fee pool token and max fees if required + * @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 plug, - bytes params, + bytes overrides, bytes payload ); diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index 6907325a..6da8f8dc 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -14,7 +14,7 @@ contract TriggerTest is DeliveryHelperTest { bytes32 triggerId, uint32 chainSlug, address plug, - bytes params, + bytes overrides, bytes payload ); diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index f5697a2b..1776c491 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -89,17 +89,15 @@ contract MockSocket is ISocket { /** * @notice To send message to a connected remote chain. Should only be called by a plug. - * @param payload bytes to be delivered to the Plug on the siblingChainSlug_ - * @param params a 32 bytes param to add details for execution, for eg: fees to be paid for execution */ function callAppGateway( bytes calldata payload, - bytes calldata params + bytes calldata overrides ) external returns (bytes32 triggerId) { PlugConfig memory plugConfig = _plugConfigs[msg.sender]; // creates a unique ID for the message triggerId = _encodeTriggerId(plugConfig.appGateway); - emit AppGatewayCallRequested(triggerId, chainSlug, msg.sender, params, payload); + emit AppGatewayCallRequested(triggerId, chainSlug, msg.sender, overrides, payload); } /** From 5ff4ec9b162e58a5c68f28a9fcf05425f2171635 Mon Sep 17 00:00:00 2001 From: Akash Date: Tue, 8 Apr 2025 14:52:56 +0530 Subject: [PATCH 015/108] chore: cleanup switchboard --- .../socket/switchboard/SwitchboardBase.sol | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/contracts/protocol/socket/switchboard/SwitchboardBase.sol b/contracts/protocol/socket/switchboard/SwitchboardBase.sol index e628bb20..7b78aae8 100644 --- a/contracts/protocol/socket/switchboard/SwitchboardBase.sol +++ b/contracts/protocol/socket/switchboard/SwitchboardBase.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.21; import "../../../interfaces/ISwitchboard.sol"; import "../../../interfaces/ISocket.sol"; import "../../utils/AccessControl.sol"; -import {RESCUE_ROLE} from "../../utils/common/AccessRoles.sol"; +import {RESCUE_ROLE, WATCHER_ROLE} from "../../utils/common/AccessRoles.sol"; import "../../utils/RescueFundsLib.sol"; import {ECDSA} from "solady/utils/ECDSA.sol"; @@ -13,20 +13,7 @@ abstract contract SwitchboardBase is ISwitchboard, AccessControl { // chain slug of deployed chain uint32 public immutable chainSlug; - - // incrementing nonce for each signer - // watcher => nextNonce - mapping(address => uint256) public nextNonce; - - // destinationChainSlug => initialPacketCount - packets with packetCount after this will be accepted at the switchboard. - // This is to prevent attacks with sending payloads for chain slugs before the switchboard is registered for them. - mapping(uint32 => uint256) public initialPacketCount; - - // Error hit when a signature with unexpected nonce is received - error InvalidNonce(); - - bytes32 constant WATCHER_ROLE = keccak256("WATCHER_ROLE"); - + /** * @dev Constructor of SwitchboardBase * @param chainSlug_ Chain slug of deployment chain From 20142b1927817f500b283a2f89f66c3750b2987f Mon Sep 17 00:00:00 2001 From: Akash Date: Tue, 8 Apr 2025 14:53:09 +0530 Subject: [PATCH 016/108] feat: add version to digest --- contracts/protocol/socket/SocketUtils.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index 32f5cfe1..a8a4fa49 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -65,13 +65,14 @@ abstract contract SocketUtils is SocketConfig { return keccak256( abi.encode( + version, transmitter_, payloadId_, executeParams_.deadline, executeParams_.callType, executeParams_.writeFinality, executeParams_.gasLimit, - msg.value, + executeParams_.msgValue, executeParams_.readAt, executeParams_.payload, executeParams_.target, From 0c590a9c44e15ec08681194a12975c5cf4b2660a Mon Sep 17 00:00:00 2001 From: Akash Date: Tue, 8 Apr 2025 14:53:37 +0530 Subject: [PATCH 017/108] fix: retry failed payloads, switchboard check --- contracts/protocol/socket/Socket.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 4f19540e..3ca809eb 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -33,7 +33,7 @@ contract Socket is SocketUtils { error LowGasLimit(); error InvalidSlug(); error DeadlinePassed(); - + error InsufficientMsgValue(); constructor( uint32 chainSlug_, address owner_, @@ -51,6 +51,7 @@ contract Socket is SocketUtils { PlugConfig memory plugConfig = _plugConfigs[executeParams_.target]; if (plugConfig.appGateway == address(0)) revert PlugDisconnected(); + if (msg.value < executeParams_.msgValue) revert InsufficientMsgValue(); bytes32 payloadId = _createPayloadId(plugConfig.switchboard, executeParams_); _validateExecutionStatus(payloadId); @@ -73,6 +74,8 @@ contract Socket is SocketUtils { ////////////////// INTERNAL FUNCS ////////////////////// //////////////////////////////////////////////////////// function _verify(bytes32 digest_, bytes32 payloadId_, address switchboard_) internal view { + if (isValidSwitchboard[switchboard_] != SwitchboardStatus.REGISTERED) + revert InvalidSwitchboard(); // NOTE: is the the first un-trusted call in the system, another one is Plug.call if (!ISwitchboard(switchboard_).allowPacket(digest_, payloadId_)) revert VerificationFailed(); @@ -106,7 +109,7 @@ contract Socket is SocketUtils { } function _validateExecutionStatus(bytes32 payloadId_) internal { - if (payloadExecuted[payloadId_] != ExecutionStatus.NotExecuted) + if (payloadExecuted[payloadId_] == ExecutionStatus.Executed) revert PayloadAlreadyExecuted(payloadExecuted[payloadId_]); payloadExecuted[payloadId_] = ExecutionStatus.Executed; } From 71460480339d371cfc419c385fda2d0afd095a2c Mon Sep 17 00:00:00 2001 From: Akash Date: Tue, 8 Apr 2025 14:57:46 +0530 Subject: [PATCH 018/108] fix: deadline check for on-chain reverts --- contracts/interfaces/IWatcherPrecompile.sol | 2 +- contracts/protocol/watcherPrecompile/WatcherPrecompile.sol | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 54a1b25f..86763681 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -75,7 +75,7 @@ interface IWatcherPrecompile { error RequestCancelled(); error AlreadyStarted(); error InvalidLevelNumber(); - + error DeadlineNotPassedForOnChainRevert(); /// @notice Calculates the digest hash of payload parameters /// @param params_ The payload parameters /// @return digest The calculated digest diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index f7dc8789..29dfff0b 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -212,6 +212,8 @@ contract WatcherPrecompile is RequestHandler { ); PayloadParams storage payloadParams = payloads[payloadId_]; + if (payloadParams.deadline > block.timestamp) revert DeadlineNotPassedForOnChainRevert(); + RequestParams storage currentRequestParams = requestParams[ payloadParams.dump.getRequestCount() ]; From 92f1f62998eb6f0ca2bbbdf27d743f667a65f9c6 Mon Sep 17 00:00:00 2001 From: Akash Date: Tue, 8 Apr 2025 15:40:04 +0530 Subject: [PATCH 019/108] fix: tests, added value in overrides --- contracts/base/AppGatewayBase.sol | 10 ++++++++-- contracts/interfaces/IAppGateway.sol | 2 +- contracts/protocol/Forwarder.sol | 3 ++- contracts/protocol/socket/Socket.sol | 2 +- contracts/protocol/socket/SocketUtils.sol | 4 ++-- contracts/protocol/utils/common/Structs.sol | 3 +++ .../watcherPrecompile/WatcherPrecompileCore.sol | 6 +++++- test/SetupTest.t.sol | 2 ++ 8 files changed, 24 insertions(+), 8 deletions(-) diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index b0dd03fb..b16f8b32 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -143,7 +143,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin target: address(0), appGateway: address(this), gasLimit: overrideParams.gasLimit, - value: 0, + value: overrideParams.value, readAt: overrideParams.readAt, payload: creationCodeWithArgs[contractId_], initCallData: initCallData_ @@ -207,6 +207,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin overrideParams.isReadCall = Read.OFF; overrideParams.isParallelCall = Parallel.OFF; overrideParams.gasLimit = 0; + overrideParams.value = 0; overrideParams.readAt = 0; overrideParams.writeFinality = WriteFinality.LOW; } @@ -269,6 +270,10 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin overrideParams.gasLimit = gasLimit_; } + function _setMsgValue(uint256 value_) internal { + overrideParams.value = value_; + } + /// @notice Sets fees overrides /// @param fees_ The fees configuration function _setOverrides(Fees memory fees_) internal { @@ -278,7 +283,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin function getOverrideParams() public view - returns (Read, Parallel, WriteFinality, uint256, uint256, bytes32) + returns (Read, Parallel, WriteFinality, uint256, uint256, uint256, bytes32) { return ( overrideParams.isReadCall, @@ -286,6 +291,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin overrideParams.writeFinality, overrideParams.readAt, overrideParams.gasLimit, + overrideParams.value, sbType ); } diff --git a/contracts/interfaces/IAppGateway.sol b/contracts/interfaces/IAppGateway.sol index 21200d0e..bdda2198 100644 --- a/contracts/interfaces/IAppGateway.sol +++ b/contracts/interfaces/IAppGateway.sol @@ -9,7 +9,7 @@ interface IAppGateway { function getOverrideParams() external view - returns (Read, Parallel, WriteFinality, uint256, uint256, bytes32); + returns (Read, Parallel, WriteFinality, uint256, uint256, uint256, bytes32); function onRequestComplete(uint40 requestCount_, bytes calldata onCompleteData_) external; diff --git a/contracts/protocol/Forwarder.sol b/contracts/protocol/Forwarder.sol index d9231e1a..cb611373 100644 --- a/contracts/protocol/Forwarder.sol +++ b/contracts/protocol/Forwarder.sol @@ -96,6 +96,7 @@ contract Forwarder is ForwarderStorage, Initializable, AddressResolverUtil { WriteFinality writeFinality, uint256 readAt, uint256 gasLimit, + uint256 value, bytes32 sbType ) = IAppGateway(msg.sender).getOverrideParams(); address switchboard = watcherPrecompileConfig().switchboards(chainSlug, sbType); @@ -113,7 +114,7 @@ contract Forwarder is ForwarderStorage, Initializable, AddressResolverUtil { target: onChainAddress, appGateway: msg.sender, gasLimit: gasLimit, - value: 0, + value: value, readAt: readAt, payload: msg.data, initCallData: bytes("") diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 3ca809eb..ba900449 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -51,7 +51,7 @@ contract Socket is SocketUtils { PlugConfig memory plugConfig = _plugConfigs[executeParams_.target]; if (plugConfig.appGateway == address(0)) revert PlugDisconnected(); - if (msg.value < executeParams_.msgValue) revert InsufficientMsgValue(); + if (msg.value < executeParams_.value) revert InsufficientMsgValue(); bytes32 payloadId = _createPayloadId(plugConfig.switchboard, executeParams_); _validateExecutionStatus(payloadId); diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index a8a4fa49..ce97e8a2 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -65,14 +65,14 @@ abstract contract SocketUtils is SocketConfig { return keccak256( abi.encode( - version, + address(this), transmitter_, payloadId_, executeParams_.deadline, executeParams_.callType, executeParams_.writeFinality, executeParams_.gasLimit, - executeParams_.msgValue, + executeParams_.value, executeParams_.readAt, executeParams_.payload, executeParams_.target, diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 91091792..02ed6140 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -119,6 +119,7 @@ struct OverrideParams { Parallel isParallelCall; WriteFinality writeFinality; uint256 gasLimit; + uint256 value; uint256 readAt; } @@ -131,6 +132,7 @@ struct Fees { // digest: struct DigestParams { + address socket; address transmitter; bytes32 payloadId; uint256 deadline; @@ -227,6 +229,7 @@ struct ExecuteParams { CallType callType; WriteFinality writeFinality; uint256 gasLimit; + uint256 value; uint256 readAt; bytes payload; address target; diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol index 44f583ab..2ee056f7 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol @@ -49,9 +49,10 @@ abstract contract WatcherPrecompileCore is PayloadParams memory params_, address transmitter_ ) internal returns (bytes32 digest) { + uint32 chainSlug = params_.dump.getChainSlug(); // Verify that the app gateway is properly configured for this chain and target watcherPrecompileConfig__.verifyConnections( - params_.dump.getChainSlug(), + chainSlug, params_.target, params_.appGateway, params_.switchboard @@ -69,6 +70,7 @@ abstract contract WatcherPrecompileCore is // Construct parameters for digest calculation DigestParams memory digestParams_ = DigestParams( + watcherPrecompileConfig__.sockets(chainSlug), transmitter_, params_.payloadId, deadline, @@ -116,6 +118,7 @@ abstract contract WatcherPrecompileCore is function getDigest(DigestParams memory params_) public pure returns (bytes32 digest) { digest = keccak256( abi.encode( + params_.socket, params_.transmitter, params_.payloadId, params_.deadline, @@ -149,6 +152,7 @@ abstract contract WatcherPrecompileCore is for (uint40 i = 0; i < previousPayloads.length; i++) { PayloadParams memory p = payloads[previousPayloads[i].payloadId]; DigestParams memory digestParams = DigestParams( + watcherPrecompileConfig__.sockets(p.dump.getChainSlug()), p.finalizedTransmitter, p.payloadId, p.deadline, diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 32112c65..55a7a7bd 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -292,6 +292,7 @@ contract SetupTest is Test { ) internal view returns (bytes memory, bytes32) { SocketContracts memory socketConfig = getSocketConfig(params_.dump.getChainSlug()); DigestParams memory digestParams_ = DigestParams( + address(socketConfig.socket), transmitterEOA, params_.payloadId, params_.deadline, @@ -349,6 +350,7 @@ contract SetupTest is Test { callType: payloadParams.dump.getCallType(), writeFinality: payloadParams.dump.getWriteFinality(), gasLimit: payloadParams.gasLimit, + value: payloadParams.value, readAt: payloadParams.readAt, payload: payloadParams.payload, target: payloadParams.target, From 93f08318aaa30d21d0159d16f3dffdc9ab51f8ab Mon Sep 17 00:00:00 2001 From: Akash Date: Tue, 8 Apr 2025 17:52:10 +0530 Subject: [PATCH 020/108] fix: excessively safe call --- contracts/protocol/AsyncPromise.sol | 21 ++- .../payload-delivery/ContractFactoryPlug.sol | 11 +- contracts/protocol/socket/Socket.sol | 15 +- contracts/protocol/socket/SocketConfig.sol | 5 + .../protocol/utils/ExcessivelySafeCall.sol | 133 ++++++++++++++++++ .../watcherPrecompile/WatcherPrecompile.sol | 18 ++- 6 files changed, 189 insertions(+), 14 deletions(-) create mode 100644 contracts/protocol/utils/ExcessivelySafeCall.sol diff --git a/contracts/protocol/AsyncPromise.sol b/contracts/protocol/AsyncPromise.sol index ff81e4b7..e528752c 100644 --- a/contracts/protocol/AsyncPromise.sol +++ b/contracts/protocol/AsyncPromise.sol @@ -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 "./utils/ExcessivelySafeCall.sol"; abstract contract AsyncPromiseStorage is IPromise { // slots [0-49] reserved for gap @@ -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 ExcessivelySafeCall 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 @@ -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.excessivelySafeCall(gasleft(), 0, 0, combinedCalldata); if (success) return success; _handleRevert(requestCount_, payloadId_, AsyncPromiseState.CALLBACK_REVERTING); @@ -115,9 +119,16 @@ 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_) + bytes memory combinedCalldata = abi.encodeWithSelector( + IAppGateway.handleRevert.selector, + requestCount_, + payloadId_ + ); + (bool success, ) = localInvoker.excessivelySafeCall( + gasleft(), + 0, + 0, // setting max_copy_bytes to 0 as not using returnData right now + combinedCalldata ); if (!success) revert PromiseRevertFailed(); } diff --git a/contracts/protocol/payload-delivery/ContractFactoryPlug.sol b/contracts/protocol/payload-delivery/ContractFactoryPlug.sol index 6e137162..a637248b 100644 --- a/contracts/protocol/payload-delivery/ContractFactoryPlug.sol +++ b/contracts/protocol/payload-delivery/ContractFactoryPlug.sol @@ -7,10 +7,14 @@ import "../utils/RescueFundsLib.sol"; import {NotSocket} from "../utils/common/Errors.sol"; import "../../base/PlugBase.sol"; import "../../interfaces/IContractFactoryPlug.sol"; +import "../utils/ExcessivelySafeCall.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 ExcessivelySafeCall for address; + event Deployed(address addr, bytes32 salt, bytes returnData); /// @notice Error thrown if it failed to deploy the create2 contract @@ -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.excessivelySafeCall( + gasleft(), + 0, + MAX_COPY_BYTES, + initCallData_ + ); if (!success) { // Additional error logging diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index ba900449..db72f321 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -2,9 +2,10 @@ pragma solidity ^0.8.21; import "./SocketUtils.sol"; - +import "../utils/ExcessivelySafeCall.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 @@ -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 ExcessivelySafeCall for address; + //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// //////////////////////////////////////////////////////// @@ -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.excessivelySafeCall( + executeParams_.gasLimit, + msg.value, + maxCopyBytes, + executeParams_.payload + ); if (!success) { payloadExecuted[payloadId_] = ExecutionStatus.Reverted; diff --git a/contracts/protocol/socket/SocketConfig.sol b/contracts/protocol/socket/SocketConfig.sol index 346a6da9..f7234c4b 100644 --- a/contracts/protocol/socket/SocketConfig.sol +++ b/contracts/protocol/socket/SocketConfig.sol @@ -20,6 +20,7 @@ abstract contract SocketConfig is ISocket, AccessControl { // plug => (appGateway, switchboard__) mapping(address => PlugConfig) internal _plugConfigs; + uint16 public maxCopyBytes = 256; // Error triggered when a connection is invalid error InvalidConnection(); error InvalidSwitchboard(); @@ -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 diff --git a/contracts/protocol/utils/ExcessivelySafeCall.sol b/contracts/protocol/utils/ExcessivelySafeCall.sol new file mode 100644 index 00000000..6669891e --- /dev/null +++ b/contracts/protocol/utils/ExcessivelySafeCall.sol @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity 0.8.22; + +library ExcessivelySafeCall { + uint constant LOW_28_MASK = 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + + /// @notice Use when you _really_ really _really_ don't trust the called + /// contract. This prevents the called contract from causing reversion of + /// the caller in as many ways as we can. + /// @dev The main difference between this and a solidity low-level call is + /// that we limit the number of bytes that the callee can cause to be + /// copied to caller memory. This prevents stupid things like malicious + /// contracts returning 10,000,000 bytes causing a local OOG when copying + /// to memory. + /// @param _target The address to call + /// @param _gas The amount of gas to forward to the remote contract + /// @param _maxCopy The maximum number of bytes of returndata to copy + /// to memory. + /// @param _calldata The data to send to the remote contract + /// @return success and returndata, as `.call()`. Returndata is capped to + /// `_maxCopy` bytes. + function excessivelySafeCall( + address _target, + uint _gas, + uint256 _value, + uint16 _maxCopy, + bytes memory _calldata + ) internal returns (bool, bytes memory) { + // set up for assembly call + uint _toCopy; + bool _success; + bytes memory _returnData = new bytes(_maxCopy); + // dispatch message to recipient + // by assembly calling "handle" function + // we call via assembly to avoid memcopying a very large returndata + // returned by a malicious contract + assembly { + _success := call( + _gas, // gas + _target, // recipient + _value, // ether value + add(_calldata, 0x20), // inloc + mload(_calldata), // inlen + 0, // outloc + 0 // outlen + ) + // limit our copy to 256 bytes + _toCopy := returndatasize() + if gt(_toCopy, _maxCopy) { + _toCopy := _maxCopy + } + // Store the length of the copied bytes + mstore(_returnData, _toCopy) + // copy the bytes from returndata[0:_toCopy] + returndatacopy(add(_returnData, 0x20), 0, _toCopy) + } + return (_success, _returnData); + } + + /// @notice Use when you _really_ really _really_ don't trust the called + /// contract. This prevents the called contract from causing reversion of + /// the caller in as many ways as we can. + /// @dev The main difference between this and a solidity low-level call is + /// that we limit the number of bytes that the callee can cause to be + /// copied to caller memory. This prevents stupid things like malicious + /// contracts returning 10,000,000 bytes causing a local OOG when copying + /// to memory. + /// @param _target The address to call + /// @param _gas The amount of gas to forward to the remote contract + /// @param _maxCopy The maximum number of bytes of returndata to copy + /// to memory. + /// @param _calldata The data to send to the remote contract + /// @return success and returndata, as `.call()`. Returndata is capped to + /// `_maxCopy` bytes. + function excessivelySafeStaticCall( + address _target, + uint _gas, + uint16 _maxCopy, + bytes memory _calldata + ) internal view returns (bool, bytes memory) { + // set up for assembly call + uint _toCopy; + bool _success; + bytes memory _returnData = new bytes(_maxCopy); + // dispatch message to recipient + // by assembly calling "handle" function + // we call via assembly to avoid memcopying a very large returndata + // returned by a malicious contract + assembly { + _success := staticcall( + _gas, // gas + _target, // recipient + add(_calldata, 0x20), // inloc + mload(_calldata), // inlen + 0, // outloc + 0 // outlen + ) + // limit our copy to 256 bytes + _toCopy := returndatasize() + if gt(_toCopy, _maxCopy) { + _toCopy := _maxCopy + } + // Store the length of the copied bytes + mstore(_returnData, _toCopy) + // copy the bytes from returndata[0:_toCopy] + returndatacopy(add(_returnData, 0x20), 0, _toCopy) + } + return (_success, _returnData); + } + + /** + * @notice Swaps function selectors in encoded contract calls + * @dev Allows reuse of encoded calldata for functions with identical + * argument types but different names. It simply swaps out the first 4 bytes + * for the new selector. This function modifies memory in place, and should + * only be used with caution. + * @param _newSelector The new 4-byte selector + * @param _buf The encoded contract args + */ + function swapSelector(bytes4 _newSelector, bytes memory _buf) internal pure { + require(_buf.length >= 4); + uint _mask = LOW_28_MASK; + assembly { + // load the first word of + let _word := mload(add(_buf, 0x20)) + // mask out the top 4 bytes + // /x + _word := and(_word, _mask) + _word := or(_newSelector, _word) + mstore(add(_buf, 0x20), _word) + } + } +} diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index 29dfff0b..7cda2797 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -2,12 +2,14 @@ pragma solidity ^0.8.21; import "./RequestHandler.sol"; +import "../utils/ExcessivelySafeCall.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 ExcessivelySafeCall for address; constructor() { _disableInitializers(); // disable for implementation } @@ -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, ) = address(timeoutRequest_.target).excessivelySafeCall( + gasleft(), + 0, + 0, // setting max_copy_bytes to 0 as not using returnData right now + timeoutRequest_.payload + ); if (!success) revert CallFailed(); timeoutRequest_.isResolved = true; @@ -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, ) = address(appGateway).excessivelySafeCall( + gasleft(), + 0, + 0, // setting max_copy_bytes to 0 as not using returnData right now + params_[i].payload + ); if (!success) { emit AppGatewayCallFailed(params_[i].triggerId); } else { From c437faed8fe465d5b425104f01c1d1cd58b7141f Mon Sep 17 00:00:00 2001 From: Akash Date: Tue, 8 Apr 2025 17:53:02 +0530 Subject: [PATCH 021/108] fix: payload limits, msgValue limits --- .../app-gateway/DeliveryHelperStorage.sol | 3 ++- .../app-gateway/DeliveryUtils.sol | 19 +++++++++++++++++++ .../app-gateway/RequestQueue.sol | 8 ++++++-- contracts/protocol/utils/common/Constants.sol | 3 +++ 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol index d6daf1f4..de3c471c 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol @@ -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; } diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol index 6532aae7..caa890f8 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol @@ -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); @@ -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]; + } + } } diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index de8e7f36..54daf50f 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -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 @@ -43,6 +43,8 @@ abstract contract RequestQueue is DeliveryUtils { bytes memory onCompleteData_ ) internal returns (uint40 requestCount) { if (queuePayloadParams.length == 0) return 0; + if (queuePayloadParams.length > REQUEST_PAYLOAD_COUNT_LIMIT) + revert RequestPayloadCountLimitExceeded(); if (!IFeesManager(addressResolver__.feesManager()).isFeesEnough(appGateway_, fees_)) revert InsufficientFees(); @@ -144,10 +146,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_, diff --git a/contracts/protocol/utils/common/Constants.sol b/contracts/protocol/utils/common/Constants.sol index 082dcf62..88907514 100644 --- a/contracts/protocol/utils/common/Constants.sol +++ b/contracts/protocol/utils/common/Constants.sol @@ -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 = 256; From 0856242863c6ad2adc5733130bf6483851846e75 Mon Sep 17 00:00:00 2001 From: Akash Date: Tue, 8 Apr 2025 21:26:04 +0530 Subject: [PATCH 022/108] fix: comments --- contracts/protocol/AsyncPromise.sol | 17 +++++------------ .../app-gateway/RequestQueue.sol | 5 ++--- contracts/protocol/socket/SocketConfig.sol | 2 +- contracts/protocol/utils/common/Constants.sol | 2 +- .../watcherPrecompile/WatcherPrecompile.sol | 4 ++-- 5 files changed, 11 insertions(+), 19 deletions(-) diff --git a/contracts/protocol/AsyncPromise.sol b/contracts/protocol/AsyncPromise.sol index e528752c..6d33ccb1 100644 --- a/contracts/protocol/AsyncPromise.sol +++ b/contracts/protocol/AsyncPromise.sol @@ -119,18 +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_; - bytes memory combinedCalldata = abi.encodeWithSelector( - IAppGateway.handleRevert.selector, - requestCount_, - payloadId_ - ); - (bool success, ) = localInvoker.excessivelySafeCall( - gasleft(), - 0, - 0, // setting max_copy_bytes to 0 as not using returnData right now - combinedCalldata - ); - 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. diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index 54daf50f..5338e2bc 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -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_); } @@ -43,9 +45,6 @@ abstract contract RequestQueue is DeliveryUtils { bytes memory onCompleteData_ ) internal returns (uint40 requestCount) { if (queuePayloadParams.length == 0) return 0; - if (queuePayloadParams.length > REQUEST_PAYLOAD_COUNT_LIMIT) - revert RequestPayloadCountLimitExceeded(); - if (!IFeesManager(addressResolver__.feesManager()).isFeesEnough(appGateway_, fees_)) revert InsufficientFees(); diff --git a/contracts/protocol/socket/SocketConfig.sol b/contracts/protocol/socket/SocketConfig.sol index f7234c4b..09aaf19f 100644 --- a/contracts/protocol/socket/SocketConfig.sol +++ b/contracts/protocol/socket/SocketConfig.sol @@ -20,7 +20,7 @@ abstract contract SocketConfig is ISocket, AccessControl { // plug => (appGateway, switchboard__) mapping(address => PlugConfig) internal _plugConfigs; - uint16 public maxCopyBytes = 256; + uint16 public maxCopyBytes = 2048; // 2KB // Error triggered when a connection is invalid error InvalidConnection(); error InvalidSwitchboard(); diff --git a/contracts/protocol/utils/common/Constants.sol b/contracts/protocol/utils/common/Constants.sol index 88907514..7790eed7 100644 --- a/contracts/protocol/utils/common/Constants.sol +++ b/contracts/protocol/utils/common/Constants.sol @@ -19,4 +19,4 @@ 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 = 256; +uint16 constant MAX_COPY_BYTES = 2048; // 2KB diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index 7cda2797..08096756 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -65,7 +65,7 @@ contract WatcherPrecompile is RequestHandler { if (timeoutRequest_.isResolved) revert TimeoutAlreadyResolved(); if (block.timestamp < timeoutRequest_.executeAt) revert ResolvingTimeoutTooEarly(); - (bool success, ) = address(timeoutRequest_.target).excessivelySafeCall( + (bool success, ) = timeoutRequest_.target.excessivelySafeCall( gasleft(), 0, 0, // setting max_copy_bytes to 0 as not using returnData right now @@ -271,7 +271,7 @@ contract WatcherPrecompile is RequestHandler { appGatewayCaller = appGateway; appGatewayCalled[params_[i].triggerId] = true; - (bool success, ) = address(appGateway).excessivelySafeCall( + (bool success, ) = appGateway.excessivelySafeCall( gasleft(), 0, 0, // setting max_copy_bytes to 0 as not using returnData right now From e0899f76d6fe77c33b4ac8469eff6f3c50453fd3 Mon Sep 17 00:00:00 2001 From: Akash Date: Tue, 8 Apr 2025 21:44:35 +0530 Subject: [PATCH 023/108] fix: added LibCall from solady --- contracts/protocol/AsyncPromise.sol | 6 +- .../payload-delivery/ContractFactoryPlug.sol | 8 +- contracts/protocol/socket/Socket.sol | 8 +- .../protocol/utils/ExcessivelySafeCall.sol | 133 ------------------ .../watcherPrecompile/WatcherPrecompile.sol | 12 +- 5 files changed, 17 insertions(+), 150 deletions(-) delete mode 100644 contracts/protocol/utils/ExcessivelySafeCall.sol diff --git a/contracts/protocol/AsyncPromise.sol b/contracts/protocol/AsyncPromise.sol index 6d33ccb1..1758c0de 100644 --- a/contracts/protocol/AsyncPromise.sol +++ b/contracts/protocol/AsyncPromise.sol @@ -7,7 +7,7 @@ import {IAppGateway} from "../interfaces/IAppGateway.sol"; import {Initializable} from "solady/utils/Initializable.sol"; import {AsyncPromiseState} from "./utils/common/Structs.sol"; import {MAX_COPY_BYTES} from "./utils/common/Constants.sol"; -import "./utils/ExcessivelySafeCall.sol"; +import {LibCall} from "solady/utils/LibCall.sol"; abstract contract AsyncPromiseStorage is IPromise { // slots [0-49] reserved for gap @@ -46,7 +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 ExcessivelySafeCall for address; + 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 @@ -96,7 +96,7 @@ contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil abi.encode(callbackData, returnData_) ); // setting max_copy_bytes to 0 as not using returnData right now - (success, ) = localInvoker.excessivelySafeCall(gasleft(), 0, 0, combinedCalldata); + (success, , ) = localInvoker.tryCall(0, gasleft(), 0, combinedCalldata); if (success) return success; _handleRevert(requestCount_, payloadId_, AsyncPromiseState.CALLBACK_REVERTING); diff --git a/contracts/protocol/payload-delivery/ContractFactoryPlug.sol b/contracts/protocol/payload-delivery/ContractFactoryPlug.sol index a637248b..54e4fdac 100644 --- a/contracts/protocol/payload-delivery/ContractFactoryPlug.sol +++ b/contracts/protocol/payload-delivery/ContractFactoryPlug.sol @@ -7,13 +7,13 @@ import "../utils/RescueFundsLib.sol"; import {NotSocket} from "../utils/common/Errors.sol"; import "../../base/PlugBase.sol"; import "../../interfaces/IContractFactoryPlug.sol"; -import "../utils/ExcessivelySafeCall.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 ExcessivelySafeCall for address; + using LibCall for address; event Deployed(address addr, bytes32 salt, bytes returnData); @@ -52,9 +52,9 @@ contract ContractFactoryPlug is PlugBase, AccessControl, IContractFactoryPlug { bytes memory returnData; if (initCallData_.length > 0) { // Capture more detailed error information - (bool success, bytes memory returnData_) = addr.excessivelySafeCall( - gasleft(), + (bool success, , bytes memory returnData_) = addr.tryCall( 0, + gasleft(), MAX_COPY_BYTES, initCallData_ ); diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index db72f321..e223b28d 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.21; import "./SocketUtils.sol"; -import "../utils/ExcessivelySafeCall.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"; @@ -16,7 +16,7 @@ import {MAX_COPY_BYTES} from "../utils/common/Constants.sol"; * It also includes functions for payload execution and verification */ contract Socket is SocketUtils { - using ExcessivelySafeCall for address; + using LibCall for address; //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// @@ -96,9 +96,9 @@ contract Socket is SocketUtils { if (gasleft() < executeParams_.gasLimit) revert LowGasLimit(); // NOTE: external un-trusted call - (bool success, bytes memory returnData) = executeParams_.target.excessivelySafeCall( - executeParams_.gasLimit, + (bool success, , bytes memory returnData) = executeParams_.target.tryCall( msg.value, + executeParams_.gasLimit, maxCopyBytes, executeParams_.payload ); diff --git a/contracts/protocol/utils/ExcessivelySafeCall.sol b/contracts/protocol/utils/ExcessivelySafeCall.sol deleted file mode 100644 index 6669891e..00000000 --- a/contracts/protocol/utils/ExcessivelySafeCall.sol +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-License-Identifier: MIT OR Apache-2.0 -pragma solidity 0.8.22; - -library ExcessivelySafeCall { - uint constant LOW_28_MASK = 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - - /// @notice Use when you _really_ really _really_ don't trust the called - /// contract. This prevents the called contract from causing reversion of - /// the caller in as many ways as we can. - /// @dev The main difference between this and a solidity low-level call is - /// that we limit the number of bytes that the callee can cause to be - /// copied to caller memory. This prevents stupid things like malicious - /// contracts returning 10,000,000 bytes causing a local OOG when copying - /// to memory. - /// @param _target The address to call - /// @param _gas The amount of gas to forward to the remote contract - /// @param _maxCopy The maximum number of bytes of returndata to copy - /// to memory. - /// @param _calldata The data to send to the remote contract - /// @return success and returndata, as `.call()`. Returndata is capped to - /// `_maxCopy` bytes. - function excessivelySafeCall( - address _target, - uint _gas, - uint256 _value, - uint16 _maxCopy, - bytes memory _calldata - ) internal returns (bool, bytes memory) { - // set up for assembly call - uint _toCopy; - bool _success; - bytes memory _returnData = new bytes(_maxCopy); - // dispatch message to recipient - // by assembly calling "handle" function - // we call via assembly to avoid memcopying a very large returndata - // returned by a malicious contract - assembly { - _success := call( - _gas, // gas - _target, // recipient - _value, // ether value - add(_calldata, 0x20), // inloc - mload(_calldata), // inlen - 0, // outloc - 0 // outlen - ) - // limit our copy to 256 bytes - _toCopy := returndatasize() - if gt(_toCopy, _maxCopy) { - _toCopy := _maxCopy - } - // Store the length of the copied bytes - mstore(_returnData, _toCopy) - // copy the bytes from returndata[0:_toCopy] - returndatacopy(add(_returnData, 0x20), 0, _toCopy) - } - return (_success, _returnData); - } - - /// @notice Use when you _really_ really _really_ don't trust the called - /// contract. This prevents the called contract from causing reversion of - /// the caller in as many ways as we can. - /// @dev The main difference between this and a solidity low-level call is - /// that we limit the number of bytes that the callee can cause to be - /// copied to caller memory. This prevents stupid things like malicious - /// contracts returning 10,000,000 bytes causing a local OOG when copying - /// to memory. - /// @param _target The address to call - /// @param _gas The amount of gas to forward to the remote contract - /// @param _maxCopy The maximum number of bytes of returndata to copy - /// to memory. - /// @param _calldata The data to send to the remote contract - /// @return success and returndata, as `.call()`. Returndata is capped to - /// `_maxCopy` bytes. - function excessivelySafeStaticCall( - address _target, - uint _gas, - uint16 _maxCopy, - bytes memory _calldata - ) internal view returns (bool, bytes memory) { - // set up for assembly call - uint _toCopy; - bool _success; - bytes memory _returnData = new bytes(_maxCopy); - // dispatch message to recipient - // by assembly calling "handle" function - // we call via assembly to avoid memcopying a very large returndata - // returned by a malicious contract - assembly { - _success := staticcall( - _gas, // gas - _target, // recipient - add(_calldata, 0x20), // inloc - mload(_calldata), // inlen - 0, // outloc - 0 // outlen - ) - // limit our copy to 256 bytes - _toCopy := returndatasize() - if gt(_toCopy, _maxCopy) { - _toCopy := _maxCopy - } - // Store the length of the copied bytes - mstore(_returnData, _toCopy) - // copy the bytes from returndata[0:_toCopy] - returndatacopy(add(_returnData, 0x20), 0, _toCopy) - } - return (_success, _returnData); - } - - /** - * @notice Swaps function selectors in encoded contract calls - * @dev Allows reuse of encoded calldata for functions with identical - * argument types but different names. It simply swaps out the first 4 bytes - * for the new selector. This function modifies memory in place, and should - * only be used with caution. - * @param _newSelector The new 4-byte selector - * @param _buf The encoded contract args - */ - function swapSelector(bytes4 _newSelector, bytes memory _buf) internal pure { - require(_buf.length >= 4); - uint _mask = LOW_28_MASK; - assembly { - // load the first word of - let _word := mload(add(_buf, 0x20)) - // mask out the top 4 bytes - // /x - _word := and(_word, _mask) - _word := or(_newSelector, _word) - mstore(add(_buf, 0x20), _word) - } - } -} diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index 08096756..a05aaff3 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -2,14 +2,14 @@ pragma solidity ^0.8.21; import "./RequestHandler.sol"; -import "../utils/ExcessivelySafeCall.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 ExcessivelySafeCall for address; + using LibCall for address; constructor() { _disableInitializers(); // disable for implementation } @@ -65,9 +65,9 @@ contract WatcherPrecompile is RequestHandler { if (timeoutRequest_.isResolved) revert TimeoutAlreadyResolved(); if (block.timestamp < timeoutRequest_.executeAt) revert ResolvingTimeoutTooEarly(); - (bool success, ) = timeoutRequest_.target.excessivelySafeCall( - gasleft(), + (bool success, , ) = timeoutRequest_.target.tryCall( 0, + gasleft(), 0, // setting max_copy_bytes to 0 as not using returnData right now timeoutRequest_.payload ); @@ -271,9 +271,9 @@ contract WatcherPrecompile is RequestHandler { appGatewayCaller = appGateway; appGatewayCalled[params_[i].triggerId] = true; - (bool success, ) = appGateway.excessivelySafeCall( - gasleft(), + (bool success, , ) = appGateway.tryCall( 0, + gasleft(), 0, // setting max_copy_bytes to 0 as not using returnData right now params_[i].payload ); From 04473769c286fdcac660c39bc3e95ee7412f29f8 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 11 Apr 2025 16:13:53 +0530 Subject: [PATCH 024/108] fix: async promise --- contracts/interfaces/IPromise.sol | 13 ++++++++++++- contracts/protocol/AsyncPromise.sol | 26 +++++++++++++++----------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/contracts/interfaces/IPromise.sol b/contracts/interfaces/IPromise.sol index 13318ac0..d2f21e8c 100644 --- a/contracts/interfaces/IPromise.sol +++ b/contracts/interfaces/IPromise.sol @@ -1,16 +1,27 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.21; +/// @title IPromise interface IPromise { - function then(bytes4 selector_, bytes memory data_) external returns (address _promise); + /// @notice Sets the callback selector and data for the promise. + /// @param selector_ The function selector for the callback. + /// @param data_ The data to be passed to the callback. + /// @return promise_ The address of the current promise + function then(bytes4 selector_, bytes memory data_) external returns (address promise_); + /// @notice Marks the promise as resolved and executes the callback if set. + /// @dev Only callable by the watcher precompile. + /// @param returnData_ The data returned from the async payload execution. function markResolved( uint40 requestCount_, bytes32 payloadId_, bytes memory returnData_ ) external returns (bool success); + /// @notice Marks the promise as onchain reverting. + /// @dev Only callable by the watcher precompile. function markOnchainRevert(uint40 requestCount_, bytes32 payloadId_) external; + /// @notice Indicates whether the promise has been resolved. function resolved() external view returns (bool); } diff --git a/contracts/protocol/AsyncPromise.sol b/contracts/protocol/AsyncPromise.sol index 3bcd95ec..27950069 100644 --- a/contracts/protocol/AsyncPromise.sol +++ b/contracts/protocol/AsyncPromise.sol @@ -27,7 +27,7 @@ abstract contract AsyncPromiseStorage is IPromise { address public localInvoker; // slot 51 - /// @notice The forwarder address which can call the callback + /// @notice The forwarder address which can set the callback selector and data address public forwarder; // slot 52 @@ -41,7 +41,7 @@ abstract contract AsyncPromiseStorage is IPromise { } /// @title AsyncPromise -/// @notice this contract stores the callback address and data to be executed once the previous call is executed +/// @notice this contract stores the callback selector and data to be executed once the on-chain call is executed /// This promise expires once the callback is executed contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil { /// @notice Error thrown when attempting to resolve an already resolved promise. @@ -57,25 +57,24 @@ contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil _disableInitializers(); // disable for implementation } - /// @notice Initializer to replace constructor for upgradeable contracts - /// @param invoker_ The address of the local invoker. - /// @param forwarder_ The address of the forwarder. - /// @param addressResolver_ The address resolver contract address. + /// @notice Initialize promise states + /// @param invoker_ The address of the local invoker + /// @param forwarder_ The address of the forwarder + /// @param addressResolver_ The address resolver contract address function initialize( address invoker_, address forwarder_, address addressResolver_ ) public initializer { - _setAddressResolver(addressResolver_); localInvoker = invoker_; forwarder = forwarder_; - state = AsyncPromiseState.WAITING_FOR_SET_CALLBACK_SELECTOR; - resolved = false; + + _setAddressResolver(addressResolver_); } /// @notice Marks the promise as resolved and executes the callback if set. - /// @param returnData_ The data returned from the async payload execution. /// @dev Only callable by the watcher precompile. + /// @param returnData_ The data returned from the async payload execution. function markResolved( uint40 requestCount_, bytes32 payloadId_, @@ -88,6 +87,7 @@ contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil // Call callback to app gateway if (callbackSelector == bytes4(0)) return true; + bytes memory combinedCalldata = abi.encodePacked( callbackSelector, abi.encode(callbackData, returnData_) @@ -113,9 +113,10 @@ contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil AsyncPromiseState state_ ) internal { // to update the state in case selector is bytes(0) but reverting onchain - resolved = false; + resolved = true; state = state_; + // call handleRevert on app gateway to inform that the promise or execution is reverting onchain (bool success, ) = localInvoker.call( abi.encodeWithSelector(IAppGateway.handleRevert.selector, requestCount_, payloadId_) ); @@ -130,14 +131,17 @@ contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil bytes4 selector_, bytes memory data_ ) external override returns (address promise_) { + // allows forwarder or local invoker to set the callback selector and data if (msg.sender != forwarder && msg.sender != localInvoker) { revert OnlyForwarderOrLocalInvoker(); } + // if the promise is already set up, revert if (state == AsyncPromiseState.WAITING_FOR_CALLBACK_EXECUTION) { revert PromiseAlreadySetUp(); } + // if the promise is waiting for the callback selector, set it and update the state if (state == AsyncPromiseState.WAITING_FOR_SET_CALLBACK_SELECTOR) { callbackSelector = selector_; callbackData = data_; From f775fe5ea501fc43a7113c7c2e331971658db7a9 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 11 Apr 2025 16:25:28 +0530 Subject: [PATCH 025/108] fix: .then sender and request validation --- contracts/interfaces/IWatcherPrecompile.sol | 2 + contracts/protocol/Forwarder.sol | 47 +++++++++++++-------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 5ce28d64..3a415612 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -166,4 +166,6 @@ interface IWatcherPrecompile { function watcherPrecompileLimits__() external view returns (IWatcherPrecompileLimits); function getRequestParams(uint40 requestCount) external view returns (RequestParams memory); + + function nextRequestCount() external view returns (uint40); } diff --git a/contracts/protocol/Forwarder.sol b/contracts/protocol/Forwarder.sol index 8426bc4d..b18e0933 100644 --- a/contracts/protocol/Forwarder.sol +++ b/contracts/protocol/Forwarder.sol @@ -6,16 +6,18 @@ import "../interfaces/IMiddleware.sol"; import "../interfaces/IAppGateway.sol"; import "../interfaces/IPromise.sol"; import "../interfaces/IForwarder.sol"; - import {AddressResolverUtil} from "./utils/AddressResolverUtil.sol"; +import {AsyncModifierNotUsed} from "./utils/common/Errors.sol"; import "solady/utils/Initializable.sol"; +/// @title Forwarder Storage +/// @notice Storage contract for the Forwarder contract that contains the state variables abstract contract ForwarderStorage is IForwarder { // slots [0-49] reserved for gap uint256[50] _gap_before; // slot 50 - /// @notice chain id + /// @notice chain slug on which the contract is deployed uint32 public chainSlug; /// @notice on-chain address associated with this forwarder address public onChainAddress; @@ -24,25 +26,29 @@ abstract contract ForwarderStorage is IForwarder { /// @notice caches the latest async promise address for the last call address public latestAsyncPromise; - // slots [52-101] reserved for gap + // slot 52 + /// @notice the address of the contract that called the latest async promise + address public latestPromiseCaller; + /// @notice the request count of the latest async promise + uint40 public latestRequestCount; + + // slots [53-102] reserved for gap uint256[50] _gap_after; - // slots 102-153 (51) reserved for addr resolver util + // slots 103-154 (51) reserved for addr resolver util } /// @title Forwarder Contract /// @notice This contract acts as a forwarder for async calls to the on-chain contracts. contract Forwarder is ForwarderStorage, Initializable, AddressResolverUtil { - error AsyncModifierNotUsed(); - constructor() { _disableInitializers(); // disable for implementation } /// @notice Initializer to replace constructor for upgradeable contracts - /// @param chainSlug_ chain id - /// @param onChainAddress_ on-chain address - /// @param addressResolver_ address resolver contract address + /// @param chainSlug_ chain slug on which the contract is deployed + /// @param onChainAddress_ on-chain address associated with this forwarder + /// @param addressResolver_ address resolver contract function initialize( uint32 chainSlug_, address onChainAddress_, @@ -55,11 +61,16 @@ contract Forwarder is ForwarderStorage, Initializable, AddressResolverUtil { /// @notice Stores the callback address and data to be executed once the promise is resolved. /// @dev This function should not be called before the fallback function. + /// @dev It resets the latest async promise address /// @param selector_ The function selector for callback /// @param data_ The data to be passed to callback /// @return promise_ The address of the new promise function then(bytes4 selector_, bytes memory data_) external returns (address promise_) { if (latestAsyncPromise == address(0)) revert("Forwarder: no async promise found"); + if (latestPromiseCaller != msg.sender) revert("Forwarder: promise caller mismatch"); + if (latestRequestCount != watcherPrecompile__().nextRequestCount()) + revert("Forwarder: request count mismatch"); + promise_ = IPromise(latestAsyncPromise).then(selector_, data_); latestAsyncPromise = address(0); } @@ -70,27 +81,29 @@ contract Forwarder is ForwarderStorage, Initializable, AddressResolverUtil { return onChainAddress; } - /// @notice Returns the chain id - /// @return chain id + /// @notice Returns the chain slug on which the contract is deployed. + /// @return chain slug function getChainSlug() external view returns (uint32) { return chainSlug; } /// @notice Fallback function to process the contract calls to onChainAddress - /// @dev It queues the calls in the auction house and deploys the promise contract - fallback() external payable { - // Retrieve the auction house address from the address resolver. + /// @dev It queues the calls in the middleware and deploys the promise contract + fallback() external { if (address(deliveryHelper__()) == address(0)) { revert("Forwarder: deliveryHelper not found"); } + // validates if the async modifier is set bool isAsyncModifierSet = IAppGateway(msg.sender).isAsyncModifierSet(); if (!isAsyncModifierSet) revert AsyncModifierNotUsed(); // Deploy a new async promise contract. latestAsyncPromise = addressResolver__.deployAsyncPromiseContract(msg.sender); + latestPromiseCaller = msg.sender; + latestRequestCount = watcherPrecompile__().nextRequestCount(); - // Determine if the call is a read or write operation. + // fetch the override params ( Read isReadCall, Parallel isParallelCall, @@ -101,7 +114,7 @@ contract Forwarder is ForwarderStorage, Initializable, AddressResolverUtil { ) = IAppGateway(msg.sender).getOverrideParams(); address switchboard = watcherPrecompileConfig().switchboards(chainSlug, sbType); - // Queue the call in the auction house. + // Queue the call in the middleware. deliveryHelper__().queue( QueuePayloadParams({ chainSlug: chainSlug, @@ -121,6 +134,4 @@ contract Forwarder is ForwarderStorage, Initializable, AddressResolverUtil { }) ); } - - receive() external payable {} } From 9d44c652a80d7882beb8b08f4b0e7ec4f3f1e436 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 11 Apr 2025 16:35:48 +0530 Subject: [PATCH 026/108] fix: forwarder --- contracts/interfaces/IForwarder.sol | 7 ++++++- contracts/protocol/Forwarder.sol | 8 ++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/contracts/interfaces/IForwarder.sol b/contracts/interfaces/IForwarder.sol index 9372cc5f..25072ea3 100644 --- a/contracts/interfaces/IForwarder.sol +++ b/contracts/interfaces/IForwarder.sol @@ -1,9 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.21; +/// @title IForwarder +/// @notice Interface for the Forwarder contract that allows contracts to call async promises interface IForwarder { - // View functions + /// @notice Returns the on-chain address of the contract being referenced + /// @return The on-chain address function getOnChainAddress() external view returns (address); + /// @notice Returns the chain slug of the on chain contract + /// @return The chain slug function getChainSlug() external view returns (uint32); } diff --git a/contracts/protocol/Forwarder.sol b/contracts/protocol/Forwarder.sol index b18e0933..c2e44f65 100644 --- a/contracts/protocol/Forwarder.sol +++ b/contracts/protocol/Forwarder.sol @@ -71,8 +71,8 @@ contract Forwarder is ForwarderStorage, Initializable, AddressResolverUtil { if (latestRequestCount != watcherPrecompile__().nextRequestCount()) revert("Forwarder: request count mismatch"); - promise_ = IPromise(latestAsyncPromise).then(selector_, data_); latestAsyncPromise = address(0); + promise_ = IPromise(latestAsyncPromise).then(selector_, data_); } /// @notice Returns the on-chain address associated with this forwarder. @@ -100,10 +100,12 @@ contract Forwarder is ForwarderStorage, Initializable, AddressResolverUtil { // Deploy a new async promise contract. latestAsyncPromise = addressResolver__.deployAsyncPromiseContract(msg.sender); + + // set the latest promise caller and request count for validating if the future .then call is valid latestPromiseCaller = msg.sender; latestRequestCount = watcherPrecompile__().nextRequestCount(); - // fetch the override params + // fetch the override params from app gateway ( Read isReadCall, Parallel isParallelCall, @@ -112,6 +114,8 @@ contract Forwarder is ForwarderStorage, Initializable, AddressResolverUtil { uint256 gasLimit, bytes32 sbType ) = IAppGateway(msg.sender).getOverrideParams(); + + // get the switchboard address from the watcher precompile config address switchboard = watcherPrecompileConfig().switchboards(chainSlug, sbType); // Queue the call in the middleware. From b48336652584e5a0e9aa87f8fb4814d315aa428e Mon Sep 17 00:00:00 2001 From: Akash Date: Fri, 11 Apr 2025 17:02:40 +0530 Subject: [PATCH 027/108] fix: open transmitter option, appGatewayId --- contracts/base/PlugBase.sol | 17 ++++++------- contracts/interfaces/IContractFactoryPlug.sol | 2 +- contracts/interfaces/IPlug.sol | 2 +- contracts/interfaces/ISocket.sol | 10 ++++---- .../interfaces/IWatcherPrecompileConfig.sol | 2 +- .../payload-delivery/ContractFactoryPlug.sol | 8 +++---- .../protocol/payload-delivery/FeesPlug.sol | 4 ++-- contracts/protocol/socket/Socket.sol | 15 ++++++------ contracts/protocol/socket/SocketConfig.sol | 12 +++++----- contracts/protocol/socket/SocketUtils.sol | 12 ++++------ .../socket/switchboard/SwitchboardBase.sol | 2 +- contracts/protocol/utils/common/Structs.sol | 8 +++---- .../watcherPrecompile/WatcherPrecompile.sol | 2 +- .../WatcherPrecompileConfig.sol | 23 +++++++++--------- .../WatcherPrecompileCore.sol | 14 ++++++----- .../WatcherPrecompileUtils.sol | 12 ++++++++++ test/DeliveryHelper.t.sol | 24 +++++++++++-------- test/Inbox.t.sol | 6 ++--- test/SetupTest.t.sol | 10 +++++++- test/mock/MockSocket.sol | 16 ++++++------- test/mock/MockWatcherPrecompile.sol | 4 ++-- 21 files changed, 114 insertions(+), 91 deletions(-) create mode 100644 contracts/protocol/watcherPrecompile/WatcherPrecompileUtils.sol diff --git a/contracts/base/PlugBase.sol b/contracts/base/PlugBase.sol index 64dff39d..e86daa9f 100644 --- a/contracts/base/PlugBase.sol +++ b/contracts/base/PlugBase.sol @@ -9,7 +9,7 @@ import {NotSocket} from "../protocol/utils/common/Errors.sol"; /// @notice Abstract contract for plugs abstract contract PlugBase is IPlug { ISocket public socket__; - address public appGateway; + bytes32 public appGatewayId; uint256 public isSocketInitialized; bytes public overrides; @@ -31,19 +31,20 @@ abstract contract PlugBase is IPlug { } /// @notice Connects the plug to the app gateway and switchboard - /// @param appGateway_ The app gateway address + /// @param appGatewayId_ The app gateway id + /// @param socket_ The socket address /// @param switchboard_ The switchboard address - function _connectSocket(address appGateway_, address socket_, address switchboard_) internal { + function _connectSocket(bytes32 appGatewayId_, address socket_, address switchboard_) internal { _setSocket(socket_); - appGateway = appGateway_; + appGatewayId = appGatewayId_; - socket__.connect(appGateway_, switchboard_); + socket__.connect(appGatewayId_, switchboard_); } /// @notice Disconnects the plug from the socket function _disconnectSocket() internal { (, address switchboard) = socket__.getPlugConfig(address(this)); - socket__.connect(address(0), switchboard); + socket__.connect(bytes32(0), switchboard); emit ConnectorPlugDisconnected(); } @@ -60,10 +61,10 @@ abstract contract PlugBase is IPlug { } function initSocket( - address appGateway_, + bytes32 appGatewayId_, address socket_, address switchboard_ ) external virtual socketInitializer { - _connectSocket(appGateway_, socket_, switchboard_); + _connectSocket(appGatewayId_, socket_, switchboard_); } } diff --git a/contracts/interfaces/IContractFactoryPlug.sol b/contracts/interfaces/IContractFactoryPlug.sol index a3b5bac0..89de42e3 100644 --- a/contracts/interfaces/IContractFactoryPlug.sol +++ b/contracts/interfaces/IContractFactoryPlug.sol @@ -14,7 +14,7 @@ interface IContractFactoryPlug { function deployContract( IsPlug isPlug_, bytes32 salt_, - address appGateway_, + bytes32 appGatewayId_, address switchboard_, bytes memory creationCode_, bytes memory initCallData_ diff --git a/contracts/interfaces/IPlug.sol b/contracts/interfaces/IPlug.sol index 552ae8f0..c7cad4aa 100644 --- a/contracts/interfaces/IPlug.sol +++ b/contracts/interfaces/IPlug.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.21; * @notice Interface for a plug contract that executes the payload received from a source chain. */ interface IPlug { - function initSocket(address appGateway_, address socket_, address switchboard_) external; + function initSocket(bytes32 appGatewayId_, address socket_, address switchboard_) external; function overrides() external view returns (bytes memory); } diff --git a/contracts/interfaces/ISocket.sol b/contracts/interfaces/ISocket.sol index 42713610..dc924f87 100644 --- a/contracts/interfaces/ISocket.sol +++ b/contracts/interfaces/ISocket.sol @@ -27,10 +27,10 @@ interface ISocket { /** * @notice emits the config set by a plug for a remoteChainSlug * @param plug address of plug on current chain - * @param appGateway address of plug on sibling chain + * @param appGatewayId address of plug on sibling chain * @param switchboard outbound switchboard (select from registered options) */ - event PlugConnected(address plug, address appGateway, address switchboard); + event PlugConnected(address plug, bytes32 appGatewayId, address switchboard); /** * @notice emits the message details when a new message arrives at outbound @@ -58,10 +58,10 @@ interface ISocket { /** * @notice sets the config specific to the plug - * @param appGateway_ address of plug present at sibling chain + * @param appGatewayId_ address of plug present at sibling chain * @param switchboard_ the address of switchboard to use for executing payloads */ - function connect(address appGateway_, address switchboard_) external; + function connect(bytes32 appGatewayId_, address switchboard_) external; function registerSwitchboard() external; @@ -71,5 +71,5 @@ interface ISocket { */ function getPlugConfig( address plugAddress_ - ) external view returns (address appGateway, address switchboard); + ) external view returns (bytes32 appGatewayId, address switchboard); } diff --git a/contracts/interfaces/IWatcherPrecompileConfig.sol b/contracts/interfaces/IWatcherPrecompileConfig.sol index 85831eca..bf4ccf6f 100644 --- a/contracts/interfaces/IWatcherPrecompileConfig.sol +++ b/contracts/interfaces/IWatcherPrecompileConfig.sol @@ -43,7 +43,7 @@ interface IWatcherPrecompileConfig { function getPlugConfigs( uint32 chainSlug_, address plug_ - ) external view returns (address, address); + ) external view returns (bytes32, address); /// @notice Verifies connections between components function verifyConnections( diff --git a/contracts/protocol/payload-delivery/ContractFactoryPlug.sol b/contracts/protocol/payload-delivery/ContractFactoryPlug.sol index 54e4fdac..94634e8b 100644 --- a/contracts/protocol/payload-delivery/ContractFactoryPlug.sol +++ b/contracts/protocol/payload-delivery/ContractFactoryPlug.sol @@ -29,7 +29,7 @@ contract ContractFactoryPlug is PlugBase, AccessControl, IContractFactoryPlug { function deployContract( IsPlug isPlug_, bytes32 salt_, - address appGateway_, + bytes32 appGatewayId_, address switchboard_, bytes memory creationCode_, bytes memory initCallData_ @@ -47,7 +47,7 @@ contract ContractFactoryPlug is PlugBase, AccessControl, IContractFactoryPlug { } } - if (isPlug_ == IsPlug.YES) IPlug(addr).initSocket(appGateway_, msg.sender, switchboard_); + if (isPlug_ == IsPlug.YES) IPlug(addr).initSocket(appGatewayId_, msg.sender, switchboard_); bytes memory returnData; if (initCallData_.length > 0) { @@ -89,11 +89,11 @@ contract ContractFactoryPlug is PlugBase, AccessControl, IContractFactoryPlug { } function connectSocket( - address appGateway_, + bytes32 appGatewayId_, address socket_, address switchboard_ ) external onlyOwner { - _connectSocket(appGateway_, socket_, switchboard_); + _connectSocket(appGatewayId_, socket_, switchboard_); } /** diff --git a/contracts/protocol/payload-delivery/FeesPlug.sol b/contracts/protocol/payload-delivery/FeesPlug.sol index 77778f05..c73200e2 100644 --- a/contracts/protocol/payload-delivery/FeesPlug.sol +++ b/contracts/protocol/payload-delivery/FeesPlug.sol @@ -103,11 +103,11 @@ contract FeesPlug is PlugBase, AccessControl { } function connectSocket( - address appGateway_, + bytes32 appGatewayId_, address socket_, address switchboard_ ) external onlyOwner { - _connectSocket(appGateway_, socket_, switchboard_); + _connectSocket(appGatewayId_, socket_, switchboard_); } /// @notice Adds a token to the whitelist diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index e223b28d..1506f056 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -52,21 +52,20 @@ contract Socket is SocketUtils { ) external payable returns (bytes memory) { if (executeParams_.deadline < block.timestamp) revert DeadlinePassed(); PlugConfig memory plugConfig = _plugConfigs[executeParams_.target]; - if (plugConfig.appGateway == address(0)) revert PlugDisconnected(); + if (plugConfig.appGatewayId == bytes32(0)) revert PlugDisconnected(); if (msg.value < executeParams_.value) revert InsufficientMsgValue(); bytes32 payloadId = _createPayloadId(plugConfig.switchboard, executeParams_); _validateExecutionStatus(payloadId); - address transmitter = _recoverSigner( - keccak256(abi.encode(address(this), payloadId)), - transmitterSignature_ - ); + address transmitter = transmitterSignature_.length > 0 + ? _recoverSigner(keccak256(abi.encode(address(this), payloadId)), transmitterSignature_) + : address(0); bytes32 digest = _createDigest( transmitter, payloadId, - plugConfig.appGateway, + plugConfig.appGatewayId, executeParams_ ); _verify(digest, payloadId, plugConfig.switchboard); @@ -135,10 +134,10 @@ contract Socket is SocketUtils { PlugConfig memory plugConfig = _plugConfigs[plug_]; // if no sibling plug is found for the given chain slug, revert - if (plugConfig.appGateway == address(0)) revert PlugDisconnected(); + if (plugConfig.appGatewayId == bytes32(0)) revert PlugDisconnected(); // creates a unique ID for the message - triggerId = _encodeTriggerId(plugConfig.appGateway); + triggerId = _encodeTriggerId(plugConfig.appGatewayId); emit AppGatewayCallRequested(triggerId, chainSlug, plug_, overrides_, payload_); } diff --git a/contracts/protocol/socket/SocketConfig.sol b/contracts/protocol/socket/SocketConfig.sol index 09aaf19f..4ccc6a82 100644 --- a/contracts/protocol/socket/SocketConfig.sol +++ b/contracts/protocol/socket/SocketConfig.sol @@ -47,16 +47,16 @@ abstract contract SocketConfig is ISocket, AccessControl { /** * @notice connects Plug to Socket and sets the config for given `siblingChainSlug_` */ - function connect(address appGateway_, address switchboard_) external override { + function connect(bytes32 appGatewayId_, address switchboard_) external override { if (isValidSwitchboard[switchboard_] != SwitchboardStatus.REGISTERED) revert InvalidSwitchboard(); PlugConfig storage _plugConfig = _plugConfigs[msg.sender]; - _plugConfig.appGateway = appGateway_; + _plugConfig.appGatewayId = appGatewayId_; _plugConfig.switchboard = switchboard_; - emit PlugConnected(msg.sender, appGateway_, switchboard_); + emit PlugConnected(msg.sender, appGatewayId_, switchboard_); } function setMaxCopyBytes(uint16 maxCopyBytes_) external onlyRole(GOVERNANCE_ROLE) { @@ -64,13 +64,13 @@ abstract contract SocketConfig is ISocket, AccessControl { } /** - * @notice returns the config for given `plugAddress_` and `siblingChainSlug_` + * @notice returns the config for given `plugAddress_` * @param plugAddress_ address of plug present at current chain */ function getPlugConfig( address plugAddress_ - ) external view returns (address appGateway, address switchboard) { + ) external view returns (bytes32 appGatewayId, address switchboard) { PlugConfig memory _plugConfig = _plugConfigs[plugAddress_]; - return (_plugConfig.appGateway, _plugConfig.switchboard); + return (_plugConfig.appGatewayId, _plugConfig.switchboard); } } diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index ce97e8a2..0d905a3c 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -52,14 +52,14 @@ abstract contract SocketUtils is SocketConfig { * @notice creates the digest for the payload * @param transmitter_ The address of the transmitter * @param payloadId_ The ID of the payload - * @param appGateway_ The address of the app gateway + * @param appGatewayId_ The id of the app gateway * @param executeParams_ The parameters of the payload * @return The packed payload as a bytes32 hash */ function _createDigest( address transmitter_, bytes32 payloadId_, - address appGateway_, + bytes32 appGatewayId_, ExecuteParams memory executeParams_ ) internal view returns (bytes32) { return @@ -76,7 +76,7 @@ abstract contract SocketUtils is SocketConfig { executeParams_.readAt, executeParams_.payload, executeParams_.target, - appGateway_, + appGatewayId_, executeParams_.prevDigestsHash ) ); @@ -116,12 +116,10 @@ abstract contract SocketUtils is SocketConfig { // Packs the local plug, local chain slug, remote chain slug and nonce // triggerCounter++ will take care of call id overflow as well // triggerId(256) = localChainSlug(32) | appGateway_(160) | nonce(64) - function _encodeTriggerId(address appGateway_) internal returns (bytes32) { + function _encodeTriggerId(bytes32 appGatewayId_) internal returns (bytes32) { return bytes32( - (uint256(chainSlug) << 224) | - (uint256(uint160(appGateway_)) << 64) | - triggerCounter++ + (uint256(chainSlug) << 224) | (uint256(appGatewayId_) << 64) | triggerCounter++ ); } diff --git a/contracts/protocol/socket/switchboard/SwitchboardBase.sol b/contracts/protocol/socket/switchboard/SwitchboardBase.sol index 7b78aae8..2f5249a5 100644 --- a/contracts/protocol/socket/switchboard/SwitchboardBase.sol +++ b/contracts/protocol/socket/switchboard/SwitchboardBase.sol @@ -13,7 +13,7 @@ abstract contract SwitchboardBase is ISwitchboard, AccessControl { // chain slug of deployed chain uint32 public immutable chainSlug; - + /** * @dev Constructor of SwitchboardBase * @param chainSlug_ Chain slug of deployment chain diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 02ed6140..a3ffce79 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -66,13 +66,13 @@ struct UpdateLimitParams { } struct AppGatewayConfig { address plug; - address appGateway; + bytes32 appGatewayId; address switchboard; uint32 chainSlug; } // Plug config: struct PlugConfig { - address appGateway; + bytes32 appGatewayId; address switchboard; } //trigger: @@ -80,7 +80,7 @@ struct TriggerParams { bytes32 triggerId; bytes32 params; address plug; - address appGateway; + bytes32 appGatewayId; uint32 chainSlug; bytes payload; } @@ -143,7 +143,7 @@ struct DigestParams { uint256 readAt; bytes payload; address target; - address appGateway; + bytes32 appGatewayId; bytes32 prevDigestsHash; // should be id? hash of hashes } diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index a05aaff3..7983f415 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -259,7 +259,7 @@ contract WatcherPrecompile is RequestHandler { for (uint256 i = 0; i < params_.length; i++) { if (appGatewayCalled[params_[i].triggerId]) revert AppGatewayAlreadyCalled(); - address appGateway = _decodeAppGateway(params_[i].triggerId); + address appGateway = _decodeAppGatewayId(params_[i].appGatewayId); if ( !watcherPrecompileConfig__.isValidPlug( appGateway, diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index 881b3706..e9b30baa 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -5,7 +5,7 @@ import "./WatcherPrecompileLimits.sol"; import {ECDSA} from "solady/utils/ECDSA.sol"; import "solady/utils/Initializable.sol"; import "../../interfaces/IWatcherPrecompileConfig.sol"; - +import "./WatcherPrecompileUtils.sol"; /// @title WatcherPrecompileConfig /// @notice Configuration contract for the Watcher Precompile system /// @dev Handles the mapping between networks, plugs, and app gateways for payload execution @@ -13,7 +13,8 @@ contract WatcherPrecompileConfig is IWatcherPrecompileConfig, Initializable, AccessControl, - AddressResolverUtil + AddressResolverUtil, + WatcherPrecompileUtils { // slot 52: evmxSlug /// @notice The chain slug of the watcher precompile @@ -54,10 +55,10 @@ contract WatcherPrecompileConfig is mapping(address => mapping(uint32 => mapping(address => bool))) public isValidPlug; /// @notice Emitted when a new plug is configured for an app gateway - /// @param appGateway The address of the app gateway + /// @param appGatewayId The id of the app gateway /// @param chainSlug The identifier of the destination network /// @param plug The address of the plug - event PlugAdded(address appGateway, uint32 chainSlug, address plug); + event PlugAdded(bytes32 appGatewayId, uint32 chainSlug, address plug); /// @notice Emitted when a switchboard is set for a network /// @param chainSlug The identifier of the network @@ -114,11 +115,11 @@ contract WatcherPrecompileConfig is for (uint256 i = 0; i < configs_.length; i++) { // Store the plug configuration for this network and plug _plugConfigs[configs_[i].chainSlug][configs_[i].plug] = PlugConfig({ - appGateway: configs_[i].appGateway, + appGatewayId: configs_[i].appGatewayId, switchboard: configs_[i].switchboard }); - emit PlugAdded(configs_[i].appGateway, configs_[i].chainSlug, configs_[i].plug); + emit PlugAdded(configs_[i].appGatewayId, configs_[i].chainSlug, configs_[i].plug); } } @@ -157,14 +158,14 @@ contract WatcherPrecompileConfig is /// @notice Retrieves the configuration for a specific plug on a network /// @param chainSlug_ The identifier of the network /// @param plug_ The address of the plug - /// @return The app gateway address and switchboard address for the plug + /// @return The app gateway id and switchboard address for the plug /// @dev Returns zero addresses if configuration doesn't exist function getPlugConfigs( uint32 chainSlug_, address plug_ - ) public view returns (address, address) { + ) public view returns (bytes32, address) { return ( - _plugConfigs[chainSlug_][plug_].appGateway, + _plugConfigs[chainSlug_][plug_].appGatewayId, _plugConfigs[chainSlug_][plug_].switchboard ); } @@ -179,8 +180,8 @@ contract WatcherPrecompileConfig is // if target is contractFactoryPlug, return if (target_ == contractFactoryPlug[chainSlug_]) return; - (address appGateway, address switchboard) = getPlugConfigs(chainSlug_, target_); - if (appGateway != appGateway_) revert InvalidGateway(); + (bytes32 appGatewayId, address switchboard) = getPlugConfigs(chainSlug_, target_); + if (appGatewayId != _encodeAppGatewayId(appGateway_)) revert InvalidGateway(); if (switchboard != switchboard_) revert InvalidSwitchboard(); } diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol index 2ee056f7..f195217a 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileCore.sol @@ -6,6 +6,7 @@ import {ECDSA} from "solady/utils/ECDSA.sol"; import {AccessControl} from "../utils/AccessControl.sol"; import "solady/utils/Initializable.sol"; import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; +import "./WatcherPrecompileUtils.sol"; /// @title WatcherPrecompile /// @notice Contract that handles payload verification, execution and app configurations @@ -14,7 +15,8 @@ abstract contract WatcherPrecompileCore is WatcherPrecompileStorage, Initializable, AccessControl, - AddressResolverUtil + AddressResolverUtil, + WatcherPrecompileUtils { using DumpDecoder for bytes32; @@ -81,7 +83,7 @@ abstract contract WatcherPrecompileCore is params_.readAt, params_.payload, params_.target, - params_.appGateway, + _encodeAppGatewayId(params_.appGateway), prevDigestsHash ); @@ -129,7 +131,7 @@ abstract contract WatcherPrecompileCore is params_.readAt, params_.payload, params_.target, - params_.appGateway, + params_.appGatewayId, params_.prevDigestsHash ) ); @@ -163,7 +165,7 @@ abstract contract WatcherPrecompileCore is p.readAt, p.payload, p.target, - p.appGateway, + _encodeAppGatewayId(p.appGateway), p.prevDigestsHash ); prevDigestsHash = keccak256(abi.encodePacked(prevDigestsHash, getDigest(digestParams))); @@ -188,11 +190,11 @@ abstract contract WatcherPrecompileCore is // if target is contractFactoryPlug, return if (target_ == watcherPrecompileConfig__.contractFactoryPlug(chainSlug_)) return; - (address appGateway, address switchboard) = watcherPrecompileConfig__.getPlugConfigs( + (bytes32 appGatewayId, address switchboard) = watcherPrecompileConfig__.getPlugConfigs( chainSlug_, target_ ); - if (appGateway != appGateway_) revert InvalidGateway(); + if (appGatewayId != _encodeAppGatewayId(appGateway_)) revert InvalidGateway(); if (switchboard != switchboard_) revert InvalidSwitchboard(); } diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileUtils.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileUtils.sol new file mode 100644 index 00000000..c47786e8 --- /dev/null +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileUtils.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.22; + +abstract contract WatcherPrecompileUtils { + 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_))); + } +} diff --git a/test/DeliveryHelper.t.sol b/test/DeliveryHelper.t.sol index ea346775..eead05aa 100644 --- a/test/DeliveryHelper.t.sol +++ b/test/DeliveryHelper.t.sol @@ -116,23 +116,23 @@ contract DeliveryHelperTest is SetupTest { function connectDeliveryHelper() internal { vm.startPrank(owner); arbConfig.contractFactoryPlug.initSocket( - address(deliveryHelper), + _encodeAppGatewayId(address(deliveryHelper)), address(arbConfig.socket), address(arbConfig.switchboard) ); optConfig.contractFactoryPlug.initSocket( - address(deliveryHelper), + _encodeAppGatewayId(address(deliveryHelper)), address(optConfig.socket), address(optConfig.switchboard) ); arbConfig.feesPlug.initSocket( - address(feesManager), + _encodeAppGatewayId(address(feesManager)), address(arbConfig.socket), address(arbConfig.switchboard) ); optConfig.feesPlug.initSocket( - address(feesManager), + _encodeAppGatewayId(address(feesManager)), address(optConfig.socket), address(optConfig.switchboard) ); @@ -142,25 +142,25 @@ contract DeliveryHelperTest is SetupTest { gateways[0] = AppGatewayConfig({ plug: address(arbConfig.contractFactoryPlug), chainSlug: arbChainSlug, - appGateway: address(deliveryHelper), + appGatewayId: _encodeAppGatewayId(address(deliveryHelper)), switchboard: address(arbConfig.switchboard) }); gateways[1] = AppGatewayConfig({ plug: address(optConfig.contractFactoryPlug), chainSlug: optChainSlug, - appGateway: address(deliveryHelper), + appGatewayId: _encodeAppGatewayId(address(deliveryHelper)), switchboard: address(optConfig.switchboard) }); gateways[2] = AppGatewayConfig({ plug: address(arbConfig.feesPlug), chainSlug: arbChainSlug, - appGateway: address(feesManager), + appGatewayId: _encodeAppGatewayId(address(feesManager)), switchboard: address(arbConfig.switchboard) }); gateways[3] = AppGatewayConfig({ plug: address(optConfig.feesPlug), chainSlug: optChainSlug, - appGateway: address(feesManager), + appGatewayId: _encodeAppGatewayId(address(feesManager)), switchboard: address(optConfig.switchboard) }); @@ -250,7 +250,7 @@ contract DeliveryHelperTest is SetupTest { gateways[i] = AppGatewayConfig({ plug: plug, chainSlug: chainSlug_, - appGateway: address(appGateway_), + appGatewayId: _encodeAppGatewayId(address(appGateway_)), switchboard: address(socketConfig.switchboard) }); } @@ -279,7 +279,11 @@ contract DeliveryHelperTest is SetupTest { function endAuction(uint40 requestCount_) internal { if (auctionEndDelaySeconds == 0) return; - bytes32 timeoutId = _encodeTimeoutId(evmxSlug, address(watcherPrecompile), timeoutIdCounter++); + bytes32 timeoutId = _encodeTimeoutId( + evmxSlug, + address(watcherPrecompile), + timeoutIdCounter++ + ); bytes memory watcherSignature = _createWatcherSignature( address(watcherPrecompile), diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index 6da8f8dc..d1763190 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -31,7 +31,7 @@ contract TriggerTest is DeliveryHelperTest { // Connect the counter to the gateway and socket counter.initSocket( - address(gateway), + _encodeAppGatewayId(address(gateway)), address(arbConfig.socket), address(arbConfig.switchboard) ); @@ -41,7 +41,7 @@ contract TriggerTest is DeliveryHelperTest { gateways[0] = AppGatewayConfig({ plug: address(counter), chainSlug: arbChainSlug, - appGateway: address(gateway), + appGatewayId: _encodeAppGatewayId(address(gateway)), switchboard: address(arbConfig.switchboard) }); @@ -76,7 +76,7 @@ contract TriggerTest is DeliveryHelperTest { params[0] = TriggerParams({ triggerId: triggerId, chainSlug: arbChainSlug, - appGateway: address(gateway), + appGatewayId: _encodeAppGatewayId(address(gateway)), plug: address(counter), payload: payload, params: bytes32(0) diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 55a7a7bd..b752a6c7 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -303,7 +303,7 @@ contract SetupTest is Test { params_.readAt, params_.payload, params_.target, - params_.appGateway, + _encodeAppGatewayId(params_.appGateway), params_.prevDigestsHash ); bytes32 digest = watcherPrecompile.getDigest(digestParams_); @@ -401,4 +401,12 @@ contract SetupTest is Test { mstore(add(sig, 64), sigS) } } + + 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_))); + } } diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index 1776c491..26650c35 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -16,7 +16,7 @@ import "../../contracts/interfaces/ISocket.sol"; contract MockSocket is ISocket { struct PlugConfig { // address of the sibling plug on the remote chain - address appGateway; + bytes32 appGatewayId; // switchboard instance for the plug connection ISwitchboard switchboard__; } @@ -26,12 +26,12 @@ contract MockSocket is ISocket { function getPlugConfig( address plugAddress_ - ) external view returns (address appGateway, address switchboard__) { + ) external view returns (bytes32 appGatewayId, address switchboard__) { PlugConfig memory _plugConfig = _plugConfigs[plugAddress_]; - return (_plugConfig.appGateway, address(_plugConfig.switchboard__)); + return (_plugConfig.appGatewayId, address(_plugConfig.switchboard__)); } - function connect(address appGateway_, address switchboard_) external override {} + function connect(bytes32 appGatewayId_, address switchboard_) external override {} function registerSwitchboard() external override {} @@ -96,7 +96,7 @@ contract MockSocket is ISocket { ) external returns (bytes32 triggerId) { PlugConfig memory plugConfig = _plugConfigs[msg.sender]; // creates a unique ID for the message - triggerId = _encodeTriggerId(plugConfig.appGateway); + triggerId = _encodeTriggerId(plugConfig.appGatewayId); emit AppGatewayCallRequested(triggerId, chainSlug, msg.sender, overrides, payload); } @@ -167,12 +167,10 @@ contract MockSocket is ISocket { // Packs the local plug, local chain slug, remote chain slug and nonce // triggerCounter++ will take care of call id overflow as well // triggerId(256) = localChainSlug(32) | appGateway_(160) | nonce(64) - function _encodeTriggerId(address appGateway_) internal returns (bytes32) { + function _encodeTriggerId(bytes32 appGatewayId_) internal returns (bytes32) { return bytes32( - (uint256(chainSlug) << 224) | - (uint256(uint160(appGateway_)) << 64) | - triggerCounter++ + (uint256(chainSlug) << 224) | (uint256(appGatewayId_) << 64) | triggerCounter++ ); } } diff --git a/test/mock/MockWatcherPrecompile.sol b/test/mock/MockWatcherPrecompile.sol index 7d886e6c..12c4c720 100644 --- a/test/mock/MockWatcherPrecompile.sol +++ b/test/mock/MockWatcherPrecompile.sol @@ -190,9 +190,9 @@ contract MockWatcherPrecompile { function getPlugConfigs( uint32 chainSlug_, address plug_ - ) public view returns (address, address) { + ) public view returns (bytes32, address) { return ( - _plugConfigs[chainSlug_][plug_].appGateway, + _plugConfigs[chainSlug_][plug_].appGatewayId, _plugConfigs[chainSlug_][plug_].switchboard ); } From 11a4d813c738f5a3fa9eb301ecf6156f3733c94d Mon Sep 17 00:00:00 2001 From: Akash Date: Fri, 11 Apr 2025 17:26:15 +0530 Subject: [PATCH 028/108] feat: socket fees support --- contracts/interfaces/ISocket.sol | 4 +- contracts/interfaces/ISocketFeeManager.sol | 34 +++++++++ contracts/protocol/socket/Socket.sol | 20 +++-- contracts/protocol/socket/SocketBatcher.sol | 12 ++- contracts/protocol/socket/SocketConfig.sol | 7 +- .../protocol/socket/SocketFeeManager.sol | 73 +++++++++++++++++++ contracts/protocol/utils/common/Structs.sol | 6 ++ test/mock/MockSocket.sol | 2 +- 8 files changed, 147 insertions(+), 11 deletions(-) create mode 100644 contracts/interfaces/ISocketFeeManager.sol create mode 100644 contracts/protocol/socket/SocketFeeManager.sol diff --git a/contracts/interfaces/ISocket.sol b/contracts/interfaces/ISocket.sol index dc924f87..51c5288a 100644 --- a/contracts/interfaces/ISocket.sol +++ b/contracts/interfaces/ISocket.sol @@ -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 @@ -53,7 +53,7 @@ interface ISocket { */ function execute( ExecuteParams memory executeParams_, - bytes memory transmitterSignature_ + TransmissionParams memory transmissionParams_ ) external payable returns (bytes memory); /** diff --git a/contracts/interfaces/ISocketFeeManager.sol b/contracts/interfaces/ISocketFeeManager.sol new file mode 100644 index 00000000..1d7958c0 --- /dev/null +++ b/contracts/interfaces/ISocketFeeManager.sol @@ -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); +} diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 1506f056..2b5eb08a 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -48,20 +48,30 @@ contract Socket is SocketUtils { */ function execute( ExecuteParams memory executeParams_, - bytes memory transmitterSignature_ + TransmissionParams memory transmissionParams_ ) external payable returns (bytes memory) { if (executeParams_.deadline < block.timestamp) revert DeadlinePassed(); PlugConfig memory plugConfig = _plugConfigs[executeParams_.target]; if (plugConfig.appGatewayId == bytes32(0)) revert PlugDisconnected(); - if (msg.value < executeParams_.value) revert InsufficientMsgValue(); + if (msg.value < executeParams_.value + transmissionParams_.socketFees) + revert InsufficientMsgValue(); bytes32 payloadId = _createPayloadId(plugConfig.switchboard, executeParams_); _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); + if (address(socketFeeManager) != address(0)) { + socketFeeManager.payAndCheckFees{value: transmissionParams_.socketFees}( + executeParams_, + transmissionParams_ + ); + } bytes32 digest = _createDigest( transmitter, payloadId, @@ -96,7 +106,7 @@ contract Socket is SocketUtils { // NOTE: external un-trusted call (bool success, , bytes memory returnData) = executeParams_.target.tryCall( - msg.value, + executeParams_.value, executeParams_.gasLimit, maxCopyBytes, executeParams_.payload diff --git a/contracts/protocol/socket/SocketBatcher.sol b/contracts/protocol/socket/SocketBatcher.sol index 49eb1966..187f012a 100644 --- a/contracts/protocol/socket/SocketBatcher.sol +++ b/contracts/protocol/socket/SocketBatcher.sol @@ -5,7 +5,7 @@ import "solady/auth/Ownable.sol"; import "../../interfaces/ISocket.sol"; import "../../interfaces/ISwitchboard.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"; /** @@ -33,7 +33,15 @@ contract SocketBatcher is ISocketBatcher, Ownable { bytes calldata transmitterSignature_ ) 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: "" + }) + ); } function rescueFunds(address token_, address to_, uint256 amount_) external onlyOwner { diff --git a/contracts/protocol/socket/SocketConfig.sol b/contracts/protocol/socket/SocketConfig.sol index 4ccc6a82..fc70ed79 100644 --- a/contracts/protocol/socket/SocketConfig.sol +++ b/contracts/protocol/socket/SocketConfig.sol @@ -6,7 +6,7 @@ import "../../interfaces/ISwitchboard.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"; /** * @title SocketConfig * @notice An abstract contract for configuring socket connections for plugs between different chains, @@ -14,6 +14,7 @@ import {PlugConfig, SwitchboardStatus, ExecutionStatus} from "../utils/common/St * @dev This contract is meant to be inherited by other contracts that require socket configuration functionality */ abstract contract SocketConfig is ISocket, AccessControl { + ISocketFeeManager public socketFeeManager; // Error triggered when a switchboard already exists mapping(address => SwitchboardStatus) public isValidSwitchboard; @@ -44,6 +45,10 @@ abstract contract SocketConfig is ISocket, AccessControl { emit SwitchboardDisabled(msg.sender); } + function setSocketFeeManager(address socketFeeManager_) external onlyRole(GOVERNANCE_ROLE) { + socketFeeManager = ISocketFeeManager(socketFeeManager_); + } + /** * @notice connects Plug to Socket and sets the config for given `siblingChainSlug_` */ diff --git a/contracts/protocol/socket/SocketFeeManager.sol b/contracts/protocol/socket/SocketFeeManager.sol new file mode 100644 index 00000000..ab94ccb2 --- /dev/null +++ b/contracts/protocol/socket/SocketFeeManager.sol @@ -0,0 +1,73 @@ +// 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(); + + /** + * @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_) { + socketFees = socketFees_; + _grantRole(GOVERNANCE_ROLE, owner_); + _grantRole(RESCUE_ROLE, owner_); + } + + /** + * @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 { + 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) { + 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_); + } +} diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index a3ffce79..9c45e437 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -240,6 +240,12 @@ struct ExecuteParams { address switchboard; } +struct TransmissionParams { + bytes transmitterSignature; + uint256 socketFees; + bytes extraData; +} + struct PayloadIdParams { uint40 requestCount; uint40 batchCount; diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index 26650c35..bfb39f15 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -105,7 +105,7 @@ contract MockSocket is ISocket { */ function execute( ExecuteParams memory executeParams_, - bytes memory transmitterSignature_ + TransmissionParams memory transmissionParams_ ) external payable override returns (bytes memory) { // execute payload // return From 7a3e68becc5888edcb6a0bd0e44341205a463e23 Mon Sep 17 00:00:00 2001 From: Akash Date: Fri, 11 Apr 2025 17:30:37 +0530 Subject: [PATCH 029/108] fix: added events for socketFeesManager --- contracts/protocol/socket/SocketConfig.sol | 5 +++++ contracts/protocol/socket/SocketFeeManager.sol | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/contracts/protocol/socket/SocketConfig.sol b/contracts/protocol/socket/SocketConfig.sol index fc70ed79..d51eecab 100644 --- a/contracts/protocol/socket/SocketConfig.sol +++ b/contracts/protocol/socket/SocketConfig.sol @@ -14,7 +14,9 @@ import "../../interfaces/ISocketFeeManager.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; + // Error triggered when a switchboard already exists mapping(address => SwitchboardStatus) public isValidSwitchboard; @@ -31,6 +33,7 @@ abstract contract SocketConfig is ISocket, AccessControl { // Event triggered when a new switchboard is added event SwitchboardAdded(address switchboard); event SwitchboardDisabled(address switchboard); + event SocketFeeManagerUpdated(address oldSocketFeeManager, address newSocketFeeManager); function registerSwitchboard() external { if (isValidSwitchboard[msg.sender] != SwitchboardStatus.NOT_REGISTERED) @@ -45,7 +48,9 @@ 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_); } diff --git a/contracts/protocol/socket/SocketFeeManager.sol b/contracts/protocol/socket/SocketFeeManager.sol index ab94ccb2..29672e3c 100644 --- a/contracts/protocol/socket/SocketFeeManager.sol +++ b/contracts/protocol/socket/SocketFeeManager.sol @@ -18,12 +18,15 @@ contract SocketFeeManager is ISocketFeeManager, AccessControl { 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_); @@ -54,6 +57,7 @@ contract SocketFeeManager is ISocketFeeManager, AccessControl { * @param socketFees_ New socket fees amount */ function setSocketFees(uint256 socketFees_) external onlyRole(GOVERNANCE_ROLE) { + emit SocketFeesUpdated(socketFees, socketFees_); socketFees = socketFees_; } From 1f68edbfd5345cabf3744360c416d9ece8a348ad Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 11 Apr 2025 17:33:53 +0530 Subject: [PATCH 030/108] fix: addr resolver --- contracts/interfaces/IAddressResolver.sol | 59 +++++++++++++---------- contracts/protocol/AddressResolver.sol | 26 +++++++--- 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/contracts/interfaces/IAddressResolver.sol b/contracts/interfaces/IAddressResolver.sol index 7a34fcbd..bb401e57 100644 --- a/contracts/interfaces/IAddressResolver.sol +++ b/contracts/interfaces/IAddressResolver.sol @@ -12,24 +12,45 @@ interface IAddressResolver { /// @param newAddress The new address of the contract event AddressSet(bytes32 indexed name, address oldAddress, address newAddress); + /// @notice Emitted when a new plug is added to the resolver + /// @param appGateway The address of the app gateway + /// @param chainSlug The chain slug + /// @param plug The address of the plug + event PlugAdded(address appGateway, uint32 chainSlug, address plug); + + /// @notice Emitted when a new forwarder is deployed + /// @param newForwarder The address of the new forwarder + /// @param salt The salt used to deploy the forwarder + event ForwarderDeployed(address newForwarder, bytes32 salt); + + /// @notice Emitted when a new async promise is deployed + /// @param newAsyncPromise The address of the new async promise + /// @param salt The salt used to deploy the async promise + event AsyncPromiseDeployed(address newAsyncPromise, bytes32 salt); + + /// @notice Emitted when an implementation is updated + /// @param contractName The name of the contract + /// @param newImplementation The new implementation address + event ImplementationUpdated(string contractName, address newImplementation); + /// @notice Gets the address of the delivery helper contract - /// @return IMiddleware The delivery helper interface - /// @dev Returns interface pointing to zero address if not configured + /// @return The delivery helper contract address + /// @dev Returns zero address if not configured function deliveryHelper() external view returns (address); /// @notice Gets the address of the fees manager contract - /// @return IFeesManager The fees manager interface - /// @dev Returns interface pointing to zero address if not configured + /// @return The fees manager contract address + /// @dev Returns zero address if not configured function feesManager() external view returns (address); /// @notice Gets the address of the default auction manager contract - /// @return IAuctionManager The auction manager interface - /// @dev Returns interface pointing to zero address if not configured + /// @return The auction manager contract address + /// @dev Returns zero address if not configured function defaultAuctionManager() external view returns (address); - /// @notice Gets the watcher precompile contract interface - /// @return IWatcherPrecompile The watcher precompile interface - /// @dev Returns interface pointing to zero address if not configured + /// @notice Gets the watcher precompile contract instance + /// @return The watcher precompile contract instance + /// @dev Returns instance with zero address if not configured function watcherPrecompile__() external view returns (IWatcherPrecompile); /// @notice Maps contract addresses to their corresponding gateway addresses @@ -41,29 +62,17 @@ interface IAddressResolver { /// @return Array of async promise contract addresses function getPromises() external view returns (address[] memory); - // State-changing functions - /// @notice Sets the auction house contract address - /// @param deliveryHelper_ The new delivery helper contract address - /// @dev Only callable by contract owner - function setDeliveryHelper(address deliveryHelper_) external; - - /// @notice Sets the watcher precompile contract address - /// @param watcherPrecompile_ The new watcher precompile contract address - /// @dev Only callable by contract owner - function setWatcherPrecompile(address watcherPrecompile_) external; - /// @notice Maps a contract address to its gateway /// @param contractAddress_ The contract address to map /// @dev Creates bidirectional mapping between contract and gateway function setContractsToGateways(address contractAddress_) external; - /// @notice Clears the list of deployed async promise contracts - /// @dev Only callable by contract owner + /// @notice Clears the list of deployed async promise contracts array function clearPromises() external; - /// @notice Deploys a new forwarder contract if not already deployed - /// @param chainContractAddress_ The contract address on the destination chain - /// @param chainSlug_ The identifier of the destination chain + /// @notice Deploys or returns the address of a new forwarder contract if not already deployed + /// @param chainContractAddress_ The contract address on the `chainSlug_` + /// @param chainSlug_ The identifier of the chain /// @return The address of the newly deployed forwarder contract function getOrDeployForwarderContract( address appGateway_, diff --git a/contracts/protocol/AddressResolver.sol b/contracts/protocol/AddressResolver.sol index f2347f5d..f37da613 100644 --- a/contracts/protocol/AddressResolver.sol +++ b/contracts/protocol/AddressResolver.sol @@ -58,16 +58,13 @@ contract AddressResolver is AddressResolverStorage, Initializable, Ownable { /// @notice Error thrown if AppGateway contract was already set by a different address error InvalidAppGateway(address contractAddress_); - event PlugAdded(address appGateway, uint32 chainSlug, address plug); - event ForwarderDeployed(address newForwarder, bytes32 salt); - event AsyncPromiseDeployed(address newAsyncPromise, bytes32 salt); - event ImplementationUpdated(string contractName, address newImplementation); - constructor() { _disableInitializers(); // disable for implementation } /// @notice Initializer to replace constructor for upgradeable contracts + /// @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_) public reinitializer(1) { version = 1; @@ -82,6 +79,7 @@ contract AddressResolver is AddressResolverStorage, Initializable, Ownable { } /// @notice Gets or deploys a Forwarder proxy contract + /// @dev it checks if the forwarder is already deployed, if yes, it returns the address /// @param chainContractAddress_ The address of the chain contract /// @param chainSlug_ The chain slug /// @return newForwarder The address of the deployed Forwarder proxy contract @@ -92,18 +90,25 @@ contract AddressResolver is AddressResolverStorage, Initializable, Ownable { ) public returns (address newForwarder) { // predict address address forwarderAddress = getForwarderAddress(chainContractAddress_, chainSlug_); + // check if addr has code, if yes, return if (forwarderAddress.code.length > 0) { return forwarderAddress; } + // creates init data and salt (bytes32 salt, bytes memory initData) = _createForwarderParams( chainContractAddress_, chainSlug_ ); + // deploys the proxy newForwarder = _deployProxy(salt, address(forwarderBeacon), initData); + + // sets the config _setConfig(appGateway_, newForwarder); + + // emits the event emit ForwarderDeployed(newForwarder, salt); } @@ -118,6 +123,8 @@ contract AddressResolver is AddressResolverStorage, Initializable, Ownable { chainContractAddress_, address(this) ); + + // creates salt with constructor args salt = keccak256(constructorArgs); } @@ -125,6 +132,8 @@ contract AddressResolver is AddressResolverStorage, Initializable, Ownable { address invoker_ ) internal view returns (bytes32 salt, bytes memory initData) { bytes memory constructorArgs = abi.encode(invoker_, msg.sender, address(this)); + + // creates init data initData = abi.encodeWithSelector( AsyncPromise.initialize.selector, invoker_, @@ -132,6 +141,7 @@ contract AddressResolver is AddressResolverStorage, Initializable, Ownable { address(this) ); + // creates salt with a counter salt = keccak256(abi.encodePacked(constructorArgs, asyncPromiseCounter)); } @@ -141,9 +151,11 @@ contract AddressResolver is AddressResolverStorage, Initializable, Ownable { function deployAsyncPromiseContract( address invoker_ ) external returns (address newAsyncPromise) { + // creates init data and salt (bytes32 salt, bytes memory initData) = _createAsyncPromiseParams(invoker_); asyncPromiseCounter++; + // deploys the proxy newAsyncPromise = _deployProxy(salt, address(asyncPromiseBeacon), initData); _promises.push(newAsyncPromise); @@ -155,10 +167,10 @@ contract AddressResolver is AddressResolverStorage, Initializable, Ownable { address beacon_, bytes memory initData_ ) internal returns (address) { - // 1. Deploy proxy without initialization args + // Deploy proxy without initialization args address proxy = LibClone.deployDeterministicERC1967BeaconProxy(beacon_, salt_); - // 2. Explicitly initialize after deployment + // Explicitly initialize after deployment (bool success, ) = proxy.call(initData_); require(success, "Initialization failed"); From 516a36532dbb00756620190f801476e96e5c9634 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 11 Apr 2025 17:45:54 +0530 Subject: [PATCH 031/108] fix: refactor utils --- contracts/interfaces/IWatcherPrecompile.sol | 2 -- contracts/protocol/Forwarder.sol | 4 +++- .../protocol/payload-delivery/FeesManager.sol | 6 +----- .../protocol/payload-delivery/FeesPlug.sol | 4 +--- .../app-gateway/DeliveryHelperStorage.sol | 2 +- .../app-gateway/DeliveryUtils.sol | 2 -- contracts/protocol/socket/SocketUtils.sol | 10 +--------- .../protocol/utils/AddressResolverUtil.sol | 10 +++++----- contracts/protocol/utils/common/AccessRoles.sol | 3 +-- contracts/protocol/utils/common/Constants.sol | 7 ------- contracts/protocol/utils/common/Errors.sol | 11 ----------- contracts/protocol/utils/common/Structs.sol | 17 ++--------------- .../WatcherPrecompileConfig.sol | 3 +-- test/mock/MockWatcherPrecompile.sol | 1 - 14 files changed, 16 insertions(+), 66 deletions(-) diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 3a415612..e0d9eddd 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -62,8 +62,6 @@ interface IWatcherPrecompile { error InvalidChainSlug(); /// @notice Error thrown when an invalid app gateway reaches a plug error InvalidConnection(); - /// @notice Error thrown if winning bid is assigned to an invalid transmitter - error InvalidTransmitter(); /// @notice Error thrown when a timeout request is invalid error InvalidTimeoutRequest(); /// @notice Error thrown when a payload id is invalid diff --git a/contracts/protocol/Forwarder.sol b/contracts/protocol/Forwarder.sol index c2e44f65..6097512d 100644 --- a/contracts/protocol/Forwarder.sol +++ b/contracts/protocol/Forwarder.sol @@ -71,8 +71,10 @@ contract Forwarder is ForwarderStorage, Initializable, AddressResolverUtil { if (latestRequestCount != watcherPrecompile__().nextRequestCount()) revert("Forwarder: request count mismatch"); + address latestAsyncPromise_ = latestAsyncPromise; latestAsyncPromise = address(0); - promise_ = IPromise(latestAsyncPromise).then(selector_, data_); + + promise_ = IPromise(latestAsyncPromise_).then(selector_, data_); } /// @notice Returns the on-chain address associated with this forwarder. diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 7af43ca5..237dc290 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -9,7 +9,7 @@ import {IFeesPlug} from "../../interfaces/IFeesPlug.sol"; import {IFeesManager} from "../../interfaces/IFeesManager.sol"; import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; -import {NotAuctionManager} from "../utils/common/Errors.sol"; +import {NotAuctionManager, InvalidWatcherSignature, NonceUsed} from "../utils/common/Errors.sol"; import {Bid, Fees, CallType, Parallel, WriteFinality, TokenBalance, QueuePayloadParams, IsPlug, PayloadSubmitParams, RequestParams, RequestMetadata} from "../utils/common/Structs.sol"; abstract contract FeesManagerStorage is IFeesManager { @@ -110,10 +110,6 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol error NoFeesForTransmitter(); /// @notice Error thrown when no fees was blocked error NoFeesBlocked(); - /// @notice Error thrown when watcher signature is invalid - error InvalidWatcherSignature(); - /// @notice Error thrown when nonce is used - error NonceUsed(); /// @notice Error thrown when caller is invalid error InvalidCaller(); diff --git a/contracts/protocol/payload-delivery/FeesPlug.sol b/contracts/protocol/payload-delivery/FeesPlug.sol index 77778f05..16d3c18f 100644 --- a/contracts/protocol/payload-delivery/FeesPlug.sol +++ b/contracts/protocol/payload-delivery/FeesPlug.sol @@ -7,7 +7,7 @@ import "../utils/AccessControl.sol"; import {RESCUE_ROLE} from "../utils/common/AccessRoles.sol"; import "../utils/RescueFundsLib.sol"; import {ETH_ADDRESS} from "../utils/common/Constants.sol"; -import {InvalidTokenAddress} from "../utils/common/Errors.sol"; +import {InvalidTokenAddress, FeesAlreadyPaid} from "../utils/common/Errors.sol"; /// @title FeesManager /// @notice Abstract contract for managing fees @@ -16,8 +16,6 @@ contract FeesPlug is PlugBase, AccessControl { mapping(bytes32 => bool) public feesRedeemed; mapping(address => bool) public whitelistedTokens; - /// @notice Error thrown when attempting to pay fees again - error FeesAlreadyPaid(); /// @notice Error thrown when balance is not enough to cover fees error InsufficientTokenBalance(address token_); /// @notice Error thrown when deposit amount does not match msg.value diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol index b6fedb25..82422ecb 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol @@ -12,7 +12,7 @@ import {IAuctionManager} from "../../../interfaces/IAuctionManager.sol"; import {IFeesManager} from "../../../interfaces/IFeesManager.sol"; import {QueuePayloadParams, Fees, CallType, Bid, Parallel, IsPlug, WriteFinality, RequestMetadata} from "../../utils/common/Structs.sol"; -import {NotAuctionManager, InvalidPromise, InvalidIndex, PromisesNotResolved, InvalidTransmitter} from "../../utils/common/Errors.sol"; +import {NotAuctionManager, InvalidTransmitter} from "../../utils/common/Errors.sol"; import {FORWARD_CALL, DISTRIBUTE_FEE, DEPLOY, QUERY, FINALIZE} from "../../utils/common/Constants.sol"; /// @title DeliveryHelperStorage diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol index 3620b17b..905c535a 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol @@ -21,8 +21,6 @@ abstract contract DeliveryUtils is error AllPayloadsExecuted(); /// @notice Error thrown request did not come from Forwarder address error NotFromForwarder(); - /// @notice Error thrown when a payload call fails - error CallFailed(bytes32 payloadId); /// @notice Error thrown if payload is too large error PayloadTooLarge(); /// @notice Error thrown if trying to cancel a batch without being the application gateway diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index 97aa46ba..96b3b2fd 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.21; import "../utils/RescueFundsLib.sol"; import "./SocketConfig.sol"; import {ECDSA} from "solady/utils/ECDSA.sol"; +import {InvalidTransmitter} from "../utils/common/Errors.sol"; /** * @title SocketUtils @@ -39,15 +40,6 @@ abstract contract SocketUtils is SocketConfig { _initializeOwner(owner_); } - //////////////////////////////////////////////////////// - ////////////////////// ERRORS ////////////////////////// - //////////////////////////////////////////////////////// - - /** - * @dev Error thrown when non-transmitter tries to execute - */ - error InvalidTransmitter(); - /** * @notice creates the digest for the payload * @param transmitter_ The address of the transmitter diff --git a/contracts/protocol/utils/AddressResolverUtil.sol b/contracts/protocol/utils/AddressResolverUtil.sol index f168022c..adb8b5e5 100644 --- a/contracts/protocol/utils/AddressResolverUtil.sol +++ b/contracts/protocol/utils/AddressResolverUtil.sol @@ -26,8 +26,8 @@ abstract contract AddressResolverUtil { /// @notice Error thrown when an invalid address attempts to call the Watcher precompile or delivery helper error OnlyWatcherPrecompileOrDeliveryHelper(); - /// @notice Restricts function access to the auction house contract - /// @dev Validates that msg.sender matches the registered auction house address + /// @notice Restricts function access to the delivery helper contract + /// @dev Validates that msg.sender matches the registered delivery helper address modifier onlyDeliveryHelper() { if (msg.sender != addressResolver__.deliveryHelper()) { revert OnlyPayloadDelivery(); @@ -59,9 +59,9 @@ abstract contract AddressResolverUtil { _; } - /// @notice Gets the auction house contract interface - /// @return IMiddleware interface of the registered auction house - /// @dev Resolves and returns the auction house contract for interaction + /// @notice Gets the delivery helper contract interface + /// @return IMiddleware interface of the registered delivery helper + /// @dev Resolves and returns the delivery helper contract for interaction function deliveryHelper__() public view returns (IMiddleware) { return IMiddleware(addressResolver__.deliveryHelper()); } diff --git a/contracts/protocol/utils/common/AccessRoles.sol b/contracts/protocol/utils/common/AccessRoles.sol index 04a7d1b7..45472a4b 100644 --- a/contracts/protocol/utils/common/AccessRoles.sol +++ b/contracts/protocol/utils/common/AccessRoles.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -// contains role hashes used in socket for various different operations - +// contains role hashes used in socket for various different operation // used to rescue funds bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE"); // used by governance diff --git a/contracts/protocol/utils/common/Constants.sol b/contracts/protocol/utils/common/Constants.sol index 082dcf62..54be8294 100644 --- a/contracts/protocol/utils/common/Constants.sol +++ b/contracts/protocol/utils/common/Constants.sol @@ -3,17 +3,10 @@ pragma solidity ^0.8.21; address constant ETH_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); -address constant ZERO_ADDRESS = address(0); - bytes32 constant FORWARD_CALL = keccak256("FORWARD_CALL"); bytes32 constant DISTRIBUTE_FEE = keccak256("DISTRIBUTE_FEE"); bytes32 constant DEPLOY = keccak256("DEPLOY"); -bytes32 constant CONFIGURE = keccak256("CONFIGURE"); -bytes32 constant CONNECT = keccak256("CONNECT"); bytes32 constant QUERY = keccak256("QUERY"); bytes32 constant FINALIZE = keccak256("FINALIZE"); bytes32 constant SCHEDULE = keccak256("SCHEDULE"); bytes32 constant FAST = keccak256("FAST"); - -uint256 constant DEPLOY_GAS_LIMIT = 5_000_000; -uint256 constant CONFIGURE_GAS_LIMIT = 1_000_000; diff --git a/contracts/protocol/utils/common/Errors.sol b/contracts/protocol/utils/common/Errors.sol index bdebefd1..f801df1f 100644 --- a/contracts/protocol/utils/common/Errors.sol +++ b/contracts/protocol/utils/common/Errors.sol @@ -1,17 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.21; -error NotAuthorized(); -error NotBridge(); error NotSocket(); -error ConnectorUnavailable(); -error InvalidTokenContract(); -error ZeroAddressReceiver(); error ZeroAddress(); -error ZeroAmount(); -error InsufficientFunds(); -error InvalidSigner(); -error InvalidFunction(); error TimeoutDelayTooLarge(); error TimeoutAlreadyResolved(); error ResolvingTimeoutTooEarly(); @@ -23,9 +14,7 @@ error PlugDisconnected(); error InvalidAppGateway(); error AppGatewayAlreadyCalled(); error InvalidInboxCaller(); -error PromisesNotResolved(); error InvalidPromise(); -error InvalidIndex(); error InvalidTransmitter(); error FeesNotSet(); error InvalidTokenAddress(); diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 2b7c1f55..9566e2f6 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -64,6 +64,7 @@ struct UpdateLimitParams { uint256 maxLimit; uint256 ratePerSecond; } + struct AppGatewayConfig { address plug; address appGateway; @@ -94,13 +95,7 @@ struct TimeoutRequest { bool isResolved; bytes payload; } -struct QueryResults { - address target; - uint256 queryCounter; - bytes functionSelector; - bytes returnData; - bytes callback; -} + struct ResolvedPromises { bytes32 payloadId; bytes returnData; @@ -237,14 +232,6 @@ struct ExecuteParams { address switchboard; } -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 diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index 6a6a5a58..e0d9a1ac 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -6,6 +6,7 @@ import {ECDSA} from "solady/utils/ECDSA.sol"; 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"; /// @title WatcherPrecompileConfig /// @notice Configuration contract for the Watcher Precompile system @@ -85,8 +86,6 @@ contract WatcherPrecompileConfig is error InvalidGateway(); error InvalidSwitchboard(); - error NonceUsed(); - error InvalidWatcherSignature(); /// @notice Initial initialization (version 1) function initialize( diff --git a/test/mock/MockWatcherPrecompile.sol b/test/mock/MockWatcherPrecompile.sol index 42247124..ea424bbd 100644 --- a/test/mock/MockWatcherPrecompile.sol +++ b/test/mock/MockWatcherPrecompile.sol @@ -26,7 +26,6 @@ contract MockWatcherPrecompile { /// @notice Error thrown when an invalid chain slug is provided error InvalidChainSlug(); - error InvalidTransmitter(); event CalledAppGateway( bytes32 callId, From c1edfb43fbc0c9763c2a9a4dd53a402a6ef2a468 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 14 Apr 2025 16:00:57 +0530 Subject: [PATCH 032/108] fix: watcher config --- contracts/interfaces/IWatcherPrecompile.sol | 35 ++++++++++++++----- .../interfaces/IWatcherPrecompileConfig.sol | 3 +- contracts/protocol/AddressResolver.sol | 1 + .../WatcherPrecompileConfig.sol | 33 +++++++++++------ 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 615b9372..14658633 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -9,8 +9,12 @@ import {IWatcherPrecompileConfig} from "./IWatcherPrecompileConfig.sol"; /// @notice Interface for the Watcher Precompile system that handles payload verification and execution /// @dev Defines core functionality for payload processing and promise resolution interface IWatcherPrecompile { + /// @notice Emitted when a new call is made to an app gateway + /// @param triggerId The unique identifier for the trigger event CalledAppGateway(bytes32 triggerId); + /// @notice Emitted when a call to an app gateway fails + /// @param triggerId The unique identifier for the trigger event AppGatewayCallFailed(bytes32 triggerId); /// @notice Emitted when a new query is requested @@ -32,20 +36,31 @@ interface IWatcherPrecompile { /// @param payloadId The unique identifier for the not resolved promise event PromiseNotResolved(bytes32 indexed payloadId, address asyncPromise); + /// @notice Emitted when a payload is marked as revert + /// @param payloadId The unique identifier for the payload + /// @param isRevertingOnchain Whether the payload is reverting onchain event MarkedRevert(bytes32 indexed payloadId, bool isRevertingOnchain); - event TimeoutRequested( - bytes32 timeoutId, - address target, - bytes payload, - uint256 executeAt // Epoch time when the task should execute - ); + + /// @notice Emitted when a timeout is requested + /// @param timeoutId The unique identifier for the timeout + /// @param target The target address for the timeout callback + /// @param payload The payload data + /// @param executeAt The epoch time when the task should execute + event TimeoutRequested(bytes32 timeoutId, address target, bytes payload, uint256 executeAt); /// @notice Emitted when a timeout is resolved /// @param timeoutId The unique identifier for the timeout - /// @param target The target address for the timeout + /// @param target The target address for the callback /// @param payload The payload data /// @param executedAt The epoch time when the task was executed - event TimeoutResolved(bytes32 timeoutId, address target, bytes payload, uint256 executedAt); + /// @param returnData The return data from the callback + event TimeoutResolved( + bytes32 timeoutId, + address target, + bytes payload, + uint256 executedAt, + bytes returnData + ); event RequestSubmitted( address middleware, @@ -53,6 +68,10 @@ interface IWatcherPrecompile { PayloadParams[] payloadParamsArray ); + event MaxTimeoutDelayInSecondsSet(uint256 maxTimeoutDelayInSeconds); + + event ExpiryTimeSet(uint256 expiryTime); + /// @notice Error thrown when an invalid chain slug is provided error InvalidChainSlug(); /// @notice Error thrown when an invalid app gateway reaches a plug diff --git a/contracts/interfaces/IWatcherPrecompileConfig.sol b/contracts/interfaces/IWatcherPrecompileConfig.sol index 85831eca..4f28a33e 100644 --- a/contracts/interfaces/IWatcherPrecompileConfig.sol +++ b/contracts/interfaces/IWatcherPrecompileConfig.sol @@ -50,7 +50,8 @@ interface IWatcherPrecompileConfig { uint32 chainSlug_, address target_, address appGateway_, - address switchboard_ + address switchboard_, + address middleware_ ) external view; function setAppGateways( diff --git a/contracts/protocol/AddressResolver.sol b/contracts/protocol/AddressResolver.sol index f37da613..ba905769 100644 --- a/contracts/protocol/AddressResolver.sol +++ b/contracts/protocol/AddressResolver.sol @@ -80,6 +80,7 @@ contract AddressResolver is AddressResolverStorage, Initializable, Ownable { /// @notice Gets or deploys a Forwarder proxy contract /// @dev it checks if the forwarder is already deployed, if yes, it returns the address + /// @dev it maps the forwarder with the app gateway which is used for verifying if they are linked /// @param chainContractAddress_ The address of the chain contract /// @param chainSlug_ The chain slug /// @return newForwarder The address of the deployed Forwarder proxy contract diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index e0d9a1ac..fb6ac6c9 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -99,12 +99,10 @@ contract WatcherPrecompileConfig is evmxSlug = evmxSlug_; } - /// @notice Emitted when a plug is set as valid for an app gateway - /// @notice Configures app gateways with their respective plugs and switchboards - /// @param configs_ Array of configurations containing app gateway, network, plug, and switchboard details - /// @dev Only callable by the contract owner + /// @dev Only callable by the watcher /// @dev This helps in verifying that plugs are called by respective app gateways + /// @param configs_ Array of configurations containing app gateway, network, plug, and switchboard details function setAppGateways( AppGatewayConfig[] calldata configs_, uint256 signatureNonce_, @@ -127,7 +125,7 @@ contract WatcherPrecompileConfig is } } - /// @notice Sets the switchboard for a network + /// @notice Sets the socket, contract factory plug, and fees plug for a network /// @param chainSlug_ The identifier of the network function setOnChainContracts( uint32 chainSlug_, @@ -144,6 +142,7 @@ contract WatcherPrecompileConfig is /// @notice Sets the switchboard for a network /// @param chainSlug_ The identifier of the network + /// @param sbType_ The type of switchboard, hash of a string /// @param switchboard_ The address of the switchboard function setSwitchboard( uint32 chainSlug_, @@ -154,16 +153,21 @@ contract WatcherPrecompileConfig is emit SwitchboardSet(chainSlug_, sbType_, switchboard_); } - // @dev app gateway can set the valid plugs for each chain slug + /// @notice Sets the valid plugs for an app gateway + /// @dev Only callable by the app gateway + /// @dev This helps in verifying that app gateways are called by respective plugs + /// @param chainSlug_ The identifier of the network + /// @param plug_ The address of the plug + /// @param isValid_ Whether the plug is valid function setIsValidPlug(uint32 chainSlug_, address plug_, bool isValid_) external { isValidPlug[msg.sender][chainSlug_][plug_] = isValid_; } /// @notice Retrieves the configuration for a specific plug on a network + /// @dev Returns zero addresses if configuration doesn't exist /// @param chainSlug_ The identifier of the network /// @param plug_ The address of the plug /// @return The app gateway address and switchboard address for the plug - /// @dev Returns zero addresses if configuration doesn't exist function getPlugConfigs( uint32 chainSlug_, address plug_ @@ -174,15 +178,24 @@ contract WatcherPrecompileConfig is ); } + /// @notice Verifies the connections between the target, app gateway, and switchboard + /// @dev Only callable by the watcher + /// @param chainSlug_ The identifier of the network + /// @param target_ The address of the target + /// @param appGateway_ The address of the app gateway + /// @param switchboard_ The address of the switchboard function verifyConnections( uint32 chainSlug_, address target_, address appGateway_, - address switchboard_ + address switchboard_, + address middleware_ ) external view { - // todo: revisit this // if target is contractFactoryPlug, return - if (target_ == contractFactoryPlug[chainSlug_]) return; + // as connection is with middleware delivery helper and not app gateway + if ( + middleware_ == address(deliveryHelper__()) && target_ == contractFactoryPlug[chainSlug_] + ) return; (address appGateway, address switchboard) = getPlugConfigs(chainSlug_, target_); if (appGateway != appGateway_) revert InvalidGateway(); From b0ebff6f43d4ae8a31f27da05b393f7bda4072a2 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 14 Apr 2025 16:01:09 +0530 Subject: [PATCH 033/108] fix: utils and common --- contracts/protocol/utils/FeesPlugin.sol | 2 +- contracts/protocol/utils/RescueFundsLib.sol | 1 - contracts/protocol/utils/common/Structs.sol | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/contracts/protocol/utils/FeesPlugin.sol b/contracts/protocol/utils/FeesPlugin.sol index a5320f38..5a4d7514 100644 --- a/contracts/protocol/utils/FeesPlugin.sol +++ b/contracts/protocol/utils/FeesPlugin.sol @@ -8,7 +8,7 @@ import {Fees} from "../utils/common/Structs.sol"; /// @dev Provides base functionality for fee management in the system abstract contract FeesPlugin { /// @notice Storage for the current fee configuration - /// @dev Contains fee parameters like rates, limits, and recipient addresses + /// @dev Contains fee parameters like chain slug, token address, and amount Fees public fees; /// @notice Retrieves the current fee configuration diff --git a/contracts/protocol/utils/RescueFundsLib.sol b/contracts/protocol/utils/RescueFundsLib.sol index 189f2a7b..738ec796 100644 --- a/contracts/protocol/utils/RescueFundsLib.sol +++ b/contracts/protocol/utils/RescueFundsLib.sol @@ -9,7 +9,6 @@ import {ETH_ADDRESS} from "./common/Constants.sol"; * @title RescueFundsLib * @dev A library that provides a function to rescue funds from a contract. */ - library RescueFundsLib { /** * @dev Rescues funds from a contract. diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 6bd7c97a..dc184031 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -203,9 +203,11 @@ struct PayloadParams { struct RequestParams { bool isRequestCancelled; uint40 currentBatch; + // updated while processing request uint256 currentBatchPayloadsLeft; uint256 payloadsRemaining; address middleware; + // updated after auction address transmitter; PayloadParams[] payloadParamsArray; } From 7209c6b233492fe3d941022770018259f455c129 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 14 Apr 2025 16:11:05 +0530 Subject: [PATCH 034/108] fix: fees plug --- contracts/interfaces/IFeesPlug.sol | 8 ++-- .../protocol/payload-delivery/FeesPlug.sol | 44 ++++++++++++++----- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/contracts/interfaces/IFeesPlug.sol b/contracts/interfaces/IFeesPlug.sol index 9515ee81..cc546977 100644 --- a/contracts/interfaces/IFeesPlug.sol +++ b/contracts/interfaces/IFeesPlug.sol @@ -2,19 +2,17 @@ pragma solidity ^0.8.21; interface IFeesPlug { - function balanceOf(address appGateway_, address token_) external view returns (uint256); + function balanceOf(address token_) external view returns (uint256); - function feesRedeemed(uint256 feesCounter_) external view returns (bool); + function feesRedeemed(bytes32 feesId_) external view returns (bool); function deposit(address token_, address appGateway_, uint256 amount_) external payable; - function connect(address appGateway_, address switchboard_) external; - function distributeFee( address feeToken_, uint256 fee_, address transmitter_, - bytes32 feesCounter_ + bytes32 feesId_ ) external; function withdrawFees(address token_, uint256 amount_, address receiver_) external; diff --git a/contracts/protocol/payload-delivery/FeesPlug.sol b/contracts/protocol/payload-delivery/FeesPlug.sol index 16d3c18f..a3411462 100644 --- a/contracts/protocol/payload-delivery/FeesPlug.sol +++ b/contracts/protocol/payload-delivery/FeesPlug.sol @@ -5,21 +5,28 @@ import "solady/utils/SafeTransferLib.sol"; import "../../base/PlugBase.sol"; import "../utils/AccessControl.sol"; import {RESCUE_ROLE} from "../utils/common/AccessRoles.sol"; +import {IFeesPlug} from "../../interfaces/IFeesPlug.sol"; import "../utils/RescueFundsLib.sol"; import {ETH_ADDRESS} from "../utils/common/Constants.sol"; import {InvalidTokenAddress, FeesAlreadyPaid} from "../utils/common/Errors.sol"; /// @title FeesManager -/// @notice Abstract contract for managing fees -contract FeesPlug is PlugBase, AccessControl { - mapping(address => uint256) public balanceOf; - mapping(bytes32 => bool) public feesRedeemed; +/// @notice Contract for managing fees on a network +/// @dev The amount deposited here is locked and updated in the EVMx for an app gateway +/// @dev The fees are redeemed by the transmitters executing request or can be withdrawn by the owner +contract FeesPlug is IFeesPlug, PlugBase, AccessControl { + /// @notice Mapping to store the balance of each token + mapping(address => uint256) public override balanceOf; + /// @notice Mapping to store if fees have been redeemed for a given fees ID + mapping(bytes32 => bool) public override feesRedeemed; + /// @notice Mapping to store if a token is whitelisted mapping(address => bool) public whitelistedTokens; /// @notice Error thrown when balance is not enough to cover fees error InsufficientTokenBalance(address token_); /// @notice Error thrown when deposit amount does not match msg.value error InvalidDepositAmount(); + /// @notice Error thrown when token is not whitelisted error TokenNotWhitelisted(address token_); /// @notice Event emitted when fees are deposited @@ -31,38 +38,53 @@ contract FeesPlug is PlugBase, AccessControl { /// @notice Event emitted when a token is removed from whitelist event TokenRemovedFromWhitelist(address token); + /// @notice Modifier to check if the balance of a token is enough to withdraw modifier isFeesEnough(uint256 fee_, address feeToken_) { if (balanceOf[feeToken_] < fee_) revert InsufficientTokenBalance(feeToken_); _; } + /// @notice Constructor for the FeesPlug contract + /// @param socket_ The socket address + /// @param owner_ The owner address constructor(address socket_, address owner_) { _setSocket(socket_); _initializeOwner(owner_); - whitelistedTokens[ETH_ADDRESS] = true; // ETH is whitelisted by default + + // ETH is whitelisted by default + whitelistedTokens[ETH_ADDRESS] = true; } + /// @notice Distributes fees to the transmitter + /// @param feeToken_ The token address + /// @param fee_ The amount of fees + /// @param transmitter_ The transmitter address + /// @param feesId_ The fees ID function distributeFee( address feeToken_, uint256 fee_, address transmitter_, bytes32 feesId_ - ) external onlySocket isFeesEnough(fee_, feeToken_) { + ) external override onlySocket isFeesEnough(fee_, feeToken_) { if (feesRedeemed[feesId_]) revert FeesAlreadyPaid(); feesRedeemed[feesId_] = true; - balanceOf[feeToken_] -= fee_; + _transferTokens(feeToken_, fee_, transmitter_); } + /// @notice Withdraws fees + /// @param token_ The token address + /// @param amount_ The amount + /// @param receiver_ The receiver address function withdrawFees( address token_, uint256 amount_, address receiver_ - ) external onlySocket isFeesEnough(amount_, token_) { + ) external override onlySocket isFeesEnough(amount_, token_) { balanceOf[token_] -= amount_; - _transferTokens(token_, amount_, receiver_); + _transferTokens(token_, amount_, receiver_); emit FeesWithdrawn(token_, amount_, receiver_); } @@ -70,7 +92,7 @@ contract FeesPlug is PlugBase, AccessControl { /// @param token_ The token address /// @param amount_ The amount /// @param appGateway_ The app gateway address - function deposit(address token_, address appGateway_, uint256 amount_) external payable { + function deposit(address token_, address appGateway_, uint256 amount_) external payable override{ if (!whitelistedTokens[token_]) revert TokenNotWhitelisted(token_); if (token_ == ETH_ADDRESS) { @@ -138,7 +160,5 @@ contract FeesPlug is PlugBase, AccessControl { RescueFundsLib._rescueFunds(token_, rescueTo_, amount_); } - fallback() external payable {} - receive() external payable {} } From 820fbb0368949fa8637aca3e248c4835583391c0 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 14 Apr 2025 17:42:52 +0530 Subject: [PATCH 035/108] fix: base contracts --- contracts/base/AppGatewayBase.sol | 34 ++++++++++++++++------------ contracts/base/PlugBase.sol | 4 ++-- contracts/base/ProxyFactory.sol | 4 +--- contracts/interfaces/IAppGateway.sol | 26 +++++++++++++++++++++ contracts/interfaces/IPlug.sol | 8 ++++++- 5 files changed, 56 insertions(+), 20 deletions(-) diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index b16f8b32..78ab2c79 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -13,12 +13,13 @@ import {FAST} from "../protocol/utils/common/Constants.sol"; /// @title AppGatewayBase /// @notice Abstract contract for the app gateway +/// @dev This contract contains helpers for contract deployment, overrides, hooks and request processing abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin { OverrideParams public overrideParams; + bool public isAsyncModifierSet; address public auctionManager; - bytes public onCompleteData; bytes32 public sbType; - bool public isAsyncModifierSet; + bytes public onCompleteData; mapping(address => bool) public isValidPromise; mapping(bytes32 => mapping(uint32 => address)) public override forwarderAddresses; @@ -27,11 +28,14 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin /// @notice Modifier to treat functions async modifier async() { if (fees.feePoolChain == 0) revert FeesNotSet(); + isAsyncModifierSet = true; deliveryHelper__().clearQueue(); addressResolver__.clearPromises(); _clearOverrides(); + _; + isAsyncModifierSet = false; deliveryHelper__().batch(fees, auctionManager, onCompleteData); _markValidPromises(); @@ -87,13 +91,6 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin } } - /// @notice Gets the socket address - /// @param chainSlug_ The chain slug - /// @return socketAddress_ The socket address - function getSocketAddress(uint32 chainSlug_) public view returns (address) { - return watcherPrecompileConfig().sockets(chainSlug_); - } - /// @notice Sets the validity of an onchain contract (plug) to authorize it to send information to a specific AppGateway /// @param chainSlug_ The unique identifier of the chain where the contract resides /// @param contractId The bytes32 identifier of the contract to be validated @@ -156,7 +153,6 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin /// @param returnData_ The return data function setAddress(bytes memory data_, bytes memory returnData_) external onlyPromises { (uint32 chainSlug, bytes32 contractId) = abi.decode(data_, (uint32, bytes32)); - address forwarderContractAddress = addressResolver__.getOrDeployForwarderContract( address(this), abi.decode(returnData_, (address)), @@ -166,6 +162,13 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin forwarderAddresses[contractId][chainSlug] = forwarderContractAddress; } + /// @notice Gets the socket address + /// @param chainSlug_ The chain slug + /// @return socketAddress_ The socket address + function getSocketAddress(uint32 chainSlug_) public view returns (address) { + return watcherPrecompileConfig().sockets(chainSlug_); + } + /// @notice Gets the on-chain address /// @param contractId_ The contract ID /// @param chainSlug_ The chain slug @@ -178,8 +181,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin return address(0); } - onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]) - .getOnChainAddress(); + onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]).getOnChainAddress(); } //////////////////////////////////////////////////////////////////////////////////////////////// @@ -342,7 +344,10 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin /// @param onCompleteData_ The on complete data /// @dev only payload delivery can call this /// @dev callback in pd promise to be called after all contracts are deployed - function onRequestComplete(uint40, bytes calldata onCompleteData_) external override { + function onRequestComplete( + uint40, + bytes calldata onCompleteData_ + ) external override onlyDeliveryHelper { if (onCompleteData_.length == 0) return; (uint32 chainSlug, bool isDeploy) = abi.decode(onCompleteData_, (uint32, bool)); if (isDeploy) { @@ -350,7 +355,8 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin } } - /// @notice Initializes the contract + /// @notice Initializes the contract after deployment + /// @dev can be overridden by the app gateway to add custom logic /// @param chainSlug_ The chain slug function initialize(uint32 chainSlug_) public virtual {} diff --git a/contracts/base/PlugBase.sol b/contracts/base/PlugBase.sol index 64dff39d..99d84613 100644 --- a/contracts/base/PlugBase.sol +++ b/contracts/base/PlugBase.sol @@ -7,6 +7,7 @@ import {NotSocket} from "../protocol/utils/common/Errors.sol"; /// @title PlugBase /// @notice Abstract contract for plugs +/// @dev This contract contains helpers for socket connection, disconnection, and overrides abstract contract PlugBase is IPlug { ISocket public socket__; address public appGateway; @@ -23,7 +24,7 @@ abstract contract PlugBase is IPlug { _; } - /// @notice Modifier to ensure the socket is initialized + /// @notice Modifier to ensure the socket is initialized and if not already initialized, it will be initialized modifier socketInitializer() { if (isSocketInitialized == 1) revert SocketAlreadyInitialized(); isSocketInitialized = 1; @@ -36,7 +37,6 @@ abstract contract PlugBase is IPlug { function _connectSocket(address appGateway_, address socket_, address switchboard_) internal { _setSocket(socket_); appGateway = appGateway_; - socket__.connect(appGateway_, switchboard_); } diff --git a/contracts/base/ProxyFactory.sol b/contracts/base/ProxyFactory.sol index 0f903337..5c081ec3 100644 --- a/contracts/base/ProxyFactory.sol +++ b/contracts/base/ProxyFactory.sol @@ -2,6 +2,4 @@ pragma solidity ^0.8.0; import {ERC1967Factory} from "solady/utils/ERC1967Factory.sol"; -contract ProxyFactory is ERC1967Factory { - constructor() {} -} +contract ProxyFactory is ERC1967Factory {} diff --git a/contracts/interfaces/IAppGateway.sol b/contracts/interfaces/IAppGateway.sol index bdda2198..84136288 100644 --- a/contracts/interfaces/IAppGateway.sol +++ b/contracts/interfaces/IAppGateway.sol @@ -3,31 +3,57 @@ pragma solidity ^0.8.21; import {Fees, Read, Parallel, QueuePayloadParams, OverrideParams, CallType, WriteFinality, PayloadParams} from "../protocol/utils/common/Structs.sol"; +/// @title IAppGateway +/// @notice Interface for the app gateway interface IAppGateway { + /// @notice Checks if the async modifier is set + /// @return isAsyncModifierSet_ True if the async modifier is set, false otherwise function isAsyncModifierSet() external view returns (bool); + /// @notice Gets the override parameters + /// @return read_ The read parameters + /// @return parallel_ The parallel parameters + /// @return writeFinality_ The write finality parameters + /// @return readTimeout_ The read timeout + /// @return writeTimeout_ The write timeout + /// @return writeFinalityTimeout_ The write finality timeout + /// @return sbType_ The switchboard type function getOverrideParams() external view returns (Read, Parallel, WriteFinality, uint256, uint256, uint256, bytes32); + /// @notice Handles the request complete event + /// @param requestCount_ The request count + /// @param onCompleteData_ The on complete data function onRequestComplete(uint40 requestCount_, bytes calldata onCompleteData_) external; + /// @notice Handles the revert event + /// @param requestCount_ The request count + /// @param payloadId_ The payload id function handleRevert(uint40 requestCount_, bytes32 payloadId_) external; /// @notice initialize the contracts on chain + /// @param chainSlug_ The chain slug function initialize(uint32 chainSlug_) external; /// @notice deploy contracts to chain + /// @param chainSlug_ The chain slug function deployContracts(uint32 chainSlug_) external; /// @notice get the on-chain address of a contract + /// @param contractId_ The contract id + /// @param chainSlug_ The chain slug + /// @return onChainAddress The on-chain address function getOnChainAddress( bytes32 contractId_, uint32 chainSlug_ ) external view returns (address onChainAddress); /// @notice get the forwarder address of a contract + /// @param contractId_ The contract id + /// @param chainSlug_ The chain slug + /// @return forwarderAddress The forwarder address function forwarderAddresses( bytes32 contractId_, uint32 chainSlug_ diff --git a/contracts/interfaces/IPlug.sol b/contracts/interfaces/IPlug.sol index 552ae8f0..775a37e1 100644 --- a/contracts/interfaces/IPlug.sol +++ b/contracts/interfaces/IPlug.sol @@ -6,7 +6,13 @@ pragma solidity ^0.8.21; * @notice Interface for a plug contract that executes the payload received from a source chain. */ interface IPlug { + /// @notice Initializes the socket + /// @param appGateway_ The app gateway address + /// @param socket_ The socket address + /// @param switchboard_ The switchboard address function initSocket(address appGateway_, address socket_, address switchboard_) external; - function overrides() external view returns (bytes memory); + /// @notice Gets the overrides + /// @return overrides_ The overrides + function overrides() external view returns (bytes memory overrides_); } From e0dc2585184bb9b0f42e6ce72d4cb60810acff1e Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 14 Apr 2025 17:43:39 +0530 Subject: [PATCH 036/108] fix: lint --- contracts/base/AppGatewayBase.sol | 3 ++- contracts/interfaces/IAddressResolver.sol | 4 ++-- contracts/interfaces/IAppGateway.sol | 2 +- contracts/protocol/AddressResolver.sol | 2 +- contracts/protocol/payload-delivery/FeesPlug.sol | 6 +++++- contracts/protocol/socket/switchboard/SwitchboardBase.sol | 2 +- contracts/protocol/utils/common/Errors.sol | 2 +- test/DeliveryHelper.t.sol | 6 +++++- test/mock/MockWatcherPrecompile.sol | 8 +++----- 9 files changed, 21 insertions(+), 14 deletions(-) diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index 78ab2c79..0908299f 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -181,7 +181,8 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin return address(0); } - onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]).getOnChainAddress(); + onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]) + .getOnChainAddress(); } //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/contracts/interfaces/IAddressResolver.sol b/contracts/interfaces/IAddressResolver.sol index bb401e57..ce5fcdbf 100644 --- a/contracts/interfaces/IAddressResolver.sol +++ b/contracts/interfaces/IAddressResolver.sol @@ -12,7 +12,7 @@ interface IAddressResolver { /// @param newAddress The new address of the contract event AddressSet(bytes32 indexed name, address oldAddress, address newAddress); - /// @notice Emitted when a new plug is added to the resolver + /// @notice Emitted when a new plug is added to the resolver /// @param appGateway The address of the app gateway /// @param chainSlug The chain slug /// @param plug The address of the plug @@ -22,7 +22,7 @@ interface IAddressResolver { /// @param newForwarder The address of the new forwarder /// @param salt The salt used to deploy the forwarder event ForwarderDeployed(address newForwarder, bytes32 salt); - + /// @notice Emitted when a new async promise is deployed /// @param newAsyncPromise The address of the new async promise /// @param salt The salt used to deploy the async promise diff --git a/contracts/interfaces/IAppGateway.sol b/contracts/interfaces/IAppGateway.sol index 84136288..79a78889 100644 --- a/contracts/interfaces/IAppGateway.sol +++ b/contracts/interfaces/IAppGateway.sol @@ -24,7 +24,7 @@ interface IAppGateway { returns (Read, Parallel, WriteFinality, uint256, uint256, uint256, bytes32); /// @notice Handles the request complete event - /// @param requestCount_ The request count + /// @param requestCount_ The request count /// @param onCompleteData_ The on complete data function onRequestComplete(uint40 requestCount_, bytes calldata onCompleteData_) external; diff --git a/contracts/protocol/AddressResolver.sol b/contracts/protocol/AddressResolver.sol index ba905769..f7cbc322 100644 --- a/contracts/protocol/AddressResolver.sol +++ b/contracts/protocol/AddressResolver.sol @@ -91,7 +91,7 @@ contract AddressResolver is AddressResolverStorage, Initializable, Ownable { ) public returns (address newForwarder) { // predict address address forwarderAddress = getForwarderAddress(chainContractAddress_, chainSlug_); - + // check if addr has code, if yes, return if (forwarderAddress.code.length > 0) { return forwarderAddress; diff --git a/contracts/protocol/payload-delivery/FeesPlug.sol b/contracts/protocol/payload-delivery/FeesPlug.sol index a3411462..4278b030 100644 --- a/contracts/protocol/payload-delivery/FeesPlug.sol +++ b/contracts/protocol/payload-delivery/FeesPlug.sol @@ -92,7 +92,11 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { /// @param token_ The token address /// @param amount_ The amount /// @param appGateway_ The app gateway address - function deposit(address token_, address appGateway_, uint256 amount_) external payable override{ + function deposit( + address token_, + address appGateway_, + uint256 amount_ + ) external payable override { if (!whitelistedTokens[token_]) revert TokenNotWhitelisted(token_); if (token_ == ETH_ADDRESS) { diff --git a/contracts/protocol/socket/switchboard/SwitchboardBase.sol b/contracts/protocol/socket/switchboard/SwitchboardBase.sol index 7b78aae8..2f5249a5 100644 --- a/contracts/protocol/socket/switchboard/SwitchboardBase.sol +++ b/contracts/protocol/socket/switchboard/SwitchboardBase.sol @@ -13,7 +13,7 @@ abstract contract SwitchboardBase is ISwitchboard, AccessControl { // chain slug of deployed chain uint32 public immutable chainSlug; - + /** * @dev Constructor of SwitchboardBase * @param chainSlug_ Chain slug of deployment chain diff --git a/contracts/protocol/utils/common/Errors.sol b/contracts/protocol/utils/common/Errors.sol index 5dbf53e0..3aa47e55 100644 --- a/contracts/protocol/utils/common/Errors.sol +++ b/contracts/protocol/utils/common/Errors.sol @@ -31,4 +31,4 @@ error BidExceedsMaxFees(); /// @notice Error thrown if a lower bid already exists error LowerBidAlreadyExists(); error AsyncModifierNotUsed(); -error InvalidIndex(); \ No newline at end of file +error InvalidIndex(); diff --git a/test/DeliveryHelper.t.sol b/test/DeliveryHelper.t.sol index ea346775..645fe47d 100644 --- a/test/DeliveryHelper.t.sol +++ b/test/DeliveryHelper.t.sol @@ -279,7 +279,11 @@ contract DeliveryHelperTest is SetupTest { function endAuction(uint40 requestCount_) internal { if (auctionEndDelaySeconds == 0) return; - bytes32 timeoutId = _encodeTimeoutId(evmxSlug, address(watcherPrecompile), timeoutIdCounter++); + bytes32 timeoutId = _encodeTimeoutId( + evmxSlug, + address(watcherPrecompile), + timeoutIdCounter++ + ); bytes memory watcherSignature = _createWatcherSignature( address(watcherPrecompile), diff --git a/test/mock/MockWatcherPrecompile.sol b/test/mock/MockWatcherPrecompile.sol index c6989e43..90dca4de 100644 --- a/test/mock/MockWatcherPrecompile.sol +++ b/test/mock/MockWatcherPrecompile.sol @@ -16,8 +16,6 @@ contract MockWatcherPrecompile { uint256 public maxTimeoutDelayInSeconds = 24 * 60 * 60; // 24 hours /// @notice Counter for tracking payload execution requests uint256 public payloadCounter; - /// @notice Counter for tracking timeout requests - uint256 public timeoutCounter; /// @notice Mapping to store timeout requests /// @dev timeoutId => TimeoutRequest struct mapping(bytes32 => TimeoutRequest) public timeoutRequests; @@ -73,7 +71,7 @@ contract MockWatcherPrecompile { /// @param delayInSeconds_ The delay in seconds function setTimeout(bytes calldata payload_, uint256 delayInSeconds_) external { uint256 executeAt = block.timestamp + delayInSeconds_; - bytes32 timeoutId = _encodeTimeoutId(timeoutCounter++); + bytes32 timeoutId = _encodeTimeoutId(); timeoutRequests[timeoutId] = TimeoutRequest( timeoutId, msg.sender, @@ -176,9 +174,9 @@ contract MockWatcherPrecompile { ); } - function _encodeTimeoutId(uint256 timeoutCounter_) internal view returns (bytes32) { + function _encodeTimeoutId() internal returns (bytes32) { // watcher address (160 bits) | counter (64 bits) - return bytes32((uint256(uint160(address(this))) << 64) | timeoutCounter_); + return bytes32((uint256(uint160(address(this))) << 64) | payloadCounter++); } /// @notice Retrieves the configuration for a specific plug on a network From b4768061c1ddb7eaaf617ac2e3c696787e728c11 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 14 Apr 2025 22:20:05 +0530 Subject: [PATCH 037/108] doc: auction manager --- contracts/interfaces/IAuctionManager.sol | 15 ++++- .../payload-delivery/AuctionManager.sol | 64 ++++++++++++------- 2 files changed, 56 insertions(+), 23 deletions(-) diff --git a/contracts/interfaces/IAuctionManager.sol b/contracts/interfaces/IAuctionManager.sol index bee93dc1..12046099 100644 --- a/contracts/interfaces/IAuctionManager.sol +++ b/contracts/interfaces/IAuctionManager.sol @@ -1,9 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.3; -import {Bid, Fees} from "../protocol/utils/common/Structs.sol"; +import {Bid, Fees, RequestMetadata, RequestParams} from "../protocol/utils/common/Structs.sol"; interface IAuctionManager { + /// @notice Bids for an auction + /// @param requestCount_ The request count + /// @param fee_ The fee + /// @param transmitterSignature_ The transmitter signature + /// @param extraData_ The extra data function bid( uint40 requestCount_, uint256 fee_, @@ -11,9 +16,17 @@ interface IAuctionManager { bytes memory extraData_ ) external; + /// @notice Ends an auction + /// @param requestCount_ The request count function endAuction(uint40 requestCount_) external; + /// @notice Checks if an auction is closed + /// @param requestCount_ The request count + /// @return isClosed_ Whether the auction is closed function auctionClosed(uint40 requestCount_) external view returns (bool); + /// @notice Checks if an auction is started + /// @param requestCount_ The request count + /// @return isStarted_ Whether the auction is started function auctionStarted(uint40 requestCount_) external view returns (bool); } diff --git a/contracts/protocol/payload-delivery/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol index e9c97c68..c2e45473 100644 --- a/contracts/protocol/payload-delivery/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -4,16 +4,15 @@ pragma solidity ^0.8.0; import {ECDSA} from "solady/utils/ECDSA.sol"; import "solady/utils/Initializable.sol"; import "../utils/AccessControl.sol"; - +import "../../interfaces/IAuctionManager.sol"; import {IMiddleware} from "../../interfaces/IMiddleware.sol"; import {IFeesManager} from "../../interfaces/IFeesManager.sol"; -import {IAuctionManager} from "../../interfaces/IAuctionManager.sol"; - import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; -import {Fees, Bid, RequestMetadata, RequestParams} from "../utils/common/Structs.sol"; import {AuctionClosed, AuctionAlreadyStarted, BidExceedsMaxFees, LowerBidAlreadyExists, InvalidTransmitter} from "../utils/common/Errors.sol"; import {TRANSMITTER_ROLE} from "../utils/common/AccessRoles.sol"; +/// @title AuctionManagerStorage +/// @notice Storage for the AuctionManager contract abstract contract AuctionManagerStorage is IAuctionManager { // slots [0-49] reserved for gap uint256[50] _gap_before; @@ -38,16 +37,13 @@ abstract contract AuctionManagerStorage is IAuctionManager { mapping(uint40 => bool) public override auctionStarted; // slot 56 - mapping(address => bool) public whitelistedTransmitters; - - // slot 57 mapping(uint40 => uint256) public reAuctionCount; - // slots [58-107] reserved for gap + // slots [57-106] reserved for gap uint256[50] _gap_after; - // slots 108-158 (51) reserved for access control - // slots 159-209 (51) reserved for addr resolver util + // slots 107-157 (51) reserved for access control + // slots 158-208 (51) reserved for addr resolver util } /// @title AuctionManager @@ -62,6 +58,7 @@ contract AuctionManager is event AuctionStarted(uint40 requestCount); event AuctionEnded(uint40 requestCount, Bid winningBid); event BidPlaced(uint40 requestCount, Bid bid); + event AuctionEndDelaySecondsSet(uint256 auctionEndDelaySeconds); error InvalidBid(); error MaxReAuctionCountReached(); @@ -85,6 +82,7 @@ contract AuctionManager is ) public reinitializer(1) { _setAddressResolver(addressResolver_); _initializeOwner(owner_); + evmxSlug = evmxSlug_; auctionEndDelaySeconds = auctionEndDelaySeconds_; maxReAuctionCount = maxReAuctionCount_; @@ -92,14 +90,7 @@ contract AuctionManager is function setAuctionEndDelaySeconds(uint256 auctionEndDelaySeconds_) external onlyOwner { auctionEndDelaySeconds = auctionEndDelaySeconds_; - } - - function startAuction(uint40 requestCount_) internal { - if (auctionClosed[requestCount_]) revert AuctionClosed(); - if (auctionStarted[requestCount_]) revert AuctionAlreadyStarted(); - - auctionStarted[requestCount_] = true; - emit AuctionStarted(requestCount_); + emit AuctionEndDelaySecondsSet(auctionEndDelaySeconds_); } /// @notice Places a bid for an auction @@ -114,25 +105,34 @@ contract AuctionManager is ) external { if (auctionClosed[requestCount_]) revert AuctionClosed(); + // check if the transmitter is valid address transmitter = _recoverSigner( keccak256(abi.encode(address(this), evmxSlug, requestCount_, fee, extraData)), transmitterSignature ); if (!_hasRole(TRANSMITTER_ROLE, transmitter)) revert InvalidTransmitter(); + // create a new bid Bid memory newBid = Bid({fee: fee, transmitter: transmitter, extraData: extraData}); + // get the request metadata RequestMetadata memory requestMetadata = IMiddleware(addressResolver__.deliveryHelper()) .getRequestMetadata(requestCount_); - if (fee > requestMetadata.fees.amount) revert BidExceedsMaxFees(); + + // check if the bid is for this auction manager if (requestMetadata.auctionManager != address(this)) revert InvalidBid(); + // check if the bid exceeds the max fees quoted by app gateway + if (fee > requestMetadata.fees.amount) revert BidExceedsMaxFees(); + // check if the bid is lower than the existing bid if ( winningBids[requestCount_].transmitter != address(0) && fee >= winningBids[requestCount_].fee ) revert LowerBidAlreadyExists(); + // update the winning bid winningBids[requestCount_] = newBid; + // block the fees IFeesManager(addressResolver__.feesManager()).blockFees( requestMetadata.appGateway, requestMetadata.fees, @@ -140,8 +140,9 @@ contract AuctionManager is requestCount_ ); + // end the auction if the no auction end delay if (auctionEndDelaySeconds > 0) { - startAuction(requestCount_); + _startAuction(requestCount_); watcherPrecompile__().setTimeout( auctionEndDelaySeconds, abi.encodeWithSelector(this.endAuction.selector, requestCount_) @@ -151,31 +152,42 @@ contract AuctionManager is } emit BidPlaced(requestCount_, newBid); - auctionClosed[requestCount_] = true; } /// @notice Ends an auction /// @param requestCount_ The ID of the auction function endAuction(uint40 requestCount_) external onlyWatcherPrecompile { + if (auctionClosed[requestCount_]) return; _endAuction(requestCount_); } function _endAuction(uint40 requestCount_) internal { - auctionClosed[requestCount_] = true; + // get the winning bid, if no transmitter is set, revert Bid memory winningBid = winningBids[requestCount_]; if (winningBid.transmitter == address(0)) revert InvalidTransmitter(); + auctionClosed[requestCount_] = true; + + // set the timeout for the bid expiration + // useful in case a transmitter did bid but did not execute payloads watcherPrecompile__().setTimeout( IMiddleware(addressResolver__.deliveryHelper()).bidTimeout(), abi.encodeWithSelector(this.expireBid.selector, requestCount_) ); + + // start the request processing, it will finalize the request IMiddleware(addressResolver__.deliveryHelper()).startRequestProcessing( requestCount_, winningBid ); + emit AuctionEnded(requestCount_, winningBid); } + /// @notice Expires a bid and restarts an auction in case a request is not fully executed. + /// @dev Auction can be restarted only for `maxReAuctionCount` times. + /// @dev It also unblocks the fees from last transmitter to be assigned to the new winner. + /// @param requestCount_ The request id function expireBid(uint40 requestCount_) external onlyWatcherPrecompile { if (reAuctionCount[requestCount_] >= maxReAuctionCount) revert MaxReAuctionCountReached(); RequestParams memory requestParams = watcherPrecompile__().getRequestParams(requestCount_); @@ -190,6 +202,14 @@ contract AuctionManager is emit AuctionRestarted(requestCount_); } + function _startAuction(uint40 requestCount_) internal { + if (auctionClosed[requestCount_]) revert AuctionClosed(); + if (auctionStarted[requestCount_]) revert AuctionAlreadyStarted(); + + auctionStarted[requestCount_] = true; + emit AuctionStarted(requestCount_); + } + function _recoverSigner( bytes32 digest_, bytes memory signature_ From 7d312a9803b10b9873921f90da7f5ba607df1745 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 14 Apr 2025 22:20:57 +0530 Subject: [PATCH 038/108] fix: contract factory plug --- .../payload-delivery/ContractFactoryPlug.sol | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/contracts/protocol/payload-delivery/ContractFactoryPlug.sol b/contracts/protocol/payload-delivery/ContractFactoryPlug.sol index 54e4fdac..45858fee 100644 --- a/contracts/protocol/payload-delivery/ContractFactoryPlug.sol +++ b/contracts/protocol/payload-delivery/ContractFactoryPlug.sol @@ -21,11 +21,22 @@ contract ContractFactoryPlug is PlugBase, AccessControl, IContractFactoryPlug { error DeploymentFailed(); error ExecutionFailed(); + /// @notice Constructor for the ContractFactoryPlug + /// @param socket_ The socket address + /// @param owner_ The owner address constructor(address socket_, address owner_) { _initializeOwner(owner_); _setSocket(socket_); } + /// @notice Deploys a contract + /// @param isPlug_ Whether the contract to be deployed is a plug + /// @param salt_ The salt used for create 2 + /// @param appGateway_ The app gateway address + /// @param switchboard_ The switchboard address + /// @param creationCode_ The creation code + /// @param initCallData_ The init call data + /// @return addr The address of the deployed contract function deployContract( IsPlug isPlug_, bytes32 salt_, @@ -33,12 +44,7 @@ contract ContractFactoryPlug is PlugBase, AccessControl, IContractFactoryPlug { address switchboard_, bytes memory creationCode_, bytes memory initCallData_ - ) public override returns (address) { - if (msg.sender != address(socket__)) { - revert NotSocket(); - } - - address addr; + ) public override onlySocket returns (address addr) { assembly { addr := create2(callvalue(), add(creationCode_, 0x20), mload(creationCode_), salt_) if iszero(addr) { @@ -73,7 +79,6 @@ contract ContractFactoryPlug is PlugBase, AccessControl, IContractFactoryPlug { } emit Deployed(addr, salt_, returnData); - return addr; } /// @notice Gets the address for a deployed contract From ad58512f609f559e1d318bd3fb1b9b2b9f4a7dd7 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 15 Apr 2025 01:10:09 +0530 Subject: [PATCH 039/108] docs: delivery helper --- contracts/interfaces/IMiddleware.sol | 41 ++++++- .../protocol/payload-delivery/FeesManager.sol | 2 +- .../app-gateway/DeliveryHelper.sol | 46 +++++--- .../app-gateway/DeliveryHelperStorage.sol | 21 ++-- .../app-gateway/DeliveryUtils.sol | 17 ++- .../app-gateway/FeesHelpers.sol | 10 +- .../app-gateway/RequestQueue.sol | 108 ++++++++++-------- 7 files changed, 151 insertions(+), 94 deletions(-) diff --git a/contracts/interfaces/IMiddleware.sol b/contracts/interfaces/IMiddleware.sol index 3509457d..658c15fe 100644 --- a/contracts/interfaces/IMiddleware.sol +++ b/contracts/interfaces/IMiddleware.sol @@ -1,29 +1,45 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.3; -import {QueuePayloadParams, Bid, Fees, WriteFinality, CallType, Parallel, IsPlug, RequestMetadata} from "../protocol/utils/common/Structs.sol"; +import {PayloadSubmitParams, QueuePayloadParams, Bid, Fees, WriteFinality, CallType, Parallel, IsPlug, RequestMetadata} from "../protocol/utils/common/Structs.sol"; +/// @title IMiddleware +/// @notice Interface for the Middleware contract interface IMiddleware { - event AuctionEnded( - uint40 indexed requestCount, - Bid winningBid // Replaced winningTransmitter and winningBid with Bid struct - ); - + /// @notice Returns the timeout after which a bid expires function bidTimeout() external view returns (uint128); + /// @notice Returns the metadata for a request + /// @param requestCount_ The request id + /// @return requestMetadata The metadata for the request function getRequestMetadata( uint40 requestCount_ ) external view returns (RequestMetadata memory); + /// @notice Clears the temporary queue used to store payloads for a request function clearQueue() external; + /// @notice Queues a payload for a request + /// @param queuePayloadParams_ The parameters for the payload function queue(QueuePayloadParams memory queuePayloadParams_) external; + /// @notice Batches a request + /// @param fees_ The fees for the request + /// @param auctionManager_ The address of the auction manager + /// @param onCompleteData_ The data to be passed to the onComplete callback + /// @return requestCount The request id function batch( Fees memory fees_, address auctionManager_, bytes memory onCompleteData_ ) external returns (uint40 requestCount); + /// @notice Withdraws funds to a receiver + /// @param chainSlug_ The chain slug + /// @param token_ The token address + /// @param amount_ The amount to withdraw + /// @param receiver_ The receiver address + /// @param auctionManager_ The address of the auction manager + /// @param fees_ The fees for the request function withdrawTo( uint32 chainSlug_, address token_, @@ -33,15 +49,28 @@ interface IMiddleware { Fees memory fees_ ) external returns (uint40); + /// @notice Cancels a request + /// @param requestCount_ The request id function cancelRequest(uint40 requestCount_) external; + /// @notice Increases the fees for a request + /// @param requestCount_ The request id + /// @param fees_ The new fees function increaseFees(uint40 requestCount_, uint256 fees_) external; + /// @notice Starts the request processing + /// @param requestCount_ The request id + /// @param winningBid_ The winning bid function startRequestProcessing(uint40 requestCount_, Bid memory winningBid_) external; + /// @notice Returns the fees for a request function getFees(uint40 requestCount_) external view returns (Fees memory); + /// @notice Finishes a request by assigning fees and calling the onComplete callback + /// @param requestCount_ The request id function finishRequest(uint40 requestCount_) external; + /// @notice Handles request reverts by unblocking the fees and calling the onRevert callback + /// @param requestCount_ The request id function handleRequestReverts(uint40 requestCount_) external; } diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 237dc290..c6fdd9a6 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -187,7 +187,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol return availableFees >= fees_.amount; } - /// @notice Blocks fees for transmitter + /// @notice Blocks fees for a request count /// @param originAppGateway_ The app gateway address /// @param feesGivenByApp_ The fees data struct given by the app gateway /// @param requestCount_ The batch identifier diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol index 70588fd0..52e58f4a 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol @@ -3,6 +3,8 @@ pragma solidity ^0.8.0; import "./FeesHelpers.sol"; +/// @title DeliveryHelper +/// @notice Contract for managing payload delivery contract DeliveryHelper is FeesHelpers { constructor() { _disableInitializers(); // disable for implementation @@ -17,14 +19,15 @@ contract DeliveryHelper is FeesHelpers { uint128 bidTimeout_ ) public reinitializer(1) { _setAddressResolver(addressResolver_); - bidTimeout = bidTimeout_; _initializeOwner(owner_); - } - function endTimeout(uint40 requestCount_) external onlyWatcherPrecompile { - IAuctionManager(requests[requestCount_].auctionManager).endAuction(requestCount_); + bidTimeout = bidTimeout_; } + /// @notice Calls the watcher precompile to start processing a request + /// @dev If a transmitter was already assigned, it updates the transmitter in watcher precompile too + /// @param requestCount_ The ID of the request + /// @param winningBid_ The winning bid function startRequestProcessing( uint40 requestCount_, Bid memory winningBid_ @@ -33,6 +36,7 @@ contract DeliveryHelper is FeesHelpers { if (winningBid_.transmitter == address(0)) revert InvalidTransmitter(); RequestMetadata storage requestMetadata_ = requests[requestCount_]; + // if a transmitter was already assigned, it means the request was restarted bool isRestarted = requestMetadata_.winningBid.transmitter != address(0); requestMetadata_.winningBid.transmitter = winningBid_.transmitter; @@ -43,6 +47,8 @@ contract DeliveryHelper is FeesHelpers { } } + /// @notice Finishes the request processing by assigning fees and calling the on complete hook on app gateway + /// @param requestCount_ The ID of the request function finishRequest(uint40 requestCount_) external onlyWatcherPrecompile { RequestMetadata storage requestMetadata_ = requests[requestCount_]; @@ -59,32 +65,32 @@ contract DeliveryHelper is FeesHelpers { ); } - /// @notice Cancels a request + /// @notice Cancels a request and settles the fees + /// @dev if no transmitter was assigned, fees is unblocked to app gateway + /// @dev Only app gateway can call this function /// @param requestCount_ The ID of the request function cancelRequest(uint40 requestCount_) external { if (msg.sender != requests[requestCount_].appGateway) { revert OnlyAppGateway(); } - // If the request has a winning bid, ie. transmitter already assigned, unblock and assign fees - if (requests[requestCount_].winningBid.transmitter != address(0)) { - IFeesManager(addressResolver__.feesManager()).unblockAndAssignFees( - requestCount_, - requests[requestCount_].winningBid.transmitter, - requests[requestCount_].appGateway - ); - } else { - // If the request has no winning bid, ie. transmitter not assigned, unblock fees - IFeesManager(addressResolver__.feesManager()).unblockFees(requestCount_); - } + _settleFees(requestCount_); watcherPrecompile__().cancelRequest(requestCount_); emit RequestCancelled(requestCount_); } - /// @notice Handles request reverts + /// @notice For request reverts, settles the fees /// @param requestCount_ The ID of the request function handleRequestReverts(uint40 requestCount_) external onlyWatcherPrecompile { - // assign fees after expiry time + _settleFees(requestCount_); + } + + /// @notice Settles the fees for a request + /// @dev If a transmitter was already assigned, it unblocks and assigns fees to the transmitter + /// @dev If no transmitter was assigned, it unblocks fees to the app gateway + /// @param requestCount_ The ID of the request + function _settleFees(uint40 requestCount_) internal { + // If the request has a winning bid, ie. transmitter already assigned, unblock and assign fees if (requests[requestCount_].winningBid.transmitter != address(0)) { IFeesManager(addressResolver__.feesManager()).unblockAndAssignFees( requestCount_, @@ -92,10 +98,14 @@ contract DeliveryHelper is FeesHelpers { requests[requestCount_].appGateway ); } else { + // If the request has no winning bid, ie. transmitter not assigned, unblock fees IFeesManager(addressResolver__.feesManager()).unblockFees(requestCount_); } } + /// @notice Returns the request metadata + /// @param requestCount_ The ID of the request + /// @return requestMetadata The request metadata function getRequestMetadata( uint40 requestCount_ ) external view returns (RequestMetadata memory) { diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol index 81bbc0dc..6b986bab 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol @@ -1,19 +1,15 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.21; -import {IMiddleware} from "../../../interfaces/IMiddleware.sol"; -import {IPromise} from "../../../interfaces/IPromise.sol"; +import "../../../interfaces/IMiddleware.sol"; import {IAddressResolver} from "../../../interfaces/IAddressResolver.sol"; import {IContractFactoryPlug} from "../../../interfaces/IContractFactoryPlug.sol"; import {IAppGateway} from "../../../interfaces/IAppGateway.sol"; -import {IAppGateway} from "../../../interfaces/IAppGateway.sol"; -import {IAddressResolver} from "../../../interfaces/IAddressResolver.sol"; import {IAuctionManager} from "../../../interfaces/IAuctionManager.sol"; import {IFeesManager} from "../../../interfaces/IFeesManager.sol"; -import {QueuePayloadParams, Fees, CallType, Bid, Parallel, IsPlug, WriteFinality, RequestMetadata} from "../../utils/common/Structs.sol"; import {NotAuctionManager, InvalidTransmitter, InvalidIndex} from "../../utils/common/Errors.sol"; -import {FORWARD_CALL, DISTRIBUTE_FEE, DEPLOY, QUERY, FINALIZE} from "../../utils/common/Constants.sol"; +import {DEPLOY, PAYLOAD_SIZE_LIMIT, REQUEST_PAYLOAD_COUNT_LIMIT} from "../../utils/common/Constants.sol"; /// @title DeliveryHelperStorage /// @notice Storage contract for DeliveryHelper @@ -22,22 +18,27 @@ abstract contract DeliveryHelperStorage is IMiddleware { uint256[50] _gap_before; // slot 50 + /// @notice The timeout after which a bid expires uint128 public bidTimeout; + // slot 51 + /// @notice The counter for the salt used to generate/deploy the contract address uint256 public saltCounter; // slot 52 - /// @notice The call parameters array + /// @notice The parameters array used to store payloads for a request QueuePayloadParams[] public queuePayloadParams; // slot 53 + /// @notice The metadata for a request mapping(uint40 => RequestMetadata) public requests; // slot 54 + /// @notice The maximum message value limit for a chain mapping(uint32 => uint256) public chainMaxMsgValueLimit; - // slots [55-103] reserved for gap - uint256[49] _gap_after; + // slots [55-104] reserved for gap + uint256[50] _gap_after; - // slots 104-154 (51) reserved for addr resolver util + // slots 105-155 (51) reserved for addr resolver utils } diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol index 988e17f4..9bd0baa9 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol @@ -1,11 +1,10 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.21; -import {Ownable} from "solady/auth/Ownable.sol"; import "solady/utils/Initializable.sol"; +import {Ownable} from "solady/auth/Ownable.sol"; import {AddressResolverUtil} from "../../utils/AddressResolverUtil.sol"; import "./DeliveryHelperStorage.sol"; -import {PayloadSubmitParams} from "../../utils/common/Structs.sol"; /// @notice Abstract contract for managing asynchronous payloads abstract contract DeliveryUtils is @@ -14,13 +13,9 @@ abstract contract DeliveryUtils is Ownable, AddressResolverUtil { - // slots [155-205] reserved for gap + // slots [156-206] reserved for gap uint256[50] _gap_delivery_utils; - /// @notice Error thrown when attempting to executed payloads after all have been executed - error AllPayloadsExecuted(); - /// @notice Error thrown request did not come from Forwarder address - error NotFromForwarder(); /// @notice Error thrown if payload is too large error PayloadTooLarge(); /// @notice Error thrown if trying to cancel a batch without being the application gateway @@ -37,8 +32,6 @@ abstract contract DeliveryUtils is /// @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); event PayloadSubmitted( uint40 indexed requestCount, @@ -54,6 +47,10 @@ abstract contract DeliveryUtils is uint40 indexed requestCount, uint256 newMaxFees ); + /// @notice Emitted when chain max message value limits are updated + event ChainMaxMsgValueLimitsUpdated(uint32[] chainSlugs, uint256[] maxMsgValueLimits); + /// @notice Emitted when a request is cancelled + event RequestCancelled(uint40 indexed requestCount); modifier onlyAuctionManager(uint40 requestCount_) { if (msg.sender != requests[requestCount_].auctionManager) revert NotAuctionManager(); @@ -86,5 +83,7 @@ abstract contract DeliveryUtils is for (uint256 i = 0; i < chainSlugs_.length; i++) { chainMaxMsgValueLimit[chainSlugs_[i]] = maxMsgValueLimits_[i]; } + + emit ChainMaxMsgValueLimitsUpdated(chainSlugs_, maxMsgValueLimits_); } } diff --git a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol index 2bf5cd46..ad64dc3b 100644 --- a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol +++ b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol @@ -6,11 +6,16 @@ import "./RequestQueue.sol"; /// @title RequestAsync /// @notice Abstract contract for managing asynchronous payload batches abstract contract FeesHelpers is RequestQueue { - // slots [256-306] reserved for gap + // slots [258-308] reserved for gap uint256[50] _gap_batch_async; + /// @notice Increases the fees for a request if no bid is placed + /// @param requestCount_ The ID of the request + /// @param newMaxFees_ The new maximum fees function increaseFees(uint40 requestCount_, uint256 newMaxFees_) external override { address appGateway = _getCoreAppGateway(msg.sender); + + // todo: should we allow core app gateway too? if (appGateway != requests[requestCount_].appGateway) { revert OnlyAppGateway(); } @@ -45,6 +50,9 @@ abstract contract FeesHelpers is RequestQueue { return _batch(msg.sender, auctionManager_, fees_, bytes("")); } + /// @notice Returns the fees for a request + /// @param requestCount_ The ID of the request + /// @return fees The fees data function getFees(uint40 requestCount_) external view returns (Fees memory) { return requests[requestCount_].fees; } diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index 27a7c314..250e9181 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -1,14 +1,11 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.21; -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 [205-255] reserved for gap + // slots [207-257] reserved for gap uint256[50] _gap_queue_async; /// @notice Clears the call parameters array @@ -19,8 +16,6 @@ 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_); } @@ -37,6 +32,27 @@ abstract contract RequestQueue is DeliveryUtils { return _batch(appGateway, auctionManager_, fees_, onCompleteData_); } + function _checkBatch( + address appGateway_, + address auctionManager_, + Fees memory fees_ + ) internal view returns (address) { + if (queuePayloadParams.length > REQUEST_PAYLOAD_COUNT_LIMIT) + revert RequestPayloadCountLimitExceeded(); + + if (!IFeesManager(addressResolver__.feesManager()).isFeesEnough(appGateway_, fees_)) + revert InsufficientFees(); + + return + auctionManager_ == address(0) + ? IAddressResolver(addressResolver__).defaultAuctionManager() + : auctionManager_; + } + + /// @notice Initiates a batch of payloads + /// @dev it checks fees, payload limits and creates the payload submit params array after assigning proper levels + /// @dev It also modifies the deploy payloads as needed by contract factory plug + /// @dev Stores request metadata and submits the request to watcher precompile function _batch( address appGateway_, address auctionManager_, @@ -44,16 +60,13 @@ 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(); + auctionManager_ = _checkBatch(appGateway_, auctionManager_, fees_); + // create the payload submit params array in desired format ( PayloadSubmitParams[] memory payloadSubmitParamsArray, - , bool onlyReadRequests ) = _createPayloadSubmitParamsArray(); - if (auctionManager_ == address(0)) - auctionManager_ = IAddressResolver(addressResolver__).defaultAuctionManager(); RequestMetadata memory requestMetadata = RequestMetadata({ appGateway: appGateway_, @@ -63,7 +76,7 @@ abstract contract RequestQueue is DeliveryUtils { onCompleteData: onCompleteData_, onlyReadRequests: onlyReadRequests }); - + // process and submit the queue of payloads to watcher precompile requestCount = watcherPrecompile__().submitRequest(payloadSubmitParamsArray); requests[requestCount] = requestMetadata; @@ -86,34 +99,50 @@ abstract contract RequestQueue is DeliveryUtils { /// @return payloadDetailsArray An array of payload details function _createPayloadSubmitParamsArray() internal - returns ( - PayloadSubmitParams[] memory payloadDetailsArray, - uint256 totalLevels, - bool onlyReadRequests - ) + returns (PayloadSubmitParams[] memory payloadDetailsArray, bool onlyReadRequests) { - if (queuePayloadParams.length == 0) - return (payloadDetailsArray, totalLevels, onlyReadRequests); payloadDetailsArray = new PayloadSubmitParams[](queuePayloadParams.length); - - totalLevels = 0; onlyReadRequests = queuePayloadParams[0].callType == CallType.READ; + + uint256 currentLevel = 0; for (uint256 i = 0; i < queuePayloadParams.length; i++) { if (queuePayloadParams[i].callType != CallType.READ) { onlyReadRequests = false; } - // Update level for sequential calls + // Update level for calls if (i > 0 && queuePayloadParams[i].isParallel != Parallel.ON) { - totalLevels = totalLevels + 1; + currentLevel = currentLevel + 1; } - payloadDetailsArray[i] = _createPayloadDetails(totalLevels, queuePayloadParams[i]); + payloadDetailsArray[i] = _createPayloadDetails(currentLevel, queuePayloadParams[i]); } clearQueue(); } + function _createDeployPayloadDetails( + QueuePayloadParams memory queuePayloadParams_ + ) internal returns (bytes memory payload, address target) { + bytes32 salt = keccak256( + abi.encode(queuePayloadParams_.appGateway, queuePayloadParams_.chainSlug, saltCounter++) + ); + + // app gateway is set in the plug deployed on chain + payload = abi.encodeWithSelector( + IContractFactoryPlug.deployContract.selector, + queuePayloadParams_.isPlug, + salt, + queuePayloadParams_.appGateway, + queuePayloadParams_.switchboard, + queuePayloadParams_.payload, + queuePayloadParams_.initCallData + ); + + // getting app gateway for deployer as the plug is connected to the app gateway + target = getDeliveryHelperPlugAddress(queuePayloadParams_.chainSlug); + } + /// @notice Creates the payload details for a given call parameters /// @param queuePayloadParams_ The call parameters /// @return payloadDetails The payload details @@ -121,35 +150,16 @@ abstract contract RequestQueue is DeliveryUtils { uint256 level_, QueuePayloadParams memory queuePayloadParams_ ) internal returns (PayloadSubmitParams memory) { - bytes memory payload_ = queuePayloadParams_.payload; + bytes memory payload = queuePayloadParams_.payload; address target = queuePayloadParams_.target; if (queuePayloadParams_.callType == CallType.DEPLOY) { - // getting app gateway for deployer as the plug is connected to the app gateway - bytes32 salt_ = keccak256( - abi.encode( - queuePayloadParams_.appGateway, - queuePayloadParams_.chainSlug, - saltCounter++ - ) - ); - - // app gateway is set in the plug deployed on chain - payload_ = abi.encodeWithSelector( - IContractFactoryPlug.deployContract.selector, - queuePayloadParams_.isPlug, - salt_, - queuePayloadParams_.appGateway, - queuePayloadParams_.switchboard, - payload_, - queuePayloadParams_.initCallData - ); - - target = getDeliveryHelperPlugAddress(queuePayloadParams_.chainSlug); + (payload, target) = _createDeployPayloadDetails(queuePayloadParams_); } - if (payload_.length > PAYLOAD_SIZE_LIMIT) revert PayloadTooLarge(); + if (payload.length > PAYLOAD_SIZE_LIMIT) revert PayloadTooLarge(); if (queuePayloadParams_.value > chainMaxMsgValueLimit[queuePayloadParams_.chainSlug]) revert MaxMsgValueLimitExceeded(); + return PayloadSubmitParams({ levelNumber: level_, @@ -166,7 +176,7 @@ abstract contract RequestQueue is DeliveryUtils { : queuePayloadParams_.gasLimit, value: queuePayloadParams_.value, readAt: queuePayloadParams_.readAt, - payload: payload_ + payload: payload }); } } From abc03c28fd0956629272d8c06dfadd6bf5fd90fa Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 15 Apr 2025 01:19:33 +0530 Subject: [PATCH 040/108] docs: watcher limits --- .../interfaces/IWatcherPrecompileLimits.sol | 19 +++++-------------- contracts/protocol/utils/common/Errors.sol | 1 + .../WatcherPrecompileLimits.sol | 18 +++++++++--------- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/contracts/interfaces/IWatcherPrecompileLimits.sol b/contracts/interfaces/IWatcherPrecompileLimits.sol index 887c379f..62b52ce4 100644 --- a/contracts/interfaces/IWatcherPrecompileLimits.sol +++ b/contracts/interfaces/IWatcherPrecompileLimits.sol @@ -31,11 +31,7 @@ interface IWatcherPrecompileLimits { /// @notice Set the default limit value /// @param defaultLimit_ The new default limit value - function setDefaultLimit(uint256 defaultLimit_) external; - - /// @notice Set the rate at which limit replenishes - /// @param defaultRatePerSecond_ The new rate per second - function setDefaultRatePerSecond(uint256 defaultRatePerSecond_) external; + function setDefaultLimitAndRatePerSecond(uint256 defaultLimit_) external; /// @notice Number of decimals used in limit calculations function limitDecimals() external view returns (uint256); @@ -46,6 +42,10 @@ interface IWatcherPrecompileLimits { /// @notice Rate at which limit replenishes per second function defaultRatePerSecond() external view returns (uint256); + /// @notice Consumes a limit for an app gateway + /// @param appGateway_ The app gateway address + /// @param limitType_ The type of limit to consume + /// @param consumeLimit_ The amount of limit to consume function consumeLimit(address appGateway_, bytes32 limitType_, uint256 consumeLimit_) external; /// @notice Emitted when limit parameters are updated @@ -53,13 +53,4 @@ interface IWatcherPrecompileLimits { /// @notice Emitted when an app gateway is activated with default limits event AppGatewayActivated(address indexed appGateway, uint256 maxLimit, uint256 ratePerSecond); - - error ActionNotSupported(address appGateway_, bytes32 limitType_); - error NotDeliveryHelper(); - error LimitExceeded( - address appGateway, - bytes32 limitType, - uint256 requested, - uint256 available - ); } diff --git a/contracts/protocol/utils/common/Errors.sol b/contracts/protocol/utils/common/Errors.sol index 3aa47e55..03078541 100644 --- a/contracts/protocol/utils/common/Errors.sol +++ b/contracts/protocol/utils/common/Errors.sol @@ -32,3 +32,4 @@ error BidExceedsMaxFees(); error LowerBidAlreadyExists(); error AsyncModifierNotUsed(); error InvalidIndex(); +error RequestAlreadyExecuted(); diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index 4f78a84f..4fdc00b9 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -8,6 +8,8 @@ import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; import "../../interfaces/IWatcherPrecompileLimits.sol"; import {SCHEDULE, QUERY, FINALIZE} from "../utils/common/Constants.sol"; +/// @title WatcherPrecompileLimits +/// @notice Contract for managing watcher precompile limits contract WatcherPrecompileLimits is IWatcherPrecompileLimits, Initializable, @@ -41,6 +43,9 @@ contract WatcherPrecompileLimits is // Mapping to track active app gateways mapping(address => bool) internal _activeAppGateways; + /// @notice Emitted when the default limit and rate per second are set + event DefaultLimitAndRatePerSecondSet(uint256 defaultLimit, uint256 defaultRatePerSecond); + /// @notice Initial initialization (version 1) function initialize( address owner_, @@ -120,7 +125,7 @@ contract WatcherPrecompileLimits is ) external override onlyWatcherPrecompile { LimitParams storage limitParams = _limitParams[appGateway_][limitType_]; - // Initialize limit if not active + // Initialize limit if not active, give default limit and rate per second if (!_activeAppGateways[appGateway_]) { LimitParams memory limitParam = LimitParams({ maxLimit: defaultLimit, @@ -145,15 +150,10 @@ contract WatcherPrecompileLimits is * @notice Set the default limit value * @param defaultLimit_ The new default limit value */ - function setDefaultLimit(uint256 defaultLimit_) external onlyOwner { + function setDefaultLimitAndRatePerSecond(uint256 defaultLimit_) external onlyOwner { defaultLimit = defaultLimit_; - } + defaultRatePerSecond = defaultLimit / (24 * 60 * 60); - /** - * @notice Set the rate at which limit replenishes - * @param defaultRatePerSecond_ The new rate per second - */ - function setDefaultRatePerSecond(uint256 defaultRatePerSecond_) external onlyOwner { - defaultRatePerSecond = defaultRatePerSecond_; + emit DefaultLimitAndRatePerSecondSet(defaultLimit, defaultRatePerSecond); } } From 2233f7d2f24330cf2ce646dbd83fa58b1be4c7dc Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 15 Apr 2025 01:52:04 +0530 Subject: [PATCH 041/108] fix: watcher precompile --- contracts/interfaces/IWatcherPrecompile.sol | 6 + .../watcherPrecompile/core/RequestHandler.sol | 84 ++++++--- .../core/WatcherPrecompile.sol | 161 +++++++++++++----- .../core/WatcherPrecompileCore.sol | 130 ++++++++------ .../core/WatcherPrecompileStorage.sol | 24 ++- 5 files changed, 281 insertions(+), 124 deletions(-) diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 14658633..45de683c 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -72,6 +72,10 @@ interface IWatcherPrecompile { event ExpiryTimeSet(uint256 expiryTime); + event WatcherPrecompileLimitsSet(address watcherPrecompileLimits); + + event WatcherPrecompileConfigSet(address watcherPrecompileConfig); + /// @notice Error thrown when an invalid chain slug is provided error InvalidChainSlug(); /// @notice Error thrown when an invalid app gateway reaches a plug @@ -91,8 +95,10 @@ interface IWatcherPrecompile { error RequestCancelled(); error AlreadyStarted(); + error RequestNotProcessing(); error InvalidLevelNumber(); error DeadlineNotPassedForOnChainRevert(); + /// @notice Calculates the digest hash of payload parameters /// @param params_ The payload parameters /// @return digest The calculated digest diff --git a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol index c5d5f1ab..4337f634 100644 --- a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol +++ b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol @@ -3,12 +3,21 @@ pragma solidity ^0.8.21; import "./WatcherPrecompileCore.sol"; +/// @title RequestHandler +/// @notice Contract that handles request submission and processing +/// @dev This contract extends WatcherPrecompileCore to provide request handling functionality +/// @dev It manages the submission of payload requests and their processing abstract contract RequestHandler is WatcherPrecompileCore { using DumpDecoder for bytes32; // slots [266-315] reserved for gap uint256[50] _request_handler_gap; + /// @notice Submits a batch of payload requests from middleware + /// @param payloadSubmitParams Array of payload submit parameters + /// @return requestCount The unique identifier for the submitted request + /// @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 ) public returns (uint40 requestCount) { @@ -25,11 +34,12 @@ abstract contract RequestHandler is WatcherPrecompileCore { for (uint256 i = 0; i < payloadSubmitParams.length; i++) { PayloadSubmitParams memory p = payloadSubmitParams[i]; - // Count reads and writes + // Count reads and writes for checking limits if (p.callType == CallType.READ) { readCount++; } else writeCount++; + // checking level number for batching if (i > 0) { if (p.levelNumber != lastP.levelNumber && p.levelNumber != lastP.levelNumber + 1) revert InvalidLevelNumber(); @@ -40,7 +50,13 @@ abstract contract RequestHandler is WatcherPrecompileCore { } uint40 localPayloadCount = payloadCounter++; - bytes32 payloadId = _createPayloadId(p, requestCount, batchCount, localPayloadCount); + bytes32 payloadId = _createPayloadId( + requestCount, + batchCount, + localPayloadCount, + p.switchboard, + p.chainSlug + ); batchPayloadIds[batchCount].push(payloadId); bytes32 dump; @@ -60,28 +76,25 @@ abstract contract RequestHandler is WatcherPrecompileCore { ? addressResolver__.deliveryHelper() : p.appGateway; payloads[payloadId].payloadId = payloadId; - payloads[payloadId].prevDigestsHash = bytes32(0); payloads[payloadId].gasLimit = p.gasLimit; payloads[payloadId].value = p.value; payloads[payloadId].readAt = p.readAt; - payloads[payloadId].deadline = 0; payloads[payloadId].payload = p.payload; - payloads[payloadId].finalizedTransmitter = address(0); requestParams[requestCount].payloadParamsArray.push(payloads[payloadId]); lastP = p; } + // Push the final batch ID to the request's batch list and increment the counter + // 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].isRequestCancelled = false; + requestParams[requestCount].currentBatch = currentBatch; - requestParams[requestCount].currentBatchPayloadsLeft = 0; requestParams[requestCount].payloadsRemaining = payloadSubmitParams.length; requestParams[requestCount].middleware = msg.sender; - requestParams[requestCount].transmitter = address(0); emit RequestSubmitted( msg.sender, @@ -90,22 +103,35 @@ abstract contract RequestHandler is WatcherPrecompileCore { ); } + /// @notice Checks if all app gateways in the payload submit parameters are valid and same + /// @dev It also handles special cases for the delivery helper + /// @param payloadSubmitParams Array of payload submit parameters + /// @return appGateway The core app gateway address function _checkAppGateways( PayloadSubmitParams[] calldata payloadSubmitParams ) internal view returns (address appGateway) { bool isDeliveryHelper = msg.sender == addressResolver__.deliveryHelper(); + + // Get first app gateway and use it as reference address coreAppGateway = isDeliveryHelper - ? payloadSubmitParams[0].appGateway - : _getCoreAppGateway(payloadSubmitParams[0].appGateway); - for (uint256 i = 0; i < payloadSubmitParams.length; i++) { - address callerAppGateway = isDeliveryHelper - ? payloadSubmitParams[i].appGateway - : msg.sender; - appGateway = _getCoreAppGateway(callerAppGateway); + ? _getCoreAppGateway(payloadSubmitParams[0].appGateway) + : _getCoreAppGateway(msg.sender); + + // Skip first element since we already checked it + for (uint256 i = 1; i < payloadSubmitParams.length; i++) { + appGateway = isDeliveryHelper + ? _getCoreAppGateway(payloadSubmitParams[i].appGateway) + : coreAppGateway; + if (appGateway != coreAppGateway) revert InvalidGateway(); } } + /// @notice Starts processing a request with a specified transmitter + /// @param requestCount The request count to start processing + /// @param transmitter The address of the transmitter + /// @dev This function initiates the processing of a request by a transmitter + /// @dev It verifies that the caller is the middleware and that the request hasn't been started yet function startProcessingRequest(uint40 requestCount, address transmitter) public { RequestParams storage r = requestParams[requestCount]; if (r.middleware != msg.sender) revert InvalidCaller(); @@ -116,24 +142,23 @@ abstract contract RequestHandler is WatcherPrecompileCore { r.transmitter = transmitter; r.currentBatch = batchCount; - uint256 totalPayloadsLeft = _processBatch(requestCount, batchCount); - // todo: for retry cases - r.currentBatchPayloadsLeft = totalPayloadsLeft; + _processBatch(requestCount, batchCount); } - function _processBatch( - uint40 requestCount_, - uint40 batchCount_ - ) internal returns (uint256 totalPayloadsLeft) { - RequestParams memory r = requestParams[requestCount_]; + /// @notice Processes a batch of payloads for a request + /// @param requestCount_ The request count to process + /// @param batchCount_ The batch count to process + /// @dev This function processes all payloads in a batch, either finalizing them or querying them + /// @dev It skips payloads that have already been executed + function _processBatch(uint40 requestCount_, uint40 batchCount_) internal { + RequestParams storage r = requestParams[requestCount_]; PayloadParams[] memory payloadParamsArray = _getBatch(batchCount_); - if (r.isRequestCancelled) revert RequestCancelled(); + uint256 totalPayloads = 0; for (uint40 i = 0; i < payloadParamsArray.length; i++) { - bool executed = isPromiseExecuted[payloadParamsArray[i].payloadId]; - if (executed) continue; - totalPayloadsLeft++; + if (isPromiseExecuted[payloadParamsArray[i].payloadId]) continue; + totalPayloads++; if (payloadParamsArray[i].dump.getCallType() != CallType.READ) { _finalize(payloadParamsArray[i], r.transmitter); @@ -141,8 +166,13 @@ abstract contract RequestHandler is WatcherPrecompileCore { _query(payloadParamsArray[i]); } } + + r.currentBatchPayloadsLeft = totalPayloads; } + /// @notice Gets the current request count + /// @return The current request count + /// @dev This function returns the next request count, which is the current request count function getCurrentRequestCount() external view returns (uint40) { return nextRequestCount; } diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index a09f78ff..f8b8cf19 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -5,15 +5,26 @@ import "./RequestHandler.sol"; import {LibCall} from "solady/utils/LibCall.sol"; /// @title WatcherPrecompile -/// @notice Contract that handles payload verification, execution and app configurations +/// @notice Contract that handles request submission, iteration and execution +/// @dev This contract extends RequestHandler to provide the main functionality for the WatcherPrecompile system +/// @dev It handles timeout requests, finalization, queries, and promise resolution contract WatcherPrecompile is RequestHandler { using DumpDecoder for bytes32; using LibCall for address; + + /// @notice Constructor that disables initializers for the implementation constructor() { - _disableInitializers(); // disable for implementation + _disableInitializers(); } - /// @notice Initial initialization (version 1) + /// @notice Initializes the contract with the required parameters + /// @param owner_ The address of the owner + /// @param addressResolver_ The address of the address resolver + /// @param expiryTime_ The expiry time for payload execution + /// @param evmxSlug_ The EVM chain slug + /// @param watcherPrecompileLimits_ The address of the watcher precompile limits contract + /// @param watcherPrecompileConfig_ The address of the watcher precompile config contract + /// @dev This function initializes the contract with the required parameters and sets up the initial state function initialize( address owner_, address addressResolver_, @@ -36,18 +47,24 @@ contract WatcherPrecompile is RequestHandler { // ================== Timeout functions ================== /// @notice Sets a timeout for a payload execution on app gateway - /// @param payload_ The payload data - /// @param delayInSeconds_ The delay in seconds + /// @dev This function creates a timeout request that will be executed after the specified `delayInSeconds_` + /// @dev request is executed on msg.sender + /// @dev msg sender needs SCHEDULE precompile limit + /// @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) { - return _setTimeout(payload_, delayInSeconds_); + return _setTimeout(delayInSeconds_, payload_); } /// @notice Ends the timeouts and calls the target address with the callback payload /// @param timeoutId_ The unique identifier for the timeout - /// @dev Only callable by the contract owner + /// @param signatureNonce_ The nonce used in the watcher's signature + /// @param signature_ The watcher's signature + /// @dev It verifies if the signature is valid and the timeout hasn't been resolved yet function resolveTimeout( bytes32 timeoutId_, uint256 signatureNonce_, @@ -64,7 +81,7 @@ contract WatcherPrecompile is RequestHandler { if (timeoutRequest_.isResolved) revert TimeoutAlreadyResolved(); if (block.timestamp < timeoutRequest_.executeAt) revert ResolvingTimeoutTooEarly(); - (bool success, , ) = timeoutRequest_.target.tryCall( + (bool success, , bytes memory returnData) = timeoutRequest_.target.tryCall( 0, gasleft(), 0, // setting max_copy_bytes to 0 as not using returnData right now @@ -79,7 +96,8 @@ contract WatcherPrecompile is RequestHandler { timeoutId_, timeoutRequest_.target, timeoutRequest_.payload, - block.timestamp + block.timestamp, + returnData ); } @@ -88,16 +106,20 @@ contract WatcherPrecompile is RequestHandler { /// @notice Finalizes a payload request, requests the watcher to release the proofs to execute on chain /// @param params_ The payload parameters /// @param transmitter_ The address of the transmitter + /// @return The digest hash of the finalized payload + /// @dev This function finalizes a payload request and requests the watcher to release the proofs function finalize( PayloadParams memory params_, address transmitter_ - ) external returns (bytes32 digest) { - digest = _finalize(params_, transmitter_); + ) external returns (bytes32) { + return _finalize(params_, transmitter_); } // ================== Query functions ================== + /// @notice Creates a new query request /// @param params_ The payload parameters + /// @dev This function creates a new query request function query(PayloadParams memory params_) external { _query(params_); } @@ -107,7 +129,8 @@ contract WatcherPrecompile is RequestHandler { /// @param proof_ The watcher's proof /// @param signatureNonce_ The nonce of the signature /// @param signature_ The signature of the watcher - /// @dev Only callable by the contract owner + /// @dev This function marks a request as finalized with a proof + /// @dev It verifies that the signature is valid /// @dev Watcher signs on following digest for validation on switchboard: /// @dev keccak256(abi.encode(switchboard, digest)) function finalized( @@ -126,17 +149,26 @@ contract WatcherPrecompile is RequestHandler { emit Finalized(payloadId_, proof_); } + /// @notice Updates the transmitter for a request + /// @param requestCount The request count to update + /// @param transmitter The new transmitter address + /// @dev This function updates the transmitter for a request + /// @dev It verifies that the caller is the middleware and that the request hasn't been started yet function updateTransmitter(uint40 requestCount, address transmitter) public { RequestParams storage r = requestParams[requestCount]; if (r.isRequestCancelled) revert RequestCancelled(); + if (r.payloadsRemaining == 0) revert RequestAlreadyExecuted(); if (r.middleware != msg.sender) revert InvalidCaller(); - if (r.transmitter != address(0)) revert AlreadyStarted(); - + if (r.transmitter != address(0)) revert RequestNotProcessing(); r.transmitter = transmitter; - /// todo: recheck limits + _processBatch(requestCount, r.currentBatch); } + /// @notice Cancels a request + /// @param requestCount The request count to cancel + /// @dev This function cancels a request + /// @dev It verifies that the caller is the middleware and that the request hasn't been cancelled yet function cancelRequest(uint40 requestCount) external { RequestParams storage r = requestParams[requestCount]; if (r.isRequestCancelled) revert RequestAlreadyCancelled(); @@ -147,7 +179,11 @@ contract WatcherPrecompile is RequestHandler { /// @notice Resolves multiple promises with their return data /// @param resolvedPromises_ Array of resolved promises and their return data - /// @dev Only callable by the contract owner + /// @param signatureNonce_ The nonce of the signature + /// @param signature_ The signature of the watcher + /// @dev This function resolves multiple promises with their return data + /// @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_, uint256 signatureNonce_, @@ -163,38 +199,37 @@ contract WatcherPrecompile is RequestHandler { // Get the array of promise addresses for this payload PayloadParams memory payloadParams = payloads[resolvedPromises_[i].payloadId]; address asyncPromise = payloadParams.asyncPromise; - if (asyncPromise == address(0)) continue; - // Resolve each promise with its corresponding return data - bool success = IPromise(asyncPromise).markResolved( - payloadParams.dump.getRequestCount(), - resolvedPromises_[i].payloadId, - resolvedPromises_[i].returnData - ); + if (asyncPromise != address(0)) { + // Resolve each promise with its corresponding return data + bool success = IPromise(asyncPromise).markResolved( + payloadParams.dump.getRequestCount(), + resolvedPromises_[i].payloadId, + resolvedPromises_[i].returnData + ); - isPromiseExecuted[resolvedPromises_[i].payloadId] = true; - if (!success) { - emit PromiseNotResolved(resolvedPromises_[i].payloadId, asyncPromise); - continue; + if (!success) { + emit PromiseNotResolved(resolvedPromises_[i].payloadId, asyncPromise); + continue; + } } + isPromiseExecuted[resolvedPromises_[i].payloadId] = true; + RequestParams storage requestParams_ = requestParams[ payloadParams.dump.getRequestCount() ]; - requestParams_.currentBatchPayloadsLeft--; requestParams_.payloadsRemaining--; + // if all payloads of a batch are executed, process the next batch if ( requestParams_.currentBatchPayloadsLeft == 0 && requestParams_.payloadsRemaining > 0 ) { - uint256 totalPayloadsLeft = _processBatch( - payloadParams.dump.getRequestCount(), - ++requestParams_.currentBatch - ); - requestParams_.currentBatchPayloadsLeft = totalPayloadsLeft; + _processBatch(payloadParams.dump.getRequestCount(), ++requestParams_.currentBatch); } + // if all payloads of a request are executed, finish the request if (requestParams_.payloadsRemaining == 0) { IMiddleware(requestParams_.middleware).finishRequest( payloadParams.dump.getRequestCount() @@ -204,7 +239,14 @@ contract WatcherPrecompile is RequestHandler { } } - // wait till expiry time to assign fees + /// @notice Marks a request as reverting + /// @param isRevertingOnchain_ Whether the request is reverting onchain + /// @param payloadId_ The unique identifier of the payload + /// @param signatureNonce_ The nonce of the signature + /// @param signature_ The signature of the watcher + /// @dev This function marks a request as reverting + /// @dev It verifies that the signature is valid + /// @dev It also handles onchain reverts if specified function markRevert( bool isRevertingOnchain_, bytes32 payloadId_, @@ -217,7 +259,7 @@ contract WatcherPrecompile is RequestHandler { signature_ ); - PayloadParams storage payloadParams = payloads[payloadId_]; + PayloadParams memory payloadParams = payloads[payloadId_]; if (payloadParams.deadline > block.timestamp) revert DeadlineNotPassedForOnChainRevert(); RequestParams storage currentRequestParams = requestParams[ @@ -225,7 +267,7 @@ contract WatcherPrecompile is RequestHandler { ]; currentRequestParams.isRequestCancelled = true; - if (isRevertingOnchain_) + if (isRevertingOnchain_ && payloadParams.asyncPromise != address(0)) IPromise(payloadParams.asyncPromise).markOnchainRevert( payloadParams.dump.getRequestCount(), payloadId_ @@ -238,12 +280,14 @@ contract WatcherPrecompile is RequestHandler { emit MarkedRevert(payloadId_, isRevertingOnchain_); } - function setMaxTimeoutDelayInSeconds(uint256 maxTimeoutDelayInSeconds_) external onlyOwner { - maxTimeoutDelayInSeconds = maxTimeoutDelayInSeconds_; - } - - // ================== On-Chain Trigger ================== + // ================== On-Chain Inbox ================== + /// @notice Calls app gateways with the specified parameters + /// @param params_ Array of call from chain parameters + /// @param signatureNonce_ The nonce of the signature + /// @param signature_ The signature of the watcher + /// @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_, uint256 signatureNonce_, @@ -288,10 +332,45 @@ contract WatcherPrecompile is RequestHandler { // ================== Helper functions ================== + /// @notice Sets the maximum timeout delay in seconds + /// @param maxTimeoutDelayInSeconds_ The maximum timeout delay in seconds + /// @dev This function sets the maximum timeout delay in seconds + /// @dev Only callable by the contract owner + function setMaxTimeoutDelayInSeconds(uint256 maxTimeoutDelayInSeconds_) external onlyOwner { + maxTimeoutDelayInSeconds = maxTimeoutDelayInSeconds_; + emit MaxTimeoutDelayInSecondsSet(maxTimeoutDelayInSeconds_); + } + + /// @notice Sets the expiry time for payload execution + /// @param expiryTime_ The expiry time in seconds + /// @dev This function sets the expiry time for payload execution + /// @dev Only callable by the contract owner function setExpiryTime(uint256 expiryTime_) external onlyOwner { expiryTime = expiryTime_; + emit ExpiryTimeSet(expiryTime_); + } + + /// @notice Sets the watcher precompile limits contract + /// @param watcherPrecompileLimits_ The address of the watcher precompile limits contract + /// @dev This function sets the watcher precompile limits contract + /// @dev Only callable by the contract owner + function setWatcherPrecompileLimits(address watcherPrecompileLimits_) external onlyOwner { + watcherPrecompileLimits__ = IWatcherPrecompileLimits(watcherPrecompileLimits_); + emit WatcherPrecompileLimitsSet(watcherPrecompileLimits_); + } + + /// @notice Sets the watcher precompile config contract + /// @param watcherPrecompileConfig_ The address of the watcher precompile config contract + /// @dev This function sets the watcher precompile config contract + /// @dev Only callable by the contract owner + function setWatcherPrecompileConfig(address watcherPrecompileConfig_) external onlyOwner { + watcherPrecompileConfig__ = IWatcherPrecompileConfig(watcherPrecompileConfig_); + emit WatcherPrecompileConfigSet(watcherPrecompileConfig_); } + /// @notice Gets the request parameters for a request + /// @param requestCount The request count to get the parameters for + /// @return The request parameters for the given request count function getRequestParams(uint40 requestCount) external view returns (RequestParams memory) { return requestParams[requestCount]; } diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 9e5ea29b..9748bf19 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -7,8 +7,10 @@ import {AccessControl} from "../../utils/AccessControl.sol"; import "solady/utils/Initializable.sol"; import {AddressResolverUtil} from "../../utils/AddressResolverUtil.sol"; -/// @title WatcherPrecompile -/// @notice Contract that handles payload verification, execution and app configurations +/// @title WatcherPrecompileCore +/// @notice Core functionality for the WatcherPrecompile system +/// @dev This contract implements the core functionality for payload verification, execution, and app configurations +/// @dev It is inherited by WatcherPrecompile and provides the base implementation for request handling abstract contract WatcherPrecompileCore is IWatcherPrecompile, WatcherPrecompileStorage, @@ -24,18 +26,17 @@ abstract contract WatcherPrecompileCore is // ================== Timeout functions ================== /// @notice Sets a timeout for a payload execution on app gateway - /// @param payload_ The payload data - /// @param delayInSeconds_ The delay in seconds + /// @return timeoutId The unique identifier for the timeout request function _setTimeout( - bytes calldata payload_, - uint256 delayInSeconds_ + uint256 delayInSeconds_, + bytes calldata payload_ ) internal returns (bytes32 timeoutId) { if (delayInSeconds_ > maxTimeoutDelayInSeconds) revert TimeoutDelayTooLarge(); - // from auction manager - watcherPrecompileLimits__.consumeLimit(_getCoreAppGateway(msg.sender), SCHEDULE, 1); uint256 executeAt = block.timestamp + delayInSeconds_; - timeoutId = _encodeTimeoutId(evmxSlug, address(this)); + timeoutId = _encodeTimeoutId(); + + // stores timeout request for watcher to track and resolve when timeout is reached timeoutRequests[timeoutId] = TimeoutRequest( timeoutId, msg.sender, @@ -45,20 +46,32 @@ abstract contract WatcherPrecompileCore is false, payload_ ); + + // consumes limit for SCHEDULE precompile + watcherPrecompileLimits__.consumeLimit(_getCoreAppGateway(msg.sender), SCHEDULE, 1); + + // emits event for watcher to track timeout and resolve when timeout is reached emit TimeoutRequested(timeoutId, msg.sender, payload_, executeAt); } + /// @notice Finalizes a payload request and requests the watcher to release the proofs + /// @param params_ The payload parameters to be finalized + /// @param transmitter_ The address of the transmitter + /// @return digest The digest hash of the finalized payload + /// @dev This function verifies the app gateway configuration and creates a digest for the payload function _finalize( PayloadParams memory params_, address transmitter_ ) internal returns (bytes32 digest) { uint32 chainSlug = params_.dump.getChainSlug(); + // Verify that the app gateway is properly configured for this chain and target watcherPrecompileConfig__.verifyConnections( chainSlug, params_.target, params_.appGateway, - params_.switchboard + params_.switchboard, + requestParams[params_.dump.getRequestCount()].middleware ); uint256 deadline = block.timestamp + expiryTime; @@ -90,28 +103,23 @@ abstract contract WatcherPrecompileCore is emit FinalizeRequested(digest, payloads[params_.payloadId]); } - function _getBatch(uint40 batchCount) internal view returns (PayloadParams[] memory) { - bytes32[] memory payloadIds = batchPayloadIds[batchCount]; - PayloadParams[] memory payloadParamsArray = new PayloadParams[](payloadIds.length); - - for (uint40 i = 0; i < payloadIds.length; i++) { - payloadParamsArray[i] = payloads[payloadIds[i]]; - } - return payloadParamsArray; - } - // ================== Query functions ================== + /// @notice Creates a new query request - /// @param params_ The payload parameters + /// @param params_ The payload parameters for the query + /// @dev This function sets up a query request and emits a QueryRequested event function _query(PayloadParams memory params_) internal { bytes32 prevDigestsHash = _getPreviousDigestsHash(params_.dump.getBatchCount()); payloads[params_.payloadId].prevDigestsHash = prevDigestsHash; emit QueryRequested(params_); } + // ================== Helper functions ================== + /// @notice Calculates the digest hash of payload parameters - /// @param params_ The payload parameters - /// @return digest The calculated digest + /// @param params_ The payload parameters to calculate the digest for + /// @return digest The calculated digest hash + /// @dev This function creates a keccak256 hash of the payload parameters function getDigest(DigestParams memory params_) public pure returns (bytes32 digest) { digest = keccak256( abi.encode( @@ -132,13 +140,15 @@ abstract contract WatcherPrecompileCore is ); } + /// @notice Gets the hash of previous batch digests + /// @param batchCount_ The batch count to get the previous digests hash + /// @return The hash of all digests in the previous batch function _getPreviousDigestsHash(uint40 batchCount_) internal view returns (bytes32) { bytes32[] memory payloadIds = batchPayloadIds[batchCount_]; bytes32 prevDigestsHash = bytes32(0); for (uint40 i = 0; i < payloadIds.length; i++) { PayloadParams memory p = payloads[payloadIds[i]]; - DigestParams memory digestParams = DigestParams( watcherPrecompileConfig__.sockets(p.dump.getChainSlug()), p.finalizedTransmitter, @@ -159,52 +169,59 @@ abstract contract WatcherPrecompileCore is return prevDigestsHash; } - // ================== Helper functions ================== + /// @notice Gets the batch of payload parameters for a given batch count + /// @param batchCount The batch count to get the payload parameters for + /// @return An array of PayloadParams for the given batch + /// @dev This function retrieves all payload parameters for a specific batch + function _getBatch(uint40 batchCount) internal view returns (PayloadParams[] memory) { + bytes32[] memory payloadIds = batchPayloadIds[batchCount]; + PayloadParams[] memory payloadParamsArray = new PayloadParams[](payloadIds.length); - /// @notice Verifies the connection between chain slug, target, and app gateway - /// @param chainSlug_ The identifier of the chain - /// @param target_ The target address - /// @param appGateway_ The app gateway address to verify - /// @dev Internal function to validate connections - function _verifyConnections( - uint32 chainSlug_, - address target_, - address appGateway_, - address switchboard_ - ) internal view { - // todo: revisit this - // if target is contractFactoryPlug, return - if (target_ == watcherPrecompileConfig__.contractFactoryPlug(chainSlug_)) return; - - (address appGateway, address switchboard) = watcherPrecompileConfig__.getPlugConfigs( - chainSlug_, - target_ - ); - if (appGateway != appGateway_) revert InvalidGateway(); - if (switchboard != switchboard_) revert InvalidSwitchboard(); + for (uint40 i = 0; i < payloadIds.length; i++) { + payloadParamsArray[i] = payloads[payloadIds[i]]; + } + return payloadParamsArray; } - function _encodeTimeoutId(uint32 chainSlug_, address watcher_) internal returns (bytes32) { + /// @notice Encodes an ID for a timeout or payload + /// @return The encoded ID + /// @dev This function creates a unique ID by combining the chain slug, address, and a counter + function _encodeTimeoutId() internal returns (bytes32) { // Encode timeout ID by bit-shifting and combining: - // chainSlug (32 bits) | switchboard or watcher precompile address (160 bits) | counter (64 bits) + // EVMx chainSlug (32 bits) | watcher precompile address (160 bits) | counter (64 bits) return bytes32( - (uint256(chainSlug_) << 224) | (uint256(uint160(watcher_)) << 64) | timeoutCounter++ + (uint256(evmxSlug) << 224) | + (uint256(uint160(address(this))) << 64) | + payloadCounter++ ); } + /// @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( - PayloadSubmitParams memory p_, uint40 requestCount_, uint40 batchCount_, - uint40 payloadCount_ + uint40 payloadCount_, + address switchboard_, + uint32 chainSlug_ ) internal pure returns (bytes32) { return keccak256( - abi.encode(requestCount_, batchCount_, payloadCount_, p_.switchboard, p_.chainSlug) + abi.encode(requestCount_, batchCount_, payloadCount_, switchboard_, chainSlug_) ); } + /// @notice Verifies that a watcher signature is valid + /// @param digest_ The digest to verify + /// @param signatureNonce_ The nonce of the signature + /// @param signature_ The signature to verify + /// @dev This function verifies that the signature was created by the watcher and that the nonce has not been used before function _isWatcherSignatureValid( bytes memory digest_, uint256 signatureNonce_, @@ -221,14 +238,23 @@ abstract contract WatcherPrecompileCore is if (signer != owner()) revert InvalidWatcherSignature(); } + /// @notice Gets the batch IDs for a request + /// @param requestCount_ The request count to get the batch IDs for + /// @return An array of batch IDs for the given request function getBatches(uint40 requestCount_) external view returns (uint40[] memory) { return requestBatchIds[requestCount_]; } + /// @notice Gets the payload IDs for a batch + /// @param batchCount_ The batch count to get the payload IDs for + /// @return An array of payload IDs for the given batch function getBatchPayloadIds(uint40 batchCount_) external view returns (bytes32[] memory) { return batchPayloadIds[batchCount_]; } + /// @notice Gets the payload parameters for a payload ID + /// @param payloadId_ The payload ID to get the parameters for + /// @return The payload parameters for the given payload ID function getPayloadParams(bytes32 payloadId_) external view returns (PayloadParams memory) { return payloads[payloadId_]; } diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol index 14725337..019d1b58 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol @@ -9,9 +9,13 @@ import "../DumpDecoder.sol"; import {IMiddleware} from "../../../interfaces/IMiddleware.sol"; import {QUERY, FINALIZE, SCHEDULE, MAX_COPY_BYTES} from "../../utils/common/Constants.sol"; -import {InvalidCallerTriggered, TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidInboxCaller, ResolvingTimeoutTooEarly, CallFailed, AppGatewayAlreadyCalled, InvalidWatcherSignature, NonceUsed} from "../../utils/common/Errors.sol"; +import {InvalidCallerTriggered, TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidInboxCaller, ResolvingTimeoutTooEarly, CallFailed, AppGatewayAlreadyCalled, InvalidWatcherSignature, NonceUsed, RequestAlreadyExecuted} from "../../utils/common/Errors.sol"; import {ResolvedPromises, AppGatewayConfig, LimitParams, WriteFinality, UpdateLimitParams, PlugConfig, DigestParams, TimeoutRequest, QueuePayloadParams, PayloadParams, RequestParams} from "../../utils/common/Structs.sol"; +/// @title WatcherPrecompileStorage +/// @notice Storage contract for the WatcherPrecompile system +/// @dev This contract contains all the storage variables used by the WatcherPrecompile system +/// @dev It is inherited by WatcherPrecompileCore and WatcherPrecompile abstract contract WatcherPrecompileStorage is IWatcherPrecompile { // slots [0-49]: gap for future storage variables uint256[50] _gap_before; @@ -19,21 +23,24 @@ abstract contract WatcherPrecompileStorage is IWatcherPrecompile { // slot 50 /// @notice The chain slug of the watcher precompile uint32 public evmxSlug; + /// @notice Counter for tracking payload requests uint40 public payloadCounter; - /// @notice Counter for tracking timeout requests - uint40 public timeoutCounter; + /// @notice Counter for tracking request counts - uint40 public nextRequestCount; + uint40 public override nextRequestCount; + /// @notice Counter for tracking batch counts uint40 public nextBatchCount; // slot 51 /// @notice The time from finalize for the payload to be executed + /// @dev Expiry time in seconds for payload execution uint256 public expiryTime; // slot 52 /// @notice The maximum delay for a timeout + /// @dev Maximum timeout delay in seconds uint256 public maxTimeoutDelayInSeconds; // slot 53 @@ -42,37 +49,46 @@ abstract contract WatcherPrecompileStorage is IWatcherPrecompile { // slot 54 /// @notice Maps nonce to whether it has been used + /// @dev Used to prevent replay attacks with signature nonces /// @dev signatureNonce => isValid mapping(uint256 => bool) public isNonceUsed; // slot 55 /// @notice Mapping to store timeout requests + /// @dev Maps timeout ID to TimeoutRequest struct /// @dev timeoutId => TimeoutRequest struct mapping(bytes32 => TimeoutRequest) public timeoutRequests; // slot 56 /// @notice Mapping to store watcher proofs + /// @dev Maps payload ID to proof bytes /// @dev payloadId => proof bytes mapping(bytes32 => bytes) public watcherProofs; // slot 57 /// @notice Mapping to store if appGateway has been called with trigger from on-chain Inbox + /// @dev Maps call ID to boolean indicating if the appGateway has been called /// @dev callId => bool mapping(bytes32 => bool) public appGatewayCalled; // slot 58 + /// @notice Mapping to store the request parameters for each request count mapping(uint40 => RequestParams) public requestParams; // slot 59 + /// @notice Mapping to store the list of payload IDs for each batch mapping(uint40 => bytes32[]) public batchPayloadIds; // slot 60 + /// @notice Mapping to store the batch IDs for each request mapping(uint40 => uint40[]) public requestBatchIds; // slot 61 + /// @notice Mapping to store the payload parameters for each payload ID mapping(bytes32 => PayloadParams) public payloads; // slot 62 + /// @notice Mapping to store if a promise has been executed mapping(bytes32 => bool) public isPromiseExecuted; // slot 63 From dfef0d1659d91349e496ff54d822402d1e5d211a Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 15 Apr 2025 12:44:18 +0530 Subject: [PATCH 042/108] doc: sb --- contracts/interfaces/ISwitchboard.sol | 11 +++++--- .../socket/switchboard/FastSwitchboard.sol | 26 +++++++++---------- .../socket/switchboard/SwitchboardBase.sol | 10 +++++-- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/contracts/interfaces/ISwitchboard.sol b/contracts/interfaces/ISwitchboard.sol index c6998445..4f1095ea 100644 --- a/contracts/interfaces/ISwitchboard.sol +++ b/contracts/interfaces/ISwitchboard.sol @@ -3,8 +3,8 @@ pragma solidity ^0.8.21; /** * @title ISwitchboard - * @dev The interface for a switchboard contract that is responsible for verification of payloadss between - * different blockchain networks. + * @dev The interface for a switchboard contract that is responsible for verification of payloads if the correct + * digest is executed. */ interface ISwitchboard { /** @@ -13,7 +13,12 @@ interface ISwitchboard { * @param payloadId_ The unique identifier for the payloads. * @return A boolean indicating whether the payloads is allowed to go through the switchboard or not. */ - function allowPacket(bytes32 digest_, bytes32 payloadId_) external view returns (bool); + function allowPayload(bytes32 digest_, bytes32 payloadId_) external view returns (bool); + /** + * @notice Attests a payload + * @param digest_ The digest of the payload + * @param proof_ The proof of the payload + */ function attest(bytes32 digest_, bytes calldata proof_) external; } diff --git a/contracts/protocol/socket/switchboard/FastSwitchboard.sol b/contracts/protocol/socket/switchboard/FastSwitchboard.sol index 308e07f6..12e0500a 100644 --- a/contracts/protocol/socket/switchboard/FastSwitchboard.sol +++ b/contracts/protocol/socket/switchboard/FastSwitchboard.sol @@ -2,24 +2,24 @@ pragma solidity ^0.8.21; import "./SwitchboardBase.sol"; +import {WATCHER_ROLE} from "../../utils/common/AccessRoles.sol"; /** * @title FastSwitchboard contract * @dev This contract implements a fast version of the SwitchboardBase contract - * that enables packet attestations + * that enables payload attestations from watchers */ contract FastSwitchboard is SwitchboardBase { - // used to track if watcher have attested a digest - // digest => isAttested + // used to track if watcher have attested a payload + // payloadId => isAttested mapping(bytes32 => bool) public isAttested; - // Error emitted when a digest is already attested by watcher. + // Error emitted when a payload is already attested by watcher. error AlreadyAttested(); // Error emitted when watcher is not valid error WatcherNotFound(); - - // Event emitted when watcher attests a digest - event Attested(bytes32 digest_, address watcher); + // Event emitted when watcher attests a payload + event Attested(bytes32 payloadId_, address watcher); /** * @dev Constructor function for the FastSwitchboard contract @@ -34,25 +34,25 @@ contract FastSwitchboard is SwitchboardBase { ) SwitchboardBase(chainSlug_, socket_, owner_) {} /** - * @dev Function to attest a packet + * @dev Function to attest a payload * @param digest_ digest of the payload to be executed * @param proof_ proof from watcher - * @notice we are attesting a digest uniquely identified with payloadId. + * @notice we are attesting a payload uniquely identified with digest. */ function attest(bytes32 digest_, bytes calldata proof_) external { - address watcher = _recoverSigner(keccak256(abi.encode(address(this), digest_)), proof_); - if (isAttested[digest_]) revert AlreadyAttested(); + + address watcher = _recoverSigner(keccak256(abi.encode(address(this), digest_)), proof_); if (!_hasRole(WATCHER_ROLE, watcher)) revert WatcherNotFound(); - isAttested[digest_] = true; + isAttested[digest_] = true; emit Attested(digest_, watcher); } /** * @inheritdoc ISwitchboard */ - function allowPacket(bytes32 digest_, bytes32) external view returns (bool) { + function allowPayload(bytes32 digest_, bytes32) external view returns (bool) { // digest has enough attestations return isAttested[digest_]; } diff --git a/contracts/protocol/socket/switchboard/SwitchboardBase.sol b/contracts/protocol/socket/switchboard/SwitchboardBase.sol index 2f5249a5..0c99eaac 100644 --- a/contracts/protocol/socket/switchboard/SwitchboardBase.sol +++ b/contracts/protocol/socket/switchboard/SwitchboardBase.sol @@ -1,13 +1,15 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; +import {ECDSA} from "solady/utils/ECDSA.sol"; import "../../../interfaces/ISwitchboard.sol"; import "../../../interfaces/ISocket.sol"; import "../../utils/AccessControl.sol"; -import {RESCUE_ROLE, WATCHER_ROLE} from "../../utils/common/AccessRoles.sol"; import "../../utils/RescueFundsLib.sol"; -import {ECDSA} from "solady/utils/ECDSA.sol"; +import {RESCUE_ROLE} from "../../utils/common/AccessRoles.sol"; +/// @title SwitchboardBase +/// @notice Base contract for switchboards, contains common and util functions for all switchboards abstract contract SwitchboardBase is ISwitchboard, AccessControl { ISocket public immutable socket__; @@ -25,6 +27,10 @@ abstract contract SwitchboardBase is ISwitchboard, AccessControl { _initializeOwner(owner_); } + /// @notice Recovers the signer from the signature + /// @param digest_ The digest of the payload + /// @param signature_ The signature of the watcher + /// @return signer The address of the signer function _recoverSigner( bytes32 digest_, bytes memory signature_ From c62c1cf5d0f07498c8eff7f46cffd6c24547a795 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 15 Apr 2025 13:06:40 +0530 Subject: [PATCH 043/108] doc: socket and batcher --- contracts/interfaces/ISocket.sol | 11 +-- contracts/interfaces/ISocketBatcher.sol | 12 ++++ contracts/protocol/socket/Socket.sol | 71 ++++++++++++++----- contracts/protocol/socket/SocketBatcher.sol | 20 +++++- contracts/protocol/socket/SocketConfig.sol | 42 +++++++---- contracts/protocol/socket/SocketUtils.sol | 26 ++++--- .../protocol/utils/common/AccessRoles.sol | 2 +- test/mock/MockSocket.sol | 13 ++-- 8 files changed, 141 insertions(+), 56 deletions(-) diff --git a/contracts/interfaces/ISocket.sol b/contracts/interfaces/ISocket.sol index 42713610..e3fd5b8e 100644 --- a/contracts/interfaces/ISocket.sol +++ b/contracts/interfaces/ISocket.sol @@ -14,13 +14,13 @@ import {ExecuteParams} from "../protocol/utils/common/Structs.sol"; interface ISocket { /** * @notice emits the status of payload after external call - * @param payloadId msg id which is executed + * @param payloadId payload id which is executed */ event ExecutionSuccess(bytes32 payloadId, bytes returnData); /** * @notice emits the status of payload after external call - * @param payloadId msg id which is executed + * @param payloadId payload id which is executed */ event ExecutionFailed(bytes32 payloadId, bytes returnData); @@ -33,8 +33,8 @@ interface ISocket { event PlugConnected(address plug, address appGateway, address switchboard); /** - * @notice emits the message details when a new message arrives at outbound - * @param triggerId call id + * @notice emits the payload details when a new payload arrives at outbound + * @param triggerId trigger id * @param chainSlug local chain slug * @param plug local plug address * @param overrides params, for specifying details like fee pool chain, fee pool token and max fees if required @@ -63,6 +63,9 @@ interface ISocket { */ function connect(address appGateway_, address switchboard_) external; + /** + * @notice registers a switchboard for the socket + */ function registerSwitchboard() external; /** diff --git a/contracts/interfaces/ISocketBatcher.sol b/contracts/interfaces/ISocketBatcher.sol index ab753866..d2991f4e 100644 --- a/contracts/interfaces/ISocketBatcher.sol +++ b/contracts/interfaces/ISocketBatcher.sol @@ -3,7 +3,19 @@ pragma solidity ^0.8.21; import {ExecuteParams} from "../protocol/utils/common/Structs.sol"; +/** + * @title ISocketBatcher + * @notice Interface for a helper contract for socket which batches attest (on sb) and execute calls (on socket). + */ interface ISocketBatcher { + /** + * @notice Attests a payload and executes it + * @param executeParams_ The execution parameters + * @param digest_ The digest of the payload + * @param proof_ The proof of the payload + * @param transmitterSignature_ The signature of the transmitter + * @return The return data after execution + */ function attestAndExecute( ExecuteParams calldata executeParams_, bytes32 digest_, diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index e223b28d..b49efebb 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -1,19 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-only 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"; +import "./SocketUtils.sol"; /** - * @title SocketDst - * @dev SocketDst is an abstract contract that inherits from SocketUtils and - * provides functionality for payload execution, verification. - * It manages the mapping of payload execution status - * timestamps - * It also includes functions for payload execution and verification + * @title Socket + * @dev Socket is an abstract contract that inherits from SocketUtils and SocketConfig and + * provides functionality for payload execution, verification, and management of payload execution status */ contract Socket is SocketUtils { using LibCall for address; @@ -29,14 +23,33 @@ contract Socket is SocketUtils { * @dev Error emitted when verification fails */ error VerificationFailed(); - /** * @dev Error emitted when less gas limit is provided for execution than expected */ error LowGasLimit(); + /** + * @dev Error emitted when the chain slug is invalid + */ error InvalidSlug(); + /** + * @dev Error emitted when the deadline has passed + */ error DeadlinePassed(); + /** + * @dev Error emitted when the message value is insufficient + */ error InsufficientMsgValue(); + /** + * @dev Error emitted when the plug is disconnected + */ + error PlugDisconnected(); + + /** + * @notice Constructor for the Socket contract + * @param chainSlug_ The chain slug + * @param owner_ The owner of the contract + * @param version_ The version of the contract + */ constructor( uint32 chainSlug_, address owner_, @@ -50,26 +63,42 @@ contract Socket is SocketUtils { ExecuteParams memory executeParams_, bytes memory transmitterSignature_ ) external payable returns (bytes memory) { + // check if the deadline has passed if (executeParams_.deadline < block.timestamp) revert DeadlinePassed(); + PlugConfig memory plugConfig = _plugConfigs[executeParams_.target]; + + // check if the plug is disconnected if (plugConfig.appGateway == address(0)) revert PlugDisconnected(); + // check if the message value is insufficient if (msg.value < executeParams_.value) revert InsufficientMsgValue(); + + // create the payload id bytes32 payloadId = _createPayloadId(plugConfig.switchboard, executeParams_); + + // validate the execution status _validateExecutionStatus(payloadId); + // recover the signer address transmitter = _recoverSigner( keccak256(abi.encode(address(this), payloadId)), transmitterSignature_ ); + // create the digest + // transmitter, payloadId, appGateway, executeParams_ and there contents are validated using digest verification from switchboard bytes32 digest = _createDigest( transmitter, payloadId, plugConfig.appGateway, executeParams_ ); + + // verify the digest _verify(digest, payloadId, plugConfig.switchboard); + + // execute the payload and return the data return _execute(payloadId, executeParams_); } @@ -79,8 +108,9 @@ contract Socket is SocketUtils { function _verify(bytes32 digest_, bytes32 payloadId_, address switchboard_) internal view { if (isValidSwitchboard[switchboard_] != SwitchboardStatus.REGISTERED) revert InvalidSwitchboard(); + // NOTE: is the the first un-trusted call in the system, another one is Plug.call - if (!ISwitchboard(switchboard_).allowPacket(digest_, payloadId_)) + if (!ISwitchboard(switchboard_).allowPayload(digest_, payloadId_)) revert VerificationFailed(); } @@ -93,6 +123,7 @@ contract Socket is SocketUtils { bytes32 payloadId_, ExecuteParams memory executeParams_ ) internal returns (bytes memory) { + // check if the gas limit is sufficient if (gasleft() < executeParams_.gasLimit) revert LowGasLimit(); // NOTE: external un-trusted call @@ -103,6 +134,7 @@ contract Socket is SocketUtils { executeParams_.payload ); + // if the execution failed, set the execution status to reverted if (!success) { payloadExecuted[payloadId_] = ExecutionStatus.Reverted; emit ExecutionFailed(payloadId_, returnData); @@ -120,14 +152,14 @@ contract Socket is SocketUtils { } //////////////////////////////////////////////////////// - ////////////////////// OPERATIONS ////////////////////////// + ////////////////////// Trigger ////////////////////// //////////////////////////////////////////////////////// /** - * @notice To send message to a connected remote chain. Should only be called by a plug. + * @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 _callAppGateway( + function _triggerAppGateway( address plug_, bytes memory overrides_, bytes memory payload_ @@ -135,17 +167,22 @@ contract Socket is SocketUtils { PlugConfig memory plugConfig = _plugConfigs[plug_]; // if no sibling plug is found for the given chain slug, revert + // sends the trigger to connected app gateway if (plugConfig.appGateway == address(0)) revert PlugDisconnected(); - // creates a unique ID for the message + // creates a unique ID for the trigger triggerId = _encodeTriggerId(plugConfig.appGateway); emit AppGatewayCallRequested(triggerId, chainSlug, plug_, overrides_, payload_); } /// @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 abi.encode(_callAppGateway(msg.sender, overrides, msg.data)); + + // return the trigger id + return abi.encode(_triggerAppGateway(msg.sender, overrides, msg.data)); } } diff --git a/contracts/protocol/socket/SocketBatcher.sol b/contracts/protocol/socket/SocketBatcher.sol index 49eb1966..5bd605a4 100644 --- a/contracts/protocol/socket/SocketBatcher.sol +++ b/contracts/protocol/socket/SocketBatcher.sol @@ -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 "../../interfaces/ISocketBatcher.sol"; /** * @title SocketBatcher @@ -17,15 +17,23 @@ contract SocketBatcher is ISocketBatcher, Ownable { ISocket public immutable socket__; /** - * @notice Initializes the TransmitManager contract - * @param socket_ The address of socket contract + * @notice Initializes the SocketBatcher contract * @param owner_ The owner of the contract with GOVERNANCE_ROLE + * @param socket_ The address of socket contract */ constructor(address owner_, ISocket socket_) { socket__ = socket_; _initializeOwner(owner_); } + /** + * @notice Attests a payload and executes it + * @param executeParams_ The execution parameters + * @param digest_ The digest of the payload + * @param proof_ The proof of the payload + * @param transmitterSignature_ The signature of the transmitter + * @return The return data after execution + */ function attestAndExecute( ExecuteParams calldata executeParams_, bytes32 digest_, @@ -36,6 +44,12 @@ contract SocketBatcher is ISocketBatcher, Ownable { return socket__.execute{value: msg.value}(executeParams_, transmitterSignature_); } + /** + * @notice Rescues funds from the contract + * @param token_ The address of the token to rescue + * @param to_ The address to rescue the funds to + * @param amount_ The amount of funds to rescue + */ function rescueFunds(address token_, address to_, uint256 amount_) external onlyOwner { RescueFundsLib._rescueFunds(token_, to_, amount_); } diff --git a/contracts/protocol/socket/SocketConfig.sol b/contracts/protocol/socket/SocketConfig.sol index 09aaf19f..90e222f6 100644 --- a/contracts/protocol/socket/SocketConfig.sol +++ b/contracts/protocol/socket/SocketConfig.sol @@ -3,34 +3,46 @@ pragma solidity ^0.8.21; import "../../interfaces/ISocket.sol"; 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 {PlugDisconnected, InvalidAppGateway, InvalidTransmitter} from "../utils/common/Errors.sol"; +import {MAX_COPY_BYTES} from "../utils/common/Constants.sol"; /** * @title SocketConfig - * @notice An abstract contract for configuring socket connections for plugs between different chains, + * @notice An abstract contract for configuring socket connections for plugs, * manages plug configs and switchboard registrations * @dev This contract is meant to be inherited by other contracts that require socket configuration functionality */ abstract contract SocketConfig is ISocket, AccessControl { - // Error triggered when a switchboard already exists + // @notice mapping of switchboard address to its status, helps socket to block invalid switchboards mapping(address => SwitchboardStatus) public isValidSwitchboard; - // plug => (appGateway, switchboard__) + // @notice mapping of plug address to its config mapping(address => PlugConfig) internal _plugConfigs; + // @notice max copy bytes for socket uint16 public maxCopyBytes = 2048; // 2KB - // Error triggered when a connection is invalid + + // @notice error triggered when a connection is invalid error InvalidConnection(); + // @notice error triggered when a switchboard is invalid error InvalidSwitchboard(); + // @notice error triggered when a switchboard already exists error SwitchboardExists(); + // @notice error triggered when a switchboard already exists or is disabled error SwitchboardExistsOrDisabled(); - // Event triggered when a new switchboard is added + // @notice event triggered when a new switchboard is added event SwitchboardAdded(address switchboard); + // @notice event triggered when a switchboard is disabled event SwitchboardDisabled(address switchboard); + // @notice function to register a switchboard + // @dev only callable by switchboards function registerSwitchboard() external { if (isValidSwitchboard[msg.sender] != SwitchboardStatus.NOT_REGISTERED) revert SwitchboardExistsOrDisabled(); @@ -39,14 +51,17 @@ abstract contract SocketConfig is ISocket, AccessControl { emit SwitchboardAdded(msg.sender); } + // @notice function to disable a switchboard + // @dev only callable by governance role function disableSwitchboard() external onlyRole(GOVERNANCE_ROLE) { isValidSwitchboard[msg.sender] = SwitchboardStatus.DISABLED; emit SwitchboardDisabled(msg.sender); } - /** - * @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 appGateway_ address of app gateway on sibling chain + // @param switchboard_ address of switchboard on sibling chain function connect(address appGateway_, address switchboard_) external override { if (isValidSwitchboard[switchboard_] != SwitchboardStatus.REGISTERED) revert InvalidSwitchboard(); @@ -59,14 +74,17 @@ abstract contract SocketConfig is ISocket, AccessControl { emit PlugConnected(msg.sender, appGateway_, switchboard_); } + // @notice function to set the max copy bytes for socket + // @dev only callable by governance role + // @param maxCopyBytes_ max copy bytes for socket 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 - */ + // @notice function to get the config for a plug + // @param plugAddress_ address of plug present at current chain + // @return appGateway address of app gateway on sibling chain + // @return switchboard address of switchboard on sibling chain function getPlugConfig( address plugAddress_ ) external view returns (address appGateway, address switchboard) { diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index 520cda2c..6f886c5e 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -1,15 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; +import {ECDSA} from "solady/utils/ECDSA.sol"; import "../utils/RescueFundsLib.sol"; import "./SocketConfig.sol"; -import {ECDSA} from "solady/utils/ECDSA.sol"; -import {InvalidTransmitter} from "../utils/common/Errors.sol"; /** * @title SocketUtils - * @notice A contract that is responsible for common storage for src and dest contracts, governance - * setters and inherits SocketConfig + * @notice Utility functions for socket */ abstract contract SocketUtils is SocketConfig { //////////////////////////////////////////////////////////// @@ -21,11 +19,10 @@ abstract contract SocketUtils is SocketConfig { // ChainSlug for this deployed socket instance uint32 public immutable chainSlug; + // @notice counter for trigger id uint64 public triggerCounter; - /** - * @dev keeps track of whether a payload has been executed or not using payload id - */ + // @notice mapping of payload id to execution status mapping(bytes32 => ExecutionStatus) public payloadExecuted; /* @@ -83,7 +80,6 @@ abstract contract SocketUtils is SocketConfig { address switchboard_, ExecuteParams memory executeParams_ ) internal view returns (bytes32) { - // todo: match with watcher return keccak256( abi.encode( @@ -96,6 +92,12 @@ abstract contract SocketUtils is SocketConfig { ); } + /** + * @notice recovers the signer from the signature + * @param digest_ The digest of the payload + * @param signature_ The signature of the payload + * @return signer The address of the signer + */ function _recoverSigner( bytes32 digest_, bytes memory signature_ @@ -105,9 +107,11 @@ abstract contract SocketUtils is SocketConfig { signer = ECDSA.recover(digest, signature_); } - // Packs the local plug, local chain slug, remote chain slug and nonce - // triggerCounter++ will take care of call id overflow as well - // triggerId(256) = localChainSlug(32) | appGateway_(160) | nonce(64) + /** + * @notice Encodes the trigger ID with the chain slug, app gateway and nonce + * @param appGateway_ The address of the app gateway + * @return The trigger ID + */ function _encodeTriggerId(address appGateway_) internal returns (bytes32) { return bytes32( diff --git a/contracts/protocol/utils/common/AccessRoles.sol b/contracts/protocol/utils/common/AccessRoles.sol index 45472a4b..13279b25 100644 --- a/contracts/protocol/utils/common/AccessRoles.sol +++ b/contracts/protocol/utils/common/AccessRoles.sol @@ -6,7 +6,7 @@ pragma solidity ^0.8.21; bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE"); // used by governance bytes32 constant GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE"); -// used by transmitters who seal and propose packets in socket +// used by transmitters who execute payloads in socket bytes32 constant TRANSMITTER_ROLE = keccak256("TRANSMITTER_ROLE"); // used by switchboard watchers who work against transmitters bytes32 constant WATCHER_ROLE = keccak256("WATCHER_ROLE"); diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index 1776c491..f64cadd2 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -53,9 +53,6 @@ contract MockSocket is ISocket { * @dev Error emitted when verification fails */ error VerificationFailed(); - /** - * @dev Error emitted when source slugs deduced from packet id and msg id don't match - */ /** * @dev Error emitted when less gas limit is provided for execution than expected */ @@ -127,7 +124,7 @@ contract MockSocket is ISocket { ISwitchboard switchboard__ ) internal view { // NOTE: is the the first un-trusted call in the system, another one is Plug.call - if (!switchboard__.allowPacket(digest_, payloadId_)) revert VerificationFailed(); + if (!switchboard__.allowPayload(digest_, payloadId_)) revert VerificationFailed(); } /** @@ -148,7 +145,7 @@ contract MockSocket is ISocket { /** * @dev Decodes the switchboard address from a given payload id. - * @param id_ The ID of the msg to decode the switchboard from. + * @param id_ The ID of the payload to decode the switchboard from. * @return switchboard_ The address of switchboard decoded from the payload ID. */ function _decodeSwitchboard(bytes32 id_) internal pure returns (address switchboard_) { @@ -156,9 +153,9 @@ contract MockSocket is ISocket { } /** - * @dev Decodes the chain ID from a given packet/payload ID. - * @param id_ The ID of the packet/msg to decode the chain slug from. - * @return chainSlug_ The chain slug decoded from the packet/payload ID. + * @dev Decodes the chain ID from a given payload ID. + * @param id_ The ID of the payload to decode the chain slug from. + * @return chainSlug_ The chain slug decoded from the payload ID. */ function _decodeChainSlug(bytes32 id_) internal pure returns (uint32 chainSlug_) { chainSlug_ = uint32(uint256(id_) >> 224); From 98367e7efc15af7d229a94e1e178af9b4748fd08 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 15 Apr 2025 13:18:18 +0530 Subject: [PATCH 044/108] fix: test and lint --- FunctionSignatures.md | 1 - contracts/base/AppGatewayBase.sol | 2 +- contracts/protocol/payload-delivery/FeesManager.sol | 3 +++ contracts/protocol/socket/Socket.sol | 4 ---- .../protocol/watcherPrecompile/core/WatcherPrecompile.sol | 6 +++--- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/FunctionSignatures.md b/FunctionSignatures.md index 6f9cdf76..3eb60034 100644 --- a/FunctionSignatures.md +++ b/FunctionSignatures.md @@ -419,7 +419,6 @@ | `completeOwnershipHandover` | `0xf04e283e` | | `grantRole` | `0x2f2ff15d` | | `hasRole` | `0x91d14854` | -| `initialPacketCount` | `0x7c138814` | | `isAttested` | `0xc13c2396` | | `nextNonce` | `0x0cd55abf` | | `owner` | `0x8da5cb5b` | diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index 0908299f..7fdc8b47 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -124,9 +124,9 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin if (!isAsyncModifierSet) revert AsyncModifierNotUsed(); address asyncPromise = addressResolver__.deployAsyncPromiseContract(address(this)); - isValidPromise[asyncPromise] = true; IPromise(asyncPromise).then(this.setAddress.selector, abi.encode(chainSlug_, contractId_)); + isValidPromise[asyncPromise] = true; onCompleteData = abi.encode(chainSlug_, true); QueuePayloadParams memory queuePayloadParams = QueuePayloadParams({ diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index c6fdd9a6..6f600fb7 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -396,6 +396,9 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol }); } + /// @notice hook called by watcher precompile when request is finished + function finishRequest(uint40) external {} + function _queue(uint32 chainSlug_, bytes memory payload_) internal { QueuePayloadParams memory queuePayloadParams = _createQueuePayloadParams( chainSlug_, diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index b49efebb..aa2e0fb6 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -39,10 +39,6 @@ contract Socket is SocketUtils { * @dev Error emitted when the message value is insufficient */ error InsufficientMsgValue(); - /** - * @dev Error emitted when the plug is disconnected - */ - error PlugDisconnected(); /** * @notice Constructor for the Socket contract diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index f8b8cf19..09323799 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -244,9 +244,9 @@ contract WatcherPrecompile is RequestHandler { /// @param payloadId_ The unique identifier of the payload /// @param signatureNonce_ The nonce of the signature /// @param signature_ The signature of the watcher - /// @dev This function marks a request as reverting - /// @dev It verifies that the signature is valid - /// @dev It also handles onchain reverts if specified + /// @dev Only valid watcher can mark a request as reverting + /// @dev This function marks a request as reverting if callback or payload is reverting on chain + /// @dev Request is marked cancelled for both cases. function markRevert( bool isRevertingOnchain_, bytes32 payloadId_, From fe497a0fc62db852a782207b710b81e43e8225d5 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 15 Apr 2025 13:31:48 +0530 Subject: [PATCH 045/108] fix: version and license --- contracts/base/AppGatewayBase.sol | 7 +++---- contracts/base/PlugBase.sol | 2 +- contracts/base/ProxyFactory.sol | 3 ++- contracts/interfaces/IAddressResolver.sol | 2 +- contracts/interfaces/IAppGateway.sol | 2 +- contracts/interfaces/IAuctionManager.sol | 4 ++-- contracts/interfaces/IContractFactoryPlug.sol | 2 +- contracts/interfaces/IFeesManager.sol | 4 ++-- contracts/interfaces/IFeesPlug.sol | 2 +- contracts/interfaces/IForwarder.sol | 2 +- contracts/interfaces/IMiddleware.sol | 4 ++-- contracts/interfaces/IPromise.sol | 2 +- contracts/interfaces/IWatcherPrecompile.sol | 2 +- contracts/interfaces/IWatcherPrecompileConfig.sol | 2 +- contracts/interfaces/IWatcherPrecompileLimits.sol | 2 +- contracts/protocol/AddressResolver.sol | 2 +- contracts/protocol/AsyncPromise.sol | 2 +- contracts/protocol/Forwarder.sol | 2 +- contracts/protocol/payload-delivery/AuctionManager.sol | 4 ++-- .../protocol/payload-delivery/ContractFactoryPlug.sol | 2 +- contracts/protocol/payload-delivery/FeesManager.sol | 4 ++-- contracts/protocol/payload-delivery/FeesPlug.sol | 2 +- .../payload-delivery/app-gateway/DeliveryHelper.sol | 4 ++-- .../payload-delivery/app-gateway/DeliveryHelperStorage.sol | 2 +- .../payload-delivery/app-gateway/DeliveryUtils.sol | 2 +- .../protocol/payload-delivery/app-gateway/FeesHelpers.sol | 2 +- .../protocol/payload-delivery/app-gateway/RequestQueue.sol | 2 +- contracts/protocol/utils/AddressResolverUtil.sol | 2 +- contracts/protocol/utils/FeesPlugin.sol | 2 +- contracts/protocol/utils/Gauge.sol | 2 +- contracts/protocol/utils/common/Constants.sol | 2 +- contracts/protocol/utils/common/Errors.sol | 2 +- contracts/protocol/utils/common/Structs.sol | 2 +- contracts/protocol/watcherPrecompile/DumpDecoder.sol | 2 +- .../protocol/watcherPrecompile/WatcherPrecompileConfig.sol | 2 +- .../protocol/watcherPrecompile/WatcherPrecompileLimits.sol | 2 +- .../protocol/watcherPrecompile/core/RequestHandler.sol | 2 +- .../protocol/watcherPrecompile/core/WatcherPrecompile.sol | 2 +- .../watcherPrecompile/core/WatcherPrecompileCore.sol | 2 +- .../watcherPrecompile/core/WatcherPrecompileStorage.sol | 4 ++-- script/admin/UpdateAppEVMxLimits.s.sol | 4 ++-- script/admin/mock/DeployEVMx.s.sol | 4 ++-- script/admin/mock/DeploySocket.s.sol | 4 ++-- script/counter/DeployEVMxCounterApp.s.sol | 4 ++-- script/counter/DeployOnchainCounters.s.sol | 4 ++-- script/counter/IncrementCountersFromApp.s.sol | 4 ++-- script/counter/ReadOnchainCounters.s.sol | 4 ++-- script/counter/SetFees.s.sol | 5 +++-- script/counter/WithdrawFeesArbitrumFeesPlug.s.sol | 4 ++-- script/helpers/AppGatewayFeeBalance.s.sol | 4 ++-- script/helpers/CheckAppEVMxLimits.s.sol | 4 ++-- script/helpers/PayFeesInArbitrumETH.s.sol | 4 ++-- test/DeliveryHelper.t.sol | 4 ++-- test/FeesTest.t.sol | 4 ++-- test/Inbox.t.sol | 4 ++-- test/Migration.t.sol | 4 ++-- test/SetupTest.t.sol | 4 ++-- test/Storage.t.sol | 4 ++-- test/apps/Counter.t.sol | 2 +- test/apps/ParallelCounter.t.sol | 2 +- test/apps/SuperToken.t.sol | 4 ++-- test/apps/app-gateways/counter/Counter.sol | 4 ++-- test/apps/app-gateways/counter/CounterAppGateway.sol | 4 ++-- test/apps/app-gateways/counter/ICounter.sol | 4 ++-- test/apps/app-gateways/super-token/ISuperToken.sol | 2 +- test/apps/app-gateways/super-token/SuperToken.sol | 2 +- .../apps/app-gateways/super-token/SuperTokenAppGateway.sol | 2 +- test/mock/MockWatcherPrecompile.sol | 2 +- test/mock/MockWatcherPrecompileImpl.sol | 4 ++-- 69 files changed, 103 insertions(+), 102 deletions(-) diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index 7fdc8b47..254b0f3a 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import "../protocol/utils/AddressResolverUtil.sol"; import "../interfaces/IAppGateway.sol"; @@ -181,8 +181,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin return address(0); } - onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]) - .getOnChainAddress(); + onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]).getOnChainAddress(); } //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/contracts/base/PlugBase.sol b/contracts/base/PlugBase.sol index 99d84613..19b44f17 100644 --- a/contracts/base/PlugBase.sol +++ b/contracts/base/PlugBase.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {ISocket} from "../interfaces/ISocket.sol"; diff --git a/contracts/base/ProxyFactory.sol b/contracts/base/ProxyFactory.sol index 5c081ec3..a49a9af3 100644 --- a/contracts/base/ProxyFactory.sol +++ b/contracts/base/ProxyFactory.sol @@ -1,4 +1,5 @@ -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {ERC1967Factory} from "solady/utils/ERC1967Factory.sol"; diff --git a/contracts/interfaces/IAddressResolver.sol b/contracts/interfaces/IAddressResolver.sol index ce5fcdbf..1e8684bc 100644 --- a/contracts/interfaces/IAddressResolver.sol +++ b/contracts/interfaces/IAddressResolver.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "./IWatcherPrecompile.sol"; diff --git a/contracts/interfaces/IAppGateway.sol b/contracts/interfaces/IAppGateway.sol index 79a78889..8997710b 100644 --- a/contracts/interfaces/IAppGateway.sol +++ b/contracts/interfaces/IAppGateway.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {Fees, Read, Parallel, QueuePayloadParams, OverrideParams, CallType, WriteFinality, PayloadParams} from "../protocol/utils/common/Structs.sol"; diff --git a/contracts/interfaces/IAuctionManager.sol b/contracts/interfaces/IAuctionManager.sol index 12046099..45344b7a 100644 --- a/contracts/interfaces/IAuctionManager.sol +++ b/contracts/interfaces/IAuctionManager.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {Bid, Fees, RequestMetadata, RequestParams} from "../protocol/utils/common/Structs.sol"; diff --git a/contracts/interfaces/IContractFactoryPlug.sol b/contracts/interfaces/IContractFactoryPlug.sol index a3b5bac0..7e4d8abf 100644 --- a/contracts/interfaces/IContractFactoryPlug.sol +++ b/contracts/interfaces/IContractFactoryPlug.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {IsPlug} from "../protocol/utils/common/Structs.sol"; diff --git a/contracts/interfaces/IFeesManager.sol b/contracts/interfaces/IFeesManager.sol index a76d6d43..82f5fd8f 100644 --- a/contracts/interfaces/IFeesManager.sol +++ b/contracts/interfaces/IFeesManager.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {Fees, Bid, QueuePayloadParams} from "../protocol/utils/common/Structs.sol"; diff --git a/contracts/interfaces/IFeesPlug.sol b/contracts/interfaces/IFeesPlug.sol index cc546977..1f4357b8 100644 --- a/contracts/interfaces/IFeesPlug.sol +++ b/contracts/interfaces/IFeesPlug.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; interface IFeesPlug { diff --git a/contracts/interfaces/IForwarder.sol b/contracts/interfaces/IForwarder.sol index 25072ea3..b609fc31 100644 --- a/contracts/interfaces/IForwarder.sol +++ b/contracts/interfaces/IForwarder.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; /// @title IForwarder diff --git a/contracts/interfaces/IMiddleware.sol b/contracts/interfaces/IMiddleware.sol index 658c15fe..54b50a53 100644 --- a/contracts/interfaces/IMiddleware.sol +++ b/contracts/interfaces/IMiddleware.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.3; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {PayloadSubmitParams, QueuePayloadParams, Bid, Fees, WriteFinality, CallType, Parallel, IsPlug, RequestMetadata} from "../protocol/utils/common/Structs.sol"; /// @title IMiddleware diff --git a/contracts/interfaces/IPromise.sol b/contracts/interfaces/IPromise.sol index d2f21e8c..c43d6627 100644 --- a/contracts/interfaces/IPromise.sol +++ b/contracts/interfaces/IPromise.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; /// @title IPromise diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 45de683c..49615c67 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {DigestParams, ResolvedPromises, PayloadParams, TriggerParams, PayloadSubmitParams, RequestParams} from "../protocol/utils/common/Structs.sol"; diff --git a/contracts/interfaces/IWatcherPrecompileConfig.sol b/contracts/interfaces/IWatcherPrecompileConfig.sol index 4f28a33e..0b8f9c37 100644 --- a/contracts/interfaces/IWatcherPrecompileConfig.sol +++ b/contracts/interfaces/IWatcherPrecompileConfig.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {AppGatewayConfig, PlugConfig} from "../protocol/utils/common/Structs.sol"; diff --git a/contracts/interfaces/IWatcherPrecompileLimits.sol b/contracts/interfaces/IWatcherPrecompileLimits.sol index 62b52ce4..c56ed635 100644 --- a/contracts/interfaces/IWatcherPrecompileLimits.sol +++ b/contracts/interfaces/IWatcherPrecompileLimits.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {LimitParams, UpdateLimitParams} from "../protocol/utils/common/Structs.sol"; diff --git a/contracts/protocol/AddressResolver.sol b/contracts/protocol/AddressResolver.sol index f7cbc322..37e6d109 100644 --- a/contracts/protocol/AddressResolver.sol +++ b/contracts/protocol/AddressResolver.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {Ownable} from "solady/auth/Ownable.sol"; diff --git a/contracts/protocol/AsyncPromise.sol b/contracts/protocol/AsyncPromise.sol index b295b3e9..41682fc2 100644 --- a/contracts/protocol/AsyncPromise.sol +++ b/contracts/protocol/AsyncPromise.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {AddressResolverUtil} from "./utils/AddressResolverUtil.sol"; diff --git a/contracts/protocol/Forwarder.sol b/contracts/protocol/Forwarder.sol index 7ffe5d5b..b93fa5e5 100644 --- a/contracts/protocol/Forwarder.sol +++ b/contracts/protocol/Forwarder.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "../interfaces/IAddressResolver.sol"; diff --git a/contracts/protocol/payload-delivery/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol index c2e45473..26d4aede 100644 --- a/contracts/protocol/payload-delivery/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {ECDSA} from "solady/utils/ECDSA.sol"; import "solady/utils/Initializable.sol"; diff --git a/contracts/protocol/payload-delivery/ContractFactoryPlug.sol b/contracts/protocol/payload-delivery/ContractFactoryPlug.sol index 45858fee..01443b13 100644 --- a/contracts/protocol/payload-delivery/ContractFactoryPlug.sol +++ b/contracts/protocol/payload-delivery/ContractFactoryPlug.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "../utils/AccessControl.sol"; diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 6f600fb7..62b8089e 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {Ownable} from "solady/auth/Ownable.sol"; import "solady/utils/Initializable.sol"; diff --git a/contracts/protocol/payload-delivery/FeesPlug.sol b/contracts/protocol/payload-delivery/FeesPlug.sol index 4278b030..fbd9a13e 100644 --- a/contracts/protocol/payload-delivery/FeesPlug.sol +++ b/contracts/protocol/payload-delivery/FeesPlug.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "solady/utils/SafeTransferLib.sol"; diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol index 52e58f4a..6df859ac 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import "./FeesHelpers.sol"; diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol index 6b986bab..73c89be1 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "../../../interfaces/IMiddleware.sol"; diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol index 9bd0baa9..0a63674b 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "solady/utils/Initializable.sol"; diff --git a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol index ad64dc3b..1c59ac64 100644 --- a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol +++ b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "./RequestQueue.sol"; diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index 250e9181..b49ae310 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "./DeliveryUtils.sol"; diff --git a/contracts/protocol/utils/AddressResolverUtil.sol b/contracts/protocol/utils/AddressResolverUtil.sol index adb8b5e5..ec6ba238 100644 --- a/contracts/protocol/utils/AddressResolverUtil.sol +++ b/contracts/protocol/utils/AddressResolverUtil.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "../../interfaces/IAddressResolver.sol"; diff --git a/contracts/protocol/utils/FeesPlugin.sol b/contracts/protocol/utils/FeesPlugin.sol index 5a4d7514..c78bc932 100644 --- a/contracts/protocol/utils/FeesPlugin.sol +++ b/contracts/protocol/utils/FeesPlugin.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {Fees} from "../utils/common/Structs.sol"; diff --git a/contracts/protocol/utils/Gauge.sol b/contracts/protocol/utils/Gauge.sol index 0a0d7b59..fa2aba78 100644 --- a/contracts/protocol/utils/Gauge.sol +++ b/contracts/protocol/utils/Gauge.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {LimitParams} from "../utils/common/Structs.sol"; import {LimitReached} from "../utils/common/Errors.sol"; diff --git a/contracts/protocol/utils/common/Constants.sol b/contracts/protocol/utils/common/Constants.sol index c797f72a..bf6c8d4a 100644 --- a/contracts/protocol/utils/common/Constants.sol +++ b/contracts/protocol/utils/common/Constants.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; address constant ETH_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); diff --git a/contracts/protocol/utils/common/Errors.sol b/contracts/protocol/utils/common/Errors.sol index 03078541..e5261dc4 100644 --- a/contracts/protocol/utils/common/Errors.sol +++ b/contracts/protocol/utils/common/Errors.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; error NotSocket(); diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index dc184031..7b6f2fc9 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; //// ENUMS //// diff --git a/contracts/protocol/watcherPrecompile/DumpDecoder.sol b/contracts/protocol/watcherPrecompile/DumpDecoder.sol index fb98c56c..15b41a95 100644 --- a/contracts/protocol/watcherPrecompile/DumpDecoder.sol +++ b/contracts/protocol/watcherPrecompile/DumpDecoder.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {CallType, Parallel, WriteFinality} from "../utils/common/Structs.sol"; diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index fb6ac6c9..5e05a4b5 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "solady/utils/Initializable.sol"; diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index 4fdc00b9..8303a440 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "solady/utils/Initializable.sol"; diff --git a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol index 4337f634..580b7f92 100644 --- a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol +++ b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "./WatcherPrecompileCore.sol"; diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 09323799..0e258044 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "./RequestHandler.sol"; diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 9748bf19..a613e38a 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "./WatcherPrecompileStorage.sol"; diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol index 019d1b58..5320ca2d 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import "../../../interfaces/IWatcherPrecompile.sol"; import {IAppGateway} from "../../../interfaces/IAppGateway.sol"; diff --git a/script/admin/UpdateAppEVMxLimits.s.sol b/script/admin/UpdateAppEVMxLimits.s.sol index 7c650dd7..4d23d16c 100644 --- a/script/admin/UpdateAppEVMxLimits.s.sol +++ b/script/admin/UpdateAppEVMxLimits.s.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import "forge-std/Script.sol"; import {WatcherPrecompile} from "../../contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol"; diff --git a/script/admin/mock/DeployEVMx.s.sol b/script/admin/mock/DeployEVMx.s.sol index e3f71807..c268e42c 100644 --- a/script/admin/mock/DeployEVMx.s.sol +++ b/script/admin/mock/DeployEVMx.s.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; diff --git a/script/admin/mock/DeploySocket.s.sol b/script/admin/mock/DeploySocket.s.sol index 603d8eea..00ed4674 100644 --- a/script/admin/mock/DeploySocket.s.sol +++ b/script/admin/mock/DeploySocket.s.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; diff --git a/script/counter/DeployEVMxCounterApp.s.sol b/script/counter/DeployEVMxCounterApp.s.sol index a62b7e81..9e0cbee8 100644 --- a/script/counter/DeployEVMxCounterApp.s.sol +++ b/script/counter/DeployEVMxCounterApp.s.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; diff --git a/script/counter/DeployOnchainCounters.s.sol b/script/counter/DeployOnchainCounters.s.sol index d786e76b..531e2b95 100644 --- a/script/counter/DeployOnchainCounters.s.sol +++ b/script/counter/DeployOnchainCounters.s.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; diff --git a/script/counter/IncrementCountersFromApp.s.sol b/script/counter/IncrementCountersFromApp.s.sol index c048237b..92c688c7 100644 --- a/script/counter/IncrementCountersFromApp.s.sol +++ b/script/counter/IncrementCountersFromApp.s.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; diff --git a/script/counter/ReadOnchainCounters.s.sol b/script/counter/ReadOnchainCounters.s.sol index 534acd66..011ab4fb 100644 --- a/script/counter/ReadOnchainCounters.s.sol +++ b/script/counter/ReadOnchainCounters.s.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; diff --git a/script/counter/SetFees.s.sol b/script/counter/SetFees.s.sol index 3d089235..c97a2b0b 100644 --- a/script/counter/SetFees.s.sol +++ b/script/counter/SetFees.s.sol @@ -1,11 +1,12 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; import {ETH_ADDRESS} from "../../contracts/protocol/utils/common/Constants.sol"; import {CounterAppGateway} from "../../test/apps/app-gateways/counter/CounterAppGateway.sol"; import {Fees} from "../../contracts/protocol/utils/common/Structs.sol"; + // source .env && forge script script/counter/DeployCounterOnchain.s.sol --broadcast --skip-simulation --legacy --gas-price 0 contract CounterSetFees is Script { function run() external { diff --git a/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol b/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol index fc954d26..3a78e9cf 100644 --- a/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol +++ b/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; diff --git a/script/helpers/AppGatewayFeeBalance.s.sol b/script/helpers/AppGatewayFeeBalance.s.sol index 0743a7d0..395c542e 100644 --- a/script/helpers/AppGatewayFeeBalance.s.sol +++ b/script/helpers/AppGatewayFeeBalance.s.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; diff --git a/script/helpers/CheckAppEVMxLimits.s.sol b/script/helpers/CheckAppEVMxLimits.s.sol index 601dca82..d31c2cdc 100644 --- a/script/helpers/CheckAppEVMxLimits.s.sol +++ b/script/helpers/CheckAppEVMxLimits.s.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.13; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import "forge-std/Script.sol"; import {WatcherPrecompile} from "../../contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol"; diff --git a/script/helpers/PayFeesInArbitrumETH.s.sol b/script/helpers/PayFeesInArbitrumETH.s.sol index ebe4f0e2..edb8b2fd 100644 --- a/script/helpers/PayFeesInArbitrumETH.s.sol +++ b/script/helpers/PayFeesInArbitrumETH.s.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; diff --git a/test/DeliveryHelper.t.sol b/test/DeliveryHelper.t.sol index 645fe47d..fd7647d7 100644 --- a/test/DeliveryHelper.t.sol +++ b/test/DeliveryHelper.t.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import "../contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol"; import "../contracts/protocol/payload-delivery/FeesManager.sol"; diff --git a/test/FeesTest.t.sol b/test/FeesTest.t.sol index 4fc2a3a0..ec2f9632 100644 --- a/test/FeesTest.t.sol +++ b/test/FeesTest.t.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.3; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import "./DeliveryHelper.t.sol"; import {Counter} from "./apps/app-gateways/counter/Counter.sol"; diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index 6da8f8dc..56d7e45f 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {CounterAppGateway} from "./apps/app-gateways/counter/CounterAppGateway.sol"; import {Counter} from "./apps/app-gateways/counter/Counter.sol"; diff --git a/test/Migration.t.sol b/test/Migration.t.sol index aed1bf3e..0dd023c5 100644 --- a/test/Migration.t.sol +++ b/test/Migration.t.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import "./SetupTest.t.sol"; import "../contracts/protocol/AddressResolver.sol"; diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index a44c46cc..d9ef6138 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import "forge-std/Test.sol"; import "../contracts/protocol/utils/common/Structs.sol"; diff --git a/test/Storage.t.sol b/test/Storage.t.sol index 67f31252..523d7834 100644 --- a/test/Storage.t.sol +++ b/test/Storage.t.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import "./DeliveryHelper.t.sol"; diff --git a/test/apps/Counter.t.sol b/test/apps/Counter.t.sol index 22207236..243abcfc 100644 --- a/test/apps/Counter.t.sol +++ b/test/apps/Counter.t.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {CounterAppGateway} from "./app-gateways/counter/CounterAppGateway.sol"; diff --git a/test/apps/ParallelCounter.t.sol b/test/apps/ParallelCounter.t.sol index b7fdd7bf..fdaa8ac4 100644 --- a/test/apps/ParallelCounter.t.sol +++ b/test/apps/ParallelCounter.t.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import {CounterAppGateway} from "./app-gateways/counter/CounterAppGateway.sol"; diff --git a/test/apps/SuperToken.t.sol b/test/apps/SuperToken.t.sol index 45d62893..f33ca720 100644 --- a/test/apps/SuperToken.t.sol +++ b/test/apps/SuperToken.t.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import {SuperTokenAppGateway} from "./app-gateways/super-token/SuperTokenAppGateway.sol"; import {SuperToken} from "./app-gateways/super-token/SuperToken.sol"; diff --git a/test/apps/app-gateways/counter/Counter.sol b/test/apps/app-gateways/counter/Counter.sol index b3bec2cb..214bab43 100644 --- a/test/apps/app-gateways/counter/Counter.sol +++ b/test/apps/app-gateways/counter/Counter.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.7.0 <0.9.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import "solady/auth/Ownable.sol"; import "../../../../contracts/base/PlugBase.sol"; diff --git a/test/apps/app-gateways/counter/CounterAppGateway.sol b/test/apps/app-gateways/counter/CounterAppGateway.sol index 1911e0f2..a27b2db1 100644 --- a/test/apps/app-gateways/counter/CounterAppGateway.sol +++ b/test/apps/app-gateways/counter/CounterAppGateway.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity >=0.7.0 <0.9.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import "../../../../contracts/base/AppGatewayBase.sol"; import "../../../../contracts/interfaces/IForwarder.sol"; diff --git a/test/apps/app-gateways/counter/ICounter.sol b/test/apps/app-gateways/counter/ICounter.sol index 0bb3b319..453fde1c 100644 --- a/test/apps/app-gateways/counter/ICounter.sol +++ b/test/apps/app-gateways/counter/ICounter.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.7.0 <0.9.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; interface ICounter { function increase() external; diff --git a/test/apps/app-gateways/super-token/ISuperToken.sol b/test/apps/app-gateways/super-token/ISuperToken.sol index 77dbb353..75923f3d 100644 --- a/test/apps/app-gateways/super-token/ISuperToken.sol +++ b/test/apps/app-gateways/super-token/ISuperToken.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; interface ISuperToken { diff --git a/test/apps/app-gateways/super-token/SuperToken.sol b/test/apps/app-gateways/super-token/SuperToken.sol index 8c6de970..f1bf0247 100644 --- a/test/apps/app-gateways/super-token/SuperToken.sol +++ b/test/apps/app-gateways/super-token/SuperToken.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "solady/tokens/ERC20.sol"; diff --git a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol index c1cd0457..3110d497 100644 --- a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol +++ b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "solady/auth/Ownable.sol"; diff --git a/test/mock/MockWatcherPrecompile.sol b/test/mock/MockWatcherPrecompile.sol index 90dca4de..4cc265e9 100644 --- a/test/mock/MockWatcherPrecompile.sol +++ b/test/mock/MockWatcherPrecompile.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: Unlicense +// SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; import "../../contracts/interfaces/IAppGateway.sol"; diff --git a/test/mock/MockWatcherPrecompileImpl.sol b/test/mock/MockWatcherPrecompileImpl.sol index 567189f3..86f49e22 100644 --- a/test/mock/MockWatcherPrecompileImpl.sol +++ b/test/mock/MockWatcherPrecompileImpl.sol @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; import "../../contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol"; From b4bd4b04708b8d99baf5f70832a29404a81f9ae4 Mon Sep 17 00:00:00 2001 From: Akash Date: Tue, 15 Apr 2025 15:40:38 +0530 Subject: [PATCH 046/108] fix: triggerId --- contracts/protocol/socket/Socket.sol | 2 +- contracts/protocol/socket/SocketUtils.sol | 6 ++++-- contracts/protocol/watcherPrecompile/WatcherPrecompile.sol | 4 ---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 1506f056..eb8313e7 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -137,7 +137,7 @@ contract Socket is SocketUtils { if (plugConfig.appGatewayId == bytes32(0)) revert PlugDisconnected(); // creates a unique ID for the message - triggerId = _encodeTriggerId(plugConfig.appGatewayId); + triggerId = _encodeTriggerId(); emit AppGatewayCallRequested(triggerId, chainSlug, plug_, overrides_, payload_); } diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index 0d905a3c..f7d8583d 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -116,10 +116,12 @@ abstract contract SocketUtils is SocketConfig { // Packs the local plug, local chain slug, remote chain slug and nonce // triggerCounter++ will take care of call id overflow as well // triggerId(256) = localChainSlug(32) | appGateway_(160) | nonce(64) - function _encodeTriggerId(bytes32 appGatewayId_) internal returns (bytes32) { + function _encodeTriggerId() internal returns (bytes32) { return bytes32( - (uint256(chainSlug) << 224) | (uint256(appGatewayId_) << 64) | triggerCounter++ + (uint256(chainSlug) << 224) | + (uint256(uint160(address(this))) << 64) | + triggerCounter++ ); } diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index 7983f415..11def831 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -296,8 +296,4 @@ contract WatcherPrecompile is RequestHandler { function getRequestParams(uint40 requestCount) external view returns (RequestParams memory) { return requestParams[requestCount]; } - - function _decodeAppGateway(bytes32 triggerId_) internal pure returns (address) { - return address(uint160(uint256(triggerId_) >> 64)); - } } From a2ac76125545782969c9ec8aab785d62c003cc96 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 15 Apr 2025 16:52:54 +0530 Subject: [PATCH 047/108] fix: rename dump decoder --- contracts/protocol/utils/common/Structs.sol | 2 +- ...mpDecoder.sol => PayloadHeaderDecoder.sol} | 84 +++++++++++-------- .../watcherPrecompile/core/RequestHandler.sol | 24 +++--- .../core/WatcherPrecompile.sol | 19 +++-- .../core/WatcherPrecompileCore.sol | 20 ++--- .../core/WatcherPrecompileStorage.sol | 2 +- test/SetupTest.t.sol | 28 ++++--- 7 files changed, 101 insertions(+), 78 deletions(-) rename contracts/protocol/watcherPrecompile/{DumpDecoder.sol => PayloadHeaderDecoder.sol} (56%) diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 7b6f2fc9..60a77fb2 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -178,7 +178,7 @@ struct PayloadSubmitParams { struct PayloadParams { // uint40 requestCount + uint40 batchCount + uint40 payloadCount + uint32 chainSlug // CallType callType + Parallel isParallel + WriteFinality writeFinality - bytes32 dump; + bytes32 payloadHeader; // uint40 requestCount; // uint40 batchCount; // uint40 payloadCount; diff --git a/contracts/protocol/watcherPrecompile/DumpDecoder.sol b/contracts/protocol/watcherPrecompile/PayloadHeaderDecoder.sol similarity index 56% rename from contracts/protocol/watcherPrecompile/DumpDecoder.sol rename to contracts/protocol/watcherPrecompile/PayloadHeaderDecoder.sol index 15b41a95..a7f17356 100644 --- a/contracts/protocol/watcherPrecompile/DumpDecoder.sol +++ b/contracts/protocol/watcherPrecompile/PayloadHeaderDecoder.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.21; import {CallType, Parallel, WriteFinality} from "../utils/common/Structs.sol"; -library DumpDecoder { +library PayloadHeaderDecoder { // Corrected mapping (most significant bits on the left): // [256.....................................................................80][79.............................................0] // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -21,102 +21,120 @@ library DumpDecoder { // ------------------------------------------------------------------------- // GETTERS // ------------------------------------------------------------------------- - function getRequestCount(bytes32 dump_) internal pure returns (uint40) { + function getRequestCount(bytes32 payloadHeader_) internal pure returns (uint40) { // Top 40 bits => shift right by 216 - return uint40(uint256(dump_) >> 216); + return uint40(uint256(payloadHeader_) >> 216); } - function getBatchCount(bytes32 dump_) internal pure returns (uint40) { - return uint40((uint256(dump_) >> 176) & 0xFFFFFFFFFF); + function getBatchCount(bytes32 payloadHeader_) internal pure returns (uint40) { + return uint40((uint256(payloadHeader_) >> 176) & 0xFFFFFFFFFF); } - function getPayloadCount(bytes32 dump_) internal pure returns (uint40) { - return uint40((uint256(dump_) >> 136) & 0xFFFFFFFFFF); + function getPayloadCount(bytes32 payloadHeader_) internal pure returns (uint40) { + return uint40((uint256(payloadHeader_) >> 136) & 0xFFFFFFFFFF); } - function getChainSlug(bytes32 dump_) internal pure returns (uint32) { - return uint32((uint256(dump_) >> 104) & 0xFFFFFFFF); + function getChainSlug(bytes32 payloadHeader_) internal pure returns (uint32) { + return uint32((uint256(payloadHeader_) >> 104) & 0xFFFFFFFF); } - function getCallType(bytes32 dump_) internal pure returns (CallType) { - return CallType(uint8((uint256(dump_) >> 96) & 0xFF)); + function getCallType(bytes32 payloadHeader_) internal pure returns (CallType) { + return CallType(uint8((uint256(payloadHeader_) >> 96) & 0xFF)); } - function getIsParallel(bytes32 dump_) internal pure returns (Parallel) { - return Parallel(uint8((uint256(dump_) >> 88) & 0xFF)); + function getIsParallel(bytes32 payloadHeader_) internal pure returns (Parallel) { + return Parallel(uint8((uint256(payloadHeader_) >> 88) & 0xFF)); } - function getWriteFinality(bytes32 dump_) internal pure returns (WriteFinality) { - return WriteFinality(uint8((uint256(dump_) >> 80) & 0xFF)); + function getWriteFinality(bytes32 payloadHeader_) internal pure returns (WriteFinality) { + return WriteFinality(uint8((uint256(payloadHeader_) >> 80) & 0xFF)); } // ------------------------------------------------------------------------- // SETTERS // ------------------------------------------------------------------------- - /// @notice Sets the request count in a dump (top 40 bits) - function setRequestCount(bytes32 dump_, uint40 requestCount_) internal pure returns (bytes32) { + /// @notice Sets the request count in a payloadHeader (top 40 bits) + function setRequestCount( + bytes32 payloadHeader_, + uint40 requestCount_ + ) internal pure returns (bytes32) { // Clear bits [216..255], then OR in the new requestCount << 216 return bytes32( - (uint256(dump_) & ~((uint256(0xFFFFFFFFFF)) << 216)) | + (uint256(payloadHeader_) & ~((uint256(0xFFFFFFFFFF)) << 216)) | (uint256(requestCount_) << 216) ); } - /// @notice Sets the batch count in a dump [176..215] - function setBatchCount(bytes32 dump_, uint40 batchCount_) internal pure returns (bytes32) { + /// @notice Sets the batch count in a payloadHeader [176..215] + function setBatchCount( + bytes32 payloadHeader_, + uint40 batchCount_ + ) internal pure returns (bytes32) { return bytes32( - (uint256(dump_) & ~((uint256(0xFFFFFFFFFF)) << 176)) | + (uint256(payloadHeader_) & ~((uint256(0xFFFFFFFFFF)) << 176)) | ((uint256(batchCount_) & 0xFFFFFFFFFF) << 176) ); } /// @notice Sets the payload count [136..175] - function setPayloadCount(bytes32 dump_, uint40 payloadCount_) internal pure returns (bytes32) { + function setPayloadCount( + bytes32 payloadHeader_, + uint40 payloadCount_ + ) internal pure returns (bytes32) { return bytes32( - (uint256(dump_) & ~((uint256(0xFFFFFFFFFF)) << 136)) | + (uint256(payloadHeader_) & ~((uint256(0xFFFFFFFFFF)) << 136)) | ((uint256(payloadCount_) & 0xFFFFFFFFFF) << 136) ); } /// @notice Sets the chain slug [104..135] - function setChainSlug(bytes32 dump_, uint32 chainSlug_) internal pure returns (bytes32) { + function setChainSlug( + bytes32 payloadHeader_, + uint32 chainSlug_ + ) internal pure returns (bytes32) { return bytes32( - (uint256(dump_) & ~((uint256(0xFFFFFFFF)) << 104)) | + (uint256(payloadHeader_) & ~((uint256(0xFFFFFFFF)) << 104)) | ((uint256(chainSlug_) & 0xFFFFFFFF) << 104) ); } /// @notice Sets the call type [96..103] - function setCallType(bytes32 dump_, CallType callType_) internal pure returns (bytes32) { + function setCallType( + bytes32 payloadHeader_, + CallType callType_ + ) internal pure returns (bytes32) { return bytes32( - (uint256(dump_) & ~((uint256(0xFF)) << 96)) | + (uint256(payloadHeader_) & ~((uint256(0xFF)) << 96)) | ((uint256(uint8(callType_)) & 0xFF) << 96) ); } /// @notice Sets the parallel flag [88..95] - function setIsParallel(bytes32 dump_, Parallel isParallel_) internal pure returns (bytes32) { + function setIsParallel( + bytes32 payloadHeader_, + Parallel isParallel_ + ) internal pure returns (bytes32) { return bytes32( - (uint256(dump_) & ~((uint256(0xFF)) << 88)) | + (uint256(payloadHeader_) & ~((uint256(0xFF)) << 88)) | ((uint256(uint8(isParallel_)) & 0xFF) << 88) ); } /// @notice Sets the write finality [80..87] function setWriteFinality( - bytes32 dump_, + bytes32 payloadHeader_, WriteFinality writeFinality_ ) internal pure returns (bytes32) { return bytes32( - (uint256(dump_) & ~((uint256(0xFF)) << 80)) | + (uint256(payloadHeader_) & ~((uint256(0xFF)) << 80)) | ((uint256(uint8(writeFinality_)) & 0xFF) << 80) ); } @@ -124,8 +142,8 @@ library DumpDecoder { // ------------------------------------------------------------------------- // CREATE // ------------------------------------------------------------------------- - /// @notice Creates a new dump with all fields set - function createDump( + /// @notice Creates a new payloadHeader with all fields set + function createPayloadHeader( uint40 requestCount_, uint40 batchCount_, uint40 payloadCount_, diff --git a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol index 580b7f92..51fe49e4 100644 --- a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol +++ b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol @@ -8,7 +8,7 @@ import "./WatcherPrecompileCore.sol"; /// @dev This contract extends WatcherPrecompileCore to provide request handling functionality /// @dev It manages the submission of payload requests and their processing abstract contract RequestHandler is WatcherPrecompileCore { - using DumpDecoder for bytes32; + using PayloadHeaderDecoder for bytes32; // slots [266-315] reserved for gap uint256[50] _request_handler_gap; @@ -59,16 +59,16 @@ abstract contract RequestHandler is WatcherPrecompileCore { ); batchPayloadIds[batchCount].push(payloadId); - bytes32 dump; - dump = dump.setRequestCount(requestCount); - dump = dump.setBatchCount(batchCount); - dump = dump.setPayloadCount(localPayloadCount); - dump = dump.setChainSlug(p.chainSlug); - dump = dump.setCallType(p.callType); - dump = dump.setIsParallel(p.isParallel); - dump = dump.setWriteFinality(p.writeFinality); + 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); - payloads[payloadId].dump = dump; + payloads[payloadId].payloadHeader = payloadHeader; payloads[payloadId].asyncPromise = p.asyncPromise; payloads[payloadId].switchboard = p.switchboard; payloads[payloadId].target = p.target; @@ -138,7 +138,7 @@ abstract contract RequestHandler is WatcherPrecompileCore { if (r.transmitter != address(0)) revert AlreadyStarted(); if (r.currentBatchPayloadsLeft > 0) revert AlreadyStarted(); - uint40 batchCount = r.payloadParamsArray[0].dump.getBatchCount(); + uint40 batchCount = r.payloadParamsArray[0].payloadHeader.getBatchCount(); r.transmitter = transmitter; r.currentBatch = batchCount; @@ -160,7 +160,7 @@ abstract contract RequestHandler is WatcherPrecompileCore { if (isPromiseExecuted[payloadParamsArray[i].payloadId]) continue; totalPayloads++; - if (payloadParamsArray[i].dump.getCallType() != CallType.READ) { + if (payloadParamsArray[i].payloadHeader.getCallType() != CallType.READ) { _finalize(payloadParamsArray[i], r.transmitter); } else { _query(payloadParamsArray[i]); diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 0e258044..8045f126 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -9,7 +9,7 @@ import {LibCall} from "solady/utils/LibCall.sol"; /// @dev This contract extends RequestHandler to provide the main functionality for the WatcherPrecompile system /// @dev It handles timeout requests, finalization, queries, and promise resolution contract WatcherPrecompile is RequestHandler { - using DumpDecoder for bytes32; + using PayloadHeaderDecoder for bytes32; using LibCall for address; /// @notice Constructor that disables initializers for the implementation @@ -203,7 +203,7 @@ contract WatcherPrecompile is RequestHandler { if (asyncPromise != address(0)) { // Resolve each promise with its corresponding return data bool success = IPromise(asyncPromise).markResolved( - payloadParams.dump.getRequestCount(), + payloadParams.payloadHeader.getRequestCount(), resolvedPromises_[i].payloadId, resolvedPromises_[i].returnData ); @@ -217,7 +217,7 @@ contract WatcherPrecompile is RequestHandler { isPromiseExecuted[resolvedPromises_[i].payloadId] = true; RequestParams storage requestParams_ = requestParams[ - payloadParams.dump.getRequestCount() + payloadParams.payloadHeader.getRequestCount() ]; requestParams_.currentBatchPayloadsLeft--; requestParams_.payloadsRemaining--; @@ -226,13 +226,16 @@ contract WatcherPrecompile is RequestHandler { if ( requestParams_.currentBatchPayloadsLeft == 0 && requestParams_.payloadsRemaining > 0 ) { - _processBatch(payloadParams.dump.getRequestCount(), ++requestParams_.currentBatch); + _processBatch( + payloadParams.payloadHeader.getRequestCount(), + ++requestParams_.currentBatch + ); } // if all payloads of a request are executed, finish the request if (requestParams_.payloadsRemaining == 0) { IMiddleware(requestParams_.middleware).finishRequest( - payloadParams.dump.getRequestCount() + payloadParams.payloadHeader.getRequestCount() ); } emit PromiseResolved(resolvedPromises_[i].payloadId, asyncPromise); @@ -263,18 +266,18 @@ contract WatcherPrecompile is RequestHandler { if (payloadParams.deadline > block.timestamp) revert DeadlineNotPassedForOnChainRevert(); RequestParams storage currentRequestParams = requestParams[ - payloadParams.dump.getRequestCount() + payloadParams.payloadHeader.getRequestCount() ]; currentRequestParams.isRequestCancelled = true; if (isRevertingOnchain_ && payloadParams.asyncPromise != address(0)) IPromise(payloadParams.asyncPromise).markOnchainRevert( - payloadParams.dump.getRequestCount(), + payloadParams.payloadHeader.getRequestCount(), payloadId_ ); IMiddleware(currentRequestParams.middleware).handleRequestReverts( - payloadParams.dump.getRequestCount() + payloadParams.payloadHeader.getRequestCount() ); emit MarkedRevert(payloadId_, isRevertingOnchain_); diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index a613e38a..f3ba6d6d 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -18,7 +18,7 @@ abstract contract WatcherPrecompileCore is AccessControl, AddressResolverUtil { - using DumpDecoder for bytes32; + using PayloadHeaderDecoder for bytes32; // slots [216-265] reserved for gap uint256[50] _core_gap; @@ -63,7 +63,7 @@ abstract contract WatcherPrecompileCore is PayloadParams memory params_, address transmitter_ ) internal returns (bytes32 digest) { - uint32 chainSlug = params_.dump.getChainSlug(); + uint32 chainSlug = params_.payloadHeader.getChainSlug(); // Verify that the app gateway is properly configured for this chain and target watcherPrecompileConfig__.verifyConnections( @@ -71,14 +71,14 @@ abstract contract WatcherPrecompileCore is params_.target, params_.appGateway, params_.switchboard, - requestParams[params_.dump.getRequestCount()].middleware + requestParams[params_.payloadHeader.getRequestCount()].middleware ); uint256 deadline = block.timestamp + expiryTime; payloads[params_.payloadId].deadline = deadline; payloads[params_.payloadId].finalizedTransmitter = transmitter_; - bytes32 prevDigestsHash = _getPreviousDigestsHash(params_.dump.getBatchCount()); + bytes32 prevDigestsHash = _getPreviousDigestsHash(params_.payloadHeader.getBatchCount()); payloads[params_.payloadId].prevDigestsHash = prevDigestsHash; // Construct parameters for digest calculation @@ -87,8 +87,8 @@ abstract contract WatcherPrecompileCore is transmitter_, params_.payloadId, deadline, - params_.dump.getCallType(), - params_.dump.getWriteFinality(), + params_.payloadHeader.getCallType(), + params_.payloadHeader.getWriteFinality(), params_.gasLimit, params_.value, params_.readAt, @@ -109,7 +109,7 @@ abstract contract WatcherPrecompileCore is /// @param params_ The payload parameters for the query /// @dev This function sets up a query request and emits a QueryRequested event function _query(PayloadParams memory params_) internal { - bytes32 prevDigestsHash = _getPreviousDigestsHash(params_.dump.getBatchCount()); + bytes32 prevDigestsHash = _getPreviousDigestsHash(params_.payloadHeader.getBatchCount()); payloads[params_.payloadId].prevDigestsHash = prevDigestsHash; emit QueryRequested(params_); } @@ -150,12 +150,12 @@ abstract contract WatcherPrecompileCore is for (uint40 i = 0; i < payloadIds.length; i++) { PayloadParams memory p = payloads[payloadIds[i]]; DigestParams memory digestParams = DigestParams( - watcherPrecompileConfig__.sockets(p.dump.getChainSlug()), + watcherPrecompileConfig__.sockets(p.payloadHeader.getChainSlug()), p.finalizedTransmitter, p.payloadId, p.deadline, - p.dump.getCallType(), - p.dump.getWriteFinality(), + p.payloadHeader.getCallType(), + p.payloadHeader.getWriteFinality(), p.gasLimit, p.value, p.readAt, diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol index 5320ca2d..80529685 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol @@ -5,7 +5,7 @@ import "../../../interfaces/IWatcherPrecompile.sol"; import {IAppGateway} from "../../../interfaces/IAppGateway.sol"; import {IFeesManager} from "../../../interfaces/IFeesManager.sol"; import {IPromise} from "../../../interfaces/IPromise.sol"; -import "../DumpDecoder.sol"; +import "../PayloadHeaderDecoder.sol"; import {IMiddleware} from "../../../interfaces/IMiddleware.sol"; import {QUERY, FINALIZE, SCHEDULE, MAX_COPY_BYTES} from "../../utils/common/Constants.sol"; diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index d9ef6138..152c1729 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -8,7 +8,7 @@ import "../contracts/protocol/utils/common/Constants.sol"; import "../contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol"; import "../contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol"; import "../contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol"; -import "../contracts/protocol/watcherPrecompile/DumpDecoder.sol"; +import "../contracts/protocol/watcherPrecompile/PayloadHeaderDecoder.sol"; import "../contracts/interfaces/IForwarder.sol"; import "../contracts/protocol/utils/common/AccessRoles.sol"; import {Socket} from "../contracts/protocol/socket/Socket.sol"; @@ -24,7 +24,7 @@ import {ResolvedPromises} from "../contracts/protocol/utils/common/Structs.sol"; import "solady/utils/ERC1967Factory.sol"; contract SetupTest is Test { - using DumpDecoder for bytes32; + using PayloadHeaderDecoder for bytes32; uint public c = 1; address owner = address(uint160(c++)); @@ -202,7 +202,7 @@ contract SetupTest is Test { for (uint i = 0; i < payloadIds.length; i++) { PayloadParams memory payloadParams = watcherPrecompile.getPayloadParams(payloadIds[i]); - if (payloadParams.dump.getCallType() != CallType.READ) { + if (payloadParams.payloadHeader.getCallType() != CallType.READ) { return false; } } @@ -236,7 +236,7 @@ contract SetupTest is Test { PayloadParams memory payloadParams = watcherPrecompile.getPayloadParams(payloadIds[i]); bool isLastPayload = i == payloadIds.length - 1 && hasMoreBatches; - if (payloadParams.dump.getCallType() == CallType.READ) { + if (payloadParams.payloadHeader.getCallType() == CallType.READ) { _resolveAndExpectFinalizeRequested( payloadParams.payloadId, payloadParams, @@ -290,14 +290,14 @@ contract SetupTest is Test { function _generateWatcherProof( PayloadParams memory params_ ) internal view returns (bytes memory, bytes32) { - SocketContracts memory socketConfig = getSocketConfig(params_.dump.getChainSlug()); + SocketContracts memory socketConfig = getSocketConfig(params_.payloadHeader.getChainSlug()); DigestParams memory digestParams_ = DigestParams( address(socketConfig.socket), transmitterEOA, params_.payloadId, params_.deadline, - params_.dump.getCallType(), - params_.dump.getWriteFinality(), + params_.payloadHeader.getCallType(), + params_.payloadHeader.getWriteFinality(), params_.gasLimit, params_.value, params_.readAt, @@ -339,7 +339,9 @@ contract SetupTest is Test { bytes memory transmitterSig ) { - SocketContracts memory socketConfig = getSocketConfig(payloadParams.dump.getChainSlug()); + SocketContracts memory socketConfig = getSocketConfig( + payloadParams.payloadHeader.getChainSlug() + ); bytes32 transmitterDigest = keccak256( abi.encode(address(socketConfig.socket), payloadParams.payloadId) ); @@ -347,16 +349,16 @@ contract SetupTest is Test { params = ExecuteParams({ deadline: payloadParams.deadline, - callType: payloadParams.dump.getCallType(), - writeFinality: payloadParams.dump.getWriteFinality(), + callType: payloadParams.payloadHeader.getCallType(), + writeFinality: payloadParams.payloadHeader.getWriteFinality(), gasLimit: payloadParams.gasLimit, value: payloadParams.value, readAt: payloadParams.readAt, payload: payloadParams.payload, target: payloadParams.target, - requestCount: payloadParams.dump.getRequestCount(), - batchCount: payloadParams.dump.getBatchCount(), - payloadCount: payloadParams.dump.getPayloadCount(), + requestCount: payloadParams.payloadHeader.getRequestCount(), + batchCount: payloadParams.payloadHeader.getBatchCount(), + payloadCount: payloadParams.payloadHeader.getPayloadCount(), prevDigestsHash: payloadParams.prevDigestsHash, switchboard: payloadParams.switchboard }); From dd6dd8cbb83969f9407926818d264a9678135f26 Mon Sep 17 00:00:00 2001 From: Akash Date: Tue, 15 Apr 2025 17:47:46 +0530 Subject: [PATCH 048/108] feat: Socket Fees Manager tests --- contracts/protocol/socket/Socket.sol | 23 +-- .../protocol/socket/SocketFeeManager.sol | 6 +- test/Inbox.t.sol | 8 +- test/SetupTest.t.sol | 8 +- test/SocketFeeManager.t.sol | 152 ++++++++++++++++++ test/mock/MockFastSwitchboard.sol | 36 +++++ 6 files changed, 210 insertions(+), 23 deletions(-) create mode 100644 test/SocketFeeManager.t.sol create mode 100644 test/mock/MockFastSwitchboard.sol diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index ae740966..8369e051 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -66,12 +66,6 @@ contract Socket is SocketUtils { ) : address(0); - if (address(socketFeeManager) != address(0)) { - socketFeeManager.payAndCheckFees{value: transmissionParams_.socketFees}( - executeParams_, - transmissionParams_ - ); - } bytes32 digest = _createDigest( transmitter, payloadId, @@ -79,7 +73,7 @@ contract Socket is SocketUtils { executeParams_ ); _verify(digest, payloadId, plugConfig.switchboard); - return _execute(payloadId, executeParams_); + return _execute(payloadId, executeParams_, transmissionParams_); } //////////////////////////////////////////////////////// @@ -100,7 +94,8 @@ contract Socket is SocketUtils { */ function _execute( bytes32 payloadId_, - ExecuteParams memory executeParams_ + ExecuteParams memory executeParams_, + TransmissionParams memory transmissionParams_ ) internal returns (bytes memory) { if (gasleft() < executeParams_.gasLimit) revert LowGasLimit(); @@ -112,11 +107,17 @@ contract Socket is SocketUtils { executeParams_.payload ); - 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; emit ExecutionFailed(payloadId_, returnData); - } else { - emit ExecutionSuccess(payloadId_, returnData); } return returnData; diff --git a/contracts/protocol/socket/SocketFeeManager.sol b/contracts/protocol/socket/SocketFeeManager.sol index 29672e3c..adc22c84 100644 --- a/contracts/protocol/socket/SocketFeeManager.sol +++ b/contracts/protocol/socket/SocketFeeManager.sol @@ -34,12 +34,10 @@ contract SocketFeeManager is ISocketFeeManager, AccessControl { /** * @notice Pays and validates fees for execution - * @param executeParams_ Execute params - * @param transmissionParams_ Transmission params */ function payAndCheckFees( - ExecuteParams memory executeParams_, - TransmissionParams memory transmissionParams_ + ExecuteParams memory, + TransmissionParams memory ) external payable { if (msg.value < socketFees) revert InsufficientFees(); } diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index d1763190..186b288a 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -62,7 +62,7 @@ contract TriggerTest is DeliveryHelperTest { // Simulate a message from another chain through the watcher uint256 incrementValue = 5; - bytes32 triggerId = _encodeTriggerId(address(gateway), arbChainSlug); + bytes32 triggerId = _encodeTriggerId(address(arbConfig.socket), arbChainSlug); bytes memory payload = abi.encodeWithSelector( CounterAppGateway.increase.selector, incrementValue @@ -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++ ); } } diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index b752a6c7..f2a22f14 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -17,7 +17,7 @@ import "../contracts/protocol/socket/SocketBatcher.sol"; import "../contracts/protocol/AddressResolver.sol"; import {ContractFactoryPlug} from "../contracts/protocol/payload-delivery/ContractFactoryPlug.sol"; import {FeesPlug} from "../contracts/protocol/payload-delivery/FeesPlug.sol"; - +import {SocketFeeManager} from "../contracts/protocol/socket/SocketFeeManager.sol"; import {ETH_ADDRESS} from "../contracts/protocol/utils/common/Constants.sol"; import {ResolvedPromises} from "../contracts/protocol/utils/common/Structs.sol"; @@ -40,7 +40,7 @@ contract SetupTest is Test { uint32 evmxSlug = 1; uint256 expiryTime = 10000000; uint256 maxReAuctionCount = 10; - + uint256 socketFees = 0.01 ether; uint256 public signatureNonce; uint256 public payloadIdCounter; uint256 public timeoutIdCounter; @@ -53,6 +53,7 @@ contract SetupTest is Test { struct SocketContracts { uint32 chainSlug; Socket socket; + SocketFeeManager socketFeeManager; FastSwitchboard switchboard; SocketBatcher socketBatcher; ContractFactoryPlug contractFactoryPlug; @@ -103,7 +104,7 @@ contract SetupTest is Test { address(contractFactoryPlug), address(feesPlug) ); - + SocketFeeManager socketFeeManager = new SocketFeeManager(owner, socketFees); hoax(watcherEOA); watcherPrecompileConfig.setSwitchboard(chainSlug_, FAST, address(switchboard)); @@ -111,6 +112,7 @@ contract SetupTest is Test { SocketContracts({ chainSlug: chainSlug_, socket: socket, + socketFeeManager: socketFeeManager, switchboard: switchboard, socketBatcher: socketBatcher, contractFactoryPlug: contractFactoryPlug, diff --git a/test/SocketFeeManager.t.sol b/test/SocketFeeManager.t.sol new file mode 100644 index 00000000..dd0cd7c4 --- /dev/null +++ b/test/SocketFeeManager.t.sol @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {CounterAppGateway} from "./apps/app-gateways/counter/CounterAppGateway.sol"; +import {Counter} from "./apps/app-gateways/counter/Counter.sol"; +import "./SetupTest.t.sol"; +import {SocketFeeManager} from "../contracts/protocol/socket/SocketFeeManager.sol"; +import {MockFastSwitchboard} from "./mock/MockFastSwitchboard.sol"; +import {ExecuteParams, TransmissionParams, CallType, WriteFinality} from "../contracts/protocol/utils/common/Structs.sol"; +import {GOVERNANCE_ROLE, RESCUE_ROLE} from "../contracts/protocol/utils/common/AccessRoles.sol"; +import {Test} from "forge-std/Test.sol"; + +contract SocketFeeManagerTest is SetupTest { + Counter public counter; + address public gateway = address(5); + MockFastSwitchboard public mockSwitchboard; + Socket public socket; + SocketFeeManager public socketFeeManager; + + function setUp() public { + socket = new Socket(arbChainSlug, owner, "test"); + vm.prank(owner); + socket.grantRole(GOVERNANCE_ROLE, address(owner)); + socketFeeManager = new SocketFeeManager(owner, socketFees); + mockSwitchboard = new MockFastSwitchboard(arbChainSlug, address(socket), owner); + mockSwitchboard.registerSwitchboard(); + + counter = new Counter(); + counter.initSocket(_encodeAppGatewayId(gateway), address(socket), address(mockSwitchboard)); + } + + function testSuccessfulExecutionWithFeeManagerNotSet() public { + // Create execute params + ( + ExecuteParams memory executeParams, + TransmissionParams memory transmissionParams + ) = _getExecutionParams(abi.encodeWithSelector(Counter.increase.selector)); + + // Fund the contract with enough ETH for fees + vm.deal(address(this), socketFees); + + // Execute with fees + socket.execute{value: socketFees}(executeParams, transmissionParams); + + // Check counter was incremented + assertEq(counter.counter(), 1, "Counter should be incremented"); + assertEq( + address(socketFeeManager).balance, + 0, + "Socket fee manager should have 0 balance when fees manager not set" + ); + } + + function testSuccessfulExecutionWithFeeManagerSet() public { + // Set the fee manager in socket + vm.prank(owner); + socket.setSocketFeeManager(address(socketFeeManager)); + + // Create execute params + ( + ExecuteParams memory executeParams, + TransmissionParams memory transmissionParams + ) = _getExecutionParams(abi.encodeWithSelector(Counter.increase.selector)); + + // Fund the contract with enough ETH for fees + vm.deal(address(this), socketFees); + + // Execute with fees + socket.execute{value: socketFees}(executeParams, transmissionParams); + + // Check counter was incremented + assertEq(counter.counter(), 1, "Counter should be incremented"); + assertEq( + address(socketFeeManager).balance, + socketFees, + "Socket fee manager should have received the fees" + ); + } + + function testSuccessfulExecutionWithInsufficientFees() public { + // Set the fee manager in socket + vm.prank(owner); + socket.setSocketFeeManager(address(socketFeeManager)); + + // Create execute params + ( + ExecuteParams memory executeParams, + TransmissionParams memory transmissionParams + ) = _getExecutionParams(abi.encodeWithSelector(Counter.increase.selector)); + + // Fund with insufficient ETH + vm.deal(address(this), socketFees - 1); + + // Should revert with InsufficientMsgValue + vm.expectRevert(Socket.InsufficientMsgValue.selector); + socket.execute{value: socketFees - 1}(executeParams, transmissionParams); + } + + function testExecutionFailed() public { + // Set the fee manager in socket + vm.prank(owner); + socket.setSocketFeeManager(address(socketFeeManager)); + + // Create execute params with invalid payload to force failure + ( + ExecuteParams memory executeParams, + TransmissionParams memory transmissionParams + ) = _getExecutionParams(abi.encodeWithSelector(bytes4(keccak256("invalid()")))); + + // Fund the contract with enough ETH for fees + vm.deal(address(this), socketFees); + + // Execute with fees + socket.execute{value: socketFees}(executeParams, transmissionParams); + + // Check counter was not incremented + assertEq(counter.counter(), 0, "Counter should not be incremented"); + assertEq( + address(socketFeeManager).balance, + 0, + "Socket fee manager should not receive fees on failed execution" + ); + } + + function _getExecutionParams( + bytes memory payload + ) internal view returns (ExecuteParams memory, TransmissionParams memory) { + ExecuteParams memory executeParams = ExecuteParams({ + deadline: block.timestamp + 1 days, + callType: CallType.WRITE, + writeFinality: WriteFinality.HIGH, + gasLimit: 100000, + value: 0, + readAt: 0, + payload: payload, + target: address(counter), + requestCount: 0, + batchCount: 0, + payloadCount: 0, + prevDigestsHash: bytes32(0), + switchboard: address(mockSwitchboard) + }); + + TransmissionParams memory transmissionParams = TransmissionParams({ + transmitterSignature: bytes(""), + socketFees: socketFees, + extraData: bytes("") + }); + + return (executeParams, transmissionParams); + } +} diff --git a/test/mock/MockFastSwitchboard.sol b/test/mock/MockFastSwitchboard.sol new file mode 100644 index 00000000..9bb75a14 --- /dev/null +++ b/test/mock/MockFastSwitchboard.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../../contracts/interfaces/ISwitchboard.sol"; +import "../../contracts/interfaces/ISocket.sol"; +contract MockFastSwitchboard is ISwitchboard { + address public owner; + ISocket public immutable socket__; + + // chain slug of deployed chain + uint32 public immutable chainSlug; + + /** + * @dev Constructor of SwitchboardBase + * @param chainSlug_ Chain slug of deployment chain + * @param socket_ socket_ contract + */ + constructor(uint32 chainSlug_, address socket_, address owner_) { + chainSlug = chainSlug_; + socket__ = ISocket(socket_); + owner = owner_; + } + + function attest(bytes32 , bytes calldata ) external { + // TODO: implement + } + + function allowPacket(bytes32 , bytes32) external pure returns (bool) { + // digest has enough attestations + return true; + } + + function registerSwitchboard() external { + socket__.registerSwitchboard(); + } +} From a9fd06aa1a9ffe684ce9921574766cdf197f59e0 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 15 Apr 2025 17:55:10 +0530 Subject: [PATCH 049/108] fix: check effect interaction --- contracts/base/AppGatewayBase.sol | 2 +- contracts/interfaces/IWatcherPrecompile.sol | 2 ++ contracts/protocol/AddressResolver.sol | 16 ++++++++++++++ contracts/protocol/AsyncPromise.sol | 1 + contracts/protocol/Forwarder.sol | 10 ++++----- .../payload-delivery/AuctionManager.sol | 21 ++++++++++--------- .../protocol/payload-delivery/FeesManager.sol | 3 +++ .../app-gateway/RequestQueue.sol | 1 + contracts/protocol/utils/common/Errors.sol | 8 +++++++ .../watcherPrecompile/core/RequestHandler.sol | 4 ++-- .../core/WatcherPrecompile.sol | 12 ++++++----- .../core/WatcherPrecompileCore.sol | 5 +++-- .../core/WatcherPrecompileStorage.sol | 1 - 13 files changed, 60 insertions(+), 26 deletions(-) diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index 254b0f3a..0216e44d 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -30,9 +30,9 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin if (fees.feePoolChain == 0) revert FeesNotSet(); isAsyncModifierSet = true; + _clearOverrides(); deliveryHelper__().clearQueue(); addressResolver__.clearPromises(); - _clearOverrides(); _; diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 49615c67..0def0da2 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -76,6 +76,8 @@ interface IWatcherPrecompile { event WatcherPrecompileConfigSet(address watcherPrecompileConfig); + event RequestCancelledFromGateway(uint40 requestCount); + /// @notice Error thrown when an invalid chain slug is provided error InvalidChainSlug(); /// @notice Error thrown when an invalid app gateway reaches a plug diff --git a/contracts/protocol/AddressResolver.sol b/contracts/protocol/AddressResolver.sol index 37e6d109..e6d0a401 100644 --- a/contracts/protocol/AddressResolver.sol +++ b/contracts/protocol/AddressResolver.sol @@ -58,6 +58,17 @@ contract AddressResolver is AddressResolverStorage, Initializable, Ownable { /// @notice Error thrown if AppGateway contract was already set by a different address error InvalidAppGateway(address contractAddress_); + /// @notice Event emitted when the delivery helper is updated + event DeliveryHelperUpdated(address deliveryHelper_); + /// @notice Event emitted when the fees manager is updated + event FeesManagerUpdated(address feesManager_); + /// @notice Event emitted when the default auction manager is updated + event DefaultAuctionManagerUpdated(address defaultAuctionManager_); + /// @notice Event emitted when the watcher precompile is updated + event WatcherPrecompileUpdated(address watcherPrecompile_); + /// @notice Event emitted when the contracts to gateways mapping is updated + event ContractsToGatewaysUpdated(address contractAddress_, address appGateway_); + constructor() { _disableInitializers(); // disable for implementation } @@ -200,6 +211,7 @@ contract AddressResolver is AddressResolverStorage, Initializable, Ownable { revert InvalidAppGateway(contractAddress_); } contractsToGateways[contractAddress_] = msg.sender; + emit ContractsToGatewaysUpdated(contractAddress_, msg.sender); } /// @notice Gets the predicted address of a Forwarder proxy contract @@ -254,23 +266,27 @@ contract AddressResolver is AddressResolverStorage, Initializable, Ownable { /// @param deliveryHelper_ The address of the delivery helper function setDeliveryHelper(address deliveryHelper_) external onlyOwner { deliveryHelper = deliveryHelper_; + emit DeliveryHelperUpdated(deliveryHelper_); } /// @notice Updates the address of the fees manager /// @param feesManager_ The address of the fees manager function setFeesManager(address feesManager_) external onlyOwner { feesManager = feesManager_; + emit FeesManagerUpdated(feesManager_); } /// @notice Updates the address of the default auction manager /// @param defaultAuctionManager_ The address of the default auction manager function setDefaultAuctionManager(address defaultAuctionManager_) external onlyOwner { defaultAuctionManager = defaultAuctionManager_; + emit DefaultAuctionManagerUpdated(defaultAuctionManager_); } /// @notice Updates the address of the watcher precompile contract /// @param watcherPrecompile_ The address of the watcher precompile contract function setWatcherPrecompile(address watcherPrecompile_) external onlyOwner { watcherPrecompile__ = IWatcherPrecompile(watcherPrecompile_); + emit WatcherPrecompileUpdated(watcherPrecompile_); } } diff --git a/contracts/protocol/AsyncPromise.sol b/contracts/protocol/AsyncPromise.sol index 41682fc2..261c9d56 100644 --- a/contracts/protocol/AsyncPromise.sol +++ b/contracts/protocol/AsyncPromise.sol @@ -95,6 +95,7 @@ contract AsyncPromise is AsyncPromiseStorage, Initializable, AddressResolverUtil callbackSelector, abi.encode(callbackData, returnData_) ); + // setting max_copy_bytes to 0 as not using returnData right now (success, , ) = localInvoker.tryCall(0, gasleft(), 0, combinedCalldata); if (success) return success; diff --git a/contracts/protocol/Forwarder.sol b/contracts/protocol/Forwarder.sol index b93fa5e5..17bb2b6d 100644 --- a/contracts/protocol/Forwarder.sol +++ b/contracts/protocol/Forwarder.sol @@ -7,7 +7,7 @@ import "../interfaces/IAppGateway.sol"; import "../interfaces/IPromise.sol"; import "../interfaces/IForwarder.sol"; import {AddressResolverUtil} from "./utils/AddressResolverUtil.sol"; -import {AsyncModifierNotUsed} from "./utils/common/Errors.sol"; +import {AsyncModifierNotUsed, NoAsyncPromiseFound, PromiseCallerMismatch, RequestCountMismatch, DeliveryHelperNotSet} from "./utils/common/Errors.sol"; import "solady/utils/Initializable.sol"; /// @title Forwarder Storage @@ -66,10 +66,10 @@ contract Forwarder is ForwarderStorage, Initializable, AddressResolverUtil { /// @param data_ The data to be passed to callback /// @return promise_ The address of the new promise function then(bytes4 selector_, bytes memory data_) external returns (address promise_) { - if (latestAsyncPromise == address(0)) revert("Forwarder: no async promise found"); - if (latestPromiseCaller != msg.sender) revert("Forwarder: promise caller mismatch"); + if (latestAsyncPromise == address(0)) revert NoAsyncPromiseFound(); + if (latestPromiseCaller != msg.sender) revert PromiseCallerMismatch(); if (latestRequestCount != watcherPrecompile__().nextRequestCount()) - revert("Forwarder: request count mismatch"); + revert RequestCountMismatch(); address latestAsyncPromise_ = latestAsyncPromise; latestAsyncPromise = address(0); @@ -93,7 +93,7 @@ contract Forwarder is ForwarderStorage, Initializable, AddressResolverUtil { /// @dev It queues the calls in the middleware and deploys the promise contract fallback() external { if (address(deliveryHelper__()) == address(0)) { - revert("Forwarder: deliveryHelper not found"); + revert DeliveryHelperNotSet(); } // validates if the async modifier is set diff --git a/contracts/protocol/payload-delivery/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol index 26d4aede..422a3863 100644 --- a/contracts/protocol/payload-delivery/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -112,8 +112,6 @@ contract AuctionManager is ); if (!_hasRole(TRANSMITTER_ROLE, transmitter)) revert InvalidTransmitter(); - // create a new bid - Bid memory newBid = Bid({fee: fee, transmitter: transmitter, extraData: extraData}); // get the request metadata RequestMetadata memory requestMetadata = IMiddleware(addressResolver__.deliveryHelper()) .getRequestMetadata(requestCount_); @@ -129,17 +127,12 @@ contract AuctionManager is fee >= winningBids[requestCount_].fee ) revert LowerBidAlreadyExists(); + // create a new bid + Bid memory newBid = Bid({fee: fee, transmitter: transmitter, extraData: extraData}); + // update the winning bid winningBids[requestCount_] = newBid; - // block the fees - IFeesManager(addressResolver__.feesManager()).blockFees( - requestMetadata.appGateway, - requestMetadata.fees, - newBid, - requestCount_ - ); - // end the auction if the no auction end delay if (auctionEndDelaySeconds > 0) { _startAuction(requestCount_); @@ -151,6 +144,14 @@ contract AuctionManager is _endAuction(requestCount_); } + // block the fees + IFeesManager(addressResolver__.feesManager()).blockFees( + requestMetadata.appGateway, + requestMetadata.fees, + newBid, + requestCount_ + ); + emit BidPlaced(requestCount_, newBid); } diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 62b8089e..8cb9ff30 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -331,6 +331,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol uint256 amount_, address receiver_ ) public { + if (msg.sender != address(deliveryHelper__())) originAppGateway_ = msg.sender; address appGateway = _getCoreAppGateway(originAppGateway_); // Check if amount is available in fees plug @@ -366,6 +367,8 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol payload: payload_ }); requestCount = watcherPrecompile__().submitRequest(payloadSubmitParamsArray); + + // same transmitter can execute requests without auction watcherPrecompile__().startProcessingRequest(requestCount, transmitter_); } diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index b49ae310..419cc5c8 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -76,6 +76,7 @@ abstract contract RequestQueue is DeliveryUtils { onCompleteData: onCompleteData_, onlyReadRequests: onlyReadRequests }); + // process and submit the queue of payloads to watcher precompile requestCount = watcherPrecompile__().submitRequest(payloadSubmitParamsArray); requests[requestCount] = requestMetadata; diff --git a/contracts/protocol/utils/common/Errors.sol b/contracts/protocol/utils/common/Errors.sol index e5261dc4..dd13ac46 100644 --- a/contracts/protocol/utils/common/Errors.sol +++ b/contracts/protocol/utils/common/Errors.sol @@ -33,3 +33,11 @@ error LowerBidAlreadyExists(); error AsyncModifierNotUsed(); error InvalidIndex(); error RequestAlreadyExecuted(); +/// @notice Error thrown when no async promise is found +error NoAsyncPromiseFound(); +/// @notice Error thrown when promise caller mismatch +error PromiseCallerMismatch(); +/// @notice Error thrown when request count mismatch +error RequestCountMismatch(); +/// @notice Error thrown when delivery helper is not set +error DeliveryHelperNotSet(); diff --git a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol index 51fe49e4..0d3a2851 100644 --- a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol +++ b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol @@ -21,12 +21,12 @@ abstract contract RequestHandler is WatcherPrecompileCore { function submitRequest( PayloadSubmitParams[] calldata payloadSubmitParams ) public returns (uint40 requestCount) { + address appGateway = _checkAppGateways(payloadSubmitParams); + requestCount = nextRequestCount++; uint40 batchCount = nextBatchCount; uint40 currentBatch = batchCount; - address appGateway = _checkAppGateways(payloadSubmitParams); - uint256 readCount; uint256 writeCount; PayloadSubmitParams memory lastP; diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 8045f126..1ebd1d4d 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -175,6 +175,8 @@ contract WatcherPrecompile is RequestHandler { if (r.middleware != msg.sender) revert InvalidCaller(); r.isRequestCancelled = true; + + emit RequestCancelledFromGateway(requestCount); } /// @notice Resolves multiple promises with their return data @@ -200,6 +202,7 @@ contract WatcherPrecompile is RequestHandler { PayloadParams memory payloadParams = payloads[resolvedPromises_[i].payloadId]; address asyncPromise = payloadParams.asyncPromise; + // todo: non trusted call if (asyncPromise != address(0)) { // Resolve each promise with its corresponding return data bool success = IPromise(asyncPromise).markResolved( @@ -215,7 +218,6 @@ contract WatcherPrecompile is RequestHandler { } isPromiseExecuted[resolvedPromises_[i].payloadId] = true; - RequestParams storage requestParams_ = requestParams[ payloadParams.payloadHeader.getRequestCount() ]; @@ -270,16 +272,16 @@ contract WatcherPrecompile is RequestHandler { ]; currentRequestParams.isRequestCancelled = true; + IMiddleware(currentRequestParams.middleware).handleRequestReverts( + payloadParams.payloadHeader.getRequestCount() + ); + if (isRevertingOnchain_ && payloadParams.asyncPromise != address(0)) IPromise(payloadParams.asyncPromise).markOnchainRevert( payloadParams.payloadHeader.getRequestCount(), payloadId_ ); - IMiddleware(currentRequestParams.middleware).handleRequestReverts( - payloadParams.payloadHeader.getRequestCount() - ); - emit MarkedRevert(payloadId_, isRevertingOnchain_); } diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index f3ba6d6d..63e32618 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -109,8 +109,9 @@ abstract contract WatcherPrecompileCore is /// @param params_ The payload parameters for the query /// @dev This function sets up a query request and emits a QueryRequested event function _query(PayloadParams memory params_) internal { - bytes32 prevDigestsHash = _getPreviousDigestsHash(params_.payloadHeader.getBatchCount()); - payloads[params_.payloadId].prevDigestsHash = prevDigestsHash; + payloads[params_.payloadId].prevDigestsHash = _getPreviousDigestsHash( + params_.payloadHeader.getBatchCount() + ); emit QueryRequested(params_); } diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol index 80529685..d13ad022 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.21; import "../../../interfaces/IWatcherPrecompile.sol"; import {IAppGateway} from "../../../interfaces/IAppGateway.sol"; -import {IFeesManager} from "../../../interfaces/IFeesManager.sol"; import {IPromise} from "../../../interfaces/IPromise.sol"; import "../PayloadHeaderDecoder.sol"; From bdd3cce64a2e5b293e3b32b8dde064c77c4dcf72 Mon Sep 17 00:00:00 2001 From: Akash Date: Wed, 16 Apr 2025 17:52:00 +0530 Subject: [PATCH 050/108] feat: fees changes, WIP --- contracts/base/AppGatewayBase.sol | 8 +- contracts/interfaces/IFeesManager.sol | 13 +++- contracts/interfaces/IMiddleware.sol | 2 + .../interfaces/IWatcherPrecompileLimits.sol | 6 ++ .../protocol/payload-delivery/FeesManager.sol | 74 +++++++++++++++---- .../protocol/payload-delivery/FeesPlug.sol | 49 +++++++++--- .../app-gateway/DeliveryUtils.sol | 1 + .../app-gateway/RequestQueue.sol | 49 +++++++++--- contracts/protocol/utils/common/Structs.sol | 1 + .../WatcherPrecompileLimits.sol | 53 ++++++++++++- .../core/WatcherPrecompile.sol | 8 +- script/helpers/AppGatewayFeeBalance.s.sol | 2 +- 12 files changed, 222 insertions(+), 44 deletions(-) diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index 0216e44d..a040a9f2 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -26,9 +26,8 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin mapping(bytes32 => bytes) public creationCodeWithArgs; /// @notice Modifier to treat functions async - modifier async() { + modifier async(address consumeFrom_) { if (fees.feePoolChain == 0) revert FeesNotSet(); - isAsyncModifierSet = true; _clearOverrides(); deliveryHelper__().clearQueue(); @@ -37,7 +36,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin _; isAsyncModifierSet = false; - deliveryHelper__().batch(fees, auctionManager, onCompleteData); + deliveryHelper__().batch(fees, auctionManager, consumeFrom_, onCompleteData); _markValidPromises(); onCompleteData = bytes(""); } @@ -181,7 +180,8 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin return address(0); } - onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]).getOnChainAddress(); + onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]) + .getOnChainAddress(); } //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/contracts/interfaces/IFeesManager.sol b/contracts/interfaces/IFeesManager.sol index 82f5fd8f..80953ba7 100644 --- a/contracts/interfaces/IFeesManager.sol +++ b/contracts/interfaces/IFeesManager.sol @@ -13,7 +13,11 @@ interface IFeesManager { function unblockFees(uint40 requestCount_) external; - function isFeesEnough(address appGateway_, Fees memory fees_) external view returns (bool); + function isFeesEnough( + address appGateway_, + address consumeFrom_, + Fees memory fees_ + ) external view returns (bool); function unblockAndAssignFees( uint40 requestCount_, @@ -28,4 +32,11 @@ interface IFeesManager { uint256 amount_, address receiver_ ) external; + + function assignWatcherPrecompileFees( + uint32 chainSlug_, + address token_, + uint256 amount_, + address consumeFrom_ + ) external; } diff --git a/contracts/interfaces/IMiddleware.sol b/contracts/interfaces/IMiddleware.sol index 54b50a53..a99ce076 100644 --- a/contracts/interfaces/IMiddleware.sol +++ b/contracts/interfaces/IMiddleware.sol @@ -25,11 +25,13 @@ interface IMiddleware { /// @notice Batches a request /// @param fees_ The fees for the request /// @param auctionManager_ The address of the auction manager + /// @param consumeFrom_ The address of the consumeFrom /// @param onCompleteData_ The data to be passed to the onComplete callback /// @return requestCount The request id function batch( Fees memory fees_, address auctionManager_, + address consumeFrom_, bytes memory onCompleteData_ ) external returns (uint40 requestCount); diff --git a/contracts/interfaces/IWatcherPrecompileLimits.sol b/contracts/interfaces/IWatcherPrecompileLimits.sol index c56ed635..f7db2adf 100644 --- a/contracts/interfaces/IWatcherPrecompileLimits.sol +++ b/contracts/interfaces/IWatcherPrecompileLimits.sol @@ -48,6 +48,12 @@ interface IWatcherPrecompileLimits { /// @param consumeLimit_ The amount of limit to consume function consumeLimit(address appGateway_, bytes32 limitType_, uint256 consumeLimit_) external; + function getTotalFeesRequired( + address token_, + uint queryCount, + uint finalizeCount, + uint scheduleCount + ) external view returns (uint256); /// @notice Emitted when limit parameters are updated event LimitParamsUpdated(UpdateLimitParams[] updates); diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 8cb9ff30..300f093e 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -27,9 +27,11 @@ abstract contract FeesManagerStorage is IFeesManager { // slot 53 /// @notice Master mapping tracking all fee information - /// @dev appGateway => chainSlug => token => TokenBalance - mapping(address => mapping(uint32 => mapping(address => TokenBalance))) - public appGatewayFeeBalances; + /// @dev userAddress => chainSlug => token => TokenBalance + mapping(address => mapping(uint32 => mapping(address => TokenBalance))) public userFeeBalances; + + // userAddress => appGateway => isWhitelisted + mapping(address => mapping(address => bool)) public isAppGatewayWhitelisted; // slot 54 /// @notice Mapping to track blocked fees for each async id @@ -41,6 +43,9 @@ abstract contract FeesManagerStorage is IFeesManager { /// @dev transmitter => chainSlug => token => amount mapping(address => mapping(uint32 => mapping(address => uint256))) public transmitterFees; + // @dev chainSlug => token => amount + mapping(uint32 => mapping(address => uint256)) public watcherPrecompileFees; + // slot 56 /// @notice Mapping to track nonce to whether it has been used /// @dev signatureNonce => isNonceUsed @@ -76,7 +81,12 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol address indexed transmitter, uint256 amount ); - + event WatcherPrecompileFeesAssigned( + uint32 chainSlug, + address token, + uint256 amount, + address consumeFrom + ); /// @notice Emitted when fees deposited are updated /// @param chainSlug The chain identifier /// @param appGateway The app gateway address @@ -104,6 +114,12 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @param appGateway The app gateway address event FeesUnblocked(uint40 indexed requestCount, address indexed appGateway); + /// @notice Emitted when insufficient watcher precompile fees are available + event InsufficientWatcherPrecompileFeesAvailable( + uint32 chainSlug, + address token, + address consumeFrom + ); /// @notice Error thrown when insufficient fees are available error InsufficientFeesAvailable(); /// @notice Error thrown when no fees are available for a transmitter @@ -140,10 +156,10 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @return The available fee amount function getAvailableFees( uint32 chainSlug_, - address appGateway_, + address consumeFrom_, address token_ ) public view returns (uint256) { - TokenBalance memory tokenBalance = appGatewayFeeBalances[appGateway_][chainSlug_][token_]; + TokenBalance memory tokenBalance = userFeeBalances[consumeFrom_][chainSlug_][token_]; if (tokenBalance.deposited == 0 || tokenBalance.deposited <= tokenBalance.blocked) return 0; return tokenBalance.deposited - tokenBalance.blocked; } @@ -169,24 +185,35 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol address appGateway = _getCoreAppGateway(originAppGateway_); - TokenBalance storage tokenBalance = appGatewayFeeBalances[appGateway][chainSlug_][token_]; + TokenBalance storage tokenBalance = userFeeBalances[appGateway][chainSlug_][token_]; tokenBalance.deposited += amount_; emit FeesDepositedUpdated(chainSlug_, appGateway, token_, amount_); } function isFeesEnough( address originAppGateway_, + address consumeFrom_, Fees memory fees_ ) external view returns (bool) { address appGateway = _getCoreAppGateway(originAppGateway_); + address consumeFromCore = _getCoreAppGateway(consumeFrom_); + if (appGateway != consumeFromCore && !isAppGatewayWhitelisted[consumeFromCore][appGateway]) + return false; uint256 availableFees = getAvailableFees( fees_.feePoolChain, - appGateway, + consumeFrom_, fees_.feePoolToken ); return availableFees >= fees_.amount; } + /// @notice Whitelists multiple app gateways for the caller + /// @param appGateways_ Array of app gateway addresses to whitelist + function whitelistApps(address[] calldata appGateways_) external { + for (uint256 i = 0; i < appGateways_.length; i++) { + isAppGatewayWhitelisted[msg.sender][appGateways_[i]] = true; + } + } /// @notice Blocks fees for a request count /// @param originAppGateway_ The app gateway address /// @param feesGivenByApp_ The fees data struct given by the app gateway @@ -213,7 +240,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol availableFees += requestCountBlockedFees[requestCount_].amount; if (availableFees < winningBid_.fee) revert InsufficientFeesAvailable(); - TokenBalance storage tokenBalance = appGatewayFeeBalances[appGateway][ + TokenBalance storage tokenBalance = userFeeBalances[appGateway][ feesGivenByApp_.feePoolChain ][feesGivenByApp_.feePoolToken]; @@ -248,7 +275,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol if (fees.amount == 0) return; address appGateway = _getCoreAppGateway(originAppGateway_); - TokenBalance storage tokenBalance = appGatewayFeeBalances[appGateway][fees.feePoolChain][ + TokenBalance storage tokenBalance = userFeeBalances[appGateway][fees.feePoolChain][ fees.feePoolToken ]; @@ -264,6 +291,21 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol emit FeesUnblockedAndAssigned(requestCount_, transmitter_, fees.amount); } + function assignWatcherPrecompileFees( + uint32 chainSlug_, + address token_, + uint256 amount_, + address consumeFrom_ + ) external onlyWatcherPrecompile { + address appGateway = _getCoreAppGateway(consumeFrom_); + TokenBalance storage tokenBalance = userFeeBalances[appGateway][chainSlug_][token_]; + if (tokenBalance.deposited < amount_) + revert InsufficientWatcherPrecompileFeesAvailable(chainSlug_, token_, consumeFrom_); + tokenBalance.deposited -= amount_; + watcherPrecompileFees[chainSlug_][token_] += amount_; + emit WatcherPrecompileFeesAssigned(chainSlug_, token_, amount_, consumeFrom_); + } + function unblockFees(uint40 requestCount_) external { RequestMetadata memory requestMetadata = deliveryHelper__().getRequestMetadata( requestCount_ @@ -277,7 +319,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol Fees memory fees = requestCountBlockedFees[requestCount_]; if (fees.amount == 0) return; - TokenBalance storage tokenBalance = appGatewayFeeBalances[requestMetadata.appGateway][ + TokenBalance storage tokenBalance = userFeeBalances[requestMetadata.appGateway][ fees.feePoolChain ][fees.feePoolToken]; @@ -325,20 +367,20 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @param amount_ The amount of tokens to withdraw /// @param receiver_ The address of the receiver function withdrawFees( - address originAppGateway_, + address originAppGatewayOrUser_, uint32 chainSlug_, address token_, uint256 amount_, address receiver_ ) public { - if (msg.sender != address(deliveryHelper__())) originAppGateway_ = msg.sender; - address appGateway = _getCoreAppGateway(originAppGateway_); + if (msg.sender != address(deliveryHelper__())) originAppGatewayOrUser_ = msg.sender; + address source = _getCoreAppGateway(originAppGatewayOrUser_); // Check if amount is available in fees plug - uint256 availableAmount = getAvailableFees(chainSlug_, appGateway, token_); + uint256 availableAmount = getAvailableFees(chainSlug_, source, token_); if (availableAmount < amount_) revert InsufficientFeesAvailable(); - TokenBalance storage tokenBalance = appGatewayFeeBalances[appGateway][chainSlug_][token_]; + TokenBalance storage tokenBalance = userFeeBalances[source][chainSlug_][token_]; tokenBalance.deposited -= amount_; // Add it to the queue and submit request diff --git a/contracts/protocol/payload-delivery/FeesPlug.sol b/contracts/protocol/payload-delivery/FeesPlug.sol index fbd9a13e..773ceb7d 100644 --- a/contracts/protocol/payload-delivery/FeesPlug.sol +++ b/contracts/protocol/payload-delivery/FeesPlug.sol @@ -30,7 +30,7 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { error TokenNotWhitelisted(address token_); /// @notice Event emitted when fees are deposited - event FeesDeposited(address appGateway, address token, uint256 amount); + event FeesDeposited(address receiver, address token, uint256 feeAmount, uint256 nativeAmount); /// @notice Event emitted when fees are withdrawn event FeesWithdrawn(address token, uint256 amount, address receiver); /// @notice Event emitted when a token is whitelisted @@ -88,30 +88,59 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { emit FeesWithdrawn(token_, amount_, receiver_); } + function depositToFee( + address token_, + uint256 amount_, + address receiver_ + ) external payable override { + _deposit(token_, receiver_, amount_, 0); + } + + function depositToFeeAndNative( + address token_, + uint256 amount_, + address receiver_ + ) external payable override { + uint256 nativeAmount_ = amount_ / 10; + uint256 feeAmount_ = amount_ - nativeAmount_; + _deposit(token_, receiver_, feeAmount_, nativeAmount_); + } + + function depositToNative( + address token_, + uint256 amount_, + address receiver_ + ) external payable override { + _deposit(token_, receiver_, 0, amount_); + } + /// @notice Deposits funds /// @param token_ The token address - /// @param amount_ The amount + /// @param feeAmount_ The amount of fees + /// @param nativeAmount_ The amount of native tokens /// @param appGateway_ The app gateway address - function deposit( + function _deposit( address token_, - address appGateway_, - uint256 amount_ - ) external payable override { + address receiver_, + uint256 feeAmount_, + uint256 nativeAmount_ + ) internal override { + uint256 totalAmount_ = feeAmount_ + nativeAmount_; if (!whitelistedTokens[token_]) revert TokenNotWhitelisted(token_); if (token_ == ETH_ADDRESS) { - if (msg.value != amount_) revert InvalidDepositAmount(); + if (msg.value != totalAmount_) revert InvalidDepositAmount(); } else { if (token_.code.length == 0) revert InvalidTokenAddress(); } - balanceOf[token_] += amount_; + balanceOf[token_] += totalAmount_; if (token_ != ETH_ADDRESS) { - SafeTransferLib.safeTransferFrom(token_, msg.sender, address(this), amount_); + SafeTransferLib.safeTransferFrom(token_, msg.sender, address(this), totalAmount_); } - emit FeesDeposited(appGateway_, token_, amount_); + emit FeesDeposited(receiver_, token_, feeAmount_, nativeAmount_); } /// @notice Transfers tokens diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol index 0a63674b..f4d20e75 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol @@ -39,6 +39,7 @@ abstract contract DeliveryUtils is PayloadSubmitParams[] payloadSubmitParams, Fees fees, address auctionManager, + address consumeFrom, bool onlyReadRequests ); /// @notice Emitted when fees are increased diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index 419cc5c8..8843f053 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -26,23 +26,40 @@ abstract contract RequestQueue is DeliveryUtils { function batch( Fees memory fees_, address auctionManager_, + address consumeFrom_, bytes memory onCompleteData_ ) external returns (uint40 requestCount) { address appGateway = _getCoreAppGateway(msg.sender); - return _batch(appGateway, auctionManager_, fees_, onCompleteData_); + return _batch(appGateway, auctionManager_, consumeFrom_, fees_, onCompleteData_); } function _checkBatch( address appGateway_, - address auctionManager_, - Fees memory fees_ - ) internal view returns (address) { + address consumeFrom_, + Fees memory fees_, + uint readCount, + uint writeCount + ) internal view { if (queuePayloadParams.length > REQUEST_PAYLOAD_COUNT_LIMIT) revert RequestPayloadCountLimitExceeded(); - if (!IFeesManager(addressResolver__.feesManager()).isFeesEnough(appGateway_, fees_)) - revert InsufficientFees(); + uint256 totalWatcherPrecompileFeesRequired = watcherPrecompileLimits__.getTotalFeesRequired( + fees_.feePoolToken, + readCount, + writeCount, + 0 + ); + fees_.amount += totalWatcherPrecompileFeesRequired; + if ( + !IFeesManager(addressResolver__.feesManager()).isFeesEnough( + appGateway_, + consumeFrom_, + fees_ + ) + ) revert InsufficientFees(); + } + function _getAuctionManager(address auctionManager_) internal view returns (address) { return auctionManager_ == address(0) ? IAddressResolver(addressResolver__).defaultAuctionManager() @@ -56,21 +73,26 @@ abstract contract RequestQueue is DeliveryUtils { function _batch( address appGateway_, address auctionManager_, + address consumeFrom_, Fees memory fees_, bytes memory onCompleteData_ ) internal returns (uint40 requestCount) { if (queuePayloadParams.length == 0) return 0; - auctionManager_ = _checkBatch(appGateway_, auctionManager_, fees_); + address auctionManager = _getAuctionManager(auctionManager_); // create the payload submit params array in desired format ( PayloadSubmitParams[] memory payloadSubmitParamsArray, + uint readCount, + uint writeCount, bool onlyReadRequests ) = _createPayloadSubmitParamsArray(); + _checkBatch(appGateway_, consumeFrom_, fees_, readCount, writeCount); RequestMetadata memory requestMetadata = RequestMetadata({ appGateway: appGateway_, - auctionManager: auctionManager_, + auctionManager: auctionManager, + consumeFrom: consumeFrom_, fees: fees_, winningBid: Bid({fee: 0, transmitter: address(0), extraData: new bytes(0)}), onCompleteData: onCompleteData_, @@ -92,6 +114,7 @@ abstract contract RequestQueue is DeliveryUtils { payloadSubmitParamsArray, fees_, auctionManager_, + consumeFrom_, onlyReadRequests ); } @@ -100,7 +123,12 @@ abstract contract RequestQueue is DeliveryUtils { /// @return payloadDetailsArray An array of payload details function _createPayloadSubmitParamsArray() internal - returns (PayloadSubmitParams[] memory payloadDetailsArray, bool onlyReadRequests) + returns ( + PayloadSubmitParams[] memory payloadDetailsArray, + uint readCount, + uint writeCount, + bool onlyReadRequests + ) { payloadDetailsArray = new PayloadSubmitParams[](queuePayloadParams.length); onlyReadRequests = queuePayloadParams[0].callType == CallType.READ; @@ -109,7 +137,8 @@ abstract contract RequestQueue is DeliveryUtils { for (uint256 i = 0; i < queuePayloadParams.length; i++) { if (queuePayloadParams[i].callType != CallType.READ) { onlyReadRequests = false; - } + writeCount++; + } else readCount++; // Update level for calls if (i > 0 && queuePayloadParams[i].isParallel != Parallel.ON) { diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 60a77fb2..4e93b06f 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -215,6 +215,7 @@ struct RequestParams { struct RequestMetadata { address appGateway; address auctionManager; + address consumeFrom; Fees fees; Bid winningBid; bytes onCompleteData; diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index 8303a440..c39cfd61 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -43,9 +43,14 @@ contract WatcherPrecompileLimits is // Mapping to track active app gateways mapping(address => bool) internal _activeAppGateways; + // token => fee amount + mapping(address => uint256) public queryFees; + mapping(address => uint256) public finalizeFees; + mapping(address => uint256) public scheduleFees; + /// @notice Emitted when the default limit and rate per second are set event DefaultLimitAndRatePerSecondSet(uint256 defaultLimit, uint256 defaultRatePerSecond); - + event WatcherFeesNotSetForToken(address token_); /// @notice Initial initialization (version 1) function initialize( address owner_, @@ -156,4 +161,50 @@ contract WatcherPrecompileLimits is emit DefaultLimitAndRatePerSecondSet(defaultLimit, defaultRatePerSecond); } + + function setQueryFees( + address[] calldata tokens_, + uint256[] calldata amounts_ + ) external onlyOwner { + require(tokens_.length == amounts_.length, "Length mismatch"); + for (uint256 i = 0; i < tokens_.length; i++) { + queryFees[tokens_[i]] = amounts_[i]; + } + } + + function setFinalizeFees( + address[] calldata tokens_, + uint256[] calldata amounts_ + ) external onlyOwner { + require(tokens_.length == amounts_.length, "Length mismatch"); + for (uint256 i = 0; i < tokens_.length; i++) { + finalizeFees[tokens_[i]] = amounts_[i]; + } + } + + function setScheduleFees( + address[] calldata tokens_, + uint256[] calldata amounts_ + ) external onlyOwner { + require(tokens_.length == amounts_.length, "Length mismatch"); + for (uint256 i = 0; i < tokens_.length; i++) { + scheduleFees[tokens_[i]] = amounts_[i]; + } + } + + function getTotalFeesRequired( + address token_, + uint queryCount, + uint finalizeCount, + uint scheduleCount + ) external view returns (uint256) { + uint256 totalFees = 0; + if (queryFees[token_] == 0 || finalizeFees[token_] == 0 || scheduleFees[token_] == 0) { + revert WatcherFeesNotSetForToken(token_); + } + totalFees += queryCount * queryFees[token_]; + totalFees += finalizeCount * finalizeFees[token_]; + totalFees += scheduleCount * scheduleFees[token_]; + return totalFees; + } } diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 1ebd1d4d..3a91c754 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -109,9 +109,15 @@ contract WatcherPrecompile is RequestHandler { /// @return The digest hash of the finalized payload /// @dev This function finalizes a payload request and requests the watcher to release the proofs function finalize( - PayloadParams memory params_, + PayloadParams memory params_,` address transmitter_ ) external returns (bytes32) { + IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFees( + evmxSlug, + params_.payloadHeader.getToken(), + watcherPrecompileLimits__.finalizeFees(params_.payloadHeader.getToken()), + msg.sender + ); return _finalize(params_, transmitter_); } diff --git a/script/helpers/AppGatewayFeeBalance.s.sol b/script/helpers/AppGatewayFeeBalance.s.sol index 395c542e..9bf7c2c9 100644 --- a/script/helpers/AppGatewayFeeBalance.s.sol +++ b/script/helpers/AppGatewayFeeBalance.s.sol @@ -14,7 +14,7 @@ contract CheckDepositedFees is Script { address appGateway = vm.envAddress("APP_GATEWAY"); uint32 chain = 421614; address token = ETH_ADDRESS; - (uint256 deposited, uint256 blocked) = feesManager.appGatewayFeeBalances( + (uint256 deposited, uint256 blocked) = feesManager.userFeeBalances( appGateway, chain, token From 5f293e02581c5591f376816a3527ad9c7baf4349 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 17 Apr 2025 17:11:04 +0530 Subject: [PATCH 051/108] fix: changes till auction --- contracts/base/AppGatewayBase.sol | 27 +++++++----- contracts/interfaces/IMiddleware.sol | 6 +-- .../payload-delivery/AuctionManager.sol | 28 ++++++++---- .../protocol/payload-delivery/FeesManager.sol | 12 +++-- .../app-gateway/DeliveryUtils.sol | 3 +- .../app-gateway/RequestQueue.sol | 44 +++++++------------ contracts/protocol/socket/Socket.sol | 1 + contracts/protocol/socket/SocketUtils.sol | 1 + contracts/protocol/utils/common/Structs.sol | 2 +- .../WatcherPrecompileLimits.sol | 15 ++++--- .../super-token/SuperTokenAppGateway.sol | 2 +- 11 files changed, 78 insertions(+), 63 deletions(-) diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index a040a9f2..82422aae 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -26,21 +26,29 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin mapping(bytes32 => bytes) public creationCodeWithArgs; /// @notice Modifier to treat functions async - modifier async(address consumeFrom_) { - if (fees.feePoolChain == 0) revert FeesNotSet(); - isAsyncModifierSet = true; - _clearOverrides(); - deliveryHelper__().clearQueue(); - addressResolver__.clearPromises(); - + modifier async(bytes memory feesApprovalData_) { + _preAsync(); _; + _postAsync(); + } + function _postAsync() internal { isAsyncModifierSet = false; - deliveryHelper__().batch(fees, auctionManager, consumeFrom_, onCompleteData); + + // todo: cache the feesApprovalData for next async in same request + deliveryHelper__().batch(fees, auctionManager, feesApprovalData_, onCompleteData); _markValidPromises(); onCompleteData = bytes(""); } + function _preAsync() internal { + if (fees.feePoolChain == 0) revert FeesNotSet(); + isAsyncModifierSet = true; + _clearOverrides(); + deliveryHelper__().clearQueue(); + addressResolver__.clearPromises(); + } + /// @notice Modifier to ensure only valid promises can call the function /// @dev only valid promises can call the function modifier onlyPromises() { @@ -180,8 +188,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin return address(0); } - onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]) - .getOnChainAddress(); + onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]).getOnChainAddress(); } //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/contracts/interfaces/IMiddleware.sol b/contracts/interfaces/IMiddleware.sol index a99ce076..ce949529 100644 --- a/contracts/interfaces/IMiddleware.sol +++ b/contracts/interfaces/IMiddleware.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -import {PayloadSubmitParams, QueuePayloadParams, Bid, Fees, WriteFinality, CallType, Parallel, IsPlug, RequestMetadata} from "../protocol/utils/common/Structs.sol"; +import {PayloadSubmitParams, QueuePayloadParams, Bid, Fees, WriteFinality, CallType, Parallel, IsPlug, RequestMetadata, } from "../protocol/utils/common/Structs.sol"; /// @title IMiddleware /// @notice Interface for the Middleware contract @@ -25,13 +25,13 @@ interface IMiddleware { /// @notice Batches a request /// @param fees_ The fees for the request /// @param auctionManager_ The address of the auction manager - /// @param consumeFrom_ The address of the consumeFrom + /// @param feesApprovalData_ the data to be passed to the fees manager /// @param onCompleteData_ The data to be passed to the onComplete callback /// @return requestCount The request id function batch( Fees memory fees_, address auctionManager_, - address consumeFrom_, + bytes memory feesApprovalData_, bytes memory onCompleteData_ ) external returns (uint40 requestCount); diff --git a/contracts/protocol/payload-delivery/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol index 422a3863..d310343c 100644 --- a/contracts/protocol/payload-delivery/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -112,14 +112,9 @@ contract AuctionManager is ); if (!_hasRole(TRANSMITTER_ROLE, transmitter)) revert InvalidTransmitter(); - // get the request metadata - RequestMetadata memory requestMetadata = IMiddleware(addressResolver__.deliveryHelper()) - .getRequestMetadata(requestCount_); - - // check if the bid is for this auction manager - if (requestMetadata.auctionManager != address(this)) revert InvalidBid(); - // check if the bid exceeds the max fees quoted by app gateway - if (fee > requestMetadata.fees.amount) revert BidExceedsMaxFees(); + // check if the bid exceeds the max fees quoted by app gateway subtracting the watcher fees + if (fee > getTransmitterMaxFeesRequired(requestMetadata.fees.token, requestCount_)) + revert BidExceedsMaxFees(); // check if the bid is lower than the existing bid if ( @@ -149,6 +144,7 @@ contract AuctionManager is requestMetadata.appGateway, requestMetadata.fees, newBid, + watcherPrecompile__().getTotalFeesRequired(requestMetadata.fees.token, requestCount_), requestCount_ ); @@ -211,6 +207,22 @@ contract AuctionManager is emit AuctionStarted(requestCount_); } + function getTransmitterMaxFeesRequired( + address token_, + uint40 requestCount_ + ) public view returns (uint256) { + // check if the bid is for this auction manager + if (requestMetadata.auctionManager != address(this)) revert InvalidBid(); + + // get the request metadata + RequestMetadata memory requestMetadata = IMiddleware(addressResolver__.deliveryHelper()) + .getRequestMetadata(requestCount_); + + // get the total fees required for the watcher precompile ops + uint256 watcherFees = watcherPrecompile__().getTotalFeesRequired(token_, requestCount_); + return requestMetadata.fees.amount - watcherFees; + } + function _recoverSigner( bytes32 digest_, bytes memory signature_ diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 300f093e..f3eaf0f3 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -214,6 +214,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol isAppGatewayWhitelisted[msg.sender][appGateways_[i]] = true; } } + /// @notice Blocks fees for a request count /// @param originAppGateway_ The app gateway address /// @param feesGivenByApp_ The fees data struct given by the app gateway @@ -223,6 +224,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol address originAppGateway_, Fees memory feesGivenByApp_, Bid memory winningBid_, + uint256 watcherFees_, uint40 requestCount_ ) external { if (msg.sender != deliveryHelper__().getRequestMetadata(requestCount_).auctionManager) @@ -239,27 +241,29 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol if (requestCountBlockedFees[requestCount_].amount > 0) availableFees += requestCountBlockedFees[requestCount_].amount; - if (availableFees < winningBid_.fee) revert InsufficientFeesAvailable(); + uint256 feesNeeded = winningBid_.fee + watcherFees_; + if (availableFees < feesNeeded) revert InsufficientFeesAvailable(); + TokenBalance storage tokenBalance = userFeeBalances[appGateway][ feesGivenByApp_.feePoolChain ][feesGivenByApp_.feePoolToken]; tokenBalance.blocked = tokenBalance.blocked + - winningBid_.fee - + feesNeeded - requestCountBlockedFees[requestCount_].amount; requestCountBlockedFees[requestCount_] = Fees({ feePoolChain: feesGivenByApp_.feePoolChain, feePoolToken: feesGivenByApp_.feePoolToken, - amount: winningBid_.fee + amount: feesNeeded }); emit FeesBlocked( requestCount_, feesGivenByApp_.feePoolChain, feesGivenByApp_.feePoolToken, - winningBid_.fee + feesNeeded ); } diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol index f4d20e75..8fa29c14 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol @@ -33,13 +33,14 @@ abstract contract DeliveryUtils is error MaxMsgValueLimitExceeded(); event BidTimeoutUpdated(uint256 newBidTimeout); + + /// @notice Emitted when a payload is submitted event PayloadSubmitted( uint40 indexed requestCount, address indexed appGateway, PayloadSubmitParams[] payloadSubmitParams, Fees fees, address auctionManager, - address consumeFrom, bool onlyReadRequests ); /// @notice Emitted when fees are increased diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index 8843f053..a3d7e801 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -26,34 +26,25 @@ abstract contract RequestQueue is DeliveryUtils { function batch( Fees memory fees_, address auctionManager_, - address consumeFrom_, + bytes memory feesApprovalData_, bytes memory onCompleteData_ ) external returns (uint40 requestCount) { address appGateway = _getCoreAppGateway(msg.sender); - return _batch(appGateway, auctionManager_, consumeFrom_, fees_, onCompleteData_); + return _batch(appGateway, auctionManager_, feesApprovalData_, onCompleteData_); } function _checkBatch( address appGateway_, - address consumeFrom_, - Fees memory fees_, - uint readCount, - uint writeCount + bytes memory feesApprovalData_, + Fees memory fees_ ) internal view { if (queuePayloadParams.length > REQUEST_PAYLOAD_COUNT_LIMIT) revert RequestPayloadCountLimitExceeded(); - uint256 totalWatcherPrecompileFeesRequired = watcherPrecompileLimits__.getTotalFeesRequired( - fees_.feePoolToken, - readCount, - writeCount, - 0 - ); - fees_.amount += totalWatcherPrecompileFeesRequired; if ( !IFeesManager(addressResolver__.feesManager()).isFeesEnough( appGateway_, - consumeFrom_, + feesApprovalData_, fees_ ) ) revert InsufficientFees(); @@ -73,8 +64,8 @@ abstract contract RequestQueue is DeliveryUtils { function _batch( address appGateway_, address auctionManager_, - address consumeFrom_, Fees memory fees_, + bytes memory feesApprovalData_, bytes memory onCompleteData_ ) internal returns (uint40 requestCount) { if (queuePayloadParams.length == 0) return 0; @@ -83,16 +74,14 @@ abstract contract RequestQueue is DeliveryUtils { // create the payload submit params array in desired format ( PayloadSubmitParams[] memory payloadSubmitParamsArray, - uint readCount, - uint writeCount, bool onlyReadRequests ) = _createPayloadSubmitParamsArray(); - _checkBatch(appGateway_, consumeFrom_, fees_, readCount, writeCount); + _checkBatch(appGateway_, feesApprovalData_, fees_); RequestMetadata memory requestMetadata = RequestMetadata({ appGateway: appGateway_, auctionManager: auctionManager, - consumeFrom: consumeFrom_, + feesApprovalData: feesApprovalData_, fees: fees_, winningBid: Bid({fee: 0, transmitter: address(0), extraData: new bytes(0)}), onCompleteData: onCompleteData_, @@ -108,13 +97,16 @@ abstract contract RequestQueue is DeliveryUtils { if (onlyReadRequests) watcherPrecompile__().startProcessingRequest(requestCount, address(0)); + // to save extra calls from transmitter + uint256 maxTransmitterFees = fees_.amount - + watcherPrecompile__().getTotalFeesRequired(fees_.token, requestCount); + emit PayloadSubmitted( requestCount, appGateway_, payloadSubmitParamsArray, - fees_, + Fees({token: fees_.token, amount: fees_.amount - maxTransmitterFees}), auctionManager_, - consumeFrom_, onlyReadRequests ); } @@ -123,12 +115,7 @@ abstract contract RequestQueue is DeliveryUtils { /// @return payloadDetailsArray An array of payload details function _createPayloadSubmitParamsArray() internal - returns ( - PayloadSubmitParams[] memory payloadDetailsArray, - uint readCount, - uint writeCount, - bool onlyReadRequests - ) + returns (PayloadSubmitParams[] memory payloadDetailsArray, bool onlyReadRequests) { payloadDetailsArray = new PayloadSubmitParams[](queuePayloadParams.length); onlyReadRequests = queuePayloadParams[0].callType == CallType.READ; @@ -137,8 +124,7 @@ abstract contract RequestQueue is DeliveryUtils { for (uint256 i = 0; i < queuePayloadParams.length; i++) { if (queuePayloadParams[i].callType != CallType.READ) { onlyReadRequests = false; - writeCount++; - } else readCount++; + } // Update level for calls if (i > 0 && queuePayloadParams[i].isParallel != Parallel.ON) { diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index aa2e0fb6..4ac83636 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -76,6 +76,7 @@ contract Socket is SocketUtils { // validate the execution status _validateExecutionStatus(payloadId); + // should we move this to a separate contract? // recover the signer address transmitter = _recoverSigner( keccak256(abi.encode(address(this), payloadId)), diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index 6f886c5e..eef99e2e 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -67,6 +67,7 @@ abstract contract SocketUtils is SocketConfig { executeParams_.target, appGateway_, executeParams_.prevDigestsHash + // can keep bytes here? ) ); } diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 4e93b06f..32170560 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -215,11 +215,11 @@ struct RequestParams { struct RequestMetadata { address appGateway; address auctionManager; - address consumeFrom; Fees fees; Bid winningBid; bytes onCompleteData; bool onlyReadRequests; + bytes feesApprovalData; } struct ExecuteParams { diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index c39cfd61..1fdf374d 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -51,6 +51,7 @@ contract WatcherPrecompileLimits is /// @notice Emitted when the default limit and rate per second are set event DefaultLimitAndRatePerSecondSet(uint256 defaultLimit, uint256 defaultRatePerSecond); event WatcherFeesNotSetForToken(address token_); + /// @notice Initial initialization (version 1) function initialize( address owner_, @@ -124,6 +125,7 @@ contract WatcherPrecompileLimits is * @param consumeLimit_ The amount of limit to consume */ function consumeLimit( + uint40 requestCount_, address appGateway_, bytes32 limitType_, uint256 consumeLimit_ @@ -148,6 +150,8 @@ contract WatcherPrecompileLimits is } // Update the limit + precompileCount[limitType_][requestCount_] += consumeLimit_; + _consumeFullLimit(consumeLimit_ * 10 ** limitDecimals, limitParams); } @@ -194,17 +198,16 @@ contract WatcherPrecompileLimits is function getTotalFeesRequired( address token_, - uint queryCount, - uint finalizeCount, - uint scheduleCount + uint40 requestCount_ ) external view returns (uint256) { uint256 totalFees = 0; if (queryFees[token_] == 0 || finalizeFees[token_] == 0 || scheduleFees[token_] == 0) { revert WatcherFeesNotSetForToken(token_); } - totalFees += queryCount * queryFees[token_]; - totalFees += finalizeCount * finalizeFees[token_]; - totalFees += scheduleCount * scheduleFees[token_]; + + totalFees += precompileCount[QUERY][requestCount_] * queryFees[token_]; + totalFees += precompileCount[FINALIZE][requestCount_] * finalizeFees[token_]; + totalFees += precompileCount[SCHEDULE][requestCount_] * scheduleFees[token_]; return totalFees; } } diff --git a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol index 3110d497..98488499 100644 --- a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol +++ b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol @@ -60,7 +60,7 @@ contract SuperTokenAppGateway is AppGatewayBase, Ownable { return; } - function transfer(bytes memory order_) external async { + function transfer(bytes memory order_) external async createFeePool { TransferOrder memory order = abi.decode(order_, (TransferOrder)); ISuperToken(order.srcToken).burn(order.user, order.srcAmount); ISuperToken(order.dstToken).mint(order.user, order.srcAmount); From aef1ebb806400b36d990d471535db02160e1aa3d Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 17 Apr 2025 18:03:40 +0530 Subject: [PATCH 052/108] feat: watcher precompile fees and callback fees --- .../protocol/payload-delivery/FeesManager.sol | 28 ++++++++++++----- .../app-gateway/DeliveryHelper.sol | 3 +- .../app-gateway/RequestQueue.sol | 2 +- .../watcherPrecompile/core/RequestHandler.sol | 11 ++++--- .../core/WatcherPrecompile.sol | 31 +++++++++---------- .../core/WatcherPrecompileCore.sol | 25 +++++++++++++++ .../core/WatcherPrecompileStorage.sol | 8 +++-- 7 files changed, 75 insertions(+), 33 deletions(-) diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index f3eaf0f3..53ac31bb 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -51,6 +51,8 @@ abstract contract FeesManagerStorage is IFeesManager { /// @dev signatureNonce => isNonceUsed mapping(uint256 => bool) public isNonceUsed; + mapping(uint40 => address) public requestCountConsumeFrom; + // slots [57-106] reserved for gap uint256[50] _gap_after; @@ -299,15 +301,14 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol uint32 chainSlug_, address token_, uint256 amount_, - address consumeFrom_ + uint40 requestCount_ ) external onlyWatcherPrecompile { - address appGateway = _getCoreAppGateway(consumeFrom_); - TokenBalance storage tokenBalance = userFeeBalances[appGateway][chainSlug_][token_]; - if (tokenBalance.deposited < amount_) - revert InsufficientWatcherPrecompileFeesAvailable(chainSlug_, token_, consumeFrom_); - tokenBalance.deposited -= amount_; + Fees storage fees = requestCountBlockedFees[requestCount_]; + if (fees.amount == 0) revert NoFeesBlocked(); + + fees.amount -= amount_; watcherPrecompileFees[chainSlug_][token_] += amount_; - emit WatcherPrecompileFeesAssigned(chainSlug_, token_, amount_, consumeFrom_); + emit WatcherPrecompileFeesAssigned(chainSlug_, token_, amount_, requestCount_); } function unblockFees(uint40 requestCount_) external { @@ -412,7 +413,18 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol readAt: 0, payload: payload_ }); - requestCount = watcherPrecompile__().submitRequest(payloadSubmitParamsArray); + + RequestMetadata memory requestMetadata = RequestMetadata({ + appGateway: address(this), + auctionManager: address(0), + feesApprovalData: bytes(""), + fees: Fees({token: token_, amount: amount_}), + winningBid: Bid({transmitter: transmitter_, fee: 0, extraData: new bytes(0)}) + }); + requestCount = watcherPrecompile__().submitRequest( + payloadSubmitParamsArray, + requestMetadata + ); // same transmitter can execute requests without auction watcherPrecompile__().startProcessingRequest(requestCount, transmitter_); diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol index 6df859ac..6c7b0c80 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol @@ -41,7 +41,7 @@ contract DeliveryHelper is FeesHelpers { requestMetadata_.winningBid.transmitter = winningBid_.transmitter; if (!isRestarted) { - watcherPrecompile__().startProcessingRequest(requestCount_, winningBid_.transmitter); + watcherPrecompile__().startProcessingRequest(requestCount_, winningBid_); } else { watcherPrecompile__().updateTransmitter(requestCount_, winningBid_.transmitter); } @@ -52,6 +52,7 @@ contract DeliveryHelper is FeesHelpers { function finishRequest(uint40 requestCount_) external onlyWatcherPrecompile { RequestMetadata storage requestMetadata_ = requests[requestCount_]; + // todo: move it to watcher precompile if (requestMetadata_.winningBid.transmitter != address(0)) IFeesManager(addressResolver__.feesManager()).unblockAndAssignFees( requestCount_, diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index a3d7e801..be9cabf7 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -89,7 +89,7 @@ abstract contract RequestQueue is DeliveryUtils { }); // process and submit the queue of payloads to watcher precompile - requestCount = watcherPrecompile__().submitRequest(payloadSubmitParamsArray); + requestCount = watcherPrecompile__().submitRequest(payloadSubmitParamsArray, requestMetadata); requests[requestCount] = requestMetadata; // send query directly if request contains only reads diff --git a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol index 0d3a2851..3d00e32d 100644 --- a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol +++ b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol @@ -19,7 +19,8 @@ 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[] calldata payloadSubmitParams, + RequestMetadata calldata requestMetadata ) public returns (uint40 requestCount) { address appGateway = _checkAppGateways(payloadSubmitParams); @@ -96,6 +97,8 @@ abstract contract RequestHandler is WatcherPrecompileCore { requestParams[requestCount].payloadsRemaining = payloadSubmitParams.length; requestParams[requestCount].middleware = msg.sender; + requestMetadata[requestCount] = requestMetadata; + emit RequestSubmitted( msg.sender, requestCount, @@ -129,17 +132,17 @@ abstract contract RequestHandler is WatcherPrecompileCore { /// @notice Starts processing a request with a specified transmitter /// @param requestCount The request count to start processing - /// @param transmitter The address of the transmitter + /// @param winningBid The winning bid, contains fees, transmitter and extra data /// @dev This function initiates the processing of a request by a transmitter /// @dev It verifies that the caller is the middleware and that the request hasn't been started yet - function startProcessingRequest(uint40 requestCount, address transmitter) public { + function startProcessingRequest(uint40 requestCount, Bid memory winningBid) public { RequestParams storage r = requestParams[requestCount]; if (r.middleware != msg.sender) revert InvalidCaller(); if (r.transmitter != address(0)) revert AlreadyStarted(); if (r.currentBatchPayloadsLeft > 0) revert AlreadyStarted(); uint40 batchCount = r.payloadParamsArray[0].payloadHeader.getBatchCount(); - r.transmitter = transmitter; + r.transmitter = winningBid.transmitter; r.currentBatch = batchCount; _processBatch(requestCount, batchCount); diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 3a91c754..27dc496b 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -112,12 +112,6 @@ contract WatcherPrecompile is RequestHandler { PayloadParams memory params_,` address transmitter_ ) external returns (bytes32) { - IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFees( - evmxSlug, - params_.payloadHeader.getToken(), - watcherPrecompileLimits__.finalizeFees(params_.payloadHeader.getToken()), - msg.sender - ); return _finalize(params_, transmitter_); } @@ -208,15 +202,25 @@ contract WatcherPrecompile is RequestHandler { PayloadParams memory payloadParams = payloads[resolvedPromises_[i].payloadId]; address asyncPromise = payloadParams.asyncPromise; + uint40 requestCount = payloadParams.payloadHeader.getRequestCount(); + // todo: non trusted call if (asyncPromise != address(0)) { // Resolve each promise with its corresponding return data + + uint256 initialGas = gasleft(); bool success = IPromise(asyncPromise).markResolved( - payloadParams.payloadHeader.getRequestCount(), + requestCount, resolvedPromises_[i].payloadId, resolvedPromises_[i].returnData ); + uint256 gasUsed = initialGas - gasleft(); + _consumeFees( + requestCount, + gasUsed * tx.gasprice + ); + if (!success) { emit PromiseNotResolved(resolvedPromises_[i].payloadId, asyncPromise); continue; @@ -224,9 +228,7 @@ contract WatcherPrecompile is RequestHandler { } isPromiseExecuted[resolvedPromises_[i].payloadId] = true; - RequestParams storage requestParams_ = requestParams[ - payloadParams.payloadHeader.getRequestCount() - ]; + RequestParams storage requestParams_ = requestParams[requestCount]; requestParams_.currentBatchPayloadsLeft--; requestParams_.payloadsRemaining--; @@ -234,17 +236,12 @@ contract WatcherPrecompile is RequestHandler { if ( requestParams_.currentBatchPayloadsLeft == 0 && requestParams_.payloadsRemaining > 0 ) { - _processBatch( - payloadParams.payloadHeader.getRequestCount(), - ++requestParams_.currentBatch - ); + _processBatch(requestCount, ++requestParams_.currentBatch); } // if all payloads of a request are executed, finish the request if (requestParams_.payloadsRemaining == 0) { - IMiddleware(requestParams_.middleware).finishRequest( - payloadParams.payloadHeader.getRequestCount() - ); + IMiddleware(requestParams_.middleware).finishRequest(requestCount); } emit PromiseResolved(resolvedPromises_[i].payloadId, asyncPromise); } diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 63e32618..70b109ad 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -33,6 +33,11 @@ abstract contract WatcherPrecompileCore is ) internal returns (bytes32 timeoutId) { if (delayInSeconds_ > maxTimeoutDelayInSeconds) revert TimeoutDelayTooLarge(); + _consumeFees( + requestCount_, + watcherPrecompileLimits__.scheduleFees(requestMetadata_.fees.feePoolToken) + ); + uint256 executeAt = block.timestamp + delayInSeconds_; timeoutId = _encodeTimeoutId(); @@ -74,6 +79,11 @@ abstract contract WatcherPrecompileCore is requestParams[params_.payloadHeader.getRequestCount()].middleware ); + _consumeFees( + params_.payloadHeader.getRequestCount(), + watcherPrecompileLimits__.finalizeFees(params_.payloadHeader.getChainSlug()) + ); + uint256 deadline = block.timestamp + expiryTime; payloads[params_.payloadId].deadline = deadline; payloads[params_.payloadId].finalizedTransmitter = transmitter_; @@ -109,6 +119,11 @@ abstract contract WatcherPrecompileCore is /// @param params_ The payload parameters for the query /// @dev This function sets up a query request and emits a QueryRequested event function _query(PayloadParams memory params_) internal { + _consumeFees( + params_.payloadHeader.getRequestCount(), + watcherPrecompileLimits__.queryFees(params_.payloadHeader.getChainSlug()) + ); + payloads[params_.payloadId].prevDigestsHash = _getPreviousDigestsHash( params_.payloadHeader.getBatchCount() ); @@ -239,6 +254,16 @@ abstract contract WatcherPrecompileCore is if (signer != owner()) revert InvalidWatcherSignature(); } + function _consumeFees(uint40 requestCount_, uint256 fees_) internal { + RequestMetadata memory requestMetadata_ = requestMetadata[requestCount_]; + IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFees( + requestMetadata_.fees.feePoolChain, + requestMetadata_.fees.feePoolToken, + fees_, + requestCount_ + ); + } + /// @notice Gets the batch IDs for a request /// @param requestCount_ The request count to get the batch IDs for /// @return An array of batch IDs for the given request diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol index d13ad022..ff40d67a 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol @@ -96,8 +96,12 @@ abstract contract WatcherPrecompileStorage is IWatcherPrecompile { // slot 64 IWatcherPrecompileConfig public watcherPrecompileConfig__; - // slots [65-114]: gap for future storage variables - uint256[50] _gap_after; + // slot 65 + /// @notice Mapping to store the request metadata for each request count + mapping(uint40 => RequestMetadata) public requestMetadata; + + // slots [66-114]: gap for future storage variables + uint256[49] _gap_after; // slots 115-165 (51) reserved for access control // slots 166-216 (51) reserved for addr resolver util From b9ca484232a546472ca98bd7c1be121b40580bd9 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 18 Apr 2025 15:01:02 +0530 Subject: [PATCH 053/108] feat: callback fees in finalize --- .../watcherPrecompile/core/WatcherPrecompile.sol | 10 ++-------- .../watcherPrecompile/core/WatcherPrecompileCore.sol | 5 ++++- .../core/WatcherPrecompileStorage.sol | 7 +++++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 27dc496b..5f11328b 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -42,6 +42,7 @@ contract WatcherPrecompile is RequestHandler { expiryTime = expiryTime_; evmxSlug = evmxSlug_; + callBackGasLimit = 5000000; } // ================== Timeout functions ================== @@ -206,21 +207,14 @@ contract WatcherPrecompile is RequestHandler { // todo: non trusted call if (asyncPromise != address(0)) { + // todo: limit the gas used for promise resolution // Resolve each promise with its corresponding return data - - uint256 initialGas = gasleft(); bool success = IPromise(asyncPromise).markResolved( requestCount, resolvedPromises_[i].payloadId, resolvedPromises_[i].returnData ); - uint256 gasUsed = initialGas - gasleft(); - _consumeFees( - requestCount, - gasUsed * tx.gasprice - ); - if (!success) { emit PromiseNotResolved(resolvedPromises_[i].payloadId, asyncPromise); continue; diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 70b109ad..22f04acf 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -256,10 +256,13 @@ abstract contract WatcherPrecompileCore is function _consumeFees(uint40 requestCount_, uint256 fees_) internal { RequestMetadata memory requestMetadata_ = requestMetadata[requestCount_]; + + // for callbacks in all precompiles + uint256 feesToConsume = fees_ + tx.gasprice * callBackGasLimit; IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFees( requestMetadata_.fees.feePoolChain, requestMetadata_.fees.feePoolToken, - fees_, + feesToConsume, requestCount_ ); } diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol index ff40d67a..e28bf412 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol @@ -100,8 +100,11 @@ abstract contract WatcherPrecompileStorage is IWatcherPrecompile { /// @notice Mapping to store the request metadata for each request count mapping(uint40 => RequestMetadata) public requestMetadata; - // slots [66-114]: gap for future storage variables - uint256[49] _gap_after; + // slot 66 + uint256 public callBackGasLimit; + + // slots [67-114]: gap for future storage variables + uint256[48] _gap_after; // slots 115-165 (51) reserved for access control // slots 166-216 (51) reserved for addr resolver util From 2a7a8e8d8f90e2f25e0c243231fcfe853b2d2547 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 18 Apr 2025 15:48:02 +0530 Subject: [PATCH 054/108] fix: unblock and assign fees --- .../payload-delivery/AuctionManager.sol | 15 ++++++----- .../protocol/payload-delivery/FeesManager.sol | 26 ++++++++++++------- .../WatcherPrecompileLimits.sol | 17 ++++++++++++ .../core/WatcherPrecompile.sol | 1 - .../core/WatcherPrecompileCore.sol | 2 +- .../core/WatcherPrecompileStorage.sol | 3 --- 6 files changed, 43 insertions(+), 21 deletions(-) diff --git a/contracts/protocol/payload-delivery/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol index d310343c..43b8e0fe 100644 --- a/contracts/protocol/payload-delivery/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -112,9 +112,12 @@ contract AuctionManager is ); if (!_hasRole(TRANSMITTER_ROLE, transmitter)) revert InvalidTransmitter(); + (uint256 watcherFees, uint256 transmitterFees) = getTransmitterMaxFeesRequired( + requestMetadata.fees.token, + requestCount_ + ); // check if the bid exceeds the max fees quoted by app gateway subtracting the watcher fees - if (fee > getTransmitterMaxFeesRequired(requestMetadata.fees.token, requestCount_)) - revert BidExceedsMaxFees(); + if (fee > transmitterFees) revert BidExceedsMaxFees(); // check if the bid is lower than the existing bid if ( @@ -141,10 +144,10 @@ contract AuctionManager is // block the fees IFeesManager(addressResolver__.feesManager()).blockFees( - requestMetadata.appGateway, + requestMetadata.consumeFrom, requestMetadata.fees, newBid, - watcherPrecompile__().getTotalFeesRequired(requestMetadata.fees.token, requestCount_), + watcherFees, requestCount_ ); @@ -210,7 +213,7 @@ contract AuctionManager is function getTransmitterMaxFeesRequired( address token_, uint40 requestCount_ - ) public view returns (uint256) { + ) public view returns (uint256, uint256) { // check if the bid is for this auction manager if (requestMetadata.auctionManager != address(this)) revert InvalidBid(); @@ -220,7 +223,7 @@ contract AuctionManager is // get the total fees required for the watcher precompile ops uint256 watcherFees = watcherPrecompile__().getTotalFeesRequired(token_, requestCount_); - return requestMetadata.fees.amount - watcherFees; + return (watcherFees, requestMetadata.fees.amount - watcherFees); } function _recoverSigner( diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 53ac31bb..3443e54b 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -223,7 +223,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @param requestCount_ The batch identifier /// @dev Only callable by delivery helper function blockFees( - address originAppGateway_, + address consumeFrom_, Fees memory feesGivenByApp_, Bid memory winningBid_, uint256 watcherFees_, @@ -232,11 +232,10 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol if (msg.sender != deliveryHelper__().getRequestMetadata(requestCount_).auctionManager) revert NotAuctionManager(); - address appGateway = _getCoreAppGateway(originAppGateway_); // Block fees uint256 availableFees = getAvailableFees( feesGivenByApp_.feePoolChain, - appGateway, + consumeFrom_, feesGivenByApp_.feePoolToken ); @@ -246,7 +245,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol uint256 feesNeeded = winningBid_.fee + watcherFees_; if (availableFees < feesNeeded) revert InsufficientFeesAvailable(); - TokenBalance storage tokenBalance = userFeeBalances[appGateway][ + TokenBalance storage tokenBalance = userFeeBalances[consumeFrom_][ feesGivenByApp_.feePoolChain ][feesGivenByApp_.feePoolToken]; @@ -260,6 +259,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol feePoolToken: feesGivenByApp_.feePoolToken, amount: feesNeeded }); + requestCountConsumeFrom[requestCount_] = consumeFrom_; emit FeesBlocked( requestCount_, @@ -274,23 +274,29 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @param transmitter_ The address of the transmitter who executed the batch function unblockAndAssignFees( uint40 requestCount_, - address transmitter_, - address originAppGateway_ + address transmitter_ ) external override onlyDeliveryHelper { Fees memory fees = requestCountBlockedFees[requestCount_]; if (fees.amount == 0) return; - address appGateway = _getCoreAppGateway(originAppGateway_); - TokenBalance storage tokenBalance = userFeeBalances[appGateway][fees.feePoolChain][ + RequestMetadata memory requestMetadata = deliveryHelper__().getRequestMetadata( + requestCount_ + ); + + TokenBalance storage tokenBalance = userFeeBalances[consumeFrom][fees.feePoolChain][ fees.feePoolToken ]; + uint256 transmitterBid = requestMetadata.winningBid.fee; + uint256 remainingFees = fees.amount - transmitterBid; + // Unblock fees from deposit tokenBalance.blocked -= fees.amount; - tokenBalance.deposited -= fees.amount; + tokenBalance.deposited -= transmitterBid; + tokenBalance.deposited -= remainingFees; // Assign fees to transmitter - transmitterFees[transmitter_][fees.feePoolChain][fees.feePoolToken] += fees.amount; + transmitterFees[transmitter_][fees.feePoolChain][fees.feePoolToken] += transmitterBid; // Clean up storage delete requestCountBlockedFees[requestCount_]; diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index 1fdf374d..0ab07deb 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -47,6 +47,7 @@ contract WatcherPrecompileLimits is mapping(address => uint256) public queryFees; mapping(address => uint256) public finalizeFees; mapping(address => uint256) public scheduleFees; + mapping(address => uint256) public callBackFees; /// @notice Emitted when the default limit and rate per second are set event DefaultLimitAndRatePerSecondSet(uint256 defaultLimit, uint256 defaultRatePerSecond); @@ -196,6 +197,16 @@ contract WatcherPrecompileLimits is } } + function setCallBackFees( + address[] calldata tokens_, + uint256[] calldata amounts_ + ) external onlyOwner { + require(tokens_.length == amounts_.length, "Length mismatch"); + for (uint256 i = 0; i < tokens_.length; i++) { + callBackFees[tokens_[i]] = amounts_[i]; + } + } + function getTotalFeesRequired( address token_, uint40 requestCount_ @@ -205,9 +216,15 @@ contract WatcherPrecompileLimits is revert WatcherFeesNotSetForToken(token_); } + uint256 totalCallbacks = precompileCount[QUERY][requestCount_] + + precompileCount[FINALIZE][requestCount_] + + precompileCount[SCHEDULE][requestCount_]; + + totalFees += totalCallbacks * callBackFees[token_]; totalFees += precompileCount[QUERY][requestCount_] * queryFees[token_]; totalFees += precompileCount[FINALIZE][requestCount_] * finalizeFees[token_]; totalFees += precompileCount[SCHEDULE][requestCount_] * scheduleFees[token_]; + return totalFees; } } diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 5f11328b..9e1ac974 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -42,7 +42,6 @@ contract WatcherPrecompile is RequestHandler { expiryTime = expiryTime_; evmxSlug = evmxSlug_; - callBackGasLimit = 5000000; } // ================== Timeout functions ================== diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 22f04acf..d8cef4f0 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -258,7 +258,7 @@ abstract contract WatcherPrecompileCore is RequestMetadata memory requestMetadata_ = requestMetadata[requestCount_]; // for callbacks in all precompiles - uint256 feesToConsume = fees_ + tx.gasprice * callBackGasLimit; + uint256 feesToConsume = fees_ + watcherPrecompileLimits__().callBackFees(); IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFees( requestMetadata_.fees.feePoolChain, requestMetadata_.fees.feePoolToken, diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol index e28bf412..12dd3d53 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol @@ -100,9 +100,6 @@ abstract contract WatcherPrecompileStorage is IWatcherPrecompile { /// @notice Mapping to store the request metadata for each request count mapping(uint40 => RequestMetadata) public requestMetadata; - // slot 66 - uint256 public callBackGasLimit; - // slots [67-114]: gap for future storage variables uint256[48] _gap_after; From b2b49b0b43d65909b9e4d8d1141bec9845d1d1ec Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 18 Apr 2025 16:17:06 +0530 Subject: [PATCH 055/108] feat: deduct fees from gateway --- contracts/protocol/payload-delivery/FeesManager.sol | 4 ++-- .../protocol/watcherPrecompile/core/WatcherPrecompile.sol | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 3443e54b..9b8d96ea 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -27,8 +27,8 @@ abstract contract FeesManagerStorage is IFeesManager { // slot 53 /// @notice Master mapping tracking all fee information - /// @dev userAddress => chainSlug => token => TokenBalance - mapping(address => mapping(uint32 => mapping(address => TokenBalance))) public userFeeBalances; + /// @dev userAddress => chainSlug => TokenBalance + mapping(address => mapping(uint32 => TokenBalance)) public userFeeBalances; // userAddress => appGateway => isWhitelisted mapping(address => mapping(address => bool)) public isAppGatewayWhitelisted; diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 9e1ac974..78f6f102 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -312,6 +312,14 @@ contract WatcherPrecompile is RequestHandler { ) ) revert InvalidCallerTriggered(); + IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFees( + address(0), + uint32(0), + watcherPrecompileLimits__().callBackFees(params_[i].payload), + uint40(0), + appGateway + ); + appGatewayCaller = appGateway; appGatewayCalled[params_[i].triggerId] = true; From 9c39bd07e01ec744f7396dd2914b0e5d3bce47c2 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 18 Apr 2025 17:57:54 +0530 Subject: [PATCH 056/108] fix: remove token and chain context from evmx --- .../payload-delivery/AuctionManager.sol | 16 +- .../protocol/payload-delivery/FeesManager.sol | 223 ++++++++---------- .../protocol/payload-delivery/FeesPlug.sol | 38 +-- 3 files changed, 105 insertions(+), 172 deletions(-) diff --git a/contracts/protocol/payload-delivery/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol index 43b8e0fe..a27cbd3f 100644 --- a/contracts/protocol/payload-delivery/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -142,15 +142,6 @@ contract AuctionManager is _endAuction(requestCount_); } - // block the fees - IFeesManager(addressResolver__.feesManager()).blockFees( - requestMetadata.consumeFrom, - requestMetadata.fees, - newBid, - watcherFees, - requestCount_ - ); - emit BidPlaced(requestCount_, newBid); } @@ -168,6 +159,13 @@ contract AuctionManager is auctionClosed[requestCount_] = true; + // block the fees + IFeesManager(addressResolver__.feesManager()).blockFees( + requestMetadata.consumeFrom, + winningBid.fee, + requestCount_ + ); + // set the timeout for the bid expiration // useful in case a transmitter did bid but did not execute payloads watcherPrecompile__().setTimeout( diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 9b8d96ea..39362e92 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -13,6 +13,16 @@ import {NotAuctionManager, InvalidWatcherSignature, NonceUsed} from "../utils/co import {Bid, Fees, CallType, Parallel, WriteFinality, TokenBalance, QueuePayloadParams, IsPlug, PayloadSubmitParams, RequestParams, RequestMetadata} from "../utils/common/Structs.sol"; abstract contract FeesManagerStorage is IFeesManager { + struct RequestFee { + uint256 blockedCredits; + address consumeFrom; + } + + struct UserCredits { + uint256 totalCredits; + uint256 blockedCredits; + } + // slots [0-49] reserved for gap uint256[50] _gap_before; @@ -25,34 +35,35 @@ abstract contract FeesManagerStorage is IFeesManager { // slot 52 bytes32 public sbType; - // slot 53 - /// @notice Master mapping tracking all fee information - /// @dev userAddress => chainSlug => TokenBalance - mapping(address => mapping(uint32 => TokenBalance)) public userFeeBalances; + // user credits + mapping(address => UserCredits) public userCredits; + + // token pool balances + // token address => chainSlug => amount + mapping(address => mapping(uint32 => uint256)) public tokenPoolBalances; + // user approved app gateways // userAddress => appGateway => isWhitelisted mapping(address => mapping(address => bool)) public isAppGatewayWhitelisted; // slot 54 - /// @notice Mapping to track blocked fees for each async id - /// @dev requestCount => Fees - mapping(uint40 => Fees) public requestCountBlockedFees; + /// @notice Mapping to track request credits details for each request count + /// @dev requestCount => RequestFee + mapping(uint40 => RequestFee) public requestCountCredits; // slot 55 - /// @notice Mapping to track fees to be distributed to transmitters - /// @dev transmitter => chainSlug => token => amount - mapping(address => mapping(uint32 => mapping(address => uint256))) public transmitterFees; + /// @notice Mapping to track credits to be distributed to transmitters + /// @dev transmitter => amount + mapping(address => uint256) public transmitterCredits; - // @dev chainSlug => token => amount - mapping(uint32 => mapping(address => uint256)) public watcherPrecompileFees; + // @dev amount + uint256 public watcherPrecompileCredits; // slot 56 /// @notice Mapping to track nonce to whether it has been used /// @dev signatureNonce => isNonceUsed mapping(uint256 => bool) public isNonceUsed; - mapping(uint40 => address) public requestCountConsumeFrom; - // slots [57-106] reserved for gap uint256[50] _gap_after; @@ -152,61 +163,41 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol } /// @notice Returns available (unblocked) fees for a gateway - /// @param chainSlug_ The chain identifier - /// @param appGateway_ The app gateway address - /// @param token_ The token address + /// @param consumeFrom_ The app gateway address /// @return The available fee amount - function getAvailableFees( - uint32 chainSlug_, - address consumeFrom_, - address token_ - ) public view returns (uint256) { - TokenBalance memory tokenBalance = userFeeBalances[consumeFrom_][chainSlug_][token_]; - if (tokenBalance.deposited == 0 || tokenBalance.deposited <= tokenBalance.blocked) return 0; - return tokenBalance.deposited - tokenBalance.blocked; + function getAvailableFees(address consumeFrom_) public view returns (uint256) { + UserCredits memory userCredit = userCredits[consumeFrom_]; + if (userCredit.totalCredits == 0 || userCredit.totalCredits <= userCredit.blockedCredits) + return 0; + return userCredit.totalCredits - userCredit.blockedCredits; } /// @notice Adds the fees deposited for an app gateway on a chain - /// @param chainSlug_ The chain identifier - /// @param originAppGateway_ The app gateway address - /// @param token_ The token address + /// @param consumeFrom_ The app gateway address /// @param amount_ The amount deposited function incrementFeesDeposited( - uint32 chainSlug_, - address originAppGateway_, - address token_, + address consumeFrom_, uint256 amount_, uint256 signatureNonce_, bytes memory signature_ ) external { - _isWatcherSignatureValid( - abi.encode(chainSlug_, originAppGateway_, token_, amount_), - signatureNonce_, - signature_ - ); + _isWatcherSignatureValid(abi.encode(consumeFrom_, amount_), signatureNonce_, signature_); - address appGateway = _getCoreAppGateway(originAppGateway_); + UserCredits storage userCredit = userCredits[consumeFrom_]; + userCredit.totalCredits += amount_; + tokenPoolBalances[token_][chainSlug_] += amount_; - TokenBalance storage tokenBalance = userFeeBalances[appGateway][chainSlug_][token_]; - tokenBalance.deposited += amount_; - emit FeesDepositedUpdated(chainSlug_, appGateway, token_, amount_); + emit FeesDepositedUpdated(consumeFrom_, amount_); } function isFeesEnough( - address originAppGateway_, + address appGateway_, address consumeFrom_, - Fees memory fees_ + uint256 amount_ ) external view returns (bool) { - address appGateway = _getCoreAppGateway(originAppGateway_); - address consumeFromCore = _getCoreAppGateway(consumeFrom_); - if (appGateway != consumeFromCore && !isAppGatewayWhitelisted[consumeFromCore][appGateway]) - return false; - uint256 availableFees = getAvailableFees( - fees_.feePoolChain, - consumeFrom_, - fees_.feePoolToken - ); - return availableFees >= fees_.amount; + address appGateway = _getCoreAppGateway(consumeFrom_); + if (!isAppGatewayWhitelisted[consumeFrom_][appGateway]) return false; + return getAvailableFees(consumeFrom_) >= amount_; } /// @notice Whitelists multiple app gateways for the caller @@ -218,55 +209,30 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol } /// @notice Blocks fees for a request count - /// @param originAppGateway_ The app gateway address - /// @param feesGivenByApp_ The fees data struct given by the app gateway + /// @param consumeFrom_ The fees payer address + /// @param totalFees_ The total fees to block /// @param requestCount_ The batch identifier /// @dev Only callable by delivery helper function blockFees( address consumeFrom_, - Fees memory feesGivenByApp_, - Bid memory winningBid_, - uint256 watcherFees_, + uint256 transmitterFees_, uint40 requestCount_ ) external { if (msg.sender != deliveryHelper__().getRequestMetadata(requestCount_).auctionManager) revert NotAuctionManager(); // Block fees - uint256 availableFees = getAvailableFees( - feesGivenByApp_.feePoolChain, - consumeFrom_, - feesGivenByApp_.feePoolToken - ); + if (getAvailableFees(consumeFrom_) < transmitterFees_) revert InsufficientFeesAvailable(); - if (requestCountBlockedFees[requestCount_].amount > 0) - availableFees += requestCountBlockedFees[requestCount_].amount; + UserCredits storage userCredit = userCredits[consumeFrom_]; + userCredit.blockedCredits += transmitterFees_; - uint256 feesNeeded = winningBid_.fee + watcherFees_; - if (availableFees < feesNeeded) revert InsufficientFeesAvailable(); - - TokenBalance storage tokenBalance = userFeeBalances[consumeFrom_][ - feesGivenByApp_.feePoolChain - ][feesGivenByApp_.feePoolToken]; - - tokenBalance.blocked = - tokenBalance.blocked + - feesNeeded - - requestCountBlockedFees[requestCount_].amount; - - requestCountBlockedFees[requestCount_] = Fees({ - feePoolChain: feesGivenByApp_.feePoolChain, - feePoolToken: feesGivenByApp_.feePoolToken, - amount: feesNeeded + requestCountCredits[requestCount_] = RequestFee({ + blockedCredits: transmitterFees_, + consumeFrom: consumeFrom_ }); - requestCountConsumeFrom[requestCount_] = consumeFrom_; - emit FeesBlocked( - requestCount_, - feesGivenByApp_.feePoolChain, - feesGivenByApp_.feePoolToken, - feesNeeded - ); + emit FeesBlocked(requestCount_, transmitterFees_); } /// @notice Unblocks fees after successful execution and assigns them to the transmitter @@ -276,45 +242,45 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol uint40 requestCount_, address transmitter_ ) external override onlyDeliveryHelper { - Fees memory fees = requestCountBlockedFees[requestCount_]; - if (fees.amount == 0) return; - - RequestMetadata memory requestMetadata = deliveryHelper__().getRequestMetadata( - requestCount_ - ); - - TokenBalance storage tokenBalance = userFeeBalances[consumeFrom][fees.feePoolChain][ - fees.feePoolToken - ]; + RequestFee memory requestFee = requestCountCredits[requestCount_]; + if (requestFee.blockedCredits == 0) return; - uint256 transmitterBid = requestMetadata.winningBid.fee; - uint256 remainingFees = fees.amount - transmitterBid; + uint256 fees = deliveryHelper__().getRequestMetadata(requestCount_).winningBid.fee; // Unblock fees from deposit - tokenBalance.blocked -= fees.amount; - tokenBalance.deposited -= transmitterBid; - tokenBalance.deposited -= remainingFees; + _useBlockedUserCredits(requestFee.consumeFrom, fees, fees); // Assign fees to transmitter - transmitterFees[transmitter_][fees.feePoolChain][fees.feePoolToken] += transmitterBid; + transmitterFees[transmitter_] += fees; // Clean up storage - delete requestCountBlockedFees[requestCount_]; - emit FeesUnblockedAndAssigned(requestCount_, transmitter_, fees.amount); + delete requestCountCredits[requestCount_]; + emit FeesUnblockedAndAssigned(requestCount_, transmitter_, fees); + } + + function _useBlockedUserCredits( + address consumeFrom_, + uint256 toBlock_, + uint256 toConsume_ + ) internal { + UserCredits storage userCredit = userCredits[consumeFrom_]; + userCredit.blockedCredits -= toBlock_; + userCredit.totalCredits -= toConsume_; } function assignWatcherPrecompileFees( - uint32 chainSlug_, - address token_, uint256 amount_, uint40 requestCount_ ) external onlyWatcherPrecompile { - Fees storage fees = requestCountBlockedFees[requestCount_]; - if (fees.amount == 0) revert NoFeesBlocked(); + RequestFee memory requestFee = requestCountCredits[requestCount_]; + if (requestFee.blockedCredits == 0) revert NoFeesBlocked(); + + // deduct the fees from the user + _useBlockedUserCredits(requestFee.consumeFrom, 0, amount_); - fees.amount -= amount_; - watcherPrecompileFees[chainSlug_][token_] += amount_; - emit WatcherPrecompileFeesAssigned(chainSlug_, token_, amount_, requestCount_); + // add the fees to the watcher precompile + watcherPrecompileCredits += amount_; + emit WatcherPrecompileFeesAssigned(requestCount_, amount_); } function unblockFees(uint40 requestCount_) external { @@ -327,19 +293,15 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol msg.sender != address(deliveryHelper__()) ) revert InvalidCaller(); - Fees memory fees = requestCountBlockedFees[requestCount_]; - if (fees.amount == 0) return; - - TokenBalance storage tokenBalance = userFeeBalances[requestMetadata.appGateway][ - fees.feePoolChain - ][fees.feePoolToken]; + RequestFee memory requestFee = requestCountCredits[requestCount_]; + if (requestFee.blockedCredits == 0) return; // Unblock fees from deposit - tokenBalance.blocked -= fees.amount; - tokenBalance.deposited += fees.amount; + UserCredits storage userCredit = userCredits[requestFee.consumeFrom]; + userCredit.blockedCredits -= requestFee.blockedCredits; - delete requestCountBlockedFees[requestCount_]; - emit FeesUnblocked(requestCount_, requestMetadata.appGateway); + delete requestCountCredits[requestCount_]; + emit FeesUnblocked(requestCount_, requestFee.consumeFrom); } /// @notice Withdraws fees to a specified receiver @@ -349,21 +311,23 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol function withdrawTransmitterFees( uint32 chainSlug_, address token_, - address receiver_ + address receiver_, + uint256 amount_ ) external returns (uint40 requestCount) { address transmitter = msg.sender; // Get total fees for the transmitter in given chain and token - uint256 totalFees = transmitterFees[transmitter][chainSlug_][token_]; - if (totalFees == 0) revert NoFeesForTransmitter(); + uint256 totalFees = transmitterFees[transmitter]; + if (totalFees >= amount_) revert InsufficientFeesAvailable(); // Clean up storage - transmitterFees[transmitter][chainSlug_][token_] = 0; + transmitterFees[transmitter] -= amount_; + tokenPoolBalances[token_][chainSlug_] -= amount_; // Create fee distribution payload bytes32 feesId = _encodeFeesId(feesCounter++); bytes memory payload = abi.encodeCall( IFeesPlug.distributeFee, - (token_, totalFees, receiver_, feesId) + (token_, amount_, receiver_, feesId) ); // finalize for plug contract @@ -388,11 +352,10 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol address source = _getCoreAppGateway(originAppGatewayOrUser_); // Check if amount is available in fees plug - uint256 availableAmount = getAvailableFees(chainSlug_, source, token_); + uint256 availableAmount = getAvailableFees(source); if (availableAmount < amount_) revert InsufficientFeesAvailable(); - TokenBalance storage tokenBalance = userFeeBalances[source][chainSlug_][token_]; - tokenBalance.deposited -= amount_; + tokenPoolBalances[token_][chainSlug_] -= amount_; // Add it to the queue and submit request _queue(chainSlug_, abi.encodeCall(IFeesPlug.withdrawFees, (token_, amount_, receiver_))); diff --git a/contracts/protocol/payload-delivery/FeesPlug.sol b/contracts/protocol/payload-delivery/FeesPlug.sol index 773ceb7d..f21826ab 100644 --- a/contracts/protocol/payload-delivery/FeesPlug.sol +++ b/contracts/protocol/payload-delivery/FeesPlug.sol @@ -50,9 +50,6 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { constructor(address socket_, address owner_) { _setSocket(socket_); _initializeOwner(owner_); - - // ETH is whitelisted by default - whitelistedTokens[ETH_ADDRESS] = true; } /// @notice Distributes fees to the transmitter @@ -88,11 +85,7 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { emit FeesWithdrawn(token_, amount_, receiver_); } - function depositToFee( - address token_, - uint256 amount_, - address receiver_ - ) external payable override { + function depositToFee(address token_, uint256 amount_, address receiver_) external override { _deposit(token_, receiver_, amount_, 0); } @@ -100,17 +93,13 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { address token_, uint256 amount_, address receiver_ - ) external payable override { + ) external override { uint256 nativeAmount_ = amount_ / 10; uint256 feeAmount_ = amount_ - nativeAmount_; _deposit(token_, receiver_, feeAmount_, nativeAmount_); } - function depositToNative( - address token_, - uint256 amount_, - address receiver_ - ) external payable override { + function depositToNative(address token_, uint256 amount_, address receiver_) external override { _deposit(token_, receiver_, 0, amount_); } @@ -127,19 +116,9 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { ) internal override { uint256 totalAmount_ = feeAmount_ + nativeAmount_; if (!whitelistedTokens[token_]) revert TokenNotWhitelisted(token_); - - if (token_ == ETH_ADDRESS) { - if (msg.value != totalAmount_) revert InvalidDepositAmount(); - } else { - if (token_.code.length == 0) revert InvalidTokenAddress(); - } + if (token_.code.length == 0) revert InvalidTokenAddress(); balanceOf[token_] += totalAmount_; - - if (token_ != ETH_ADDRESS) { - SafeTransferLib.safeTransferFrom(token_, msg.sender, address(this), totalAmount_); - } - emit FeesDeposited(receiver_, token_, feeAmount_, nativeAmount_); } @@ -148,11 +127,7 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { /// @param amount_ The amount /// @param receiver_ The receiver address function _transferTokens(address token_, uint256 amount_, address receiver_) internal { - if (token_ == ETH_ADDRESS) { - SafeTransferLib.forceSafeTransferETH(receiver_, amount_); - } else { - SafeTransferLib.safeTransfer(token_, receiver_, amount_); - } + SafeTransferLib.safeTransfer(token_, receiver_, amount_); } function connectSocket( @@ -173,7 +148,6 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { /// @notice Removes a token from the whitelist /// @param token_ The token address to remove function removeTokenFromWhitelist(address token_) external onlyOwner { - if (token_ == ETH_ADDRESS) revert(); // Cannot remove ETH from whitelist whitelistedTokens[token_] = false; emit TokenRemovedFromWhitelist(token_); } @@ -192,6 +166,4 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { ) external onlyRole(RESCUE_ROLE) { RescueFundsLib._rescueFunds(token_, rescueTo_, amount_); } - - receive() external payable {} } From e38a3952cc34e1dd0615e5e4385ae288850d8a6b Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 18 Apr 2025 17:59:49 +0530 Subject: [PATCH 057/108] fix: remove old structs --- .../protocol/payload-delivery/FeesManager.sol | 11 +---------- contracts/protocol/utils/common/Structs.sol | 18 ++++++++---------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 39362e92..c47232fe 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -10,18 +10,9 @@ import {IFeesManager} from "../../interfaces/IFeesManager.sol"; import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; import {NotAuctionManager, InvalidWatcherSignature, NonceUsed} from "../utils/common/Errors.sol"; -import {Bid, Fees, CallType, Parallel, WriteFinality, TokenBalance, QueuePayloadParams, IsPlug, PayloadSubmitParams, RequestParams, RequestMetadata} from "../utils/common/Structs.sol"; +import {Bid, CallType, Parallel, WriteFinality, QueuePayloadParams, IsPlug, PayloadSubmitParams, RequestMetadata, RequestFee, UserCredits} from "../utils/common/Structs.sol"; abstract contract FeesManagerStorage is IFeesManager { - struct RequestFee { - uint256 blockedCredits; - address consumeFrom; - } - - struct UserCredits { - uint256 totalCredits; - uint256 blockedCredits; - } // slots [0-49] reserved for gap uint256[50] _gap_before; diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 32170560..c4f00ca1 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -119,10 +119,14 @@ struct OverrideParams { } // FM: -struct Fees { - uint32 feePoolChain; - address feePoolToken; - uint256 amount; +struct RequestFee { + uint256 blockedCredits; + address consumeFrom; +} + +struct UserCredits { + uint256 totalCredits; + uint256 blockedCredits; } // digest: @@ -237,9 +241,3 @@ struct ExecuteParams { bytes32 prevDigestsHash; // should be id? hash of hashes address switchboard; } - -/// @notice Struct containing fee amounts and status -struct TokenBalance { - uint256 deposited; // Amount deposited - uint256 blocked; // Amount blocked -} From 6a399f0e4726ccaf693a54dc53d1cb8aa3a3676a Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Sat, 19 Apr 2025 05:17:50 +0530 Subject: [PATCH 058/108] fix: wip build fix --- contracts/base/AppGatewayBase.sol | 12 +++++------ contracts/interfaces/IAppGateway.sol | 2 +- contracts/interfaces/IAuctionManager.sol | 2 +- contracts/interfaces/IFeesManager.sol | 6 +++--- contracts/interfaces/IMiddleware.sol | 8 +++---- .../protocol/payload-delivery/FeesManager.sol | 8 +++---- .../protocol/payload-delivery/FeesPlug.sol | 2 +- .../app-gateway/DeliveryUtils.sol | 3 ++- .../app-gateway/FeesHelpers.sol | 6 +++--- .../app-gateway/RequestQueue.sol | 19 ++++++++++------- contracts/protocol/utils/FeesPlugin.sol | 21 ------------------- contracts/protocol/utils/common/Structs.sol | 2 +- .../core/WatcherPrecompile.sol | 2 +- script/counter/DeployEVMxCounterApp.s.sol | 7 +------ script/counter/SetFees.s.sol | 7 +------ script/helpers/AppGatewayFeeBalance.s.sol | 1 - script/helpers/PayFeesInArbitrumETH.s.sol | 1 - .../counter/CounterAppGateway.sol | 4 ++-- .../super-token/SuperTokenAppGateway.sol | 2 +- 19 files changed, 43 insertions(+), 72 deletions(-) delete mode 100644 contracts/protocol/utils/FeesPlugin.sol diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index 82422aae..abd4996d 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -7,19 +7,19 @@ import "../interfaces/IForwarder.sol"; import "../interfaces/IMiddleware.sol"; import "../interfaces/IPromise.sol"; -import {FeesPlugin} from "../protocol/utils/FeesPlugin.sol"; import {InvalidPromise, FeesNotSet, AsyncModifierNotUsed} from "../protocol/utils/common/Errors.sol"; import {FAST} from "../protocol/utils/common/Constants.sol"; /// @title AppGatewayBase /// @notice Abstract contract for the app gateway /// @dev This contract contains helpers for contract deployment, overrides, hooks and request processing -abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin { +abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { OverrideParams public overrideParams; bool public isAsyncModifierSet; address public auctionManager; bytes32 public sbType; bytes public onCompleteData; + uint256 public fees; mapping(address => bool) public isValidPromise; mapping(bytes32 => mapping(uint32 => address)) public override forwarderAddresses; @@ -29,10 +29,10 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin modifier async(bytes memory feesApprovalData_) { _preAsync(); _; - _postAsync(); + _postAsync(feesApprovalData_); } - function _postAsync() internal { + function _postAsync(bytes memory feesApprovalData_) internal { isAsyncModifierSet = false; // todo: cache the feesApprovalData for next async in same request @@ -204,7 +204,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin Read isReadCall_, Parallel isParallelCall_, uint256 gasLimit_, - Fees memory fees_ + uint256 fees_ ) internal { overrideParams.isReadCall = isReadCall_; overrideParams.isParallelCall = isParallelCall_; @@ -285,7 +285,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway, FeesPlugin /// @notice Sets fees overrides /// @param fees_ The fees configuration - function _setOverrides(Fees memory fees_) internal { + function _setOverrides(uint256 fees_) internal { fees = fees_; } diff --git a/contracts/interfaces/IAppGateway.sol b/contracts/interfaces/IAppGateway.sol index 8997710b..c8e9fd73 100644 --- a/contracts/interfaces/IAppGateway.sol +++ b/contracts/interfaces/IAppGateway.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -import {Fees, Read, Parallel, QueuePayloadParams, OverrideParams, CallType, WriteFinality, PayloadParams} from "../protocol/utils/common/Structs.sol"; +import {Read, Parallel, QueuePayloadParams, OverrideParams, CallType, WriteFinality, PayloadParams} from "../protocol/utils/common/Structs.sol"; /// @title IAppGateway /// @notice Interface for the app gateway diff --git a/contracts/interfaces/IAuctionManager.sol b/contracts/interfaces/IAuctionManager.sol index 45344b7a..a67f0200 100644 --- a/contracts/interfaces/IAuctionManager.sol +++ b/contracts/interfaces/IAuctionManager.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -import {Bid, Fees, RequestMetadata, RequestParams} from "../protocol/utils/common/Structs.sol"; +import {Bid, RequestMetadata, RequestParams} from "../protocol/utils/common/Structs.sol"; interface IAuctionManager { /// @notice Bids for an auction diff --git a/contracts/interfaces/IFeesManager.sol b/contracts/interfaces/IFeesManager.sol index 80953ba7..a6c90520 100644 --- a/contracts/interfaces/IFeesManager.sol +++ b/contracts/interfaces/IFeesManager.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -import {Fees, Bid, QueuePayloadParams} from "../protocol/utils/common/Structs.sol"; +import {Bid, QueuePayloadParams} from "../protocol/utils/common/Structs.sol"; interface IFeesManager { function blockFees( address appGateway_, - Fees memory fees_, + uint256 fees_, Bid memory winningBid_, uint40 requestCount_ ) external; @@ -16,7 +16,7 @@ interface IFeesManager { function isFeesEnough( address appGateway_, address consumeFrom_, - Fees memory fees_ + uint256 fees_ ) external view returns (bool); function unblockAndAssignFees( diff --git a/contracts/interfaces/IMiddleware.sol b/contracts/interfaces/IMiddleware.sol index ce949529..46a2410d 100644 --- a/contracts/interfaces/IMiddleware.sol +++ b/contracts/interfaces/IMiddleware.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -import {PayloadSubmitParams, QueuePayloadParams, Bid, Fees, WriteFinality, CallType, Parallel, IsPlug, RequestMetadata, } from "../protocol/utils/common/Structs.sol"; +import {PayloadSubmitParams, QueuePayloadParams, Bid, WriteFinality, CallType, Parallel, IsPlug, RequestMetadata, RequestFee} from "../protocol/utils/common/Structs.sol"; /// @title IMiddleware /// @notice Interface for the Middleware contract @@ -29,7 +29,7 @@ interface IMiddleware { /// @param onCompleteData_ The data to be passed to the onComplete callback /// @return requestCount The request id function batch( - Fees memory fees_, + uint256 fees_, address auctionManager_, bytes memory feesApprovalData_, bytes memory onCompleteData_ @@ -48,7 +48,7 @@ interface IMiddleware { uint256 amount_, address receiver_, address auctionManager_, - Fees memory fees_ + uint256 fees_ ) external returns (uint40); /// @notice Cancels a request @@ -66,7 +66,7 @@ interface IMiddleware { function startRequestProcessing(uint40 requestCount_, Bid memory winningBid_) external; /// @notice Returns the fees for a request - function getFees(uint40 requestCount_) external view returns (Fees memory); + function getFees(uint40 requestCount_) external view returns (uint256); /// @notice Finishes a request by assigning fees and calling the onComplete callback /// @param requestCount_ The request id diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index c47232fe..0e1bc855 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -13,7 +13,6 @@ import {NotAuctionManager, InvalidWatcherSignature, NonceUsed} from "../utils/co import {Bid, CallType, Parallel, WriteFinality, QueuePayloadParams, IsPlug, PayloadSubmitParams, RequestMetadata, RequestFee, UserCredits} from "../utils/common/Structs.sol"; abstract contract FeesManagerStorage is IFeesManager { - // slots [0-49] reserved for gap uint256[50] _gap_before; @@ -201,7 +200,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @notice Blocks fees for a request count /// @param consumeFrom_ The fees payer address - /// @param totalFees_ The total fees to block + /// @param transmitterFees_ The total fees to block /// @param requestCount_ The batch identifier /// @dev Only callable by delivery helper function blockFees( @@ -327,7 +326,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @notice Withdraws funds to a specified receiver /// @dev This function is used to withdraw fees from the fees plug - /// @param originAppGateway_ The address of the app gateway + /// @param originAppGatewayOrUser_ The address of the app gateway /// @param chainSlug_ The chain identifier /// @param token_ The address of the token /// @param amount_ The amount of tokens to withdraw @@ -378,9 +377,10 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol appGateway: address(this), auctionManager: address(0), feesApprovalData: bytes(""), - fees: Fees({token: token_, amount: amount_}), + fees: amount_, winningBid: Bid({transmitter: transmitter_, fee: 0, extraData: new bytes(0)}) }); + requestCount = watcherPrecompile__().submitRequest( payloadSubmitParamsArray, requestMetadata diff --git a/contracts/protocol/payload-delivery/FeesPlug.sol b/contracts/protocol/payload-delivery/FeesPlug.sol index f21826ab..2f5bc16f 100644 --- a/contracts/protocol/payload-delivery/FeesPlug.sol +++ b/contracts/protocol/payload-delivery/FeesPlug.sol @@ -107,7 +107,7 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { /// @param token_ The token address /// @param feeAmount_ The amount of fees /// @param nativeAmount_ The amount of native tokens - /// @param appGateway_ The app gateway address + /// @param receiver_ The receiver address function _deposit( address token_, address receiver_, diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol index 8fa29c14..38fcecc9 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryUtils.sol @@ -39,10 +39,11 @@ abstract contract DeliveryUtils is uint40 indexed requestCount, address indexed appGateway, PayloadSubmitParams[] payloadSubmitParams, - Fees fees, + uint256 fees, address auctionManager, bool onlyReadRequests ); + /// @notice Emitted when fees are increased event FeesIncreased( address indexed appGateway, diff --git a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol index 1c59ac64..ccda6afe 100644 --- a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol +++ b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol @@ -37,7 +37,7 @@ abstract contract FeesHelpers is RequestQueue { uint256 amount_, address receiver_, address auctionManager_, - Fees memory fees_ + uint256 fees_ ) external returns (uint40) { IFeesManager(addressResolver__.feesManager()).withdrawFees( msg.sender, @@ -53,7 +53,7 @@ abstract contract FeesHelpers is RequestQueue { /// @notice Returns the fees for a request /// @param requestCount_ The ID of the request /// @return fees The fees data - function getFees(uint40 requestCount_) external view returns (Fees memory) { - return requests[requestCount_].fees; + function getFees(uint40 requestCount_) external view returns (uint256) { + return requests[requestCount_].fees.amount; } } diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index be9cabf7..12d9d458 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -24,19 +24,19 @@ abstract contract RequestQueue is DeliveryUtils { /// @param auctionManager_ The auction manager address /// @return requestCount The ID of the batch function batch( - Fees memory fees_, + uint256 fees_, address auctionManager_, bytes memory feesApprovalData_, bytes memory onCompleteData_ ) external returns (uint40 requestCount) { address appGateway = _getCoreAppGateway(msg.sender); - return _batch(appGateway, auctionManager_, feesApprovalData_, onCompleteData_); + return _batch(appGateway, auctionManager_, fees_, feesApprovalData_, onCompleteData_); } function _checkBatch( address appGateway_, bytes memory feesApprovalData_, - Fees memory fees_ + uint256 fees_ ) internal view { if (queuePayloadParams.length > REQUEST_PAYLOAD_COUNT_LIMIT) revert RequestPayloadCountLimitExceeded(); @@ -64,7 +64,7 @@ abstract contract RequestQueue is DeliveryUtils { function _batch( address appGateway_, address auctionManager_, - Fees memory fees_, + uint256 fees_, bytes memory feesApprovalData_, bytes memory onCompleteData_ ) internal returns (uint40 requestCount) { @@ -89,7 +89,10 @@ abstract contract RequestQueue is DeliveryUtils { }); // process and submit the queue of payloads to watcher precompile - requestCount = watcherPrecompile__().submitRequest(payloadSubmitParamsArray, requestMetadata); + requestCount = watcherPrecompile__().submitRequest( + payloadSubmitParamsArray, + requestMetadata + ); requests[requestCount] = requestMetadata; // send query directly if request contains only reads @@ -98,14 +101,14 @@ abstract contract RequestQueue is DeliveryUtils { watcherPrecompile__().startProcessingRequest(requestCount, address(0)); // to save extra calls from transmitter - uint256 maxTransmitterFees = fees_.amount - - watcherPrecompile__().getTotalFeesRequired(fees_.token, requestCount); + uint256 maxTransmitterFees = fees_ - + watcherPrecompile__().getTotalFeesRequired(requestCount); emit PayloadSubmitted( requestCount, appGateway_, payloadSubmitParamsArray, - Fees({token: fees_.token, amount: fees_.amount - maxTransmitterFees}), + fees_ - maxTransmitterFees, auctionManager_, onlyReadRequests ); diff --git a/contracts/protocol/utils/FeesPlugin.sol b/contracts/protocol/utils/FeesPlugin.sol deleted file mode 100644 index c78bc932..00000000 --- a/contracts/protocol/utils/FeesPlugin.sol +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -pragma solidity ^0.8.21; - -import {Fees} from "../utils/common/Structs.sol"; - -/// @title FeesPlugin -/// @notice Abstract contract for managing fee configurations -/// @dev Provides base functionality for fee management in the system -abstract contract FeesPlugin { - /// @notice Storage for the current fee configuration - /// @dev Contains fee parameters like chain slug, token address, and amount - Fees public fees; - - /// @notice Retrieves the current fee configuration - /// @return Current fee configuration struct - /// @dev Public view function accessible to any caller - /// @dev Used by external contracts to verify fee parameters - function getFees() public view returns (Fees memory) { - return fees; - } -} diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index c4f00ca1..36c73bb0 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -219,7 +219,7 @@ struct RequestParams { struct RequestMetadata { address appGateway; address auctionManager; - Fees fees; + uint256 fees; Bid winningBid; bytes onCompleteData; bool onlyReadRequests; diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 78f6f102..40f1b13d 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -109,7 +109,7 @@ contract WatcherPrecompile is RequestHandler { /// @return The digest hash of the finalized payload /// @dev This function finalizes a payload request and requests the watcher to release the proofs function finalize( - PayloadParams memory params_,` + PayloadParams memory params_, address transmitter_ ) external returns (bytes32) { return _finalize(params_, transmitter_); diff --git a/script/counter/DeployEVMxCounterApp.s.sol b/script/counter/DeployEVMxCounterApp.s.sol index 9e0cbee8..85755639 100644 --- a/script/counter/DeployEVMxCounterApp.s.sol +++ b/script/counter/DeployEVMxCounterApp.s.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.21; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; import {CounterAppGateway} from "../../test/apps/app-gateways/counter/CounterAppGateway.sol"; -import {Fees} from "../../contracts/protocol/utils/common/Structs.sol"; import {ETH_ADDRESS} from "../../contracts/protocol/utils/common/Constants.sol"; // source .env && forge script script/counter/deployEVMxCounterApp.s.sol --broadcast --skip-simulation --legacy --gas-price 0 @@ -18,11 +17,7 @@ contract CounterDeploy is Script { vm.startBroadcast(deployerPrivateKey); // Setting fee payment on Arbitrum Sepolia - Fees memory fees = Fees({ - feePoolChain: 421614, - feePoolToken: ETH_ADDRESS, - amount: 0.00001 ether - }); + uint256 fees = 0.00001 ether; CounterAppGateway gateway = new CounterAppGateway(addressResolver, fees); diff --git a/script/counter/SetFees.s.sol b/script/counter/SetFees.s.sol index c97a2b0b..e48d9a73 100644 --- a/script/counter/SetFees.s.sol +++ b/script/counter/SetFees.s.sol @@ -5,7 +5,6 @@ import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; import {ETH_ADDRESS} from "../../contracts/protocol/utils/common/Constants.sol"; import {CounterAppGateway} from "../../test/apps/app-gateways/counter/CounterAppGateway.sol"; -import {Fees} from "../../contracts/protocol/utils/common/Structs.sol"; // source .env && forge script script/counter/DeployCounterOnchain.s.sol --broadcast --skip-simulation --legacy --gas-price 0 contract CounterSetFees is Script { @@ -22,11 +21,7 @@ contract CounterSetFees is Script { console.log("Setting fees..."); // Setting fee payment on Arbitrum Sepolia - Fees memory fees = Fees({ - feePoolChain: 421614, - feePoolToken: ETH_ADDRESS, - amount: 0.00001 ether - }); + uint256 fees = 0.00001 ether; appGateway.setFees(fees); } } diff --git a/script/helpers/AppGatewayFeeBalance.s.sol b/script/helpers/AppGatewayFeeBalance.s.sol index 9bf7c2c9..a1faade9 100644 --- a/script/helpers/AppGatewayFeeBalance.s.sol +++ b/script/helpers/AppGatewayFeeBalance.s.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.21; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; import {FeesManager} from "../../contracts/protocol/payload-delivery/FeesManager.sol"; -import {Fees} from "../../contracts/protocol/utils/common/Structs.sol"; import {ETH_ADDRESS} from "../../contracts/protocol/utils/common/Constants.sol"; contract CheckDepositedFees is Script { diff --git a/script/helpers/PayFeesInArbitrumETH.s.sol b/script/helpers/PayFeesInArbitrumETH.s.sol index edb8b2fd..6fbd7164 100644 --- a/script/helpers/PayFeesInArbitrumETH.s.sol +++ b/script/helpers/PayFeesInArbitrumETH.s.sol @@ -4,7 +4,6 @@ pragma solidity ^0.8.21; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; import {FeesPlug} from "../../contracts/protocol/payload-delivery/FeesPlug.sol"; -import {Fees} from "../../contracts/protocol/utils/common/Structs.sol"; import {ETH_ADDRESS} from "../../contracts/protocol/utils/common/Constants.sol"; // source .env && forge script script/helpers/PayFeesInArbitrumETH.s.sol --broadcast --skip-simulation diff --git a/test/apps/app-gateways/counter/CounterAppGateway.sol b/test/apps/app-gateways/counter/CounterAppGateway.sol index a27b2db1..a7b9561b 100644 --- a/test/apps/app-gateways/counter/CounterAppGateway.sol +++ b/test/apps/app-gateways/counter/CounterAppGateway.sol @@ -17,7 +17,7 @@ contract CounterAppGateway is AppGatewayBase, Ownable { uint256 public optCounter; event TimeoutResolved(uint256 creationTimestamp, uint256 executionTimestamp); - constructor(address addressResolver_, Fees memory fees_) AppGatewayBase(addressResolver_) { + constructor(address addressResolver_, uint256 fees_) AppGatewayBase(addressResolver_) { creationCodeWithArgs[counter] = abi.encodePacked(type(Counter).creationCode); creationCodeWithArgs[counter1] = abi.encodePacked(type(Counter).creationCode); _setOverrides(fees_); @@ -120,7 +120,7 @@ contract CounterAppGateway is AppGatewayBase, Ownable { } // UTILS - function setFees(Fees memory fees_) public { + function setFees(uint256 fees_) public { fees = fees_; } diff --git a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol index 98488499..58846bc6 100644 --- a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol +++ b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol @@ -29,7 +29,7 @@ contract SuperTokenAppGateway is AppGatewayBase, Ownable { constructor( address addressResolver_, address owner_, - Fees memory fees_, + uint256 fees_, ConstructorParams memory params_ ) AppGatewayBase(addressResolver_) { creationCodeWithArgs[superToken] = abi.encodePacked( From fceb0df2fcb223e6af0cbbf4acd1fc2e93476778 Mon Sep 17 00:00:00 2001 From: Akash Date: Mon, 21 Apr 2025 11:42:29 +0530 Subject: [PATCH 059/108] fix: build, wip --- FunctionSignatures.md | 2 +- contracts/base/AppGatewayBase.sol | 26 +- contracts/interfaces/IFeesManager.sol | 29 +- contracts/interfaces/IFeesPlug.sol | 6 +- contracts/interfaces/IMiddleware.sol | 2 +- contracts/interfaces/IWatcherPrecompile.sol | 5 +- .../interfaces/IWatcherPrecompileLimits.sol | 20 +- .../payload-delivery/AuctionManager.sol | 35 +-- .../protocol/payload-delivery/FeesManager.sol | 249 +++++++++++------- .../protocol/payload-delivery/FeesPlug.sol | 2 +- .../app-gateway/DeliveryHelper.sol | 8 +- .../app-gateway/FeesHelpers.sol | 6 +- .../app-gateway/RequestQueue.sol | 36 +-- .../protocol/utils/AddressResolverUtil.sol | 1 + contracts/protocol/utils/common/Constants.sol | 1 + contracts/protocol/utils/common/Structs.sol | 16 +- .../WatcherPrecompileLimits.sol | 84 +++--- .../watcherPrecompile/core/RequestHandler.sol | 23 +- .../core/WatcherPrecompile.sol | 7 +- .../core/WatcherPrecompileCore.sol | 38 +-- test/DeliveryHelper.t.sol | 20 +- test/FeesTest.t.sol | 7 +- test/Inbox.t.sol | 2 +- test/SetupTest.t.sol | 14 +- test/apps/Counter.t.sol | 7 +- test/apps/ParallelCounter.t.sol | 8 +- test/apps/SuperToken.t.sol | 7 +- .../counter/CounterAppGateway.sol | 23 +- .../super-token/SuperTokenAppGateway.sol | 2 +- 29 files changed, 374 insertions(+), 312 deletions(-) diff --git a/FunctionSignatures.md b/FunctionSignatures.md index 3eb60034..557f95f5 100644 --- a/FunctionSignatures.md +++ b/FunctionSignatures.md @@ -169,7 +169,7 @@ | `requestOwnershipHandover` | `0x25692962` | | `sbType` | `0x745de344` | | `transferOwnership` | `0xf2fde38b` | -| `transmitterFees` | `0xefb4cdea` | +| `transmitterCredits` | `0xefb4cdea` | | `unblockAndAssignFees` | `0x3c5366a2` | | `unblockFees` | `0xc1867a4b` | | `watcherPrecompileConfig` | `0x8618a912` | diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index abd4996d..3f252074 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -19,7 +19,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { address public auctionManager; bytes32 public sbType; bytes public onCompleteData; - uint256 public fees; + uint256 public maxFees; mapping(address => bool) public isValidPromise; mapping(bytes32 => mapping(uint32 => address)) public override forwarderAddresses; @@ -36,13 +36,12 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { isAsyncModifierSet = false; // todo: cache the feesApprovalData for next async in same request - deliveryHelper__().batch(fees, auctionManager, feesApprovalData_, onCompleteData); + deliveryHelper__().batch(maxFees, auctionManager, feesApprovalData_, onCompleteData); _markValidPromises(); onCompleteData = bytes(""); } function _preAsync() internal { - if (fees.feePoolChain == 0) revert FeesNotSet(); isAsyncModifierSet = true; _clearOverrides(); deliveryHelper__().clearQueue(); @@ -188,7 +187,8 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { return address(0); } - onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]).getOnChainAddress(); + onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]) + .getOnChainAddress(); } //////////////////////////////////////////////////////////////////////////////////////////////// @@ -197,7 +197,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { /// @notice Sets multiple overrides in one call /// @param isReadCall_ The read call flag - /// @param fees_ The fees configuration + /// @param fees_ The maxFees configuration /// @param gasLimit_ The gas limit /// @param isParallelCall_ The sequential call flag function _setOverrides( @@ -209,7 +209,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { overrideParams.isReadCall = isReadCall_; overrideParams.isParallelCall = isParallelCall_; overrideParams.gasLimit = gasLimit_; - fees = fees_; + maxFees = fees_; } function _clearOverrides() internal { @@ -221,7 +221,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { overrideParams.writeFinality = WriteFinality.LOW; } - /// @notice Sets isReadCall, fees and gasLimit overrides + /// @notice Sets isReadCall, maxFees and gasLimit overrides /// @param isReadCall_ The read call flag /// @param isParallelCall_ The sequential call flag /// @param gasLimit_ The gas limit @@ -283,10 +283,10 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { overrideParams.value = value_; } - /// @notice Sets fees overrides - /// @param fees_ The fees configuration - function _setOverrides(uint256 fees_) internal { - fees = fees_; + /// @notice Sets maxFees overrides + /// @param fees_ The maxFees configuration + function _setMaxFees(uint256 fees_) internal { + maxFees = fees_; } function getOverrideParams() @@ -315,7 +315,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { deliveryHelper__().cancelRequest(requestCount_); } - /// @notice increases the transaction fees + /// @notice increases the transaction maxFees /// @param requestCount_ The async ID function _increaseFees(uint40 requestCount_, uint256 newMaxFees_) internal { deliveryHelper__().increaseFees(requestCount_, newMaxFees_); @@ -339,7 +339,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { amount_, receiver_, auctionManager, - fees + maxFees ); } diff --git a/contracts/interfaces/IFeesManager.sol b/contracts/interfaces/IFeesManager.sol index a6c90520..71b0eb70 100644 --- a/contracts/interfaces/IFeesManager.sol +++ b/contracts/interfaces/IFeesManager.sol @@ -5,25 +5,20 @@ import {Bid, QueuePayloadParams} from "../protocol/utils/common/Structs.sol"; interface IFeesManager { function blockFees( - address appGateway_, - uint256 fees_, - Bid memory winningBid_, + address consumeFrom_, + uint256 transmitterCredits_, uint40 requestCount_ ) external; function unblockFees(uint40 requestCount_) external; function isFeesEnough( - address appGateway_, address consumeFrom_, - uint256 fees_ + address appGateway_, + uint256 amount_ ) external view returns (bool); - function unblockAndAssignFees( - uint40 requestCount_, - address transmitter_, - address appGateway_ - ) external; + function unblockAndAssignFees(uint40 requestCount_, address transmitter_) external; function withdrawFees( address appGateway_, @@ -33,10 +28,14 @@ interface IFeesManager { address receiver_ ) external; - function assignWatcherPrecompileFees( - uint32 chainSlug_, - address token_, - uint256 amount_, - address consumeFrom_ + function assignWatcherPrecompileFeesFromRequestCount( + uint256 fees_, + uint40 requestCount_ ) external; + + function assignWatcherPrecompileFeesFromAddress(uint256 fees_, address consumeFrom_) external; + + function setAppGatewayWhitelist( + bytes memory feeApprovalData_ + ) external returns (address consumeFrom, address appGateway, bool isApproved); } diff --git a/contracts/interfaces/IFeesPlug.sol b/contracts/interfaces/IFeesPlug.sol index 1f4357b8..edcd1aef 100644 --- a/contracts/interfaces/IFeesPlug.sol +++ b/contracts/interfaces/IFeesPlug.sol @@ -6,7 +6,11 @@ interface IFeesPlug { function feesRedeemed(bytes32 feesId_) external view returns (bool); - function deposit(address token_, address appGateway_, uint256 amount_) external payable; + function depositToFee(address token_, uint256 amount_, address receiver_) external; + + function depositToFeeAndNative(address token_, uint256 amount_, address receiver_) external; + + function depositToNative(address token_, uint256 amount_, address receiver_) external; function distributeFee( address feeToken_, diff --git a/contracts/interfaces/IMiddleware.sol b/contracts/interfaces/IMiddleware.sol index 46a2410d..d8714e91 100644 --- a/contracts/interfaces/IMiddleware.sol +++ b/contracts/interfaces/IMiddleware.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -import {PayloadSubmitParams, QueuePayloadParams, Bid, WriteFinality, CallType, Parallel, IsPlug, RequestMetadata, RequestFee} from "../protocol/utils/common/Structs.sol"; +import {PayloadSubmitParams, QueuePayloadParams, Bid, WriteFinality, CallType, Parallel, IsPlug, RequestMetadata} from "../protocol/utils/common/Structs.sol"; /// @title IMiddleware /// @notice Interface for the Middleware contract diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 0def0da2..54d7d08e 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -import {DigestParams, ResolvedPromises, PayloadParams, TriggerParams, PayloadSubmitParams, RequestParams} from "../protocol/utils/common/Structs.sol"; +import {DigestParams, ResolvedPromises, PayloadParams, TriggerParams, PayloadSubmitParams, Bid, RequestParams, RequestMetadata} from "../protocol/utils/common/Structs.sol"; import {IWatcherPrecompileLimits} from "./IWatcherPrecompileLimits.sol"; import {IWatcherPrecompileConfig} from "./IWatcherPrecompileConfig.sol"; @@ -174,7 +174,8 @@ interface IWatcherPrecompile { function setExpiryTime(uint256 expiryTime_) external; function submitRequest( - PayloadSubmitParams[] calldata payloadSubmitParams + PayloadSubmitParams[] calldata payloadSubmitParams, + RequestMetadata memory requestMetadata ) external returns (uint40 requestCount); function startProcessingRequest(uint40 requestCount, address transmitter) external; diff --git a/contracts/interfaces/IWatcherPrecompileLimits.sol b/contracts/interfaces/IWatcherPrecompileLimits.sol index f7db2adf..394a8cdc 100644 --- a/contracts/interfaces/IWatcherPrecompileLimits.sol +++ b/contracts/interfaces/IWatcherPrecompileLimits.sol @@ -46,14 +46,20 @@ interface IWatcherPrecompileLimits { /// @param appGateway_ The app gateway address /// @param limitType_ The type of limit to consume /// @param consumeLimit_ The amount of limit to consume - function consumeLimit(address appGateway_, bytes32 limitType_, uint256 consumeLimit_) external; + function consumeLimit( + uint40 requestCount_, + address appGateway_, + bytes32 limitType_, + uint256 consumeLimit_ + ) external; + + function getTotalFeesRequired(uint40 requestCount_) external view returns (uint256); + + function queryFees() external view returns (uint256); + function finalizeFees() external view returns (uint256); + function scheduleFees() external view returns (uint256); + function callBackFees() external view returns (uint256); - function getTotalFeesRequired( - address token_, - uint queryCount, - uint finalizeCount, - uint scheduleCount - ) external view returns (uint256); /// @notice Emitted when limit parameters are updated event LimitParamsUpdated(UpdateLimitParams[] updates); diff --git a/contracts/protocol/payload-delivery/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol index a27cbd3f..632a3d15 100644 --- a/contracts/protocol/payload-delivery/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -95,38 +95,38 @@ contract AuctionManager is /// @notice Places a bid for an auction /// @param requestCount_ The ID of the auction - /// @param fee The bid amount + /// @param bidFees The bid amount /// @param transmitterSignature The signature of the transmitter function bid( uint40 requestCount_, - uint256 fee, + uint256 bidFees, bytes memory transmitterSignature, bytes memory extraData ) external { if (auctionClosed[requestCount_]) revert AuctionClosed(); + RequestMetadata memory requestMetadata = _getRequestMetadata(requestCount_); // check if the transmitter is valid address transmitter = _recoverSigner( - keccak256(abi.encode(address(this), evmxSlug, requestCount_, fee, extraData)), + keccak256(abi.encode(address(this), evmxSlug, requestCount_, bidFees, extraData)), transmitterSignature ); if (!_hasRole(TRANSMITTER_ROLE, transmitter)) revert InvalidTransmitter(); - (uint256 watcherFees, uint256 transmitterFees) = getTransmitterMaxFeesRequired( - requestMetadata.fees.token, + (uint256 watcherFees, uint256 transmitterCredits) = getTransmitterMaxFeesRequired( requestCount_ + // check if the bid exceeds the max fees quoted by app gateway subtracting the watcher fees ); - // check if the bid exceeds the max fees quoted by app gateway subtracting the watcher fees - if (fee > transmitterFees) revert BidExceedsMaxFees(); + if (bidFees > transmitterCredits) revert BidExceedsMaxFees(); // check if the bid is lower than the existing bid if ( winningBids[requestCount_].transmitter != address(0) && - fee >= winningBids[requestCount_].fee + bidFees >= winningBids[requestCount_].fee ) revert LowerBidAlreadyExists(); // create a new bid - Bid memory newBid = Bid({fee: fee, transmitter: transmitter, extraData: extraData}); + Bid memory newBid = Bid({fee: bidFees, transmitter: transmitter, extraData: extraData}); // update the winning bid winningBids[requestCount_] = newBid; @@ -158,7 +158,7 @@ contract AuctionManager is if (winningBid.transmitter == address(0)) revert InvalidTransmitter(); auctionClosed[requestCount_] = true; - + RequestMetadata memory requestMetadata = _getRequestMetadata(requestCount_); // block the fees IFeesManager(addressResolver__.feesManager()).blockFees( requestMetadata.consumeFrom, @@ -209,19 +209,18 @@ contract AuctionManager is } function getTransmitterMaxFeesRequired( - address token_, uint40 requestCount_ ) public view returns (uint256, uint256) { + RequestMetadata memory requestMetadata = _getRequestMetadata(requestCount_); + // check if the bid is for this auction manager if (requestMetadata.auctionManager != address(this)) revert InvalidBid(); // get the request metadata - RequestMetadata memory requestMetadata = IMiddleware(addressResolver__.deliveryHelper()) - .getRequestMetadata(requestCount_); // get the total fees required for the watcher precompile ops - uint256 watcherFees = watcherPrecompile__().getTotalFeesRequired(token_, requestCount_); - return (watcherFees, requestMetadata.fees.amount - watcherFees); + uint256 watcherFees = watcherPrecompileLimits().getTotalFeesRequired(requestCount_); + return (watcherFees, requestMetadata.maxFees - watcherFees); } function _recoverSigner( @@ -232,4 +231,10 @@ contract AuctionManager is // recovered signer is checked for the valid roles later signer = ECDSA.recover(digest, signature_); } + + function _getRequestMetadata( + uint40 requestCount_ + ) internal view returns (RequestMetadata memory) { + return IMiddleware(addressResolver__.deliveryHelper()).getRequestMetadata(requestCount_); + } } diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 0e1bc855..260c4b52 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -10,7 +10,7 @@ import {IFeesManager} from "../../interfaces/IFeesManager.sol"; import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; import {NotAuctionManager, InvalidWatcherSignature, NonceUsed} from "../utils/common/Errors.sol"; -import {Bid, CallType, Parallel, WriteFinality, QueuePayloadParams, IsPlug, PayloadSubmitParams, RequestMetadata, RequestFee, UserCredits} from "../utils/common/Structs.sol"; +import {Bid, CallType, Parallel, WriteFinality, QueuePayloadParams, IsPlug, PayloadSubmitParams, RequestMetadata, UserCredits} from "../utils/common/Structs.sol"; abstract contract FeesManagerStorage is IFeesManager { // slots [0-49] reserved for gap @@ -28,6 +28,9 @@ abstract contract FeesManagerStorage is IFeesManager { // user credits mapping(address => UserCredits) public userCredits; + // user nonce + mapping(address => uint256) public userNonce; + // token pool balances // token address => chainSlug => amount mapping(address => mapping(uint32 => uint256)) public tokenPoolBalances; @@ -39,7 +42,7 @@ abstract contract FeesManagerStorage is IFeesManager { // slot 54 /// @notice Mapping to track request credits details for each request count /// @dev requestCount => RequestFee - mapping(uint40 => RequestFee) public requestCountCredits; + mapping(uint40 => uint256) public requestCountCredits; // slot 55 /// @notice Mapping to track credits to be distributed to transmitters @@ -65,15 +68,9 @@ abstract contract FeesManagerStorage is IFeesManager { contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResolverUtil { /// @notice Emitted when fees are blocked for a batch /// @param requestCount The batch identifier - /// @param chainSlug The chain identifier - /// @param token The token address + /// @param consumeFrom The consume from address /// @param amount The blocked amount - event FeesBlocked( - uint40 indexed requestCount, - uint32 indexed chainSlug, - address indexed token, - uint256 amount - ); + event FeesBlocked(uint40 indexed requestCount, address indexed consumeFrom, uint256 amount); /// @notice Emitted when transmitter fees are updated /// @param requestCount The batch identifier @@ -84,12 +81,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol address indexed transmitter, uint256 amount ); - event WatcherPrecompileFeesAssigned( - uint32 chainSlug, - address token, - uint256 amount, - address consumeFrom - ); + event WatcherPrecompileFeesAssigned(uint256 amount, address consumeFrom); /// @notice Emitted when fees deposited are updated /// @param chainSlug The chain identifier /// @param appGateway The app gateway address @@ -163,33 +155,72 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol } /// @notice Adds the fees deposited for an app gateway on a chain - /// @param consumeFrom_ The app gateway address + /// @param depositTo_ The app gateway address /// @param amount_ The amount deposited + // @dev only callable by watcher precompile + // @dev will need tokenAmount_ and creditAmount_ when introduce tokens except stables function incrementFeesDeposited( - address consumeFrom_, + address depositTo_, + uint32 chainSlug_, + address token_, uint256 amount_, uint256 signatureNonce_, bytes memory signature_ ) external { - _isWatcherSignatureValid(abi.encode(consumeFrom_, amount_), signatureNonce_, signature_); + _isWatcherSignatureValid( + abi.encode(depositTo_, chainSlug_, token_, amount_), + signatureNonce_, + signature_ + ); - UserCredits storage userCredit = userCredits[consumeFrom_]; + UserCredits storage userCredit = userCredits[depositTo_]; userCredit.totalCredits += amount_; tokenPoolBalances[token_][chainSlug_] += amount_; - emit FeesDepositedUpdated(consumeFrom_, amount_); + emit FeesDepositedUpdated(chainSlug_, depositTo_, token_, amount_); } function isFeesEnough( - address appGateway_, address consumeFrom_, + address appGateway_, uint256 amount_ ) external view returns (bool) { - address appGateway = _getCoreAppGateway(consumeFrom_); - if (!isAppGatewayWhitelisted[consumeFrom_][appGateway]) return false; + if (!isAppGatewayWhitelisted[consumeFrom_][appGateway_]) return false; return getAvailableFees(consumeFrom_) >= amount_; } + function _processFeeApprovalData( + bytes memory feeApprovalData_ + ) internal view returns (address consumeFrom, address appGateway, bool isApproved) { + bytes memory signature_; + (consumeFrom, appGateway, isApproved, signature_) = abi.decode( + feeApprovalData_, + (address, address, bool, bytes) + ); + if (signature_.length == 0) return (consumeFrom, appGateway, isApproved); + bytes32 digest = keccak256( + abi.encode( + address(this), + evmxSlug, + consumeFrom, + appGateway, + userNonce[consumeFrom], + isApproved + ) + ); + if (ECDSA.recover(digest, signature_) != consumeFrom) revert InvalidWatcherSignature(); + + isAppGatewayWhitelisted[consumeFrom][appGateway] = isApproved; + userNonce[consumeFrom]++; + return (consumeFrom, appGateway, isApproved); + } + + function setAppGatewayWhitelist( + bytes memory feeApprovalData_ + ) external returns (address consumeFrom, address appGateway, bool isApproved) { + return _processFeeApprovalData(feeApprovalData_); + } + /// @notice Whitelists multiple app gateways for the caller /// @param appGateways_ Array of app gateway addresses to whitelist function whitelistApps(address[] calldata appGateways_) external { @@ -197,32 +228,31 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol isAppGatewayWhitelisted[msg.sender][appGateways_[i]] = true; } } - + modifier onlyAuctionManager(uint40 requestCount_) { + if (msg.sender != deliveryHelper__().getRequestMetadata(requestCount_).auctionManager) + revert NotAuctionManager(); + _; + } /// @notice Blocks fees for a request count /// @param consumeFrom_ The fees payer address - /// @param transmitterFees_ The total fees to block + /// @param transmitterCredits_ The total fees to block /// @param requestCount_ The batch identifier /// @dev Only callable by delivery helper function blockFees( address consumeFrom_, - uint256 transmitterFees_, + uint256 transmitterCredits_, uint40 requestCount_ - ) external { - if (msg.sender != deliveryHelper__().getRequestMetadata(requestCount_).auctionManager) - revert NotAuctionManager(); - + ) external onlyAuctionManager(requestCount_) { // Block fees - if (getAvailableFees(consumeFrom_) < transmitterFees_) revert InsufficientFeesAvailable(); + if (getAvailableFees(consumeFrom_) < transmitterCredits_) + revert InsufficientFeesAvailable(); UserCredits storage userCredit = userCredits[consumeFrom_]; - userCredit.blockedCredits += transmitterFees_; + userCredit.blockedCredits += transmitterCredits_; - requestCountCredits[requestCount_] = RequestFee({ - blockedCredits: transmitterFees_, - consumeFrom: consumeFrom_ - }); + requestCountCredits[requestCount_] = transmitterCredits_; - emit FeesBlocked(requestCount_, transmitterFees_); + emit FeesBlocked(requestCount_, consumeFrom_, transmitterCredits_); } /// @notice Unblocks fees after successful execution and assigns them to the transmitter @@ -232,16 +262,18 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol uint40 requestCount_, address transmitter_ ) external override onlyDeliveryHelper { - RequestFee memory requestFee = requestCountCredits[requestCount_]; - if (requestFee.blockedCredits == 0) return; - - uint256 fees = deliveryHelper__().getRequestMetadata(requestCount_).winningBid.fee; + uint256 blockedCredits = requestCountCredits[requestCount_]; + if (blockedCredits == 0) return; + RequestMetadata memory requestMetadata = deliveryHelper__().getRequestMetadata( + requestCount_ + ); + uint256 fees = requestMetadata.winningBid.fee; // Unblock fees from deposit - _useBlockedUserCredits(requestFee.consumeFrom, fees, fees); + _useBlockedUserCredits(requestMetadata.consumeFrom, blockedCredits, fees); // Assign fees to transmitter - transmitterFees[transmitter_] += fees; + transmitterCredits[transmitter_] += fees; // Clean up storage delete requestCountCredits[requestCount_]; @@ -250,27 +282,43 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol function _useBlockedUserCredits( address consumeFrom_, - uint256 toBlock_, - uint256 toConsume_ + uint256 toConsumeFromBlocked_, + uint256 toConsumeFromTotal_ ) internal { UserCredits storage userCredit = userCredits[consumeFrom_]; - userCredit.blockedCredits -= toBlock_; + userCredit.blockedCredits -= toConsumeFromBlocked_; + userCredit.totalCredits -= toConsumeFromTotal_; + } + + function _useAvailableUserCredits(address consumeFrom_, uint256 toConsume_) internal { + UserCredits storage userCredit = userCredits[consumeFrom_]; userCredit.totalCredits -= toConsume_; } - function assignWatcherPrecompileFees( - uint256 amount_, + function assignWatcherPrecompileFeesFromRequestCount( + uint256 fees_, uint40 requestCount_ ) external onlyWatcherPrecompile { - RequestFee memory requestFee = requestCountCredits[requestCount_]; - if (requestFee.blockedCredits == 0) revert NoFeesBlocked(); + require(requestCount_ != 0, "Request count cannot be 0"); + RequestMetadata memory requestMetadata = deliveryHelper__().getRequestMetadata( + requestCount_ + ); + _assignWatcherPrecompileFees(fees_, requestMetadata.consumeFrom); + } - // deduct the fees from the user - _useBlockedUserCredits(requestFee.consumeFrom, 0, amount_); + function assignWatcherPrecompileFeesFromAddress( + uint256 fees_, + address consumeFrom_ + ) external onlyWatcherPrecompile { + _assignWatcherPrecompileFees(fees_, consumeFrom_); + } + function _assignWatcherPrecompileFees(uint256 fees_, address consumeFrom_) internal { + // deduct the fees from the user + _useAvailableUserCredits(consumeFrom_, fees_); // add the fees to the watcher precompile - watcherPrecompileCredits += amount_; - emit WatcherPrecompileFeesAssigned(requestCount_, amount_); + watcherPrecompileCredits += fees_; + emit WatcherPrecompileFeesAssigned(fees_, consumeFrom_); } function unblockFees(uint40 requestCount_) external { @@ -283,45 +331,15 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol msg.sender != address(deliveryHelper__()) ) revert InvalidCaller(); - RequestFee memory requestFee = requestCountCredits[requestCount_]; - if (requestFee.blockedCredits == 0) return; + uint256 blockedCredits = requestCountCredits[requestCount_]; + if (blockedCredits == 0) return; // Unblock fees from deposit - UserCredits storage userCredit = userCredits[requestFee.consumeFrom]; - userCredit.blockedCredits -= requestFee.blockedCredits; + UserCredits storage userCredit = userCredits[requestMetadata.consumeFrom]; + userCredit.blockedCredits -= blockedCredits; delete requestCountCredits[requestCount_]; - emit FeesUnblocked(requestCount_, requestFee.consumeFrom); - } - - /// @notice Withdraws fees to a specified receiver - /// @param chainSlug_ The chain identifier - /// @param token_ The token address - /// @param receiver_ The address of the receiver - function withdrawTransmitterFees( - uint32 chainSlug_, - address token_, - address receiver_, - uint256 amount_ - ) external returns (uint40 requestCount) { - address transmitter = msg.sender; - // Get total fees for the transmitter in given chain and token - uint256 totalFees = transmitterFees[transmitter]; - if (totalFees >= amount_) revert InsufficientFeesAvailable(); - - // Clean up storage - transmitterFees[transmitter] -= amount_; - tokenPoolBalances[token_][chainSlug_] -= amount_; - - // Create fee distribution payload - bytes32 feesId = _encodeFeesId(feesCounter++); - bytes memory payload = abi.encodeCall( - IFeesPlug.distributeFee, - (token_, amount_, receiver_, feesId) - ); - - // finalize for plug contract - return _submitAndStartProcessing(chainSlug_, payload, transmitter); + emit FeesUnblocked(requestCount_, requestMetadata.consumeFrom); } /// @notice Withdraws funds to a specified receiver @@ -351,11 +369,31 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol _queue(chainSlug_, abi.encodeCall(IFeesPlug.withdrawFees, (token_, amount_, receiver_))); } - function _submitAndStartProcessing( + /// @notice Withdraws fees to a specified receiver + /// @param chainSlug_ The chain identifier + /// @param token_ The token address + /// @param receiver_ The address of the receiver + function withdrawTransmitterFees( uint32 chainSlug_, - bytes memory payload_, - address transmitter_ - ) internal returns (uint40 requestCount) { + address token_, + address receiver_, + uint256 amount_ + ) external returns (uint40 requestCount) { + address transmitter = msg.sender; + // Get total fees for the transmitter in given chain and token + uint256 totalFees = transmitterCredits[transmitter]; + if (totalFees >= amount_) revert InsufficientFeesAvailable(); + + // Clean up storage + transmitterCredits[transmitter] -= amount_; + tokenPoolBalances[token_][chainSlug_] -= amount_; + + // Create fee distribution payload + bytes32 feesId = _encodeFeesId(feesCounter++); + bytes memory payload = abi.encodeCall( + IFeesPlug.distributeFee, + (token_, amount_, receiver_, feesId) + ); PayloadSubmitParams[] memory payloadSubmitParamsArray = new PayloadSubmitParams[](1); payloadSubmitParamsArray[0] = PayloadSubmitParams({ levelNumber: 0, @@ -370,20 +408,29 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol gasLimit: 10000000, value: 0, readAt: 0, - payload: payload_ + payload: payload }); RequestMetadata memory requestMetadata = RequestMetadata({ appGateway: address(this), auctionManager: address(0), - feesApprovalData: bytes(""), - fees: amount_, - winningBid: Bid({transmitter: transmitter_, fee: 0, extraData: new bytes(0)}) - }); + maxFees: 0, + winningBid: Bid({transmitter: transmitter, fee: 0, extraData: new bytes(0)}), + onCompleteData: bytes(""), + onlyReadRequests: false, + consumeFrom: address(0) + }); // finalize for plug contract + return _submitAndStartProcessing(payloadSubmitParamsArray, requestMetadata, transmitter); + } + function _submitAndStartProcessing( + PayloadSubmitParams[] memory payloadSubmitParamsArray_, + RequestMetadata memory requestMetadata_, + address transmitter_ + ) internal returns (uint40 requestCount) { requestCount = watcherPrecompile__().submitRequest( - payloadSubmitParamsArray, - requestMetadata + payloadSubmitParamsArray_, + requestMetadata_ ); // same transmitter can execute requests without auction diff --git a/contracts/protocol/payload-delivery/FeesPlug.sol b/contracts/protocol/payload-delivery/FeesPlug.sol index 2f5bc16f..ab6784ce 100644 --- a/contracts/protocol/payload-delivery/FeesPlug.sol +++ b/contracts/protocol/payload-delivery/FeesPlug.sol @@ -113,7 +113,7 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { address receiver_, uint256 feeAmount_, uint256 nativeAmount_ - ) internal override { + ) internal { uint256 totalAmount_ = feeAmount_ + nativeAmount_; if (!whitelistedTokens[token_]) revert TokenNotWhitelisted(token_); if (token_.code.length == 0) revert InvalidTokenAddress(); diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol index 6c7b0c80..6efaedfd 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol @@ -41,7 +41,7 @@ contract DeliveryHelper is FeesHelpers { requestMetadata_.winningBid.transmitter = winningBid_.transmitter; if (!isRestarted) { - watcherPrecompile__().startProcessingRequest(requestCount_, winningBid_); + watcherPrecompile__().startProcessingRequest(requestCount_, winningBid_.transmitter); } else { watcherPrecompile__().updateTransmitter(requestCount_, winningBid_.transmitter); } @@ -56,8 +56,7 @@ contract DeliveryHelper is FeesHelpers { if (requestMetadata_.winningBid.transmitter != address(0)) IFeesManager(addressResolver__.feesManager()).unblockAndAssignFees( requestCount_, - requestMetadata_.winningBid.transmitter, - requestMetadata_.appGateway + requestMetadata_.winningBid.transmitter ); IAppGateway(requestMetadata_.appGateway).onRequestComplete( @@ -95,8 +94,7 @@ contract DeliveryHelper is FeesHelpers { if (requests[requestCount_].winningBid.transmitter != address(0)) { IFeesManager(addressResolver__.feesManager()).unblockAndAssignFees( requestCount_, - requests[requestCount_].winningBid.transmitter, - requests[requestCount_].appGateway + requests[requestCount_].winningBid.transmitter ); } else { // If the request has no winning bid, ie. transmitter not assigned, unblock fees diff --git a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol index ccda6afe..2922ba52 100644 --- a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol +++ b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol @@ -21,7 +21,7 @@ abstract contract FeesHelpers is RequestQueue { } if (requests[requestCount_].winningBid.transmitter != address(0)) revert WinningBidExists(); - requests[requestCount_].fees.amount = newMaxFees_; + requests[requestCount_].maxFees = newMaxFees_; emit FeesIncreased(appGateway, requestCount_, newMaxFees_); } @@ -47,13 +47,13 @@ abstract contract FeesHelpers is RequestQueue { receiver_ ); - return _batch(msg.sender, auctionManager_, fees_, bytes("")); + return _batch(msg.sender, auctionManager_, fees_, bytes(""), bytes("")); } /// @notice Returns the fees for a request /// @param requestCount_ The ID of the request /// @return fees The fees data function getFees(uint40 requestCount_) external view returns (uint256) { - return requests[requestCount_].fees.amount; + return requests[requestCount_].maxFees; } } diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index 12d9d458..dac1a41d 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -20,34 +20,38 @@ abstract contract RequestQueue is DeliveryUtils { } /// @notice Initiates a batch of payloads - /// @param fees_ The fees data + /// @param maxFees_ The fees data /// @param auctionManager_ The auction manager address /// @return requestCount The ID of the batch function batch( - uint256 fees_, + uint256 maxFees_, address auctionManager_, bytes memory feesApprovalData_, bytes memory onCompleteData_ ) external returns (uint40 requestCount) { address appGateway = _getCoreAppGateway(msg.sender); - return _batch(appGateway, auctionManager_, fees_, feesApprovalData_, onCompleteData_); + return _batch(appGateway, auctionManager_, maxFees_, feesApprovalData_, onCompleteData_); } function _checkBatch( address appGateway_, bytes memory feesApprovalData_, - uint256 fees_ - ) internal view { + uint256 maxFees_ + ) internal view returns (address consumeFrom) { if (queuePayloadParams.length > REQUEST_PAYLOAD_COUNT_LIMIT) revert RequestPayloadCountLimitExceeded(); - + (consumeFrom, , ) = IFeesManager(addressResolver__.feesManager()).setAppGatewayWhitelist( + feesApprovalData_ + ); if ( !IFeesManager(addressResolver__.feesManager()).isFeesEnough( + consumeFrom, appGateway_, - feesApprovalData_, - fees_ + maxFees_ ) ) revert InsufficientFees(); + + return consumeFrom; } function _getAuctionManager(address auctionManager_) internal view returns (address) { @@ -64,7 +68,7 @@ abstract contract RequestQueue is DeliveryUtils { function _batch( address appGateway_, address auctionManager_, - uint256 fees_, + uint256 maxFees_, bytes memory feesApprovalData_, bytes memory onCompleteData_ ) internal returns (uint40 requestCount) { @@ -77,15 +81,15 @@ abstract contract RequestQueue is DeliveryUtils { bool onlyReadRequests ) = _createPayloadSubmitParamsArray(); - _checkBatch(appGateway_, feesApprovalData_, fees_); + address consumeFrom = _checkBatch(appGateway_, feesApprovalData_, maxFees_); RequestMetadata memory requestMetadata = RequestMetadata({ appGateway: appGateway_, auctionManager: auctionManager, - feesApprovalData: feesApprovalData_, - fees: fees_, + maxFees: maxFees_, winningBid: Bid({fee: 0, transmitter: address(0), extraData: new bytes(0)}), onCompleteData: onCompleteData_, - onlyReadRequests: onlyReadRequests + onlyReadRequests: onlyReadRequests, + consumeFrom: consumeFrom }); // process and submit the queue of payloads to watcher precompile @@ -101,14 +105,14 @@ abstract contract RequestQueue is DeliveryUtils { watcherPrecompile__().startProcessingRequest(requestCount, address(0)); // to save extra calls from transmitter - uint256 maxTransmitterFees = fees_ - - watcherPrecompile__().getTotalFeesRequired(requestCount); + uint256 maxTransmitterFees = maxFees_ - + watcherPrecompileLimits().getTotalFeesRequired(requestCount); emit PayloadSubmitted( requestCount, appGateway_, payloadSubmitParamsArray, - fees_ - maxTransmitterFees, + maxFees_ - maxTransmitterFees, auctionManager_, onlyReadRequests ); diff --git a/contracts/protocol/utils/AddressResolverUtil.sol b/contracts/protocol/utils/AddressResolverUtil.sol index ec6ba238..21f24611 100644 --- a/contracts/protocol/utils/AddressResolverUtil.sol +++ b/contracts/protocol/utils/AddressResolverUtil.sol @@ -6,6 +6,7 @@ import "../../interfaces/IMiddleware.sol"; import "../../interfaces/IWatcherPrecompile.sol"; import "../../interfaces/IWatcherPrecompileConfig.sol"; import "../../interfaces/IWatcherPrecompileLimits.sol"; +import "../../interfaces/IFeesManager.sol"; /// @title AddressResolverUtil /// @notice Utility contract for resolving system contract addresses diff --git a/contracts/protocol/utils/common/Constants.sol b/contracts/protocol/utils/common/Constants.sol index bf6c8d4a..7b17f483 100644 --- a/contracts/protocol/utils/common/Constants.sol +++ b/contracts/protocol/utils/common/Constants.sol @@ -9,6 +9,7 @@ bytes32 constant DEPLOY = keccak256("DEPLOY"); bytes32 constant QUERY = keccak256("QUERY"); bytes32 constant FINALIZE = keccak256("FINALIZE"); bytes32 constant SCHEDULE = keccak256("SCHEDULE"); +bytes32 constant CALLBACK = keccak256("CALLBACK"); bytes32 constant FAST = keccak256("FAST"); uint256 constant REQUEST_PAYLOAD_COUNT_LIMIT = 10; uint256 constant PAYLOAD_SIZE_LIMIT = 24_500; diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 36c73bb0..4dcffff9 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -108,6 +108,12 @@ struct Bid { bytes extraData; } +struct OnChainFees { + uint32 chainSlug; + address token; + uint256 amount; +} + // App gateway base: struct OverrideParams { Read isReadCall; @@ -118,12 +124,6 @@ struct OverrideParams { uint256 readAt; } -// FM: -struct RequestFee { - uint256 blockedCredits; - address consumeFrom; -} - struct UserCredits { uint256 totalCredits; uint256 blockedCredits; @@ -219,11 +219,11 @@ struct RequestParams { struct RequestMetadata { address appGateway; address auctionManager; - uint256 fees; + uint256 maxFees; Bid winningBid; bytes onCompleteData; bool onlyReadRequests; - bytes feesApprovalData; + address consumeFrom; } struct ExecuteParams { diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index 0ab07deb..89e99779 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -6,7 +6,7 @@ import {Ownable} from "solady/auth/Ownable.sol"; import {Gauge} from "../utils/Gauge.sol"; import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; import "../../interfaces/IWatcherPrecompileLimits.sol"; -import {SCHEDULE, QUERY, FINALIZE} from "../utils/common/Constants.sol"; +import {SCHEDULE, QUERY, FINALIZE, CALLBACK} from "../utils/common/Constants.sol"; /// @title WatcherPrecompileLimits /// @notice Contract for managing watcher precompile limits @@ -43,16 +43,20 @@ contract WatcherPrecompileLimits is // Mapping to track active app gateways mapping(address => bool) internal _activeAppGateways; - // token => fee amount - mapping(address => uint256) public queryFees; - mapping(address => uint256) public finalizeFees; - mapping(address => uint256) public scheduleFees; - mapping(address => uint256) public callBackFees; + // slot 156: precompileCount + // limitType => requestCount => count + mapping(bytes32 => mapping(uint40 => uint256)) public precompileCount; + + // slot 157: fees + uint256 public queryFees; + uint256 public finalizeFees; + uint256 public scheduleFees; + uint256 public callBackFees; /// @notice Emitted when the default limit and rate per second are set event DefaultLimitAndRatePerSecondSet(uint256 defaultLimit, uint256 defaultRatePerSecond); - event WatcherFeesNotSetForToken(address token_); + error WatcherFeesNotSet(bytes32 limitType); /// @notice Initial initialization (version 1) function initialize( address owner_, @@ -167,63 +171,45 @@ contract WatcherPrecompileLimits is emit DefaultLimitAndRatePerSecondSet(defaultLimit, defaultRatePerSecond); } - function setQueryFees( - address[] calldata tokens_, - uint256[] calldata amounts_ - ) external onlyOwner { - require(tokens_.length == amounts_.length, "Length mismatch"); - for (uint256 i = 0; i < tokens_.length; i++) { - queryFees[tokens_[i]] = amounts_[i]; - } + function setQueryFees(uint256 queryFees_) external onlyOwner { + queryFees = queryFees_; } - function setFinalizeFees( - address[] calldata tokens_, - uint256[] calldata amounts_ - ) external onlyOwner { - require(tokens_.length == amounts_.length, "Length mismatch"); - for (uint256 i = 0; i < tokens_.length; i++) { - finalizeFees[tokens_[i]] = amounts_[i]; - } + function setFinalizeFees(uint256 finalizeFees_) external onlyOwner { + finalizeFees = finalizeFees_; } - function setScheduleFees( - address[] calldata tokens_, - uint256[] calldata amounts_ - ) external onlyOwner { - require(tokens_.length == amounts_.length, "Length mismatch"); - for (uint256 i = 0; i < tokens_.length; i++) { - scheduleFees[tokens_[i]] = amounts_[i]; - } + function setScheduleFees(uint256 scheduleFees_) external onlyOwner { + scheduleFees = scheduleFees_; } - function setCallBackFees( - address[] calldata tokens_, - uint256[] calldata amounts_ - ) external onlyOwner { - require(tokens_.length == amounts_.length, "Length mismatch"); - for (uint256 i = 0; i < tokens_.length; i++) { - callBackFees[tokens_[i]] = amounts_[i]; - } + function setCallBackFees(uint256 callBackFees_) external onlyOwner { + callBackFees = callBackFees_; } - function getTotalFeesRequired( - address token_, - uint40 requestCount_ - ) external view returns (uint256) { + function getTotalFeesRequired(uint40 requestCount_) external view returns (uint256) { uint256 totalFees = 0; - if (queryFees[token_] == 0 || finalizeFees[token_] == 0 || scheduleFees[token_] == 0) { - revert WatcherFeesNotSetForToken(token_); + if (queryFees == 0) { + revert WatcherFeesNotSet(QUERY); + } + if (finalizeFees == 0) { + revert WatcherFeesNotSet(FINALIZE); + } + if (scheduleFees == 0) { + revert WatcherFeesNotSet(SCHEDULE); + } + if (callBackFees == 0) { + revert WatcherFeesNotSet(CALLBACK); } uint256 totalCallbacks = precompileCount[QUERY][requestCount_] + precompileCount[FINALIZE][requestCount_] + precompileCount[SCHEDULE][requestCount_]; - totalFees += totalCallbacks * callBackFees[token_]; - totalFees += precompileCount[QUERY][requestCount_] * queryFees[token_]; - totalFees += precompileCount[FINALIZE][requestCount_] * finalizeFees[token_]; - totalFees += precompileCount[SCHEDULE][requestCount_] * scheduleFees[token_]; + totalFees += totalCallbacks * callBackFees; + totalFees += precompileCount[QUERY][requestCount_] * queryFees; + totalFees += precompileCount[FINALIZE][requestCount_] * finalizeFees; + totalFees += precompileCount[SCHEDULE][requestCount_] * scheduleFees; return totalFees; } diff --git a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol index 3d00e32d..f89e36e5 100644 --- a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol +++ b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol @@ -14,15 +14,16 @@ abstract contract RequestHandler is WatcherPrecompileCore { uint256[50] _request_handler_gap; /// @notice Submits a batch of payload requests from middleware - /// @param payloadSubmitParams Array of payload submit parameters + /// @param payloadSubmitParams_ Array of payload submit parameters + /// @param requestMetadata_ Request metadata /// @return requestCount The unique identifier for the submitted request /// @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, - RequestMetadata calldata requestMetadata + PayloadSubmitParams[] calldata payloadSubmitParams_, + RequestMetadata calldata requestMetadata_ ) public returns (uint40 requestCount) { - address appGateway = _checkAppGateways(payloadSubmitParams); + address appGateway = _checkAppGateways(payloadSubmitParams_); requestCount = nextRequestCount++; uint40 batchCount = nextBatchCount; @@ -32,8 +33,8 @@ abstract contract RequestHandler is WatcherPrecompileCore { uint256 writeCount; PayloadSubmitParams memory lastP; - for (uint256 i = 0; i < payloadSubmitParams.length; i++) { - PayloadSubmitParams memory p = payloadSubmitParams[i]; + for (uint256 i = 0; i < payloadSubmitParams_.length; i++) { + PayloadSubmitParams memory p = payloadSubmitParams_[i]; // Count reads and writes for checking limits if (p.callType == CallType.READ) { @@ -94,11 +95,9 @@ abstract contract RequestHandler is WatcherPrecompileCore { watcherPrecompileLimits__.consumeLimit(appGateway, FINALIZE, writeCount); requestParams[requestCount].currentBatch = currentBatch; - requestParams[requestCount].payloadsRemaining = payloadSubmitParams.length; + requestParams[requestCount].payloadsRemaining = payloadSubmitParams_.length; requestParams[requestCount].middleware = msg.sender; - requestMetadata[requestCount] = requestMetadata; - emit RequestSubmitted( msg.sender, requestCount, @@ -132,17 +131,17 @@ abstract contract RequestHandler is WatcherPrecompileCore { /// @notice Starts processing a request with a specified transmitter /// @param requestCount The request count to start processing - /// @param winningBid The winning bid, contains fees, transmitter and extra data + /// @param transmitter_ The winning bid, contains fees, transmitter and extra data /// @dev This function initiates the processing of a request by a transmitter /// @dev It verifies that the caller is the middleware and that the request hasn't been started yet - function startProcessingRequest(uint40 requestCount, Bid memory winningBid) public { + function startProcessingRequest(uint40 requestCount, address transmitter_) public { RequestParams storage r = requestParams[requestCount]; if (r.middleware != msg.sender) revert InvalidCaller(); if (r.transmitter != address(0)) revert AlreadyStarted(); if (r.currentBatchPayloadsLeft > 0) revert AlreadyStarted(); uint40 batchCount = r.payloadParamsArray[0].payloadHeader.getBatchCount(); - r.transmitter = winningBid.transmitter; + r.transmitter = transmitter_; r.currentBatch = batchCount; _processBatch(requestCount, batchCount); diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 40f1b13d..bcea7bd0 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -312,11 +312,8 @@ contract WatcherPrecompile is RequestHandler { ) ) revert InvalidCallerTriggered(); - IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFees( - address(0), - uint32(0), - watcherPrecompileLimits__().callBackFees(params_[i].payload), - uint40(0), + IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFeesFromAddress( + watcherPrecompileLimits__.callBackFees(), appGateway ); diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index d8cef4f0..da6c919d 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -6,7 +6,7 @@ import {ECDSA} from "solady/utils/ECDSA.sol"; import {AccessControl} from "../../utils/AccessControl.sol"; import "solady/utils/Initializable.sol"; import {AddressResolverUtil} from "../../utils/AddressResolverUtil.sol"; - +import {IFeesManager} from "../../../interfaces/IFeesManager.sol"; /// @title WatcherPrecompileCore /// @notice Core functionality for the WatcherPrecompile system /// @dev This contract implements the core functionality for payload verification, execution, and app configurations @@ -33,10 +33,7 @@ abstract contract WatcherPrecompileCore is ) internal returns (bytes32 timeoutId) { if (delayInSeconds_ > maxTimeoutDelayInSeconds) revert TimeoutDelayTooLarge(); - _consumeFees( - requestCount_, - watcherPrecompileLimits__.scheduleFees(requestMetadata_.fees.feePoolToken) - ); + _consumeCallbackFeesFromAddress(watcherPrecompileLimits__.scheduleFees(), msg.sender); uint256 executeAt = block.timestamp + delayInSeconds_; timeoutId = _encodeTimeoutId(); @@ -79,9 +76,9 @@ abstract contract WatcherPrecompileCore is requestParams[params_.payloadHeader.getRequestCount()].middleware ); - _consumeFees( - params_.payloadHeader.getRequestCount(), - watcherPrecompileLimits__.finalizeFees(params_.payloadHeader.getChainSlug()) + _consumeCallbackFeesFromRequestCount( + watcherPrecompileLimits__.finalizeFees(), + params_.payloadHeader.getRequestCount() ); uint256 deadline = block.timestamp + expiryTime; @@ -119,9 +116,9 @@ abstract contract WatcherPrecompileCore is /// @param params_ The payload parameters for the query /// @dev This function sets up a query request and emits a QueryRequested event function _query(PayloadParams memory params_) internal { - _consumeFees( - params_.payloadHeader.getRequestCount(), - watcherPrecompileLimits__.queryFees(params_.payloadHeader.getChainSlug()) + _consumeCallbackFeesFromRequestCount( + watcherPrecompileLimits__.queryFees(), + params_.payloadHeader.getRequestCount() ); payloads[params_.payloadId].prevDigestsHash = _getPreviousDigestsHash( @@ -254,19 +251,24 @@ abstract contract WatcherPrecompileCore is if (signer != owner()) revert InvalidWatcherSignature(); } - function _consumeFees(uint40 requestCount_, uint256 fees_) internal { - RequestMetadata memory requestMetadata_ = requestMetadata[requestCount_]; - + function _consumeCallbackFeesFromRequestCount(uint256 fees_, uint40 requestCount_) internal { // for callbacks in all precompiles - uint256 feesToConsume = fees_ + watcherPrecompileLimits__().callBackFees(); - IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFees( - requestMetadata_.fees.feePoolChain, - requestMetadata_.fees.feePoolToken, + uint256 feesToConsume = fees_ + watcherPrecompileLimits__.callBackFees(); + IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFeesFromRequestCount( feesToConsume, requestCount_ ); } + function _consumeCallbackFeesFromAddress(uint256 fees_, address consumeFrom_) internal { + // for callbacks in all precompiles + uint256 feesToConsume = fees_ + watcherPrecompileLimits__.callBackFees(); + IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFeesFromAddress( + feesToConsume, + consumeFrom_ + ); + } + /// @notice Gets the batch IDs for a request /// @param requestCount_ The request count to get the batch IDs for /// @return An array of batch IDs for the given request diff --git a/test/DeliveryHelper.t.sol b/test/DeliveryHelper.t.sol index fd7647d7..45877410 100644 --- a/test/DeliveryHelper.t.sol +++ b/test/DeliveryHelper.t.sol @@ -27,7 +27,7 @@ contract DeliveryHelperTest is SetupTest { uint40 indexed requestCount, address indexed appGateway, PayloadSubmitParams[] payloadSubmitParams, - Fees fees, + uint256 maxFees, address auctionManager, bool onlyReadRequests ); @@ -173,18 +173,14 @@ contract DeliveryHelperTest is SetupTest { //////////////////////////////////// Fees //////////////////////////////////// - function depositFees(address appGateway_, Fees memory fees_) internal { - SocketContracts memory socketConfig = getSocketConfig(fees_.feePoolChain); - socketConfig.feesPlug.deposit{value: fees_.amount}( - fees_.feePoolToken, - appGateway_, - fees_.amount - ); + function depositFees(address appGateway_, OnChainFees memory fees_) internal { + SocketContracts memory socketConfig = getSocketConfig(fees_.chainSlug); + socketConfig.feesPlug.deposit{value: fees_.amount}(fees_.token, appGateway_, fees_.amount); bytes memory bytesInput = abi.encode( - fees_.feePoolChain, + fees_.chainSlug, appGateway_, - fees_.feePoolToken, + fees_.token, fees_.amount ); @@ -193,9 +189,9 @@ contract DeliveryHelperTest is SetupTest { ); bytes memory sig = _createSignature(digest, watcherPrivateKey); feesManager.incrementFeesDeposited( - fees_.feePoolChain, + fees_.chainSlug, appGateway_, - fees_.feePoolToken, + fees_.token, fees_.amount, signatureNonce++, sig diff --git a/test/FeesTest.t.sol b/test/FeesTest.t.sol index ec2f9632..7116c8f4 100644 --- a/test/FeesTest.t.sol +++ b/test/FeesTest.t.sol @@ -19,8 +19,11 @@ contract FeesTest is DeliveryHelperTest { setUpDeliveryHelper(); feesConfig = getSocketConfig(feesChainSlug); - counterGateway = new CounterAppGateway(address(addressResolver), createFees(feesAmount)); - depositFees(address(counterGateway), createFees(depositAmount)); + counterGateway = new CounterAppGateway(address(addressResolver), feesAmount); + depositFees( + address(counterGateway), + OnChainFees({chainSlug: feesChainSlug, token: ETH_ADDRESS, amount: depositAmount}) + ); bytes32[] memory contractIds = new bytes32[](1); contractIds[0] = counterGateway.counter(); diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index 56d7e45f..cdd3baa7 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -26,7 +26,7 @@ contract TriggerTest is DeliveryHelperTest { counter = new Counter(); // Deploy the gateway with fees - gateway = new CounterAppGateway(address(addressResolver), createFees(feesAmount)); + gateway = new CounterAppGateway(address(addressResolver), feesAmount); gateway.setIsValidPlug(arbChainSlug, address(counter)); // Connect the counter to the gateway and socket diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 152c1729..d5d699c9 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -19,7 +19,7 @@ import {ContractFactoryPlug} from "../contracts/protocol/payload-delivery/Contra import {FeesPlug} from "../contracts/protocol/payload-delivery/FeesPlug.sol"; import {ETH_ADDRESS} from "../contracts/protocol/utils/common/Constants.sol"; -import {ResolvedPromises} from "../contracts/protocol/utils/common/Structs.sol"; +import {ResolvedPromises, OnChainFees} from "../contracts/protocol/utils/common/Structs.sol"; import "solady/utils/ERC1967Factory.sol"; @@ -283,10 +283,6 @@ contract SetupTest is Test { return chainSlug_ == arbChainSlug ? arbConfig : optConfig; } - function createFees(uint256 maxFees_) internal view returns (Fees memory) { - return Fees({feePoolChain: arbChainSlug, feePoolToken: ETH_ADDRESS, amount: maxFees_}); - } - function _generateWatcherProof( PayloadParams memory params_ ) internal view returns (bytes memory, bytes32) { @@ -403,4 +399,12 @@ contract SetupTest is Test { mstore(add(sig, 64), sigS) } } + + function _createOnChainFees( + uint32 chainSlug_, + address token_, + uint256 amount_ + ) internal returns (OnChainFees memory) { + return OnChainFees({chainSlug: chainSlug_, token: token_, amount: amount_}); + } } diff --git a/test/apps/Counter.t.sol b/test/apps/Counter.t.sol index 243abcfc..92079168 100644 --- a/test/apps/Counter.t.sol +++ b/test/apps/Counter.t.sol @@ -16,8 +16,11 @@ contract CounterTest is DeliveryHelperTest { function deploySetup() internal { setUpDeliveryHelper(); - counterGateway = new CounterAppGateway(address(addressResolver), createFees(feesAmount)); - depositFees(address(counterGateway), createFees(1 ether)); + counterGateway = new CounterAppGateway(address(addressResolver), feesAmount); + depositFees( + address(counterGateway), + OnChainFees({chainSlug: arbChainSlug, token: ETH_ADDRESS, amount: 1 ether}) + ); counterId = counterGateway.counter(); contractIds[0] = counterId; diff --git a/test/apps/ParallelCounter.t.sol b/test/apps/ParallelCounter.t.sol index fdaa8ac4..6cfc28f3 100644 --- a/test/apps/ParallelCounter.t.sol +++ b/test/apps/ParallelCounter.t.sol @@ -16,11 +16,11 @@ contract ParallelCounterTest is DeliveryHelperTest { function deploySetup() internal { setUpDeliveryHelper(); - parallelCounterGateway = new CounterAppGateway( - address(addressResolver), - createFees(feesAmount) + parallelCounterGateway = new CounterAppGateway(address(addressResolver), feesAmount); + depositFees( + address(parallelCounterGateway), + OnChainFees({chainSlug: arbChainSlug, token: ETH_ADDRESS, amount: 1 ether}) ); - depositFees(address(parallelCounterGateway), createFees(1 ether)); counterId1 = parallelCounterGateway.counter1(); counterId2 = parallelCounterGateway.counter(); contractIds[0] = counterId1; diff --git a/test/apps/SuperToken.t.sol b/test/apps/SuperToken.t.sol index f33ca720..97eadc43 100644 --- a/test/apps/SuperToken.t.sol +++ b/test/apps/SuperToken.t.sol @@ -65,7 +65,7 @@ contract SuperTokenTest is DeliveryHelperTest { SuperTokenAppGateway superTokenApp = new SuperTokenAppGateway( address(addressResolver), owner, - createFees(maxFees), + maxFees, SuperTokenAppGateway.ConstructorParams({ name_: "SUPER TOKEN", symbol_: "SUPER", @@ -76,7 +76,10 @@ contract SuperTokenTest is DeliveryHelperTest { ); // Enable app gateways to do all operations in the Watcher: Read, Write and Schedule on EVMx // Watcher sets the limits for apps in this SOCKET protocol version - depositFees(address(superTokenApp), createFees(1 ether)); + depositFees( + address(superTokenApp), + OnChainFees({chainSlug: arbChainSlug, token: ETH_ADDRESS, amount: 1 ether}) + ); appContracts = AppContracts({ superTokenApp: superTokenApp, diff --git a/test/apps/app-gateways/counter/CounterAppGateway.sol b/test/apps/app-gateways/counter/CounterAppGateway.sol index a7b9561b..dc232047 100644 --- a/test/apps/app-gateways/counter/CounterAppGateway.sol +++ b/test/apps/app-gateways/counter/CounterAppGateway.sol @@ -25,7 +25,7 @@ contract CounterAppGateway is AppGatewayBase, Ownable { } // deploy contracts - function deployContracts(uint32 chainSlug_) external async { + function deployContracts(uint32 chainSlug_) external async(address(this)) { _deploy(counter, chainSlug_, IsPlug.YES); } @@ -33,14 +33,14 @@ contract CounterAppGateway is AppGatewayBase, Ownable { _deploy(counter, chainSlug_, IsPlug.YES); } - function deployParallelContracts(uint32 chainSlug_) external async { + function deployParallelContracts(uint32 chainSlug_) external async(address(this)) { _setOverrides(Parallel.ON); _deploy(counter, chainSlug_, IsPlug.YES); _deploy(counter1, chainSlug_, IsPlug.YES); _setOverrides(Parallel.OFF); } - function deployMultiChainContracts(uint32[] memory chainSlugs_) external async { + function deployMultiChainContracts(uint32[] memory chainSlugs_) external async(address(this)) { _setOverrides(Parallel.ON); for (uint32 i = 0; i < chainSlugs_.length; i++) { _deploy(counter, chainSlugs_[i], IsPlug.YES); @@ -53,7 +53,7 @@ contract CounterAppGateway is AppGatewayBase, Ownable { return; } - function incrementCounters(address[] memory instances_) public async { + function incrementCounters(address[] memory instances_) public async(address(this)) { // the increase function is called on given list of instances // this for (uint256 i = 0; i < instances_.length; i++) { @@ -69,7 +69,7 @@ contract CounterAppGateway is AppGatewayBase, Ownable { } } - function readCounters(address[] memory instances_) public async { + function readCounters(address[] memory instances_) public async(address(this)) { // the increase function is called on given list of instances _setOverrides(Read.ON, Parallel.ON); for (uint256 i = 0; i < instances_.length; i++) { @@ -80,7 +80,10 @@ contract CounterAppGateway is AppGatewayBase, Ownable { _setOverrides(Read.OFF, Parallel.OFF); } - function readCounterAtBlock(address instance_, uint256 blockNumber_) public async { + function readCounterAtBlock( + address instance_, + uint256 blockNumber_ + ) public async(address(this)) { uint32 chainSlug = IForwarder(instance_).getChainSlug(); _setOverrides(Read.ON, Parallel.ON, blockNumber_); ICounter(instance_).getCounter(); @@ -120,8 +123,8 @@ contract CounterAppGateway is AppGatewayBase, Ownable { } // UTILS - function setFees(uint256 fees_) public { - fees = fees_; + function setMaxFees(uint256 fees_) public { + maxFees = fees_; } function withdrawFeeTokens( @@ -133,12 +136,12 @@ contract CounterAppGateway is AppGatewayBase, Ownable { return _withdrawFeeTokens(chainSlug_, token_, amount_, receiver_); } - function testOnChainRevert(uint32 chainSlug) public async { + function testOnChainRevert(uint32 chainSlug) public async(address(this)) { address instance = forwarderAddresses[counter][chainSlug]; ICounter(instance).wrongFunction(); } - function testCallBackRevert(uint32 chainSlug) public async { + function testCallBackRevert(uint32 chainSlug) public async(address(this)) { // the increase function is called on given list of instances _setOverrides(Read.ON, Parallel.ON); address instance = forwarderAddresses[counter][chainSlug]; diff --git a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol index 58846bc6..c10045e0 100644 --- a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol +++ b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol @@ -60,7 +60,7 @@ contract SuperTokenAppGateway is AppGatewayBase, Ownable { return; } - function transfer(bytes memory order_) external async createFeePool { + function transfer(bytes memory order_) external async { TransferOrder memory order = abi.decode(order_, (TransferOrder)); ISuperToken(order.srcToken).burn(order.user, order.srcAmount); ISuperToken(order.dstToken).mint(order.user, order.srcAmount); From f525c117fa3a624ca5b86ad5be3c390e181d188a Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 21 Apr 2025 15:14:48 +0530 Subject: [PATCH 060/108] fix: test build --- contracts/interfaces/IWatcherPrecompileLimits.sol | 1 - .../protocol/payload-delivery/AuctionManager.sol | 14 +++++--------- .../protocol/payload-delivery/FeesManager.sol | 4 +++- .../payload-delivery/app-gateway/RequestQueue.sol | 2 +- .../watcherPrecompile/WatcherPrecompileLimits.sol | 4 ++-- .../watcherPrecompile/core/RequestHandler.sol | 3 +-- .../core/WatcherPrecompileCore.sol | 7 ++++++- 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/contracts/interfaces/IWatcherPrecompileLimits.sol b/contracts/interfaces/IWatcherPrecompileLimits.sol index 394a8cdc..2bede009 100644 --- a/contracts/interfaces/IWatcherPrecompileLimits.sol +++ b/contracts/interfaces/IWatcherPrecompileLimits.sol @@ -47,7 +47,6 @@ interface IWatcherPrecompileLimits { /// @param limitType_ The type of limit to consume /// @param consumeLimit_ The amount of limit to consume function consumeLimit( - uint40 requestCount_, address appGateway_, bytes32 limitType_, uint256 consumeLimit_ diff --git a/contracts/protocol/payload-delivery/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol index 632a3d15..8e51ba64 100644 --- a/contracts/protocol/payload-delivery/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -105,7 +105,6 @@ contract AuctionManager is ) external { if (auctionClosed[requestCount_]) revert AuctionClosed(); - RequestMetadata memory requestMetadata = _getRequestMetadata(requestCount_); // check if the transmitter is valid address transmitter = _recoverSigner( keccak256(abi.encode(address(this), evmxSlug, requestCount_, bidFees, extraData)), @@ -113,10 +112,9 @@ contract AuctionManager is ); if (!_hasRole(TRANSMITTER_ROLE, transmitter)) revert InvalidTransmitter(); - (uint256 watcherFees, uint256 transmitterCredits) = getTransmitterMaxFeesRequired( - requestCount_ - // check if the bid exceeds the max fees quoted by app gateway subtracting the watcher fees - ); + uint256 transmitterCredits = getTransmitterMaxFeesRequired(requestCount_); + + // check if the bid exceeds the max fees quoted by app gateway subtracting the watcher fees if (bidFees > transmitterCredits) revert BidExceedsMaxFees(); // check if the bid is lower than the existing bid @@ -210,17 +208,15 @@ contract AuctionManager is function getTransmitterMaxFeesRequired( uint40 requestCount_ - ) public view returns (uint256, uint256) { + ) public view returns (uint256) { RequestMetadata memory requestMetadata = _getRequestMetadata(requestCount_); // check if the bid is for this auction manager if (requestMetadata.auctionManager != address(this)) revert InvalidBid(); - // get the request metadata - // get the total fees required for the watcher precompile ops uint256 watcherFees = watcherPrecompileLimits().getTotalFeesRequired(requestCount_); - return (watcherFees, requestMetadata.maxFees - watcherFees); + return requestMetadata.maxFees - watcherFees; } function _recoverSigner( diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 260c4b52..99f59885 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -191,7 +191,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol function _processFeeApprovalData( bytes memory feeApprovalData_ - ) internal view returns (address consumeFrom, address appGateway, bool isApproved) { + ) internal returns (address consumeFrom, address appGateway, bool isApproved) { bytes memory signature_; (consumeFrom, appGateway, isApproved, signature_) = abi.decode( feeApprovalData_, @@ -228,11 +228,13 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol isAppGatewayWhitelisted[msg.sender][appGateways_[i]] = true; } } + modifier onlyAuctionManager(uint40 requestCount_) { if (msg.sender != deliveryHelper__().getRequestMetadata(requestCount_).auctionManager) revert NotAuctionManager(); _; } + /// @notice Blocks fees for a request count /// @param consumeFrom_ The fees payer address /// @param transmitterCredits_ The total fees to block diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index dac1a41d..f88ac9cd 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -37,7 +37,7 @@ abstract contract RequestQueue is DeliveryUtils { address appGateway_, bytes memory feesApprovalData_, uint256 maxFees_ - ) internal view returns (address consumeFrom) { + ) internal returns (address consumeFrom) { if (queuePayloadParams.length > REQUEST_PAYLOAD_COUNT_LIMIT) revert RequestPayloadCountLimitExceeded(); (consumeFrom, , ) = IFeesManager(addressResolver__.feesManager()).setAppGatewayWhitelist( diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index 89e99779..f933b29a 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -57,6 +57,7 @@ contract WatcherPrecompileLimits is event DefaultLimitAndRatePerSecondSet(uint256 defaultLimit, uint256 defaultRatePerSecond); error WatcherFeesNotSet(bytes32 limitType); + /// @notice Initial initialization (version 1) function initialize( address owner_, @@ -130,7 +131,6 @@ contract WatcherPrecompileLimits is * @param consumeLimit_ The amount of limit to consume */ function consumeLimit( - uint40 requestCount_, address appGateway_, bytes32 limitType_, uint256 consumeLimit_ @@ -155,7 +155,7 @@ contract WatcherPrecompileLimits is } // Update the limit - precompileCount[limitType_][requestCount_] += consumeLimit_; + // precompileCount[limitType_][requestCount_] += consumeLimit_; _consumeFullLimit(consumeLimit_ * 10 ** limitDecimals, limitParams); } diff --git a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol index f89e36e5..c0508f71 100644 --- a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol +++ b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol @@ -15,13 +15,12 @@ abstract contract RequestHandler is WatcherPrecompileCore { /// @notice Submits a batch of payload requests from middleware /// @param payloadSubmitParams_ Array of payload submit parameters - /// @param requestMetadata_ Request metadata /// @return requestCount The unique identifier for the submitted request /// @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_, - RequestMetadata calldata requestMetadata_ + RequestMetadata calldata ) public returns (uint40 requestCount) { address appGateway = _checkAppGateways(payloadSubmitParams_); diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index da6c919d..2e0a6d5a 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -7,6 +7,7 @@ import {AccessControl} from "../../utils/AccessControl.sol"; import "solady/utils/Initializable.sol"; import {AddressResolverUtil} from "../../utils/AddressResolverUtil.sol"; import {IFeesManager} from "../../../interfaces/IFeesManager.sol"; + /// @title WatcherPrecompileCore /// @notice Core functionality for the WatcherPrecompile system /// @dev This contract implements the core functionality for payload verification, execution, and app configurations @@ -50,7 +51,11 @@ abstract contract WatcherPrecompileCore is ); // consumes limit for SCHEDULE precompile - watcherPrecompileLimits__.consumeLimit(_getCoreAppGateway(msg.sender), SCHEDULE, 1); + watcherPrecompileLimits__.consumeLimit( + _getCoreAppGateway(msg.sender), + SCHEDULE, + 1 + ); // emits event for watcher to track timeout and resolve when timeout is reached emit TimeoutRequested(timeoutId, msg.sender, payload_, executeAt); From c38aeeb768926825c295815123d9f6168b615bd1 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 21 Apr 2025 15:22:23 +0530 Subject: [PATCH 061/108] fix: script and test build --- script/counter/SetFees.s.sol | 2 +- .../counter/WithdrawFeesArbitrumFeesPlug.s.sol | 6 +----- script/helpers/AppGatewayFeeBalance.s.sol | 17 +++++------------ script/helpers/PayFeesInArbitrumETH.s.sol | 2 +- test/DeliveryHelper.t.sol | 4 ++-- test/FeesTest.t.sol | 3 ++- test/SetupTest.t.sol | 2 +- .../app-gateways/counter/CounterAppGateway.sol | 16 ++++++++-------- .../super-token/SuperTokenAppGateway.sol | 4 ++-- 9 files changed, 23 insertions(+), 33 deletions(-) diff --git a/script/counter/SetFees.s.sol b/script/counter/SetFees.s.sol index e48d9a73..3f87f4ac 100644 --- a/script/counter/SetFees.s.sol +++ b/script/counter/SetFees.s.sol @@ -22,6 +22,6 @@ contract CounterSetFees is Script { console.log("Setting fees..."); // Setting fee payment on Arbitrum Sepolia uint256 fees = 0.00001 ether; - appGateway.setFees(fees); + // appGateway.setFees(fees); } } diff --git a/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol b/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol index 3a78e9cf..b2b50a25 100644 --- a/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol +++ b/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol @@ -17,11 +17,7 @@ contract WithdrawFees is Script { address appGatewayAddress = vm.envAddress("APP_GATEWAY"); CounterAppGateway appGateway = CounterAppGateway(appGatewayAddress); - uint256 availableFees = feesManager.getAvailableFees( - 421614, - appGatewayAddress, - ETH_ADDRESS - ); + uint256 availableFees = feesManager.getAvailableFees(appGatewayAddress); console.log("Available fees:", availableFees); if (availableFees > 0) { diff --git a/script/helpers/AppGatewayFeeBalance.s.sol b/script/helpers/AppGatewayFeeBalance.s.sol index a1faade9..d2ba38d8 100644 --- a/script/helpers/AppGatewayFeeBalance.s.sol +++ b/script/helpers/AppGatewayFeeBalance.s.sol @@ -11,21 +11,14 @@ contract CheckDepositedFees is Script { vm.createSelectFork(vm.envString("EVMX_RPC")); FeesManager feesManager = FeesManager(payable(vm.envAddress("FEES_MANAGER"))); address appGateway = vm.envAddress("APP_GATEWAY"); - uint32 chain = 421614; - address token = ETH_ADDRESS; - (uint256 deposited, uint256 blocked) = feesManager.userFeeBalances( - appGateway, - chain, - token - ); + + (uint256 totalCredits, uint256 blockedCredits) = feesManager.userCredits(appGateway); console.log("App Gateway:", appGateway); console.log("Fees Manager:", address(feesManager)); - console.logUint(chain); - console.log("Token:", token); - console.log("Deposited fees:", deposited); - console.log("Blocked fees:", blocked); + console.log("totalCredits fees:", totalCredits); + console.log("blockedCredits fees:", blockedCredits); - uint256 availableFees = feesManager.getAvailableFees(chain, appGateway, token); + uint256 availableFees = feesManager.getAvailableFees(appGateway); console.log("Available fees:", availableFees); } } diff --git a/script/helpers/PayFeesInArbitrumETH.s.sol b/script/helpers/PayFeesInArbitrumETH.s.sol index 6fbd7164..9bf56848 100644 --- a/script/helpers/PayFeesInArbitrumETH.s.sol +++ b/script/helpers/PayFeesInArbitrumETH.s.sol @@ -23,6 +23,6 @@ contract DepositFees is Script { console.log("App Gateway:", appGateway); console.log("Fees Plug:", address(feesPlug)); uint feesAmount = 0.001 ether; - feesPlug.deposit{value: feesAmount}(ETH_ADDRESS, appGateway, feesAmount); + feesPlug.depositToFeeAndNative(ETH_ADDRESS, feesAmount, appGateway); } } diff --git a/test/DeliveryHelper.t.sol b/test/DeliveryHelper.t.sol index 45877410..e915a59d 100644 --- a/test/DeliveryHelper.t.sol +++ b/test/DeliveryHelper.t.sol @@ -175,7 +175,7 @@ contract DeliveryHelperTest is SetupTest { function depositFees(address appGateway_, OnChainFees memory fees_) internal { SocketContracts memory socketConfig = getSocketConfig(fees_.chainSlug); - socketConfig.feesPlug.deposit{value: fees_.amount}(fees_.token, appGateway_, fees_.amount); + socketConfig.feesPlug.depositToFeeAndNative(fees_.token, fees_.amount, appGateway_); bytes memory bytesInput = abi.encode( fees_.chainSlug, @@ -189,8 +189,8 @@ contract DeliveryHelperTest is SetupTest { ); bytes memory sig = _createSignature(digest, watcherPrivateKey); feesManager.incrementFeesDeposited( - fees_.chainSlug, appGateway_, + fees_.chainSlug, fees_.token, fees_.amount, signatureNonce++, diff --git a/test/FeesTest.t.sol b/test/FeesTest.t.sol index 7116c8f4..4755cb15 100644 --- a/test/FeesTest.t.sol +++ b/test/FeesTest.t.sol @@ -51,7 +51,8 @@ contract FeesTest is DeliveryHelperTest { uint40 requestCount = feesManager.withdrawTransmitterFees( feesChainSlug, ETH_ADDRESS, - address(receiver) + address(receiver), + feesManager.transmitterCredits(transmitterEOA) ); uint40[] memory batches = watcherPrecompile.getBatches(requestCount); _finalizeBatch(batches[0], new bytes[](0), 0, false); diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index d5d699c9..69237ec1 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -404,7 +404,7 @@ contract SetupTest is Test { uint32 chainSlug_, address token_, uint256 amount_ - ) internal returns (OnChainFees memory) { + ) internal pure returns (OnChainFees memory) { return OnChainFees({chainSlug: chainSlug_, token: token_, amount: amount_}); } } diff --git a/test/apps/app-gateways/counter/CounterAppGateway.sol b/test/apps/app-gateways/counter/CounterAppGateway.sol index dc232047..eb5e6e7e 100644 --- a/test/apps/app-gateways/counter/CounterAppGateway.sol +++ b/test/apps/app-gateways/counter/CounterAppGateway.sol @@ -25,7 +25,7 @@ contract CounterAppGateway is AppGatewayBase, Ownable { } // deploy contracts - function deployContracts(uint32 chainSlug_) external async(address(this)) { + function deployContracts(uint32 chainSlug_) external async(bytes("")) { _deploy(counter, chainSlug_, IsPlug.YES); } @@ -33,14 +33,14 @@ contract CounterAppGateway is AppGatewayBase, Ownable { _deploy(counter, chainSlug_, IsPlug.YES); } - function deployParallelContracts(uint32 chainSlug_) external async(address(this)) { + function deployParallelContracts(uint32 chainSlug_) external async(bytes("")) { _setOverrides(Parallel.ON); _deploy(counter, chainSlug_, IsPlug.YES); _deploy(counter1, chainSlug_, IsPlug.YES); _setOverrides(Parallel.OFF); } - function deployMultiChainContracts(uint32[] memory chainSlugs_) external async(address(this)) { + function deployMultiChainContracts(uint32[] memory chainSlugs_) external async(bytes("")) { _setOverrides(Parallel.ON); for (uint32 i = 0; i < chainSlugs_.length; i++) { _deploy(counter, chainSlugs_[i], IsPlug.YES); @@ -53,7 +53,7 @@ contract CounterAppGateway is AppGatewayBase, Ownable { return; } - function incrementCounters(address[] memory instances_) public async(address(this)) { + function incrementCounters(address[] memory instances_) public async(bytes("")) { // the increase function is called on given list of instances // this for (uint256 i = 0; i < instances_.length; i++) { @@ -69,7 +69,7 @@ contract CounterAppGateway is AppGatewayBase, Ownable { } } - function readCounters(address[] memory instances_) public async(address(this)) { + function readCounters(address[] memory instances_) public async(bytes("")) { // the increase function is called on given list of instances _setOverrides(Read.ON, Parallel.ON); for (uint256 i = 0; i < instances_.length; i++) { @@ -83,7 +83,7 @@ contract CounterAppGateway is AppGatewayBase, Ownable { function readCounterAtBlock( address instance_, uint256 blockNumber_ - ) public async(address(this)) { + ) public async(bytes("")) { uint32 chainSlug = IForwarder(instance_).getChainSlug(); _setOverrides(Read.ON, Parallel.ON, blockNumber_); ICounter(instance_).getCounter(); @@ -136,12 +136,12 @@ contract CounterAppGateway is AppGatewayBase, Ownable { return _withdrawFeeTokens(chainSlug_, token_, amount_, receiver_); } - function testOnChainRevert(uint32 chainSlug) public async(address(this)) { + function testOnChainRevert(uint32 chainSlug) public async(bytes("")) { address instance = forwarderAddresses[counter][chainSlug]; ICounter(instance).wrongFunction(); } - function testCallBackRevert(uint32 chainSlug) public async(address(this)) { + function testCallBackRevert(uint32 chainSlug) public async(bytes("")) { // the increase function is called on given list of instances _setOverrides(Read.ON, Parallel.ON); address instance = forwarderAddresses[counter][chainSlug]; diff --git a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol index c10045e0..8e38065d 100644 --- a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol +++ b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol @@ -49,7 +49,7 @@ contract SuperTokenAppGateway is AppGatewayBase, Ownable { _initializeOwner(owner_); } - function deployContracts(uint32 chainSlug_) external async { + function deployContracts(uint32 chainSlug_) external async(bytes("")) { bytes memory initData = abi.encodeWithSelector(SuperToken.setOwner.selector, owner()); _deploy(superToken, chainSlug_, IsPlug.YES, initData); } @@ -60,7 +60,7 @@ contract SuperTokenAppGateway is AppGatewayBase, Ownable { return; } - function transfer(bytes memory order_) external async { + function transfer(bytes memory order_) external async(bytes("")) { TransferOrder memory order = abi.decode(order_, (TransferOrder)); ISuperToken(order.srcToken).burn(order.user, order.srcAmount); ISuperToken(order.dstToken).mint(order.user, order.srcAmount); From 0ebb7339ffa4d36c80a6907c64740b6dbb706e01 Mon Sep 17 00:00:00 2001 From: Akash Date: Mon, 21 Apr 2025 16:22:13 +0530 Subject: [PATCH 062/108] fix: added refund option on execute failure --- contracts/interfaces/ISocketBatcher.sol | 3 ++- contracts/protocol/socket/Socket.sol | 3 +++ contracts/protocol/socket/SocketBatcher.sol | 6 ++++-- contracts/protocol/socket/SocketFeeManager.sol | 5 +---- contracts/protocol/utils/common/Structs.sol | 3 ++- test/SetupTest.t.sol | 16 +++++++++++++--- test/SocketFeeManager.t.sol | 3 ++- 7 files changed, 27 insertions(+), 12 deletions(-) diff --git a/contracts/interfaces/ISocketBatcher.sol b/contracts/interfaces/ISocketBatcher.sol index ab753866..ee1e7295 100644 --- a/contracts/interfaces/ISocketBatcher.sol +++ b/contracts/interfaces/ISocketBatcher.sol @@ -8,6 +8,7 @@ interface ISocketBatcher { ExecuteParams calldata executeParams_, bytes32 digest_, bytes calldata proof_, - bytes calldata transmitterSignature_ + bytes calldata transmitterSignature_, + address refundAddress_ ) external payable returns (bytes memory); } diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 8369e051..22106450 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -117,6 +117,9 @@ contract Socket is SocketUtils { } } else { payloadExecuted[payloadId_] = ExecutionStatus.Reverted; + if (transmissionParams_.refundAddress != address(0)) { + transmissionParams_.refundAddress.call{value: msg.value}(""); + } emit ExecutionFailed(payloadId_, returnData); } diff --git a/contracts/protocol/socket/SocketBatcher.sol b/contracts/protocol/socket/SocketBatcher.sol index 187f012a..d3e8df53 100644 --- a/contracts/protocol/socket/SocketBatcher.sol +++ b/contracts/protocol/socket/SocketBatcher.sol @@ -30,7 +30,8 @@ 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 @@ -39,7 +40,8 @@ contract SocketBatcher is ISocketBatcher, Ownable { TransmissionParams({ transmitterSignature: transmitterSignature_, socketFees: 0, - extraData: "" + extraData: "", + refundAddress: refundAddress_ }) ); } diff --git a/contracts/protocol/socket/SocketFeeManager.sol b/contracts/protocol/socket/SocketFeeManager.sol index adc22c84..1bfb66f4 100644 --- a/contracts/protocol/socket/SocketFeeManager.sol +++ b/contracts/protocol/socket/SocketFeeManager.sol @@ -35,10 +35,7 @@ contract SocketFeeManager is ISocketFeeManager, AccessControl { /** * @notice Pays and validates fees for execution */ - function payAndCheckFees( - ExecuteParams memory, - TransmissionParams memory - ) external payable { + function payAndCheckFees(ExecuteParams memory, TransmissionParams memory) external payable { if (msg.value < socketFees) revert InsufficientFees(); } diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 9c45e437..9368691d 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -241,9 +241,10 @@ struct ExecuteParams { } struct TransmissionParams { - bytes transmitterSignature; uint256 socketFees; + address refundAddress; bytes extraData; + bytes transmitterSignature; } struct PayloadIdParams { diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index f2a22f14..c27db57b 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -268,10 +268,18 @@ contract SetupTest is Test { ExecuteParams memory params, SocketBatcher socketBatcher, , - bytes memory transmitterSig + bytes memory transmitterSig, + address refundAddress ) = _getExecuteParams(payloadParams); - return socketBatcher.attestAndExecute(params, digest, watcherProof, transmitterSig); + return + socketBatcher.attestAndExecute( + params, + digest, + watcherProof, + transmitterSig, + refundAddress + ); } function resolvePromises(bytes32[] memory payloadIds, bytes[] memory returnData) internal { @@ -338,7 +346,8 @@ contract SetupTest is Test { ExecuteParams memory params, SocketBatcher socketBatcher, uint256 value, - bytes memory transmitterSig + bytes memory transmitterSig, + address refundAddress ) { SocketContracts memory socketConfig = getSocketConfig(payloadParams.dump.getChainSlug()); @@ -365,6 +374,7 @@ contract SetupTest is Test { value = payloadParams.value; socketBatcher = socketConfig.socketBatcher; + refundAddress = transmitterEOA; } function _resolvePromise(bytes32 payloadId, bytes memory returnData) internal { diff --git a/test/SocketFeeManager.t.sol b/test/SocketFeeManager.t.sol index dd0cd7c4..8742fff5 100644 --- a/test/SocketFeeManager.t.sol +++ b/test/SocketFeeManager.t.sol @@ -144,7 +144,8 @@ contract SocketFeeManagerTest is SetupTest { TransmissionParams memory transmissionParams = TransmissionParams({ transmitterSignature: bytes(""), socketFees: socketFees, - extraData: bytes("") + extraData: bytes(""), + refundAddress: transmitterEOA }); return (executeParams, transmissionParams); From 724374305af5e94413c120fbe05b407f5d723564 Mon Sep 17 00:00:00 2001 From: Akash Date: Mon, 21 Apr 2025 16:27:09 +0530 Subject: [PATCH 063/108] fix : use safetransfer for eth --- contracts/protocol/socket/Socket.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 22106450..f6aee5d2 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -118,7 +118,7 @@ contract Socket is SocketUtils { } else { payloadExecuted[payloadId_] = ExecutionStatus.Reverted; if (transmissionParams_.refundAddress != address(0)) { - transmissionParams_.refundAddress.call{value: msg.value}(""); + SafeTransferLib.forceSafeTransferETH(transmissionParams_.refundAddress, msg.value); } emit ExecutionFailed(payloadId_, returnData); } From a235fccd1dbc9caadff01fbb2fe2f0b4cf2e0342 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 21 Apr 2025 16:43:37 +0530 Subject: [PATCH 064/108] fix: build --- contracts/protocol/payload-delivery/ContractFactoryPlug.sol | 2 +- .../protocol/watcherPrecompile/WatcherPrecompileConfig.sol | 2 +- .../protocol/watcherPrecompile/core/WatcherPrecompileCore.sol | 2 +- .../watcherPrecompile/{ => core}/WatcherPrecompileUtils.sol | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename contracts/protocol/watcherPrecompile/{ => core}/WatcherPrecompileUtils.sol (100%) diff --git a/contracts/protocol/payload-delivery/ContractFactoryPlug.sol b/contracts/protocol/payload-delivery/ContractFactoryPlug.sol index 11a50c62..3ef28ada 100644 --- a/contracts/protocol/payload-delivery/ContractFactoryPlug.sol +++ b/contracts/protocol/payload-delivery/ContractFactoryPlug.sol @@ -32,7 +32,7 @@ contract ContractFactoryPlug is PlugBase, AccessControl, IContractFactoryPlug { /// @notice Deploys a contract /// @param isPlug_ Whether the contract to be deployed is a plug /// @param salt_ The salt used for create 2 - /// @param appGateway_ The app gateway address + /// @param appGatewayId_ The app gateway id /// @param switchboard_ The switchboard address /// @param creationCode_ The creation code /// @param initCallData_ The init call data diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index 7972139a..d8ad1797 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -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 "./WatcherPrecompileUtils.sol"; +import "./core/WatcherPrecompileUtils.sol"; /// @title WatcherPrecompileConfig /// @notice Configuration contract for the Watcher Precompile system diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index e83f51cd..a2e201e5 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -5,7 +5,7 @@ import "./WatcherPrecompileStorage.sol"; import {ECDSA} from "solady/utils/ECDSA.sol"; import {AccessControl} from "../../utils/AccessControl.sol"; import "solady/utils/Initializable.sol"; -import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; +import {AddressResolverUtil} from "../../utils/AddressResolverUtil.sol"; import "./WatcherPrecompileUtils.sol"; /// @title WatcherPrecompileCore diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileUtils.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileUtils.sol similarity index 100% rename from contracts/protocol/watcherPrecompile/WatcherPrecompileUtils.sol rename to contracts/protocol/watcherPrecompile/core/WatcherPrecompileUtils.sol From 45c3f83f6559845d1df35771e89f9e25184792fa Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 21 Apr 2025 16:45:45 +0530 Subject: [PATCH 065/108] fix: tests --- test/Inbox.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index d1763190..1361cb9f 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -62,7 +62,7 @@ contract TriggerTest is DeliveryHelperTest { // Simulate a message from another chain through the watcher uint256 incrementValue = 5; - bytes32 triggerId = _encodeTriggerId(address(gateway), arbChainSlug); + bytes32 triggerId = _encodeTriggerId(address(arbConfig.socket), arbChainSlug); bytes memory payload = abi.encodeWithSelector( CounterAppGateway.increase.selector, incrementValue From 2788689d5605125876f44e90d14cc8e7e98ad5dd Mon Sep 17 00:00:00 2001 From: Akash Date: Mon, 21 Apr 2025 20:57:30 +0530 Subject: [PATCH 066/108] fix: default refundAddress --- contracts/protocol/socket/Socket.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index f6aee5d2..8cd42ed0 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -117,9 +117,9 @@ contract Socket is SocketUtils { } } else { payloadExecuted[payloadId_] = ExecutionStatus.Reverted; - if (transmissionParams_.refundAddress != address(0)) { - SafeTransferLib.forceSafeTransferETH(transmissionParams_.refundAddress, msg.value); - } + address receiver = transmissionParams_.refundAddress == address(0) ? msg.sender : transmissionParams_.refundAddress; + SafeTransferLib.forceSafeTransferETH(receiver, msg.value); + emit ExecutionFailed(payloadId_, returnData); } From 8c48ba3697e6ba8d503fa8a842da46a19f1713ca Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Mon, 21 Apr 2025 21:01:24 +0530 Subject: [PATCH 067/108] wip: consume limit --- contracts/protocol/utils/common/Structs.sol | 3 +++ .../WatcherPrecompileLimits.sol | 20 ++++++++----------- .../watcherPrecompile/core/RequestHandler.sol | 5 ++++- .../core/WatcherPrecompileCore.sol | 1 + 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 4dcffff9..118d772d 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -210,6 +210,9 @@ struct RequestParams { // updated while processing request uint256 currentBatchPayloadsLeft; uint256 payloadsRemaining; + uint256 queryCount; + uint256 finalizeCount; + uint256 scheduleCount; address middleware; // updated after auction address transmitter; diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index f933b29a..f0f2921a 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -43,10 +43,6 @@ contract WatcherPrecompileLimits is // Mapping to track active app gateways mapping(address => bool) internal _activeAppGateways; - // slot 156: precompileCount - // limitType => requestCount => count - mapping(bytes32 => mapping(uint40 => uint256)) public precompileCount; - // slot 157: fees uint256 public queryFees; uint256 public finalizeFees; @@ -155,8 +151,6 @@ contract WatcherPrecompileLimits is } // Update the limit - // precompileCount[limitType_][requestCount_] += consumeLimit_; - _consumeFullLimit(consumeLimit_ * 10 ** limitDecimals, limitParams); } @@ -202,14 +196,16 @@ contract WatcherPrecompileLimits is revert WatcherFeesNotSet(CALLBACK); } - uint256 totalCallbacks = precompileCount[QUERY][requestCount_] + - precompileCount[FINALIZE][requestCount_] + - precompileCount[SCHEDULE][requestCount_]; + uint256 queryCount = watcherPrecompile__().requestParams[requestCount_].queryCount; + uint256 finalizeCount = watcherPrecompile__().requestParams[requestCount_].finalizeCount; + uint256 scheduleCount = watcherPrecompile__().requestParams[requestCount_].scheduleCount; + + uint256 totalCallbacks = queryCount + finalizeCount + scheduleCount; totalFees += totalCallbacks * callBackFees; - totalFees += precompileCount[QUERY][requestCount_] * queryFees; - totalFees += precompileCount[FINALIZE][requestCount_] * finalizeFees; - totalFees += precompileCount[SCHEDULE][requestCount_] * scheduleFees; + totalFees += queryCount * queryFees; + totalFees += finalizeCount * finalizeFees; + totalFees += scheduleCount * scheduleFees; return totalFees; } diff --git a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol index c0508f71..e6b32c08 100644 --- a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol +++ b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol @@ -20,7 +20,7 @@ abstract contract RequestHandler is WatcherPrecompileCore { /// @dev It also consumes limits for the app gateway based on the number of reads and writes function submitRequest( PayloadSubmitParams[] calldata payloadSubmitParams_, - RequestMetadata calldata + RequestMetadata calldata requestMetadata_ ) public returns (uint40 requestCount) { address appGateway = _checkAppGateways(payloadSubmitParams_); @@ -93,6 +93,9 @@ abstract contract RequestHandler is WatcherPrecompileCore { watcherPrecompileLimits__.consumeLimit(appGateway, QUERY, readCount); watcherPrecompileLimits__.consumeLimit(appGateway, FINALIZE, writeCount); + requestParams[requestCount].queryCount = readCount; + requestParams[requestCount].finalizeCount = writeCount; + requestParams[requestCount].currentBatch = currentBatch; requestParams[requestCount].payloadsRemaining = payloadSubmitParams_.length; requestParams[requestCount].middleware = msg.sender; diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 2e0a6d5a..53928cd8 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -56,6 +56,7 @@ abstract contract WatcherPrecompileCore is SCHEDULE, 1 ); + // emits event for watcher to track timeout and resolve when timeout is reached emit TimeoutRequested(timeoutId, msg.sender, payload_, executeAt); From 3d542f3a34fc1949772befd370c47bae6830acb5 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 22 Apr 2025 14:02:07 +0530 Subject: [PATCH 068/108] feat: sb disable and enable --- contracts/protocol/socket/Socket.sol | 3 +++ contracts/protocol/socket/SocketConfig.sol | 13 +++++++++++-- contracts/protocol/socket/SocketUtils.sol | 3 --- contracts/protocol/utils/common/AccessRoles.sol | 2 ++ 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 83a455b6..fd392474 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -12,6 +12,9 @@ import "./SocketUtils.sol"; contract Socket is SocketUtils { using LibCall for address; + // @notice mapping of payload id to execution status + mapping(bytes32 => ExecutionStatus) public payloadExecuted; + //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// //////////////////////////////////////////////////////// diff --git a/contracts/protocol/socket/SocketConfig.sol b/contracts/protocol/socket/SocketConfig.sol index 7035601e..a480966b 100644 --- a/contracts/protocol/socket/SocketConfig.sol +++ b/contracts/protocol/socket/SocketConfig.sol @@ -6,7 +6,7 @@ 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 {GOVERNANCE_ROLE, RESCUE_ROLE, SWITCHBOARD_DISABLER_ROLE} from "../utils/common/AccessRoles.sol"; import {PlugConfig, SwitchboardStatus, ExecutionStatus} from "../utils/common/Structs.sol"; import {PlugDisconnected, InvalidAppGateway, InvalidTransmitter} from "../utils/common/Errors.sol"; import {MAX_COPY_BYTES} from "../utils/common/Constants.sol"; @@ -40,6 +40,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); // @notice function to register a switchboard // @dev only callable by switchboards @@ -53,11 +55,18 @@ 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); + } + // @notice function to connect a plug to a socket // @dev only callable by plugs (msg.sender) // @param appGatewayId_ The app gateway id diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index 805de8d4..2b8c40f2 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -22,9 +22,6 @@ abstract contract SocketUtils is SocketConfig { // @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. diff --git a/contracts/protocol/utils/common/AccessRoles.sol b/contracts/protocol/utils/common/AccessRoles.sol index 13279b25..e1406a49 100644 --- a/contracts/protocol/utils/common/AccessRoles.sol +++ b/contracts/protocol/utils/common/AccessRoles.sol @@ -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"); From 914f4a3217060f5229458bcb3e589a4d73dd71d6 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 22 Apr 2025 15:16:55 +0530 Subject: [PATCH 069/108] fix: optimise trigger --- contracts/protocol/socket/Socket.sol | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index fd392474..fa33b93b 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -152,13 +152,11 @@ 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_ + bytes memory overrides_ ) internal returns (bytes32 triggerId) { PlugConfig memory plugConfig = _plugConfigs[plug_]; @@ -168,17 +166,18 @@ contract Socket is SocketUtils { // creates a unique ID for the message triggerId = _encodeTriggerId(); - emit AppGatewayCallRequested(triggerId, chainSlug, plug_, overrides_, payload_); + emit AppGatewayCallRequested(triggerId, chainSlug, 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) { + address plug = msg.sender; // gets the overrides from the plug - bytes memory overrides = IPlug(msg.sender).overrides(); + bytes memory overrides = IPlug(plug).overrides(); // return the trigger id - return abi.encode(_triggerAppGateway(msg.sender, overrides, msg.data)); + return abi.encode(_triggerAppGateway(plug, overrides)); } } From 3be12b23133b3811f4fc266805d6dfecd13d7c18 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 22 Apr 2025 15:17:54 +0530 Subject: [PATCH 070/108] fix: rename errors --- contracts/protocol/socket/Socket.sol | 4 ++-- contracts/protocol/socket/SocketConfig.sol | 2 +- contracts/protocol/utils/common/Errors.sol | 2 +- test/mock/MockSocket.sol | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index fa33b93b..39bec0d7 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -67,7 +67,7 @@ contract Socket is SocketUtils { 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(); // check if the message value is insufficient if (msg.value < executeParams_.value) revert InsufficientMsgValue(); @@ -162,7 +162,7 @@ contract Socket is SocketUtils { // 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(); diff --git a/contracts/protocol/socket/SocketConfig.sol b/contracts/protocol/socket/SocketConfig.sol index a480966b..c2638a4c 100644 --- a/contracts/protocol/socket/SocketConfig.sol +++ b/contracts/protocol/socket/SocketConfig.sol @@ -8,7 +8,7 @@ import {IPlug} from "../../interfaces/IPlug.sol"; import "../utils/AccessControl.sol"; import {GOVERNANCE_ROLE, RESCUE_ROLE, SWITCHBOARD_DISABLER_ROLE} from "../utils/common/AccessRoles.sol"; import {PlugConfig, SwitchboardStatus, ExecutionStatus} from "../utils/common/Structs.sol"; -import {PlugDisconnected, InvalidAppGateway, InvalidTransmitter} from "../utils/common/Errors.sol"; +import {PlugNotFound, InvalidAppGateway, InvalidTransmitter} from "../utils/common/Errors.sol"; import {MAX_COPY_BYTES} from "../utils/common/Constants.sol"; /** diff --git a/contracts/protocol/utils/common/Errors.sol b/contracts/protocol/utils/common/Errors.sol index e5261dc4..8848900b 100644 --- a/contracts/protocol/utils/common/Errors.sol +++ b/contracts/protocol/utils/common/Errors.sol @@ -10,7 +10,7 @@ error LimitReached(); error FeesAlreadyPaid(); error NotAuctionManager(); error CallFailed(); -error PlugDisconnected(); +error PlugNotFound(); error InvalidAppGateway(); error AppGatewayAlreadyCalled(); error InvalidInboxCaller(); diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index 5489ea08..1e9a4fac 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -import {PlugDisconnected, InvalidAppGateway} from "../../contracts/protocol/utils/common/Errors.sol"; +import {InvalidAppGateway} from "../../contracts/protocol/utils/common/Errors.sol"; import "../../contracts/interfaces/ISwitchboard.sol"; import "../../contracts/interfaces/ISocket.sol"; From c91ab9c5154049e71217ecf5c02ca6bf01fdd890 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 22 Apr 2025 15:21:55 +0530 Subject: [PATCH 071/108] fix: event --- contracts/interfaces/ISocket.sol | 4 ++-- contracts/protocol/socket/Socket.sol | 8 +++++++- test/Inbox.t.sol | 10 ++++++++-- test/mock/MockSocket.sol | 8 +++++++- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/contracts/interfaces/ISocket.sol b/contracts/interfaces/ISocket.sol index 042fb7cc..0bca51b2 100644 --- a/contracts/interfaces/ISocket.sol +++ b/contracts/interfaces/ISocket.sol @@ -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 diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 39bec0d7..1a59d606 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -166,7 +166,13 @@ contract Socket is SocketUtils { // creates a unique ID for the message triggerId = _encodeTriggerId(); - emit AppGatewayCallRequested(triggerId, chainSlug, plug_, overrides_, msg.data); + emit AppGatewayCallRequested( + triggerId, + plugConfig.switchboard, + plug_, + overrides_, + msg.data + ); } /// @notice Fallback function that forwards all calls to Socket's callAppGateway diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index 1dc5ce62..9406b0f5 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -12,7 +12,7 @@ contract TriggerTest is DeliveryHelperTest { event AppGatewayCallRequested( bytes32 triggerId, - uint32 chainSlug, + address switchboard, address plug, bytes overrides, bytes payload @@ -69,7 +69,13 @@ contract TriggerTest is DeliveryHelperTest { ); vm.expectEmit(true, true, true, true); - emit AppGatewayCallRequested(triggerId, arbChainSlug, address(counter), bytes(""), payload); + emit AppGatewayCallRequested( + triggerId, + address(arbConfig.switchboard), + address(counter), + bytes(""), + payload + ); counter.increaseOnGateway(incrementValue); TriggerParams[] memory params = new TriggerParams[](1); diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index 1e9a4fac..77e91790 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -94,7 +94,13 @@ contract MockSocket is ISocket { PlugConfig memory plugConfig = _plugConfigs[msg.sender]; // creates a unique ID for the message triggerId = _encodeTriggerId(plugConfig.appGatewayId); - emit AppGatewayCallRequested(triggerId, chainSlug, msg.sender, overrides, payload); + emit AppGatewayCallRequested( + triggerId, + address(plugConfig.switchboard__), + msg.sender, + overrides, + payload + ); } /** From 1cc000759736ea2f310863410e4986e2db3a9580 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 22 Apr 2025 16:01:43 +0530 Subject: [PATCH 072/108] fix: remove extra params --- contracts/protocol/socket/SocketUtils.sol | 2 -- contracts/protocol/utils/common/Structs.sol | 2 -- test/SetupTest.t.sol | 2 -- 3 files changed, 6 deletions(-) diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index 2b8c40f2..2b8920d9 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -55,11 +55,9 @@ abstract contract SocketUtils is SocketConfig { transmitter_, payloadId_, executeParams_.deadline, - executeParams_.callType, executeParams_.writeFinality, executeParams_.gasLimit, executeParams_.value, - executeParams_.readAt, executeParams_.payload, executeParams_.target, appGatewayId_, diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 547306b4..2928e607 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -223,11 +223,9 @@ struct RequestMetadata { struct ExecuteParams { uint256 deadline; - CallType callType; WriteFinality writeFinality; uint256 gasLimit; uint256 value; - uint256 readAt; bytes payload; address target; uint40 requestCount; diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 88cc2f4e..77bf40cf 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -347,11 +347,9 @@ contract SetupTest is Test { params = ExecuteParams({ deadline: payloadParams.deadline, - callType: payloadParams.dump.getCallType(), writeFinality: payloadParams.dump.getWriteFinality(), gasLimit: payloadParams.gasLimit, value: payloadParams.value, - readAt: payloadParams.readAt, payload: payloadParams.payload, target: payloadParams.target, requestCount: payloadParams.dump.getRequestCount(), From 33aeece239ce854a8f740447afc5f7fbe4d5b42c Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 22 Apr 2025 16:02:07 +0530 Subject: [PATCH 073/108] fix: emit and return call return details --- contracts/interfaces/ISocket.sol | 6 +++--- contracts/interfaces/ISocketBatcher.sol | 2 +- contracts/protocol/socket/Socket.sol | 19 ++++++++----------- contracts/protocol/socket/SocketBatcher.sol | 2 +- test/SetupTest.t.sol | 4 ++-- test/mock/MockSocket.sol | 4 ++-- 6 files changed, 17 insertions(+), 20 deletions(-) diff --git a/contracts/interfaces/ISocket.sol b/contracts/interfaces/ISocket.sol index 0bca51b2..057edbc8 100644 --- a/contracts/interfaces/ISocket.sol +++ b/contracts/interfaces/ISocket.sol @@ -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 @@ -54,7 +54,7 @@ interface ISocket { function execute( ExecuteParams memory executeParams_, bytes memory transmitterSignature_ - ) external payable returns (bytes memory); + ) external payable returns (bool, bool, bytes memory); /** * @notice sets the config specific to the plug diff --git a/contracts/interfaces/ISocketBatcher.sol b/contracts/interfaces/ISocketBatcher.sol index d2991f4e..b6ae52e1 100644 --- a/contracts/interfaces/ISocketBatcher.sol +++ b/contracts/interfaces/ISocketBatcher.sol @@ -21,5 +21,5 @@ interface ISocketBatcher { bytes32 digest_, bytes calldata proof_, bytes calldata transmitterSignature_ - ) external payable returns (bytes memory); + ) external payable returns (bool, bool, bytes memory); } diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 1a59d606..1961b8c3 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -61,7 +61,7 @@ contract Socket is SocketUtils { function execute( ExecuteParams memory executeParams_, bytes memory transmitterSignature_ - ) 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(); @@ -118,27 +118,24 @@ contract Socket is SocketUtils { function _execute( bytes32 payloadId_, ExecuteParams memory executeParams_ - ) internal returns (bytes memory) { + ) internal returns (bool, bool, 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_.gasLimit, - maxCopyBytes, - executeParams_.payload - ); + (bool success, bool exceededMaxCopy, bytes memory returnData) = executeParams_ + .target + .tryCall(msg.value, executeParams_.gasLimit, maxCopyBytes, executeParams_.payload); // if the execution failed, set the execution status to reverted if (!success) { payloadExecuted[payloadId_] = ExecutionStatus.Reverted; - emit ExecutionFailed(payloadId_, returnData); + emit ExecutionFailed(payloadId_, exceededMaxCopy, returnData); } else { - emit ExecutionSuccess(payloadId_, returnData); + emit ExecutionSuccess(payloadId_, exceededMaxCopy, returnData); } - return returnData; + return (success, exceededMaxCopy, returnData); } function _validateExecutionStatus(bytes32 payloadId_) internal { diff --git a/contracts/protocol/socket/SocketBatcher.sol b/contracts/protocol/socket/SocketBatcher.sol index 5bd605a4..a3ac5ba4 100644 --- a/contracts/protocol/socket/SocketBatcher.sol +++ b/contracts/protocol/socket/SocketBatcher.sol @@ -39,7 +39,7 @@ contract SocketBatcher is ISocketBatcher, Ownable { bytes32 digest_, bytes calldata proof_, bytes calldata transmitterSignature_ - ) external payable returns (bytes memory) { + ) external payable returns (bool, bool, bytes memory) { ISwitchboard(executeParams_.switchboard).attest(digest_, proof_); return socket__.execute{value: msg.value}(executeParams_, transmitterSignature_); } diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 77bf40cf..a5c8f1dd 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -244,7 +244,7 @@ contract SetupTest is Test { isLastPayload ); } else { - bytes memory returnData = _uploadProofAndExecute(payloadParams); + (, , bytes memory returnData) = _uploadProofAndExecute(payloadParams); _resolveAndExpectFinalizeRequested( payloadParams.payloadId, payloadParams, @@ -258,7 +258,7 @@ contract SetupTest is Test { function _uploadProofAndExecute( PayloadParams memory payloadParams - ) internal returns (bytes memory) { + ) internal returns (bool, bool, bytes memory) { (bytes memory watcherProof, bytes32 digest) = _generateWatcherProof(payloadParams); _writeProof(payloadParams.payloadId, watcherProof); diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index 77e91790..b2b5478a 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -109,7 +109,7 @@ contract MockSocket is ISocket { function execute( ExecuteParams memory executeParams_, bytes memory transmitterSignature_ - ) external payable override returns (bytes memory) { + ) external payable override returns (bool, bool, bytes memory) { // execute payload // return // _execute( @@ -145,7 +145,7 @@ contract MockSocket is ISocket { bytes memory ) internal returns (bytes memory) { bytes memory returnData = hex"00010203"; - emit ExecutionSuccess(payloadId_, returnData); + emit ExecutionSuccess(payloadId_, false, returnData); return returnData; } From 8658b9546a8980ca01392065a25bfe2b748e4942 Mon Sep 17 00:00:00 2001 From: Akash Date: Tue, 22 Apr 2025 16:49:24 +0530 Subject: [PATCH 074/108] fix: fee tests --- contracts/base/AppGatewayBase.sol | 4 +- contracts/interfaces/IFeesManager.sol | 25 ++- contracts/interfaces/IFeesPlug.sol | 19 +-- contracts/interfaces/IMiddleware.sol | 2 +- contracts/interfaces/IWatcherPrecompile.sol | 3 +- .../interfaces/IWatcherPrecompileLimits.sol | 13 +- .../payload-delivery/AuctionManager.sol | 31 ++-- .../protocol/payload-delivery/FeesManager.sol | 117 +++++++------- .../protocol/payload-delivery/FeesPlug.sol | 82 +++------- .../app-gateway/DeliveryHelper.sol | 2 +- .../app-gateway/FeesHelpers.sol | 38 +++++ .../app-gateway/RequestQueue.sol | 147 +++++++++++------- contracts/protocol/utils/common/Structs.sol | 19 +++ .../WatcherPrecompileConfig.sol | 6 +- .../WatcherPrecompileLimits.sol | 34 ++-- .../watcherPrecompile/core/RequestHandler.sol | 5 +- .../core/WatcherPrecompileCore.sol | 15 +- script/helpers/PayFeesInArbitrumETH.s.sol | 2 +- test/DeliveryHelper.t.sol | 48 +++++- test/FeesTest.t.sol | 53 ++++--- test/SetupTest.t.sol | 15 +- test/apps/Counter.t.sol | 8 +- test/apps/ParallelCounter.t.sol | 8 +- test/apps/SuperToken.t.sol | 8 +- test/apps/app-gateways/USDC.sol | 41 +++++ .../counter/CounterAppGateway.sol | 7 +- .../super-token/SuperTokenAppGateway.sol | 2 +- 27 files changed, 455 insertions(+), 299 deletions(-) create mode 100644 test/apps/app-gateways/USDC.sol diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index 3f252074..572ee516 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -34,7 +34,9 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { function _postAsync(bytes memory feesApprovalData_) internal { isAsyncModifierSet = false; - + if (feesApprovalData_.length == 0) { + feesApprovalData_ = abi.encode(address(this), address(this), true, new bytes(0)); + } // todo: cache the feesApprovalData for next async in same request deliveryHelper__().batch(maxFees, auctionManager, feesApprovalData_, onCompleteData); _markValidPromises(); diff --git a/contracts/interfaces/IFeesManager.sol b/contracts/interfaces/IFeesManager.sol index 71b0eb70..7aa27314 100644 --- a/contracts/interfaces/IFeesManager.sol +++ b/contracts/interfaces/IFeesManager.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -import {Bid, QueuePayloadParams} from "../protocol/utils/common/Structs.sol"; +import {Bid, QueuePayloadParams, PayloadSubmitParams, AppGatewayWhitelistParams} from "../protocol/utils/common/Structs.sol"; interface IFeesManager { function blockFees( @@ -35,7 +35,28 @@ interface IFeesManager { function assignWatcherPrecompileFeesFromAddress(uint256 fees_, address consumeFrom_) external; - function setAppGatewayWhitelist( + function incrementFeesDeposited( + address depositTo_, + uint32 chainSlug_, + address token_, + uint256 amount_, + uint256 signatureNonce_, + bytes memory signature_ + ) external; + + function whitelistAppGatewayWithSignature( bytes memory feeApprovalData_ ) external returns (address consumeFrom, address appGateway, bool isApproved); + + function whitelistAppGateways(AppGatewayWhitelistParams[] calldata params_) external; + + function getWithdrawTransmitterFeesPayloadParams( + address transmitter_, + uint32 chainSlug_, + address token_, + address receiver_, + uint256 amount_ + ) external returns (PayloadSubmitParams[] memory); + + function getMaxFeesAvailableForWithdraw(address transmitter_) external view returns (uint256); } diff --git a/contracts/interfaces/IFeesPlug.sol b/contracts/interfaces/IFeesPlug.sol index edcd1aef..46797d62 100644 --- a/contracts/interfaces/IFeesPlug.sol +++ b/contracts/interfaces/IFeesPlug.sol @@ -2,22 +2,11 @@ pragma solidity ^0.8.21; interface IFeesPlug { - function balanceOf(address token_) external view returns (uint256); + function depositToFee(address token_, address receiver_, uint256 amount_) external; - function feesRedeemed(bytes32 feesId_) external view returns (bool); + function depositToFeeAndNative(address token_, address receiver_, uint256 amount_) external; - function depositToFee(address token_, uint256 amount_, address receiver_) external; + function depositToNative(address token_, address receiver_, uint256 amount_) external; - function depositToFeeAndNative(address token_, uint256 amount_, address receiver_) external; - - function depositToNative(address token_, uint256 amount_, address receiver_) external; - - function distributeFee( - address feeToken_, - uint256 fee_, - address transmitter_, - bytes32 feesId_ - ) external; - - function withdrawFees(address token_, uint256 amount_, address receiver_) external; + function withdrawFees(address token_, address receiver_, uint256 amount_) external; } diff --git a/contracts/interfaces/IMiddleware.sol b/contracts/interfaces/IMiddleware.sol index d8714e91..00099830 100644 --- a/contracts/interfaces/IMiddleware.sol +++ b/contracts/interfaces/IMiddleware.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -import {PayloadSubmitParams, QueuePayloadParams, Bid, WriteFinality, CallType, Parallel, IsPlug, RequestMetadata} from "../protocol/utils/common/Structs.sol"; +import {PayloadSubmitParams, QueuePayloadParams, Bid, WriteFinality, BatchParams, CallType, Parallel, IsPlug, RequestMetadata} from "../protocol/utils/common/Structs.sol"; /// @title IMiddleware /// @notice Interface for the Middleware contract diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 54d7d08e..1265f677 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -174,8 +174,7 @@ interface IWatcherPrecompile { function setExpiryTime(uint256 expiryTime_) external; function submitRequest( - PayloadSubmitParams[] calldata payloadSubmitParams, - RequestMetadata memory requestMetadata + PayloadSubmitParams[] calldata payloadSubmitParams ) external returns (uint40 requestCount); function startProcessingRequest(uint40 requestCount, address transmitter) external; diff --git a/contracts/interfaces/IWatcherPrecompileLimits.sol b/contracts/interfaces/IWatcherPrecompileLimits.sol index 2bede009..9179706a 100644 --- a/contracts/interfaces/IWatcherPrecompileLimits.sol +++ b/contracts/interfaces/IWatcherPrecompileLimits.sol @@ -46,13 +46,14 @@ interface IWatcherPrecompileLimits { /// @param appGateway_ The app gateway address /// @param limitType_ The type of limit to consume /// @param consumeLimit_ The amount of limit to consume - function consumeLimit( - address appGateway_, - bytes32 limitType_, - uint256 consumeLimit_ - ) external; + function consumeLimit(address appGateway_, bytes32 limitType_, uint256 consumeLimit_) external; - function getTotalFeesRequired(uint40 requestCount_) external view returns (uint256); + function getTotalFeesRequired( + uint256 queryCount_, + uint256 finalizeCount_, + uint256 scheduleCount_, + uint256 callbackCount_ + ) external view returns (uint256); function queryFees() external view returns (uint256); function finalizeFees() external view returns (uint256); diff --git a/contracts/protocol/payload-delivery/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol index 8e51ba64..818e28b3 100644 --- a/contracts/protocol/payload-delivery/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -112,7 +112,7 @@ contract AuctionManager is ); if (!_hasRole(TRANSMITTER_ROLE, transmitter)) revert InvalidTransmitter(); - uint256 transmitterCredits = getTransmitterMaxFeesRequired(requestCount_); + uint256 transmitterCredits = getTransmitterMaxFeesAvailable(requestCount_); // check if the bid exceeds the max fees quoted by app gateway subtracting the watcher fees if (bidFees > transmitterCredits) revert BidExceedsMaxFees(); @@ -143,6 +143,22 @@ contract AuctionManager is emit BidPlaced(requestCount_, newBid); } + function getTransmitterMaxFeesAvailable(uint40 requestCount_) public view returns (uint256) { + RequestMetadata memory requestMetadata = _getRequestMetadata(requestCount_); + + // check if the bid is for this auction manager + if (requestMetadata.auctionManager != address(this)) revert InvalidBid(); + + // get the total fees required for the watcher precompile ops + uint256 watcherFees = watcherPrecompileLimits().getTotalFeesRequired( + requestMetadata.queryCount, + requestMetadata.finalizeCount, + 0, + 0 + ); + return requestMetadata.maxFees - watcherFees; + } + /// @notice Ends an auction /// @param requestCount_ The ID of the auction function endAuction(uint40 requestCount_) external onlyWatcherPrecompile { @@ -206,19 +222,6 @@ contract AuctionManager is emit AuctionStarted(requestCount_); } - function getTransmitterMaxFeesRequired( - uint40 requestCount_ - ) public view returns (uint256) { - RequestMetadata memory requestMetadata = _getRequestMetadata(requestCount_); - - // check if the bid is for this auction manager - if (requestMetadata.auctionManager != address(this)) revert InvalidBid(); - - // get the total fees required for the watcher precompile ops - uint256 watcherFees = watcherPrecompileLimits().getTotalFeesRequired(requestCount_); - return requestMetadata.maxFees - watcherFees; - } - function _recoverSigner( bytes32 digest_, bytes memory signature_ diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 99f59885..4f13a64e 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -6,12 +6,12 @@ import "solady/utils/Initializable.sol"; import "solady/utils/ECDSA.sol"; import {IFeesPlug} from "../../interfaces/IFeesPlug.sol"; -import {IFeesManager} from "../../interfaces/IFeesManager.sol"; +import "../../interfaces/IFeesManager.sol"; import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; import {NotAuctionManager, InvalidWatcherSignature, NonceUsed} from "../utils/common/Errors.sol"; import {Bid, CallType, Parallel, WriteFinality, QueuePayloadParams, IsPlug, PayloadSubmitParams, RequestMetadata, UserCredits} from "../utils/common/Structs.sol"; - +import {console} from "forge-std/console.sol"; abstract contract FeesManagerStorage is IFeesManager { // slots [0-49] reserved for gap uint256[50] _gap_before; @@ -32,8 +32,8 @@ abstract contract FeesManagerStorage is IFeesManager { mapping(address => uint256) public userNonce; // token pool balances - // token address => chainSlug => amount - mapping(address => mapping(uint32 => uint256)) public tokenPoolBalances; + // chainSlug => token address => amount + mapping(uint32 => mapping(address => uint256)) public tokenPoolBalances; // user approved app gateways // userAddress => appGateway => isWhitelisted @@ -123,7 +123,8 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol error NoFeesBlocked(); /// @notice Error thrown when caller is invalid error InvalidCaller(); - + /// @notice Error thrown when user signature is invalid + error InvalidUserSignature(); constructor() { _disableInitializers(); // disable for implementation } @@ -168,14 +169,20 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol bytes memory signature_ ) external { _isWatcherSignatureValid( - abi.encode(depositTo_, chainSlug_, token_, amount_), + abi.encode( + this.incrementFeesDeposited.selector, + depositTo_, + chainSlug_, + token_, + amount_ + ), signatureNonce_, signature_ ); UserCredits storage userCredit = userCredits[depositTo_]; userCredit.totalCredits += amount_; - tokenPoolBalances[token_][chainSlug_] += amount_; + tokenPoolBalances[chainSlug_][token_] += amount_; emit FeesDepositedUpdated(chainSlug_, depositTo_, token_, amount_); } @@ -185,19 +192,21 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol address appGateway_, uint256 amount_ ) external view returns (bool) { - if (!isAppGatewayWhitelisted[consumeFrom_][appGateway_]) return false; + // If consumeFrom is not appGateway, check if it is whitelisted + if (consumeFrom_ != appGateway_ && !isAppGatewayWhitelisted[consumeFrom_][appGateway_]) + return false; return getAvailableFees(consumeFrom_) >= amount_; } function _processFeeApprovalData( bytes memory feeApprovalData_ - ) internal returns (address consumeFrom, address appGateway, bool isApproved) { - bytes memory signature_; - (consumeFrom, appGateway, isApproved, signature_) = abi.decode( - feeApprovalData_, - (address, address, bool, bytes) - ); - if (signature_.length == 0) return (consumeFrom, appGateway, isApproved); + ) internal returns (address, address, bool) { + (address consumeFrom, address appGateway, bool isApproved, bytes memory signature_) = abi + .decode(feeApprovalData_, (address, address, bool, bytes)); + if (signature_.length == 0) { + // If no signature, consumeFrom is appGateway + return (appGateway, appGateway, isApproved); + } bytes32 digest = keccak256( abi.encode( address(this), @@ -208,24 +217,24 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol isApproved ) ); - if (ECDSA.recover(digest, signature_) != consumeFrom) revert InvalidWatcherSignature(); + if (ECDSA.recover(digest, signature_) != consumeFrom) revert InvalidUserSignature(); isAppGatewayWhitelisted[consumeFrom][appGateway] = isApproved; userNonce[consumeFrom]++; return (consumeFrom, appGateway, isApproved); } - function setAppGatewayWhitelist( + function whitelistAppGatewayWithSignature( bytes memory feeApprovalData_ ) external returns (address consumeFrom, address appGateway, bool isApproved) { return _processFeeApprovalData(feeApprovalData_); } /// @notice Whitelists multiple app gateways for the caller - /// @param appGateways_ Array of app gateway addresses to whitelist - function whitelistApps(address[] calldata appGateways_) external { - for (uint256 i = 0; i < appGateways_.length; i++) { - isAppGatewayWhitelisted[msg.sender][appGateways_[i]] = true; + /// @param params_ Array of app gateway addresses to whitelist + function whitelistAppGateways(AppGatewayWhitelistParams[] calldata params_) external { + for (uint256 i = 0; i < params_.length; i++) { + isAppGatewayWhitelisted[msg.sender][params_[i].appGateway] = params_[i].isApproved; } } @@ -294,6 +303,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol function _useAvailableUserCredits(address consumeFrom_, uint256 toConsume_) internal { UserCredits storage userCredit = userCredits[consumeFrom_]; + if (userCredit.totalCredits < toConsume_) revert InsufficientFeesAvailable(); userCredit.totalCredits -= toConsume_; } @@ -301,7 +311,6 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol uint256 fees_, uint40 requestCount_ ) external onlyWatcherPrecompile { - require(requestCount_ != 0, "Request count cannot be 0"); RequestMetadata memory requestMetadata = deliveryHelper__().getRequestMetadata( requestCount_ ); @@ -365,37 +374,31 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol uint256 availableAmount = getAvailableFees(source); if (availableAmount < amount_) revert InsufficientFeesAvailable(); - tokenPoolBalances[token_][chainSlug_] -= amount_; + tokenPoolBalances[chainSlug_][token_] -= amount_; // Add it to the queue and submit request - _queue(chainSlug_, abi.encodeCall(IFeesPlug.withdrawFees, (token_, amount_, receiver_))); + _queue(chainSlug_, abi.encodeCall(IFeesPlug.withdrawFees, (token_, receiver_, amount_))); } /// @notice Withdraws fees to a specified receiver /// @param chainSlug_ The chain identifier /// @param token_ The token address /// @param receiver_ The address of the receiver - function withdrawTransmitterFees( + function getWithdrawTransmitterFeesPayloadParams( + address transmitter_, uint32 chainSlug_, address token_, address receiver_, uint256 amount_ - ) external returns (uint40 requestCount) { - address transmitter = msg.sender; - // Get total fees for the transmitter in given chain and token - uint256 totalFees = transmitterCredits[transmitter]; - if (totalFees >= amount_) revert InsufficientFeesAvailable(); + ) external onlyDeliveryHelper returns (PayloadSubmitParams[] memory) { + uint256 maxFeesAvailableForWithdraw = getMaxFeesAvailableForWithdraw(transmitter_); + if (amount_ > maxFeesAvailableForWithdraw) revert InsufficientFeesAvailable(); // Clean up storage - transmitterCredits[transmitter] -= amount_; - tokenPoolBalances[token_][chainSlug_] -= amount_; - - // Create fee distribution payload - bytes32 feesId = _encodeFeesId(feesCounter++); - bytes memory payload = abi.encodeCall( - IFeesPlug.distributeFee, - (token_, amount_, receiver_, feesId) - ); + transmitterCredits[transmitter_] -= amount_; + tokenPoolBalances[chainSlug_][token_] -= amount_; + + bytes memory payload = abi.encodeCall(IFeesPlug.withdrawFees, (token_, receiver_, amount_)); PayloadSubmitParams[] memory payloadSubmitParamsArray = new PayloadSubmitParams[](1); payloadSubmitParamsArray[0] = PayloadSubmitParams({ levelNumber: 0, @@ -412,31 +415,15 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol readAt: 0, payload: payload }); - - RequestMetadata memory requestMetadata = RequestMetadata({ - appGateway: address(this), - auctionManager: address(0), - maxFees: 0, - winningBid: Bid({transmitter: transmitter, fee: 0, extraData: new bytes(0)}), - onCompleteData: bytes(""), - onlyReadRequests: false, - consumeFrom: address(0) - }); // finalize for plug contract - return _submitAndStartProcessing(payloadSubmitParamsArray, requestMetadata, transmitter); + return payloadSubmitParamsArray; } - function _submitAndStartProcessing( - PayloadSubmitParams[] memory payloadSubmitParamsArray_, - RequestMetadata memory requestMetadata_, - address transmitter_ - ) internal returns (uint40 requestCount) { - requestCount = watcherPrecompile__().submitRequest( - payloadSubmitParamsArray_, - requestMetadata_ - ); - - // same transmitter can execute requests without auction - watcherPrecompile__().startProcessingRequest(requestCount, transmitter_); + function getMaxFeesAvailableForWithdraw(address transmitter_) public view returns (uint256) { + uint256 watcherFees = watcherPrecompileLimits().getTotalFeesRequired(0, 1, 0, 1); + return + transmitterCredits[transmitter_] > watcherFees + ? transmitterCredits[transmitter_] - watcherFees + : 0; } function _getSwitchboard(uint32 chainSlug_) internal view returns (address) { @@ -487,14 +474,16 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol } function _isWatcherSignatureValid( - bytes memory digest_, + bytes memory inputData_, uint256 signatureNonce_, bytes memory signature_ ) internal { if (isNonceUsed[signatureNonce_]) revert NonceUsed(); isNonceUsed[signatureNonce_] = true; - bytes32 digest = keccak256(abi.encode(address(this), evmxSlug, signatureNonce_, digest_)); + bytes32 digest = keccak256( + abi.encode(address(this), evmxSlug, signatureNonce_, inputData_) + ); digest = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", digest)); // recovered signer is checked for the valid roles later address signer = ECDSA.recover(digest, signature_); diff --git a/contracts/protocol/payload-delivery/FeesPlug.sol b/contracts/protocol/payload-delivery/FeesPlug.sol index ab6784ce..8a55d9ac 100644 --- a/contracts/protocol/payload-delivery/FeesPlug.sol +++ b/contracts/protocol/payload-delivery/FeesPlug.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.21; import "solady/utils/SafeTransferLib.sol"; +import "solady/tokens/ERC20.sol"; import "../../base/PlugBase.sol"; import "../utils/AccessControl.sol"; import {RESCUE_ROLE} from "../utils/common/AccessRoles.sol"; @@ -15,32 +16,29 @@ import {InvalidTokenAddress, FeesAlreadyPaid} from "../utils/common/Errors.sol"; /// @dev The amount deposited here is locked and updated in the EVMx for an app gateway /// @dev The fees are redeemed by the transmitters executing request or can be withdrawn by the owner contract FeesPlug is IFeesPlug, PlugBase, AccessControl { - /// @notice Mapping to store the balance of each token - mapping(address => uint256) public override balanceOf; - /// @notice Mapping to store if fees have been redeemed for a given fees ID - mapping(bytes32 => bool) public override feesRedeemed; /// @notice Mapping to store if a token is whitelisted mapping(address => bool) public whitelistedTokens; /// @notice Error thrown when balance is not enough to cover fees - error InsufficientTokenBalance(address token_); + error InsufficientTokenBalance(address token_, uint256 balance_, uint256 fee_); /// @notice Error thrown when deposit amount does not match msg.value error InvalidDepositAmount(); /// @notice Error thrown when token is not whitelisted error TokenNotWhitelisted(address token_); /// @notice Event emitted when fees are deposited - event FeesDeposited(address receiver, address token, uint256 feeAmount, uint256 nativeAmount); + event FeesDeposited(address token, address receiver, uint256 feeAmount, uint256 nativeAmount); /// @notice Event emitted when fees are withdrawn - event FeesWithdrawn(address token, uint256 amount, address receiver); + event FeesWithdrawn(address token, address receiver, uint256 amount); /// @notice Event emitted when a token is whitelisted event TokenWhitelisted(address token); /// @notice Event emitted when a token is removed from whitelist event TokenRemovedFromWhitelist(address token); /// @notice Modifier to check if the balance of a token is enough to withdraw - modifier isFeesEnough(uint256 fee_, address feeToken_) { - if (balanceOf[feeToken_] < fee_) revert InsufficientTokenBalance(feeToken_); + modifier isFeesEnough(address feeToken_, uint256 fee_) { + uint balance_ = ERC20(feeToken_).balanceOf(address(this)); + if (balance_ < fee_) revert InsufficientTokenBalance(feeToken_, balance_, fee_); _; } @@ -52,54 +50,34 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { _initializeOwner(owner_); } - /// @notice Distributes fees to the transmitter - /// @param feeToken_ The token address - /// @param fee_ The amount of fees - /// @param transmitter_ The transmitter address - /// @param feesId_ The fees ID - function distributeFee( - address feeToken_, - uint256 fee_, - address transmitter_, - bytes32 feesId_ - ) external override onlySocket isFeesEnough(fee_, feeToken_) { - if (feesRedeemed[feesId_]) revert FeesAlreadyPaid(); - feesRedeemed[feesId_] = true; - balanceOf[feeToken_] -= fee_; - - _transferTokens(feeToken_, fee_, transmitter_); - } - /// @notice Withdraws fees /// @param token_ The token address /// @param amount_ The amount /// @param receiver_ The receiver address function withdrawFees( address token_, - uint256 amount_, - address receiver_ - ) external override onlySocket isFeesEnough(amount_, token_) { - balanceOf[token_] -= amount_; - - _transferTokens(token_, amount_, receiver_); - emit FeesWithdrawn(token_, amount_, receiver_); + address receiver_, + uint256 amount_ + ) external override onlySocket isFeesEnough(token_, amount_) { + SafeTransferLib.safeTransfer(token_, receiver_, amount_); + emit FeesWithdrawn(token_, receiver_, amount_); } - function depositToFee(address token_, uint256 amount_, address receiver_) external override { + function depositToFee(address token_, address receiver_, uint256 amount_) external override { _deposit(token_, receiver_, amount_, 0); } function depositToFeeAndNative( address token_, - uint256 amount_, - address receiver_ + address receiver_, + uint256 amount_ ) external override { uint256 nativeAmount_ = amount_ / 10; uint256 feeAmount_ = amount_ - nativeAmount_; _deposit(token_, receiver_, feeAmount_, nativeAmount_); } - function depositToNative(address token_, uint256 amount_, address receiver_) external override { + function depositToNative(address token_, address receiver_, uint256 amount_) external override { _deposit(token_, receiver_, 0, amount_); } @@ -116,31 +94,14 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { ) internal { uint256 totalAmount_ = feeAmount_ + nativeAmount_; if (!whitelistedTokens[token_]) revert TokenNotWhitelisted(token_); - if (token_.code.length == 0) revert InvalidTokenAddress(); - - balanceOf[token_] += totalAmount_; + SafeTransferLib.safeTransferFrom(token_, msg.sender, address(this), totalAmount_); emit FeesDeposited(receiver_, token_, feeAmount_, nativeAmount_); } - /// @notice Transfers tokens - /// @param token_ The token address - /// @param amount_ The amount - /// @param receiver_ The receiver address - function _transferTokens(address token_, uint256 amount_, address receiver_) internal { - SafeTransferLib.safeTransfer(token_, receiver_, amount_); - } - - function connectSocket( - address appGateway_, - address socket_, - address switchboard_ - ) external onlyOwner { - _connectSocket(appGateway_, socket_, switchboard_); - } - /// @notice Adds a token to the whitelist /// @param token_ The token address to whitelist function whitelistToken(address token_) external onlyOwner { + if (token_.code.length == 0) revert InvalidTokenAddress(); whitelistedTokens[token_] = true; emit TokenWhitelisted(token_); } @@ -152,6 +113,13 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { emit TokenRemovedFromWhitelist(token_); } + function connectSocket( + address appGateway_, + address socket_, + address switchboard_ + ) external onlyOwner { + _connectSocket(appGateway_, socket_, switchboard_); + } /** * @notice Rescues funds from the contract if they are locked by mistake. This contract does not * theoretically need this function but it is added for safety. diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol index 6efaedfd..81b1464f 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol @@ -38,7 +38,7 @@ contract DeliveryHelper is FeesHelpers { RequestMetadata storage requestMetadata_ = requests[requestCount_]; // if a transmitter was already assigned, it means the request was restarted bool isRestarted = requestMetadata_.winningBid.transmitter != address(0); - requestMetadata_.winningBid.transmitter = winningBid_.transmitter; + requestMetadata_.winningBid = winningBid_; if (!isRestarted) { watcherPrecompile__().startProcessingRequest(requestCount_, winningBid_.transmitter); diff --git a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol index 2922ba52..8db2607d 100644 --- a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol +++ b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol @@ -50,6 +50,44 @@ abstract contract FeesHelpers is RequestQueue { return _batch(msg.sender, auctionManager_, fees_, bytes(""), bytes("")); } + /// @notice Withdraws fees to a specified receiver + /// @param chainSlug_ The chain identifier + /// @param token_ The token address + /// @param receiver_ The address of the receiver + function withdrawTransmitterFees( + uint32 chainSlug_, + address token_, + address receiver_, + uint256 amount_ + ) external returns (uint40 requestCount) { + address transmitter = msg.sender; + + PayloadSubmitParams[] memory payloadSubmitParamsArray = IFeesManager( + addressResolver__.feesManager() + ).getWithdrawTransmitterFeesPayloadParams( + transmitter, + chainSlug_, + token_, + receiver_, + amount_ + ); + + RequestMetadata memory requestMetadata = RequestMetadata({ + appGateway: address(this), + auctionManager: address(0), + maxFees: 0, + winningBid: Bid({transmitter: transmitter, fee: 0, extraData: new bytes(0)}), + onCompleteData: bytes(""), + onlyReadRequests: false, + consumeFrom: transmitter, + queryCount: 0, + finalizeCount: 1 + }); // finalize for plug contract + requestCount = watcherPrecompile__().submitRequest(payloadSubmitParamsArray); + requests[requestCount] = requestMetadata; + // same transmitter can execute requests without auction + watcherPrecompile__().startProcessingRequest(requestCount, transmitter); + } /// @notice Returns the fees for a request /// @param requestCount_ The ID of the request /// @return fees The fees data diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index f88ac9cd..80b09887 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -33,34 +33,6 @@ abstract contract RequestQueue is DeliveryUtils { return _batch(appGateway, auctionManager_, maxFees_, feesApprovalData_, onCompleteData_); } - function _checkBatch( - address appGateway_, - bytes memory feesApprovalData_, - uint256 maxFees_ - ) internal returns (address consumeFrom) { - if (queuePayloadParams.length > REQUEST_PAYLOAD_COUNT_LIMIT) - revert RequestPayloadCountLimitExceeded(); - (consumeFrom, , ) = IFeesManager(addressResolver__.feesManager()).setAppGatewayWhitelist( - feesApprovalData_ - ); - if ( - !IFeesManager(addressResolver__.feesManager()).isFeesEnough( - consumeFrom, - appGateway_, - maxFees_ - ) - ) revert InsufficientFees(); - - return consumeFrom; - } - - function _getAuctionManager(address auctionManager_) internal view returns (address) { - return - auctionManager_ == address(0) - ? IAddressResolver(addressResolver__).defaultAuctionManager() - : auctionManager_; - } - /// @notice Initiates a batch of payloads /// @dev it checks fees, payload limits and creates the payload submit params array after assigning proper levels /// @dev It also modifies the deploy payloads as needed by contract factory plug @@ -73,64 +45,129 @@ abstract contract RequestQueue is DeliveryUtils { bytes memory onCompleteData_ ) internal returns (uint40 requestCount) { if (queuePayloadParams.length == 0) return 0; - address auctionManager = _getAuctionManager(auctionManager_); - // create the payload submit params array in desired format + BatchParams memory params = BatchParams({ + appGateway: appGateway_, + auctionManager: _getAuctionManager(auctionManager_), + maxFees: maxFees_, + feesApprovalData: feesApprovalData_, + onCompleteData: onCompleteData_, + onlyReadRequests: false, + queryCount: 0, + finalizeCount: 0 + }); + + // Split the function into smaller parts ( PayloadSubmitParams[] memory payloadSubmitParamsArray, - bool onlyReadRequests + bool onlyReadRequests, + uint256 queryCount, + uint256 finalizeCount ) = _createPayloadSubmitParamsArray(); - address consumeFrom = _checkBatch(appGateway_, feesApprovalData_, maxFees_); + params.onlyReadRequests = onlyReadRequests; + params.queryCount = queryCount; + params.finalizeCount = finalizeCount; + + address consumeFrom = _checkBatch( + params.appGateway, + params.feesApprovalData, + params.maxFees + ); + + return _submitBatchRequest(payloadSubmitParamsArray, consumeFrom, params); + } + + function _submitBatchRequest( + PayloadSubmitParams[] memory payloadSubmitParamsArray, + address consumeFrom, + BatchParams memory params + ) internal returns (uint40 requestCount) { RequestMetadata memory requestMetadata = RequestMetadata({ - appGateway: appGateway_, - auctionManager: auctionManager, - maxFees: maxFees_, + appGateway: params.appGateway, + auctionManager: params.auctionManager, + maxFees: params.maxFees, winningBid: Bid({fee: 0, transmitter: address(0), extraData: new bytes(0)}), - onCompleteData: onCompleteData_, - onlyReadRequests: onlyReadRequests, - consumeFrom: consumeFrom + onCompleteData: params.onCompleteData, + onlyReadRequests: params.onlyReadRequests, + consumeFrom: consumeFrom, + queryCount: params.queryCount, + finalizeCount: params.finalizeCount }); - // process and submit the queue of payloads to watcher precompile - requestCount = watcherPrecompile__().submitRequest( - payloadSubmitParamsArray, - requestMetadata - ); + requestCount = watcherPrecompile__().submitRequest(payloadSubmitParamsArray); requests[requestCount] = requestMetadata; - // send query directly if request contains only reads - // transmitter should ignore the batch for auction, the transaction will also revert if someone bids - if (onlyReadRequests) + if (params.onlyReadRequests) { watcherPrecompile__().startProcessingRequest(requestCount, address(0)); + } - // to save extra calls from transmitter - uint256 maxTransmitterFees = maxFees_ - - watcherPrecompileLimits().getTotalFeesRequired(requestCount); + uint256 watcherFees = watcherPrecompileLimits().getTotalFeesRequired( + params.queryCount, + params.finalizeCount, + 0, + 0 + ); + if (watcherFees > params.maxFees) revert InsufficientFees(); + uint256 maxTransmitterFees = params.maxFees - watcherFees; emit PayloadSubmitted( requestCount, - appGateway_, + params.appGateway, payloadSubmitParamsArray, - maxFees_ - maxTransmitterFees, - auctionManager_, - onlyReadRequests + maxTransmitterFees, + params.auctionManager, + params.onlyReadRequests ); } + function _getAuctionManager(address auctionManager_) internal view returns (address) { + return + auctionManager_ == address(0) + ? IAddressResolver(addressResolver__).defaultAuctionManager() + : auctionManager_; + } + function _checkBatch( + address appGateway_, + bytes memory feesApprovalData_, + uint256 maxFees_ + ) internal returns (address consumeFrom) { + if (queuePayloadParams.length > REQUEST_PAYLOAD_COUNT_LIMIT) + revert RequestPayloadCountLimitExceeded(); + (consumeFrom, , ) = IFeesManager(addressResolver__.feesManager()) + .whitelistAppGatewayWithSignature(feesApprovalData_); + if ( + !IFeesManager(addressResolver__.feesManager()).isFeesEnough( + consumeFrom, + appGateway_, + maxFees_ + ) + ) revert InsufficientFees(); + + return consumeFrom; + } + /// @notice Creates an array of payload details /// @return payloadDetailsArray An array of payload details function _createPayloadSubmitParamsArray() internal - returns (PayloadSubmitParams[] memory payloadDetailsArray, bool onlyReadRequests) + returns ( + PayloadSubmitParams[] memory payloadDetailsArray, + bool onlyReadRequests, + uint256 queryCount, + uint256 finalizeCount + ) { payloadDetailsArray = new PayloadSubmitParams[](queuePayloadParams.length); onlyReadRequests = queuePayloadParams[0].callType == CallType.READ; uint256 currentLevel = 0; for (uint256 i = 0; i < queuePayloadParams.length; i++) { - if (queuePayloadParams[i].callType != CallType.READ) { + if (queuePayloadParams[i].callType == CallType.READ) { + queryCount++; + } else { onlyReadRequests = false; + finalizeCount++; } // Update level for calls diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 118d772d..e870602d 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -50,6 +50,23 @@ enum ExecutionStatus { Reverted } +/// @notice Creates a struct to hold batch parameters +struct BatchParams { + address appGateway; + address auctionManager; + uint256 maxFees; + bytes feesApprovalData; + bytes onCompleteData; + bool onlyReadRequests; + uint256 queryCount; + uint256 finalizeCount; +} + +struct AppGatewayWhitelistParams { + address appGateway; + bool isApproved; +} + //// STRUCTS //// // plug: struct LimitParams { @@ -227,6 +244,8 @@ struct RequestMetadata { bytes onCompleteData; bool onlyReadRequests; address consumeFrom; + uint256 queryCount; + uint256 finalizeCount; } struct ExecuteParams { diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index 5e05a4b5..c996de91 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -203,14 +203,16 @@ contract WatcherPrecompileConfig is } function _isWatcherSignatureValid( - bytes memory digest_, + bytes memory inputData_, uint256 signatureNonce_, bytes memory signature_ ) internal { if (isNonceUsed[signatureNonce_]) revert NonceUsed(); isNonceUsed[signatureNonce_] = true; - bytes32 digest = keccak256(abi.encode(address(this), evmxSlug, signatureNonce_, digest_)); + bytes32 digest = keccak256( + abi.encode(address(this), evmxSlug, signatureNonce_, inputData_) + ); digest = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", digest)); // recovered signer is checked for the valid roles later diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index f0f2921a..1ea1c2da 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -181,31 +181,17 @@ contract WatcherPrecompileLimits is callBackFees = callBackFees_; } - function getTotalFeesRequired(uint40 requestCount_) external view returns (uint256) { + function getTotalFeesRequired( + uint256 queryCount_, + uint256 finalizeCount_, + uint256 scheduleCount_, + uint256 callbackCount_ + ) external view returns (uint256) { uint256 totalFees = 0; - if (queryFees == 0) { - revert WatcherFeesNotSet(QUERY); - } - if (finalizeFees == 0) { - revert WatcherFeesNotSet(FINALIZE); - } - if (scheduleFees == 0) { - revert WatcherFeesNotSet(SCHEDULE); - } - if (callBackFees == 0) { - revert WatcherFeesNotSet(CALLBACK); - } - - uint256 queryCount = watcherPrecompile__().requestParams[requestCount_].queryCount; - uint256 finalizeCount = watcherPrecompile__().requestParams[requestCount_].finalizeCount; - uint256 scheduleCount = watcherPrecompile__().requestParams[requestCount_].scheduleCount; - - uint256 totalCallbacks = queryCount + finalizeCount + scheduleCount; - - totalFees += totalCallbacks * callBackFees; - totalFees += queryCount * queryFees; - totalFees += finalizeCount * finalizeFees; - totalFees += scheduleCount * scheduleFees; + totalFees += callbackCount_ * callBackFees; + totalFees += queryCount_ * queryFees; + totalFees += finalizeCount_ * finalizeFees; + totalFees += scheduleCount_ * scheduleFees; return totalFees; } diff --git a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol index e6b32c08..1cb77e73 100644 --- a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol +++ b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol @@ -19,8 +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_, - RequestMetadata calldata requestMetadata_ + PayloadSubmitParams[] calldata payloadSubmitParams_ ) public returns (uint40 requestCount) { address appGateway = _checkAppGateways(payloadSubmitParams_); @@ -95,7 +94,7 @@ abstract contract RequestHandler is WatcherPrecompileCore { requestParams[requestCount].queryCount = readCount; requestParams[requestCount].finalizeCount = writeCount; - + requestParams[requestCount].currentBatch = currentBatch; requestParams[requestCount].payloadsRemaining = payloadSubmitParams_.length; requestParams[requestCount].middleware = msg.sender; diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 53928cd8..14c6323d 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -51,12 +51,7 @@ abstract contract WatcherPrecompileCore is ); // consumes limit for SCHEDULE precompile - watcherPrecompileLimits__.consumeLimit( - _getCoreAppGateway(msg.sender), - SCHEDULE, - 1 - ); - + watcherPrecompileLimits__.consumeLimit(_getCoreAppGateway(msg.sender), SCHEDULE, 1); // emits event for watcher to track timeout and resolve when timeout is reached emit TimeoutRequested(timeoutId, msg.sender, payload_, executeAt); @@ -237,19 +232,21 @@ abstract contract WatcherPrecompileCore is } /// @notice Verifies that a watcher signature is valid - /// @param digest_ The digest to verify + /// @param inputData_ The input data to verify /// @param signatureNonce_ The nonce of the signature /// @param signature_ The signature to verify /// @dev This function verifies that the signature was created by the watcher and that the nonce has not been used before function _isWatcherSignatureValid( - bytes memory digest_, + bytes memory inputData_, uint256 signatureNonce_, bytes memory signature_ ) internal { if (isNonceUsed[signatureNonce_]) revert NonceUsed(); isNonceUsed[signatureNonce_] = true; - bytes32 digest = keccak256(abi.encode(address(this), evmxSlug, signatureNonce_, digest_)); + bytes32 digest = keccak256( + abi.encode(address(this), evmxSlug, signatureNonce_, inputData_) + ); digest = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", digest)); // recovered signer is checked for the valid roles later diff --git a/script/helpers/PayFeesInArbitrumETH.s.sol b/script/helpers/PayFeesInArbitrumETH.s.sol index 9bf56848..0940009a 100644 --- a/script/helpers/PayFeesInArbitrumETH.s.sol +++ b/script/helpers/PayFeesInArbitrumETH.s.sol @@ -23,6 +23,6 @@ contract DepositFees is Script { console.log("App Gateway:", appGateway); console.log("Fees Plug:", address(feesPlug)); uint feesAmount = 0.001 ether; - feesPlug.depositToFeeAndNative(ETH_ADDRESS, feesAmount, appGateway); + feesPlug.depositToFeeAndNative(ETH_ADDRESS, appGateway, feesAmount); } } diff --git a/test/DeliveryHelper.t.sol b/test/DeliveryHelper.t.sol index e915a59d..bf77d5ec 100644 --- a/test/DeliveryHelper.t.sol +++ b/test/DeliveryHelper.t.sol @@ -111,6 +111,15 @@ contract DeliveryHelperTest is SetupTest { arbConfig = deploySocket(arbChainSlug); optConfig = deploySocket(optChainSlug); connectDeliveryHelper(); + + depositUSDCFees( + address(auctionManager), + OnChainFees({ + chainSlug: arbChainSlug, + token: address(arbConfig.feesTokenUSDC), + amount: 1 ether + }) + ); } function connectDeliveryHelper() internal { @@ -173,13 +182,17 @@ contract DeliveryHelperTest is SetupTest { //////////////////////////////////// Fees //////////////////////////////////// - function depositFees(address appGateway_, OnChainFees memory fees_) internal { + function depositUSDCFees(address appGateway_, OnChainFees memory fees_) internal { SocketContracts memory socketConfig = getSocketConfig(fees_.chainSlug); - socketConfig.feesPlug.depositToFeeAndNative(fees_.token, fees_.amount, appGateway_); + vm.startPrank(owner); + ERC20(fees_.token).approve(address(socketConfig.feesPlug), fees_.amount); + socketConfig.feesPlug.depositToFeeAndNative(fees_.token, appGateway_, fees_.amount); + vm.stopPrank(); bytes memory bytesInput = abi.encode( - fees_.chainSlug, + IFeesManager.incrementFeesDeposited.selector, appGateway_, + fees_.chainSlug, fees_.token, fees_.amount ); @@ -198,6 +211,35 @@ contract DeliveryHelperTest is SetupTest { ); } + function whitelistAppGateway( + address appGateway_, + address user_, + uint256 userPrivateKey_, + uint32 chainSlug_ + ) internal { + SocketContracts memory socketConfig = getSocketConfig(chainSlug_); + // Create fee approval data with signature + bytes32 digest = keccak256( + abi.encode( + address(feesManager), + evmxSlug, + user_, + appGateway_, + feesManager.userNonce(user_), + true + ) + ); + + // Sign with consumeFrom's private key + bytes memory signature = _createSignature(digest, userPrivateKey_); + + // Encode approval data + bytes memory feeApprovalData = abi.encode(user_, appGateway_, true, signature); + + // Call whitelistAppGatewayWithSignature with approval data + feesManager.whitelistAppGatewayWithSignature(feeApprovalData); + } + ////////////////////////////////// Deployment helpers //////////////////////////////////// function _deploy( uint32 chainSlug_, diff --git a/test/FeesTest.t.sol b/test/FeesTest.t.sol index 4755cb15..325987b9 100644 --- a/test/FeesTest.t.sol +++ b/test/FeesTest.t.sol @@ -20,9 +20,13 @@ contract FeesTest is DeliveryHelperTest { feesConfig = getSocketConfig(feesChainSlug); counterGateway = new CounterAppGateway(address(addressResolver), feesAmount); - depositFees( + depositUSDCFees( address(counterGateway), - OnChainFees({chainSlug: feesChainSlug, token: ETH_ADDRESS, amount: depositAmount}) + OnChainFees({ + chainSlug: feesChainSlug, + token: address(feesConfig.feesTokenUSDC), + amount: depositAmount + }) ); bytes32[] memory contractIds = new bytes32[](1); @@ -31,40 +35,36 @@ contract FeesTest is DeliveryHelperTest { } function testDistributeFee() public { - uint256 initialFeesPlugBalance = address(feesConfig.feesPlug).balance; - - assertEq( - initialFeesPlugBalance, - address(feesConfig.feesPlug).balance, - "FeesPlug Balance should be correct" + uint256 initialFeesPlugBalance = feesConfig.feesTokenUSDC.balanceOf( + address(feesConfig.feesPlug) ); assertEq( initialFeesPlugBalance, - feesConfig.feesPlug.balanceOf(ETH_ADDRESS), - "FeesPlug balance of counterGateway should be correct" + feesConfig.feesTokenUSDC.balanceOf(address(feesConfig.feesPlug)), + "FeesPlug Balance should be correct" ); - uint256 transmitterReceiverBalanceBefore = address(receiver).balance; - - hoax(transmitterEOA); - uint40 requestCount = feesManager.withdrawTransmitterFees( + uint256 transmitterReceiverBalanceBefore = feesConfig.feesTokenUSDC.balanceOf(receiver); + uint256 withdrawAmount = feesManager.getMaxFeesAvailableForWithdraw(transmitterEOA); + vm.startPrank(transmitterEOA); + uint40 requestCount = deliveryHelper.withdrawTransmitterFees( feesChainSlug, - ETH_ADDRESS, + address(feesConfig.feesTokenUSDC), address(receiver), - feesManager.transmitterCredits(transmitterEOA) + withdrawAmount ); + vm.stopPrank(); uint40[] memory batches = watcherPrecompile.getBatches(requestCount); _finalizeBatch(batches[0], new bytes[](0), 0, false); - assertEq( transmitterReceiverBalanceBefore + bidAmount, - address(receiver).balance, + feesConfig.feesTokenUSDC.balanceOf(receiver), "Transmitter Balance should be correct" ); assertEq( initialFeesPlugBalance - bidAmount, - address(feesConfig.feesPlug).balance, + feesConfig.feesTokenUSDC.balanceOf(address(feesConfig.feesPlug)), "FeesPlug Balance should be correct" ); } @@ -72,25 +72,30 @@ contract FeesTest is DeliveryHelperTest { function testWithdrawFeeTokens() public { assertEq( depositAmount, - feesConfig.feesPlug.balanceOf(ETH_ADDRESS), + feesConfig.feesTokenUSDC.balanceOf(address(feesConfig.feesPlug)), "Balance should be correct" ); - uint256 receiverBalanceBefore = receiver.balance; + uint256 receiverBalanceBefore = feesConfig.feesTokenUSDC.balanceOf(receiver); uint256 withdrawAmount = 0.5 ether; - counterGateway.withdrawFeeTokens(feesChainSlug, ETH_ADDRESS, withdrawAmount, receiver); + counterGateway.withdrawFeeTokens( + feesChainSlug, + address(feesConfig.feesTokenUSDC), + withdrawAmount, + receiver + ); executeRequest(new bytes[](0)); assertEq( depositAmount - withdrawAmount, - address(feesConfig.feesPlug).balance, + feesConfig.feesTokenUSDC.balanceOf(address(feesConfig.feesPlug)), "Fees Balance should be correct" ); assertEq( receiverBalanceBefore + withdrawAmount, - receiver.balance, + feesConfig.feesTokenUSDC.balanceOf(receiver), "Receiver Balance should be correct" ); } diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 69237ec1..a9f0ebd8 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -22,7 +22,7 @@ import {ETH_ADDRESS} from "../contracts/protocol/utils/common/Constants.sol"; import {ResolvedPromises, OnChainFees} from "../contracts/protocol/utils/common/Structs.sol"; import "solady/utils/ERC1967Factory.sol"; - +import "./apps/app-gateways/USDC.sol"; contract SetupTest is Test { using PayloadHeaderDecoder for bytes32; uint public c = 1; @@ -57,6 +57,7 @@ contract SetupTest is Test { SocketBatcher socketBatcher; ContractFactoryPlug contractFactoryPlug; FeesPlug feesPlug; + ERC20 feesTokenUSDC; } AddressResolver public addressResolver; @@ -84,10 +85,12 @@ contract SetupTest is Test { SocketBatcher socketBatcher = new SocketBatcher(owner, socket); FastSwitchboard switchboard = new FastSwitchboard(chainSlug_, socket, owner); + ERC20 feesTokenUSDC = new USDC("USDC", "USDC", 6, owner, 1000000000000000000000000); FeesPlug feesPlug = new FeesPlug(address(socket), owner); ContractFactoryPlug contractFactoryPlug = new ContractFactoryPlug(address(socket), owner); - vm.startPrank(owner); + // feePlug whitelist token + feesPlug.whitelistToken(address(feesTokenUSDC)); // socket socket.grantRole(GOVERNANCE_ROLE, address(owner)); @@ -114,7 +117,8 @@ contract SetupTest is Test { switchboard: switchboard, socketBatcher: socketBatcher, contractFactoryPlug: contractFactoryPlug, - feesPlug: feesPlug + feesPlug: feesPlug, + feesTokenUSDC: feesTokenUSDC }); } @@ -192,6 +196,11 @@ contract SetupTest is Test { vm.startPrank(watcherEOA); addressResolver.setWatcherPrecompile(address(watcherPrecompile)); + watcherPrecompileLimits.setCallBackFees(1); + watcherPrecompileLimits.setFinalizeFees(1); + watcherPrecompileLimits.setQueryFees(1); + watcherPrecompileLimits.setScheduleFees(1); + vm.stopPrank(); } diff --git a/test/apps/Counter.t.sol b/test/apps/Counter.t.sol index 92079168..5e34404f 100644 --- a/test/apps/Counter.t.sol +++ b/test/apps/Counter.t.sol @@ -17,9 +17,13 @@ contract CounterTest is DeliveryHelperTest { setUpDeliveryHelper(); counterGateway = new CounterAppGateway(address(addressResolver), feesAmount); - depositFees( + depositUSDCFees( address(counterGateway), - OnChainFees({chainSlug: arbChainSlug, token: ETH_ADDRESS, amount: 1 ether}) + OnChainFees({ + chainSlug: arbChainSlug, + token: address(arbConfig.feesTokenUSDC), + amount: 1 ether + }) ); counterId = counterGateway.counter(); diff --git a/test/apps/ParallelCounter.t.sol b/test/apps/ParallelCounter.t.sol index 6cfc28f3..5e7e193f 100644 --- a/test/apps/ParallelCounter.t.sol +++ b/test/apps/ParallelCounter.t.sol @@ -17,9 +17,13 @@ contract ParallelCounterTest is DeliveryHelperTest { setUpDeliveryHelper(); parallelCounterGateway = new CounterAppGateway(address(addressResolver), feesAmount); - depositFees( + depositUSDCFees( address(parallelCounterGateway), - OnChainFees({chainSlug: arbChainSlug, token: ETH_ADDRESS, amount: 1 ether}) + OnChainFees({ + chainSlug: arbChainSlug, + token: address(arbConfig.feesTokenUSDC), + amount: 1 ether + }) ); counterId1 = parallelCounterGateway.counter1(); counterId2 = parallelCounterGateway.counter(); diff --git a/test/apps/SuperToken.t.sol b/test/apps/SuperToken.t.sol index 97eadc43..bd5c88ab 100644 --- a/test/apps/SuperToken.t.sol +++ b/test/apps/SuperToken.t.sol @@ -76,9 +76,13 @@ contract SuperTokenTest is DeliveryHelperTest { ); // Enable app gateways to do all operations in the Watcher: Read, Write and Schedule on EVMx // Watcher sets the limits for apps in this SOCKET protocol version - depositFees( + depositUSDCFees( address(superTokenApp), - OnChainFees({chainSlug: arbChainSlug, token: ETH_ADDRESS, amount: 1 ether}) + OnChainFees({ + chainSlug: arbChainSlug, + token: address(arbConfig.feesTokenUSDC), + amount: 1 ether + }) ); appContracts = AppContracts({ diff --git a/test/apps/app-gateways/USDC.sol b/test/apps/app-gateways/USDC.sol new file mode 100644 index 00000000..df08d360 --- /dev/null +++ b/test/apps/app-gateways/USDC.sol @@ -0,0 +1,41 @@ +pragma solidity ^0.8.21; + +import "solady/tokens/ERC20.sol"; + +contract USDC is ERC20 { + string private _name; + string private _symbol; + uint8 private _decimals; + + constructor( + string memory name_, + string memory symbol_, + uint8 decimals_, + address initialSupplyHolder_, + uint256 initialSupply_ + ) { + _name = name_; + _symbol = symbol_; + _decimals = decimals_; + _mint(initialSupplyHolder_, initialSupply_); + } + function name() public view virtual override returns (string memory) { + return _name; + } + + function symbol() public view virtual override returns (string memory) { + return _symbol; + } + + function decimals() public view virtual override returns (uint8) { + return _decimals; + } + + function mint(address to, uint256 amount) external { + _mint(to, amount); + } + + function burn(address from, uint256 amount) external { + _burn(from, amount); + } +} diff --git a/test/apps/app-gateways/counter/CounterAppGateway.sol b/test/apps/app-gateways/counter/CounterAppGateway.sol index eb5e6e7e..f5bca934 100644 --- a/test/apps/app-gateways/counter/CounterAppGateway.sol +++ b/test/apps/app-gateways/counter/CounterAppGateway.sol @@ -20,7 +20,7 @@ contract CounterAppGateway is AppGatewayBase, Ownable { constructor(address addressResolver_, uint256 fees_) AppGatewayBase(addressResolver_) { creationCodeWithArgs[counter] = abi.encodePacked(type(Counter).creationCode); creationCodeWithArgs[counter1] = abi.encodePacked(type(Counter).creationCode); - _setOverrides(fees_); + _setMaxFees(fees_); _initializeOwner(msg.sender); } @@ -80,10 +80,7 @@ contract CounterAppGateway is AppGatewayBase, Ownable { _setOverrides(Read.OFF, Parallel.OFF); } - function readCounterAtBlock( - address instance_, - uint256 blockNumber_ - ) public async(bytes("")) { + function readCounterAtBlock(address instance_, uint256 blockNumber_) public async(bytes("")) { uint32 chainSlug = IForwarder(instance_).getChainSlug(); _setOverrides(Read.ON, Parallel.ON, blockNumber_); ICounter(instance_).getCounter(); diff --git a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol index 8e38065d..fbaf6f15 100644 --- a/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol +++ b/test/apps/app-gateways/super-token/SuperTokenAppGateway.sol @@ -45,7 +45,7 @@ contract SuperTokenAppGateway is AppGatewayBase, Ownable { // sets the fees data like max fees, chain and token for all transfers // they can be updated for each transfer as well - _setOverrides(fees_); + _setMaxFees(fees_); _initializeOwner(owner_); } From 339938a67b65854b7d337876d2b78edf834f5d6f Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 22 Apr 2025 16:50:32 +0530 Subject: [PATCH 075/108] fix: clean execute params --- contracts/interfaces/ISocketBatcher.sol | 1 + contracts/protocol/socket/SocketBatcher.sol | 3 ++- contracts/protocol/socket/SocketConfig.sol | 2 +- contracts/protocol/socket/SocketUtils.sol | 2 +- contracts/protocol/utils/common/Structs.sol | 4 ---- .../core/WatcherPrecompileCore.sol | 6 ------ test/SetupTest.t.sol | 14 ++++++++------ 7 files changed, 13 insertions(+), 19 deletions(-) diff --git a/contracts/interfaces/ISocketBatcher.sol b/contracts/interfaces/ISocketBatcher.sol index b6ae52e1..e114bcb9 100644 --- a/contracts/interfaces/ISocketBatcher.sol +++ b/contracts/interfaces/ISocketBatcher.sol @@ -18,6 +18,7 @@ interface ISocketBatcher { */ function attestAndExecute( ExecuteParams calldata executeParams_, + address switchboard_, bytes32 digest_, bytes calldata proof_, bytes calldata transmitterSignature_ diff --git a/contracts/protocol/socket/SocketBatcher.sol b/contracts/protocol/socket/SocketBatcher.sol index a3ac5ba4..292ede25 100644 --- a/contracts/protocol/socket/SocketBatcher.sol +++ b/contracts/protocol/socket/SocketBatcher.sol @@ -36,11 +36,12 @@ contract SocketBatcher is ISocketBatcher, Ownable { */ function attestAndExecute( ExecuteParams calldata executeParams_, + address switchboard_, bytes32 digest_, bytes calldata proof_, bytes calldata transmitterSignature_ ) external payable returns (bool, bool, bytes memory) { - ISwitchboard(executeParams_.switchboard).attest(digest_, proof_); + ISwitchboard(switchboard_).attest(digest_, proof_); return socket__.execute{value: msg.value}(executeParams_, transmitterSignature_); } diff --git a/contracts/protocol/socket/SocketConfig.sol b/contracts/protocol/socket/SocketConfig.sol index c2638a4c..94778ad4 100644 --- a/contracts/protocol/socket/SocketConfig.sol +++ b/contracts/protocol/socket/SocketConfig.sol @@ -7,7 +7,7 @@ import {IPlug} from "../../interfaces/IPlug.sol"; import "../utils/AccessControl.sol"; import {GOVERNANCE_ROLE, RESCUE_ROLE, SWITCHBOARD_DISABLER_ROLE} from "../utils/common/AccessRoles.sol"; -import {PlugConfig, SwitchboardStatus, ExecutionStatus} from "../utils/common/Structs.sol"; +import {CallType, PlugConfig, SwitchboardStatus, ExecutionStatus} from "../utils/common/Structs.sol"; import {PlugNotFound, InvalidAppGateway, InvalidTransmitter} from "../utils/common/Errors.sol"; import {MAX_COPY_BYTES} from "../utils/common/Constants.sol"; diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index 2b8920d9..e862b6a1 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -55,7 +55,7 @@ abstract contract SocketUtils is SocketConfig { transmitter_, payloadId_, executeParams_.deadline, - executeParams_.writeFinality, + CallType.WRITE, executeParams_.gasLimit, executeParams_.value, executeParams_.payload, diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 2928e607..c6e3f29d 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -132,10 +132,8 @@ struct DigestParams { bytes32 payloadId; uint256 deadline; CallType callType; - WriteFinality writeFinality; uint256 gasLimit; uint256 value; - uint256 readAt; bytes payload; address target; bytes32 appGatewayId; @@ -223,7 +221,6 @@ struct RequestMetadata { struct ExecuteParams { uint256 deadline; - WriteFinality writeFinality; uint256 gasLimit; uint256 value; bytes payload; @@ -232,7 +229,6 @@ struct ExecuteParams { uint40 batchCount; uint40 payloadCount; bytes32 prevDigestsHash; // should be id? hash of hashes - address switchboard; } /// @notice Struct containing fee amounts and status diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index b7824a3a..1c3881dc 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -90,10 +90,8 @@ abstract contract WatcherPrecompileCore is params_.payloadId, deadline, params_.dump.getCallType(), - params_.dump.getWriteFinality(), params_.gasLimit, params_.value, - params_.readAt, params_.payload, params_.target, _encodeAppGatewayId(params_.appGateway), @@ -130,10 +128,8 @@ abstract contract WatcherPrecompileCore is params_.payloadId, params_.deadline, params_.callType, - params_.writeFinality, params_.gasLimit, params_.value, - params_.readAt, params_.payload, params_.target, params_.appGatewayId, @@ -157,10 +153,8 @@ abstract contract WatcherPrecompileCore is p.payloadId, p.deadline, p.dump.getCallType(), - p.dump.getWriteFinality(), p.gasLimit, p.value, - p.readAt, p.payload, p.target, _encodeAppGatewayId(p.appGateway), diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index a5c8f1dd..20b84491 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -269,7 +269,13 @@ contract SetupTest is Test { bytes memory transmitterSig ) = _getExecuteParams(payloadParams); - return socketBatcher.attestAndExecute(params, digest, watcherProof, transmitterSig); + return socketBatcher.attestAndExecute( + params, + payloadParams.switchboard, + digest, + watcherProof, + transmitterSig + ); } function resolvePromises(bytes32[] memory payloadIds, bytes[] memory returnData) internal { @@ -297,10 +303,8 @@ contract SetupTest is Test { params_.payloadId, params_.deadline, params_.dump.getCallType(), - params_.dump.getWriteFinality(), params_.gasLimit, params_.value, - params_.readAt, params_.payload, params_.target, _encodeAppGatewayId(params_.appGateway), @@ -347,7 +351,6 @@ contract SetupTest is Test { params = ExecuteParams({ deadline: payloadParams.deadline, - writeFinality: payloadParams.dump.getWriteFinality(), gasLimit: payloadParams.gasLimit, value: payloadParams.value, payload: payloadParams.payload, @@ -355,8 +358,7 @@ contract SetupTest is Test { requestCount: payloadParams.dump.getRequestCount(), batchCount: payloadParams.dump.getBatchCount(), payloadCount: payloadParams.dump.getPayloadCount(), - prevDigestsHash: payloadParams.prevDigestsHash, - switchboard: payloadParams.switchboard + prevDigestsHash: payloadParams.prevDigestsHash }); value = payloadParams.value; From 665150620e6a7c2ddd3562ecaa7cc576d6168140 Mon Sep 17 00:00:00 2001 From: Akash Date: Tue, 22 Apr 2025 17:39:41 +0530 Subject: [PATCH 076/108] fix: tests --- .../protocol/payload-delivery/FeesManager.sol | 19 ++++--- .../app-gateway/DeliveryHelper.sol | 10 ++-- .../app-gateway/FeesHelpers.sol | 6 +-- test/FeesTest.t.sol | 52 ++++++++++++++----- test/Inbox.t.sol | 9 +++- 5 files changed, 64 insertions(+), 32 deletions(-) diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 4f13a64e..073be21e 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -44,10 +44,6 @@ abstract contract FeesManagerStorage is IFeesManager { /// @dev requestCount => RequestFee mapping(uint40 => uint256) public requestCountCredits; - // slot 55 - /// @notice Mapping to track credits to be distributed to transmitters - /// @dev transmitter => amount - mapping(address => uint256) public transmitterCredits; // @dev amount uint256 public watcherPrecompileCredits; @@ -284,7 +280,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol _useBlockedUserCredits(requestMetadata.consumeFrom, blockedCredits, fees); // Assign fees to transmitter - transmitterCredits[transmitter_] += fees; + userCredits[transmitter_].totalCredits += fees; // Clean up storage delete requestCountCredits[requestCount_]; @@ -373,7 +369,8 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol // Check if amount is available in fees plug uint256 availableAmount = getAvailableFees(source); if (availableAmount < amount_) revert InsufficientFeesAvailable(); - + + _useAvailableUserCredits(source, amount_); tokenPoolBalances[chainSlug_][token_] -= amount_; // Add it to the queue and submit request @@ -395,7 +392,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol if (amount_ > maxFeesAvailableForWithdraw) revert InsufficientFeesAvailable(); // Clean up storage - transmitterCredits[transmitter_] -= amount_; + _useAvailableUserCredits(transmitter_, amount_); tokenPoolBalances[chainSlug_][token_] -= amount_; bytes memory payload = abi.encodeCall(IFeesPlug.withdrawFees, (token_, receiver_, amount_)); @@ -420,9 +417,10 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol function getMaxFeesAvailableForWithdraw(address transmitter_) public view returns (uint256) { uint256 watcherFees = watcherPrecompileLimits().getTotalFeesRequired(0, 1, 0, 1); + uint256 transmitterCredits = userCredits[transmitter_].totalCredits; return - transmitterCredits[transmitter_] > watcherFees - ? transmitterCredits[transmitter_] - watcherFees + transmitterCredits > watcherFees + ? transmitterCredits - watcherFees : 0; } @@ -454,7 +452,8 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol } /// @notice hook called by watcher precompile when request is finished - function finishRequest(uint40) external {} + function onRequestComplete(uint40 requestCount_, bytes memory) external { + } function _queue(uint32 chainSlug_, bytes memory payload_) internal { QueuePayloadParams memory queuePayloadParams = _createQueuePayloadParams( diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol index 81b1464f..a950bf92 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol @@ -59,10 +59,12 @@ contract DeliveryHelper is FeesHelpers { requestMetadata_.winningBid.transmitter ); - IAppGateway(requestMetadata_.appGateway).onRequestComplete( - requestCount_, - requestMetadata_.onCompleteData - ); + if (requestMetadata_.appGateway.code.length > 0) { + IAppGateway(requestMetadata_.appGateway).onRequestComplete( + requestCount_, + requestMetadata_.onCompleteData + ); + } } /// @notice Cancels a request and settles the fees diff --git a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol index 8db2607d..f737abc3 100644 --- a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol +++ b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol @@ -46,8 +46,8 @@ abstract contract FeesHelpers is RequestQueue { amount_, receiver_ ); - - return _batch(msg.sender, auctionManager_, fees_, bytes(""), bytes("")); + bytes memory feesApprovalData = abi.encode(msg.sender, msg.sender, true, bytes("")); + return _batch(msg.sender, auctionManager_, fees_, feesApprovalData, bytes("")); } /// @notice Withdraws fees to a specified receiver @@ -73,7 +73,7 @@ abstract contract FeesHelpers is RequestQueue { ); RequestMetadata memory requestMetadata = RequestMetadata({ - appGateway: address(this), + appGateway: addressResolver__.feesManager(), auctionManager: address(0), maxFees: 0, winningBid: Bid({transmitter: transmitter, fee: 0, extraData: new bytes(0)}), diff --git a/test/FeesTest.t.sol b/test/FeesTest.t.sol index 325987b9..6c9b7118 100644 --- a/test/FeesTest.t.sol +++ b/test/FeesTest.t.sol @@ -9,7 +9,7 @@ contract FeesTest is DeliveryHelperTest { uint256 constant depositAmount = 1 ether; uint256 constant feesAmount = 0.01 ether; address receiver = address(uint160(c++)); - + address user = address(uint160(c++)); uint32 feesChainSlug = arbChainSlug; SocketContracts feesConfig; @@ -34,7 +34,7 @@ contract FeesTest is DeliveryHelperTest { _deploy(feesChainSlug, IAppGateway(counterGateway), contractIds); } - function testDistributeFee() public { + function testWithdrawTransmitterFees() public { uint256 initialFeesPlugBalance = feesConfig.feesTokenUSDC.balanceOf( address(feesConfig.feesPlug) ); @@ -58,23 +58,18 @@ contract FeesTest is DeliveryHelperTest { uint40[] memory batches = watcherPrecompile.getBatches(requestCount); _finalizeBatch(batches[0], new bytes[](0), 0, false); assertEq( - transmitterReceiverBalanceBefore + bidAmount, + transmitterReceiverBalanceBefore + withdrawAmount, feesConfig.feesTokenUSDC.balanceOf(receiver), "Transmitter Balance should be correct" ); assertEq( - initialFeesPlugBalance - bidAmount, + initialFeesPlugBalance - withdrawAmount, feesConfig.feesTokenUSDC.balanceOf(address(feesConfig.feesPlug)), "FeesPlug Balance should be correct" ); } - function testWithdrawFeeTokens() public { - assertEq( - depositAmount, - feesConfig.feesTokenUSDC.balanceOf(address(feesConfig.feesPlug)), - "Balance should be correct" - ); + function testWithdrawFeeTokensAppGateway() public { uint256 receiverBalanceBefore = feesConfig.feesTokenUSDC.balanceOf(receiver); uint256 withdrawAmount = 0.5 ether; @@ -87,15 +82,44 @@ contract FeesTest is DeliveryHelperTest { ); executeRequest(new bytes[](0)); + assertEq( - depositAmount - withdrawAmount, - feesConfig.feesTokenUSDC.balanceOf(address(feesConfig.feesPlug)), - "Fees Balance should be correct" + receiverBalanceBefore + withdrawAmount, + feesConfig.feesTokenUSDC.balanceOf(receiver), + "Receiver Balance should be correct" + ); + } + + + function testWithdrawFeeTokensUser() public { + + depositUSDCFees( + user, + OnChainFees({ + chainSlug: feesChainSlug, + token: address(feesConfig.feesTokenUSDC), + amount: depositAmount + }) ); + uint256 receiverBalanceBefore = feesConfig.feesTokenUSDC.balanceOf(user); + uint256 withdrawAmount = 0.5 ether; + + vm.prank(user); + deliveryHelper.withdrawTo( + feesChainSlug, + address(feesConfig.feesTokenUSDC), + withdrawAmount, + user, + address(auctionManager), + maxFees + ); + executeRequest(new bytes[](0)); + + assertEq( receiverBalanceBefore + withdrawAmount, - feesConfig.feesTokenUSDC.balanceOf(receiver), + feesConfig.feesTokenUSDC.balanceOf(user), "Receiver Balance should be correct" ); } diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index cdd3baa7..3bdb8681 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -59,7 +59,14 @@ contract TriggerTest is DeliveryHelperTest { function testIncrementAfterTrigger() public { // Initial counter value should be 0 assertEq(gateway.counterVal(), 0, "Initial gateway counter should be 0"); - + depositUSDCFees( + address(gateway), + OnChainFees({ + chainSlug: arbChainSlug, + token: address(arbConfig.feesTokenUSDC), + amount: 1 ether + }) + ); // Simulate a message from another chain through the watcher uint256 incrementValue = 5; bytes32 triggerId = _encodeTriggerId(address(gateway), arbChainSlug); From 4d75d32fdf48305ecd687a5b6074237787f93695 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 23 Apr 2025 11:32:52 +0530 Subject: [PATCH 077/108] fix: read reentrancy --- contracts/protocol/socket/Socket.sol | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 1961b8c3..407214fc 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -155,7 +155,7 @@ contract Socket is SocketUtils { address plug_, bytes memory overrides_ ) internal returns (bytes32 triggerId) { - PlugConfig memory plugConfig = _plugConfigs[plug_]; + PlugConfig storage plugConfig = _plugConfigs[plug_]; // if no sibling plug is found for the given chain slug, revert // sends the trigger to connected app gateway @@ -167,7 +167,8 @@ contract Socket is SocketUtils { triggerId, plugConfig.switchboard, plug_, - overrides_, + // gets the overrides from the plug + IPlug(plug_).overrides(), msg.data ); } @@ -176,11 +177,7 @@ contract Socket is SocketUtils { /// @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) { - address plug = msg.sender; - // gets the overrides from the plug - bytes memory overrides = IPlug(plug).overrides(); - // return the trigger id - return abi.encode(_triggerAppGateway(plug, overrides)); + return abi.encode(_triggerAppGateway(plug, msg.sender)); } } From e7d5b0201431f8f14eb72fb64ad87fb9e8c2ebba Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 23 Apr 2025 11:35:17 +0530 Subject: [PATCH 078/108] feat: bump gas left by 5% --- contracts/protocol/socket/Socket.sol | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 407214fc..d67d97c8 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -15,6 +15,9 @@ contract Socket is SocketUtils { // @notice mapping of payload id to execution status mapping(bytes32 => ExecutionStatus) public payloadExecuted; + // @notice buffer to account for gas used by current contract execution + uint256 public constant GAS_LIMIT_BUFFER = 105; + //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// //////////////////////////////////////////////////////// @@ -120,7 +123,8 @@ contract Socket is SocketUtils { ExecuteParams memory executeParams_ ) 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, bool exceededMaxCopy, bytes memory returnData) = executeParams_ From 9d946e5708040874837069b10cf58d5db21878ad Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 23 Apr 2025 11:36:14 +0530 Subject: [PATCH 079/108] fix: unique sign for each chain --- contracts/protocol/socket/switchboard/FastSwitchboard.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contracts/protocol/socket/switchboard/FastSwitchboard.sol b/contracts/protocol/socket/switchboard/FastSwitchboard.sol index 12e0500a..ee0679a4 100644 --- a/contracts/protocol/socket/switchboard/FastSwitchboard.sol +++ b/contracts/protocol/socket/switchboard/FastSwitchboard.sol @@ -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; From 6a4d16f868a0a271a930df024e025a0da683bb12 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 23 Apr 2025 11:40:36 +0530 Subject: [PATCH 080/108] fix: build --- contracts/protocol/socket/Socket.sol | 8 ++------ test/SetupTest.t.sol | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index d67d97c8..3ea590e7 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -153,12 +153,8 @@ contract Socket is SocketUtils { //////////////////////////////////////////////////////// /** * @notice To trigger to a connected remote chain. Should only be called by a plug. - * @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_ - ) internal returns (bytes32 triggerId) { + 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 @@ -182,6 +178,6 @@ contract Socket is SocketUtils { /// @dev if ETH sent with the call, it will revert fallback(bytes calldata) external returns (bytes memory) { // return the trigger id - return abi.encode(_triggerAppGateway(plug, msg.sender)); + return abi.encode(_triggerAppGateway(msg.sender)); } } diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 20b84491..9849031e 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -269,13 +269,14 @@ contract SetupTest is Test { bytes memory transmitterSig ) = _getExecuteParams(payloadParams); - return socketBatcher.attestAndExecute( - params, - payloadParams.switchboard, - digest, - watcherProof, - transmitterSig - ); + return + socketBatcher.attestAndExecute( + params, + payloadParams.switchboard, + digest, + watcherProof, + transmitterSig + ); } function resolvePromises(bytes32[] memory payloadIds, bytes[] memory returnData) internal { @@ -312,7 +313,9 @@ contract SetupTest is Test { ); bytes32 digest = watcherPrecompile.getDigest(digestParams_); - bytes32 sigDigest = keccak256(abi.encode(address(socketConfig.switchboard), digest)); + bytes32 sigDigest = keccak256( + abi.encode(address(socketConfig.switchboard), socketConfig.chainSlug, digest) + ); bytes memory proof = _createSignature(sigDigest, watcherPrivateKey); return (proof, digest); } From c9ae29c757e7e6970872a808885ee584c2cc9330 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 23 Apr 2025 11:43:23 +0530 Subject: [PATCH 081/108] feat: store digest in socket --- contracts/protocol/socket/Socket.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 3ea590e7..f197a541 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -15,6 +15,9 @@ contract Socket is SocketUtils { // @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; @@ -93,6 +96,7 @@ contract Socket is SocketUtils { plugConfig.appGatewayId, executeParams_ ); + payloadIdToDigest[payloadId] = digest; // verify the digest _verify(digest, payloadId, plugConfig.switchboard); From 15ae407cc11c583d04b505545bce333bee580f2e Mon Sep 17 00:00:00 2001 From: Akash Date: Wed, 23 Apr 2025 13:39:02 +0530 Subject: [PATCH 082/108] fix: renames --- contracts/interfaces/IFeesManager.sol | 18 +-- .../payload-delivery/AuctionManager.sol | 4 +- .../protocol/payload-delivery/FeesManager.sol | 131 +++++++----------- .../protocol/payload-delivery/FeesPlug.sol | 4 +- .../app-gateway/DeliveryHelper.sol | 6 +- .../app-gateway/FeesHelpers.sol | 6 +- .../app-gateway/RequestQueue.sol | 2 +- .../core/WatcherPrecompile.sol | 2 +- .../core/WatcherPrecompileCore.sol | 4 +- .../WithdrawFeesArbitrumFeesPlug.s.sol | 2 +- test/DeliveryHelper.t.sol | 4 +- test/FeesTest.t.sol | 2 +- 12 files changed, 76 insertions(+), 109 deletions(-) diff --git a/contracts/interfaces/IFeesManager.sol b/contracts/interfaces/IFeesManager.sol index 7aa27314..f0fadb1b 100644 --- a/contracts/interfaces/IFeesManager.sol +++ b/contracts/interfaces/IFeesManager.sol @@ -4,21 +4,21 @@ pragma solidity ^0.8.21; import {Bid, QueuePayloadParams, PayloadSubmitParams, AppGatewayWhitelistParams} from "../protocol/utils/common/Structs.sol"; interface IFeesManager { - function blockFees( + function blockCredits( address consumeFrom_, uint256 transmitterCredits_, uint40 requestCount_ ) external; - function unblockFees(uint40 requestCount_) external; + function unblockCredits(uint40 requestCount_) external; - function isFeesEnough( + function isUserCreditsEnough( address consumeFrom_, address appGateway_, uint256 amount_ ) external view returns (bool); - function unblockAndAssignFees(uint40 requestCount_, address transmitter_) external; + function unblockAndAssignCredits(uint40 requestCount_, address transmitter_) external; function withdrawFees( address appGateway_, @@ -28,14 +28,14 @@ interface IFeesManager { address receiver_ ) external; - function assignWatcherPrecompileFeesFromRequestCount( + function assignWatcherPrecompileCreditsFromRequestCount( uint256 fees_, uint40 requestCount_ ) external; - function assignWatcherPrecompileFeesFromAddress(uint256 fees_, address consumeFrom_) external; + function assignWatcherPrecompileCreditsFromAddress(uint256 fees_, address consumeFrom_) external; - function incrementFeesDeposited( + function incrementCreditsDeposited( address depositTo_, uint32 chainSlug_, address token_, @@ -50,7 +50,7 @@ interface IFeesManager { function whitelistAppGateways(AppGatewayWhitelistParams[] calldata params_) external; - function getWithdrawTransmitterFeesPayloadParams( + function getWithdrawTransmitterCreditsPayloadParams( address transmitter_, uint32 chainSlug_, address token_, @@ -58,5 +58,5 @@ interface IFeesManager { uint256 amount_ ) external returns (PayloadSubmitParams[] memory); - function getMaxFeesAvailableForWithdraw(address transmitter_) external view returns (uint256); + function getMaxCreditsAvailableForWithdraw(address transmitter_) external view returns (uint256); } diff --git a/contracts/protocol/payload-delivery/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol index 818e28b3..40f1d38d 100644 --- a/contracts/protocol/payload-delivery/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -174,7 +174,7 @@ contract AuctionManager is auctionClosed[requestCount_] = true; RequestMetadata memory requestMetadata = _getRequestMetadata(requestCount_); // block the fees - IFeesManager(addressResolver__.feesManager()).blockFees( + IFeesManager(addressResolver__.feesManager()).blockCredits( requestMetadata.consumeFrom, winningBid.fee, requestCount_ @@ -210,7 +210,7 @@ contract AuctionManager is auctionClosed[requestCount_] = false; reAuctionCount[requestCount_]++; - IFeesManager(addressResolver__.feesManager()).unblockFees(requestCount_); + IFeesManager(addressResolver__.feesManager()).unblockCredits(requestCount_); emit AuctionRestarted(requestCount_); } diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 073be21e..7210ffea 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -66,24 +66,24 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @param requestCount The batch identifier /// @param consumeFrom The consume from address /// @param amount The blocked amount - event FeesBlocked(uint40 indexed requestCount, address indexed consumeFrom, uint256 amount); + event CreditsBlocked(uint40 indexed requestCount, address indexed consumeFrom, uint256 amount); /// @notice Emitted when transmitter fees are updated /// @param requestCount The batch identifier /// @param transmitter The transmitter address /// @param amount The new amount deposited - event TransmitterFeesUpdated( + event TransmitterCreditsUpdated( uint40 indexed requestCount, address indexed transmitter, uint256 amount ); - event WatcherPrecompileFeesAssigned(uint256 amount, address consumeFrom); + event WatcherPrecompileCreditsAssigned(uint256 amount, address consumeFrom); /// @notice Emitted when fees deposited are updated /// @param chainSlug The chain identifier /// @param appGateway The app gateway address /// @param token The token address /// @param amount The new amount deposited - event FeesDepositedUpdated( + event CreditsDepositedUpdated( uint32 indexed chainSlug, address indexed appGateway, address indexed token, @@ -94,7 +94,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @param requestCount The batch identifier /// @param transmitter The transmitter address /// @param amount The unblocked amount - event FeesUnblockedAndAssigned( + event CreditsUnblockedAndAssigned( uint40 indexed requestCount, address indexed transmitter, uint256 amount @@ -103,24 +103,28 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @notice Emitted when fees are unblocked /// @param requestCount The batch identifier /// @param appGateway The app gateway address - event FeesUnblocked(uint40 indexed requestCount, address indexed appGateway); + event CreditsUnblocked(uint40 indexed requestCount, address indexed appGateway); /// @notice Emitted when insufficient watcher precompile fees are available - event InsufficientWatcherPrecompileFeesAvailable( + event InsufficientWatcherPrecompileCreditsAvailable( uint32 chainSlug, address token, address consumeFrom ); /// @notice Error thrown when insufficient fees are available - error InsufficientFeesAvailable(); + error InsufficientCreditsAvailable(); /// @notice Error thrown when no fees are available for a transmitter error NoFeesForTransmitter(); /// @notice Error thrown when no fees was blocked - error NoFeesBlocked(); + error NoCreditsBlocked(); /// @notice Error thrown when caller is invalid error InvalidCaller(); /// @notice Error thrown when user signature is invalid error InvalidUserSignature(); + /// @notice Error thrown when app gateway is not whitelisted + error AppGatewayNotWhitelisted(); + + error InvalidAmount(); constructor() { _disableInitializers(); // disable for implementation } @@ -144,7 +148,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @notice Returns available (unblocked) fees for a gateway /// @param consumeFrom_ The app gateway address /// @return The available fee amount - function getAvailableFees(address consumeFrom_) public view returns (uint256) { + function getAvailableCredits(address consumeFrom_) public view returns (uint256) { UserCredits memory userCredit = userCredits[consumeFrom_]; if (userCredit.totalCredits == 0 || userCredit.totalCredits <= userCredit.blockedCredits) return 0; @@ -156,42 +160,28 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @param amount_ The amount deposited // @dev only callable by watcher precompile // @dev will need tokenAmount_ and creditAmount_ when introduce tokens except stables - function incrementFeesDeposited( + function depositCredits( address depositTo_, uint32 chainSlug_, address token_, - uint256 amount_, - uint256 signatureNonce_, - bytes memory signature_ - ) external { - _isWatcherSignatureValid( - abi.encode( - this.incrementFeesDeposited.selector, - depositTo_, - chainSlug_, - token_, - amount_ - ), - signatureNonce_, - signature_ - ); - + uint256 amount_ + ) external payable { + if (msg.value != amount_) revert InvalidAmount(); UserCredits storage userCredit = userCredits[depositTo_]; userCredit.totalCredits += amount_; tokenPoolBalances[chainSlug_][token_] += amount_; - - emit FeesDepositedUpdated(chainSlug_, depositTo_, token_, amount_); + emit CreditsDepositedUpdated(chainSlug_, depositTo_, token_, amount_); } - function isFeesEnough( + function isUserCreditsEnough( address consumeFrom_, address appGateway_, uint256 amount_ ) external view returns (bool) { // If consumeFrom is not appGateway, check if it is whitelisted if (consumeFrom_ != appGateway_ && !isAppGatewayWhitelisted[consumeFrom_][appGateway_]) - return false; - return getAvailableFees(consumeFrom_) >= amount_; + revert AppGatewayNotWhitelisted(); + return getAvailableCredits(consumeFrom_) >= amount_; } function _processFeeApprovalData( @@ -245,27 +235,27 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @param transmitterCredits_ The total fees to block /// @param requestCount_ The batch identifier /// @dev Only callable by delivery helper - function blockFees( + function blockCredits( address consumeFrom_, uint256 transmitterCredits_, uint40 requestCount_ ) external onlyAuctionManager(requestCount_) { // Block fees - if (getAvailableFees(consumeFrom_) < transmitterCredits_) - revert InsufficientFeesAvailable(); + if (getAvailableCredits(consumeFrom_) < transmitterCredits_) + revert InsufficientCreditsAvailable(); UserCredits storage userCredit = userCredits[consumeFrom_]; userCredit.blockedCredits += transmitterCredits_; requestCountCredits[requestCount_] = transmitterCredits_; - emit FeesBlocked(requestCount_, consumeFrom_, transmitterCredits_); + emit CreditsBlocked(requestCount_, consumeFrom_, transmitterCredits_); } /// @notice Unblocks fees after successful execution and assigns them to the transmitter /// @param requestCount_ The async ID of the executed batch /// @param transmitter_ The address of the transmitter who executed the batch - function unblockAndAssignFees( + function unblockAndAssignCredits( uint40 requestCount_, address transmitter_ ) external override onlyDeliveryHelper { @@ -284,7 +274,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol // Clean up storage delete requestCountCredits[requestCount_]; - emit FeesUnblockedAndAssigned(requestCount_, transmitter_, fees); + emit CreditsUnblockedAndAssigned(requestCount_, transmitter_, fees); } function _useBlockedUserCredits( @@ -299,36 +289,36 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol function _useAvailableUserCredits(address consumeFrom_, uint256 toConsume_) internal { UserCredits storage userCredit = userCredits[consumeFrom_]; - if (userCredit.totalCredits < toConsume_) revert InsufficientFeesAvailable(); + if (userCredit.totalCredits < toConsume_) revert InsufficientCreditsAvailable(); userCredit.totalCredits -= toConsume_; } - function assignWatcherPrecompileFeesFromRequestCount( - uint256 fees_, + function assignWatcherPrecompileCreditsFromRequestCount( + uint256 amount_, uint40 requestCount_ ) external onlyWatcherPrecompile { RequestMetadata memory requestMetadata = deliveryHelper__().getRequestMetadata( requestCount_ ); - _assignWatcherPrecompileFees(fees_, requestMetadata.consumeFrom); + _assignWatcherPrecompileCredits(amount_, requestMetadata.consumeFrom); } - function assignWatcherPrecompileFeesFromAddress( - uint256 fees_, + function assignWatcherPrecompileCreditsFromAddress( + uint256 amount_, address consumeFrom_ ) external onlyWatcherPrecompile { - _assignWatcherPrecompileFees(fees_, consumeFrom_); + _assignWatcherPrecompileCredits(amount_, consumeFrom_); } - function _assignWatcherPrecompileFees(uint256 fees_, address consumeFrom_) internal { + function _assignWatcherPrecompileCredits(uint256 amount_, address consumeFrom_) internal { // deduct the fees from the user - _useAvailableUserCredits(consumeFrom_, fees_); + _useAvailableUserCredits(consumeFrom_, amount_); // add the fees to the watcher precompile - watcherPrecompileCredits += fees_; - emit WatcherPrecompileFeesAssigned(fees_, consumeFrom_); + watcherPrecompileCredits += amount_; + emit WatcherPrecompileCreditsAssigned(amount_, consumeFrom_); } - function unblockFees(uint40 requestCount_) external { + function unblockCredits(uint40 requestCount_) external { RequestMetadata memory requestMetadata = deliveryHelper__().getRequestMetadata( requestCount_ ); @@ -346,7 +336,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol userCredit.blockedCredits -= blockedCredits; delete requestCountCredits[requestCount_]; - emit FeesUnblocked(requestCount_, requestMetadata.consumeFrom); + emit CreditsUnblocked(requestCount_, requestMetadata.consumeFrom); } /// @notice Withdraws funds to a specified receiver @@ -356,7 +346,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @param token_ The address of the token /// @param amount_ The amount of tokens to withdraw /// @param receiver_ The address of the receiver - function withdrawFees( + function withdrawCredits( address originAppGatewayOrUser_, uint32 chainSlug_, address token_, @@ -367,9 +357,9 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol address source = _getCoreAppGateway(originAppGatewayOrUser_); // Check if amount is available in fees plug - uint256 availableAmount = getAvailableFees(source); - if (availableAmount < amount_) revert InsufficientFeesAvailable(); - + uint256 availableAmount = getAvailableCredits(source); + if (availableAmount < amount_) revert InsufficientCreditsAvailable(); + _useAvailableUserCredits(source, amount_); tokenPoolBalances[chainSlug_][token_] -= amount_; @@ -381,15 +371,15 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @param chainSlug_ The chain identifier /// @param token_ The token address /// @param receiver_ The address of the receiver - function getWithdrawTransmitterFeesPayloadParams( + function getWithdrawTransmitterCreditsPayloadParams( address transmitter_, uint32 chainSlug_, address token_, address receiver_, uint256 amount_ ) external onlyDeliveryHelper returns (PayloadSubmitParams[] memory) { - uint256 maxFeesAvailableForWithdraw = getMaxFeesAvailableForWithdraw(transmitter_); - if (amount_ > maxFeesAvailableForWithdraw) revert InsufficientFeesAvailable(); + uint256 maxCreditsAvailableForWithdraw = getMaxCreditsAvailableForWithdraw(transmitter_); + if (amount_ > maxCreditsAvailableForWithdraw) revert InsufficientCreditsAvailable(); // Clean up storage _useAvailableUserCredits(transmitter_, amount_); @@ -415,7 +405,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol return payloadSubmitParamsArray; } - function getMaxFeesAvailableForWithdraw(address transmitter_) public view returns (uint256) { + function getMaxCreditsAvailableForWithdraw(address transmitter_) public view returns (uint256) { uint256 watcherFees = watcherPrecompileLimits().getTotalFeesRequired(0, 1, 0, 1); uint256 transmitterCredits = userCredits[transmitter_].totalCredits; return @@ -462,30 +452,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol ); deliveryHelper__().queue(queuePayloadParams); } - - function _encodeFeesId(uint256 feesCounter_) internal view returns (bytes32) { - // watcher address (160 bits) | counter (64 bits) - return bytes32((uint256(uint160(address(this))) << 64) | feesCounter_); - } - function _getFeesPlugAddress(uint32 chainSlug_) internal view returns (address) { return watcherPrecompileConfig().feesPlug(chainSlug_); } - - function _isWatcherSignatureValid( - bytes memory inputData_, - uint256 signatureNonce_, - bytes memory signature_ - ) internal { - if (isNonceUsed[signatureNonce_]) revert NonceUsed(); - isNonceUsed[signatureNonce_] = true; - - bytes32 digest = keccak256( - abi.encode(address(this), evmxSlug, signatureNonce_, inputData_) - ); - digest = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", digest)); - // recovered signer is checked for the valid roles later - address signer = ECDSA.recover(digest, signature_); - if (signer != owner()) revert InvalidWatcherSignature(); - } } diff --git a/contracts/protocol/payload-delivery/FeesPlug.sol b/contracts/protocol/payload-delivery/FeesPlug.sol index 8a55d9ac..eec87b85 100644 --- a/contracts/protocol/payload-delivery/FeesPlug.sol +++ b/contracts/protocol/payload-delivery/FeesPlug.sol @@ -36,7 +36,7 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { event TokenRemovedFromWhitelist(address token); /// @notice Modifier to check if the balance of a token is enough to withdraw - modifier isFeesEnough(address feeToken_, uint256 fee_) { + modifier isUserCreditsEnough(address feeToken_, uint256 fee_) { uint balance_ = ERC20(feeToken_).balanceOf(address(this)); if (balance_ < fee_) revert InsufficientTokenBalance(feeToken_, balance_, fee_); _; @@ -58,7 +58,7 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { address token_, address receiver_, uint256 amount_ - ) external override onlySocket isFeesEnough(token_, amount_) { + ) external override onlySocket isUserCreditsEnough(token_, amount_) { SafeTransferLib.safeTransfer(token_, receiver_, amount_); emit FeesWithdrawn(token_, receiver_, amount_); } diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol index a950bf92..24a78e3b 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol @@ -54,7 +54,7 @@ contract DeliveryHelper is FeesHelpers { // todo: move it to watcher precompile if (requestMetadata_.winningBid.transmitter != address(0)) - IFeesManager(addressResolver__.feesManager()).unblockAndAssignFees( + IFeesManager(addressResolver__.feesManager()).unblockAndAssignCredits( requestCount_, requestMetadata_.winningBid.transmitter ); @@ -94,13 +94,13 @@ contract DeliveryHelper is FeesHelpers { function _settleFees(uint40 requestCount_) internal { // If the request has a winning bid, ie. transmitter already assigned, unblock and assign fees if (requests[requestCount_].winningBid.transmitter != address(0)) { - IFeesManager(addressResolver__.feesManager()).unblockAndAssignFees( + IFeesManager(addressResolver__.feesManager()).unblockAndAssignCredits( requestCount_, requests[requestCount_].winningBid.transmitter ); } else { // If the request has no winning bid, ie. transmitter not assigned, unblock fees - IFeesManager(addressResolver__.feesManager()).unblockFees(requestCount_); + IFeesManager(addressResolver__.feesManager()).unblockCredits(requestCount_); } } diff --git a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol index f737abc3..d7834d8e 100644 --- a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol +++ b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol @@ -9,18 +9,18 @@ abstract contract FeesHelpers is RequestQueue { // slots [258-308] reserved for gap uint256[50] _gap_batch_async; + error NewMaxFeesLowerThanCurrent(uint256 current, uint256 new_); /// @notice Increases the fees for a request if no bid is placed /// @param requestCount_ The ID of the request /// @param newMaxFees_ The new maximum fees function increaseFees(uint40 requestCount_, uint256 newMaxFees_) external override { address appGateway = _getCoreAppGateway(msg.sender); - // todo: should we allow core app gateway too? if (appGateway != requests[requestCount_].appGateway) { revert OnlyAppGateway(); } - if (requests[requestCount_].winningBid.transmitter != address(0)) revert WinningBidExists(); + if (requests[requestCount_].maxFees >= newMaxFees_) revert NewMaxFeesLowerThanCurrent(requests[requestCount_].maxFees, newMaxFees_); requests[requestCount_].maxFees = newMaxFees_; emit FeesIncreased(appGateway, requestCount_, newMaxFees_); } @@ -64,7 +64,7 @@ abstract contract FeesHelpers is RequestQueue { PayloadSubmitParams[] memory payloadSubmitParamsArray = IFeesManager( addressResolver__.feesManager() - ).getWithdrawTransmitterFeesPayloadParams( + ).getWithdrawTransmitterCreditsPayloadParams( transmitter, chainSlug_, token_, diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index 80b09887..835b0193 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -137,7 +137,7 @@ abstract contract RequestQueue is DeliveryUtils { (consumeFrom, , ) = IFeesManager(addressResolver__.feesManager()) .whitelistAppGatewayWithSignature(feesApprovalData_); if ( - !IFeesManager(addressResolver__.feesManager()).isFeesEnough( + !IFeesManager(addressResolver__.feesManager()).isUserCreditsEnough( consumeFrom, appGateway_, maxFees_ diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index bcea7bd0..121cf7ce 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -312,7 +312,7 @@ contract WatcherPrecompile is RequestHandler { ) ) revert InvalidCallerTriggered(); - IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFeesFromAddress( + IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileCreditsFromAddress( watcherPrecompileLimits__.callBackFees(), appGateway ); diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 14c6323d..1ab6e78f 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -257,7 +257,7 @@ abstract contract WatcherPrecompileCore is function _consumeCallbackFeesFromRequestCount(uint256 fees_, uint40 requestCount_) internal { // for callbacks in all precompiles uint256 feesToConsume = fees_ + watcherPrecompileLimits__.callBackFees(); - IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFeesFromRequestCount( + IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileCreditsFromRequestCount( feesToConsume, requestCount_ ); @@ -266,7 +266,7 @@ abstract contract WatcherPrecompileCore is function _consumeCallbackFeesFromAddress(uint256 fees_, address consumeFrom_) internal { // for callbacks in all precompiles uint256 feesToConsume = fees_ + watcherPrecompileLimits__.callBackFees(); - IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileFeesFromAddress( + IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileCreditsFromAddress( feesToConsume, consumeFrom_ ); diff --git a/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol b/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol index b2b50a25..ecd8b2b6 100644 --- a/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol +++ b/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol @@ -17,7 +17,7 @@ contract WithdrawFees is Script { address appGatewayAddress = vm.envAddress("APP_GATEWAY"); CounterAppGateway appGateway = CounterAppGateway(appGatewayAddress); - uint256 availableFees = feesManager.getAvailableFees(appGatewayAddress); + uint256 availableFees = feesManager.getMaxCreditsAvailableForWithdraw(appGatewayAddress); console.log("Available fees:", availableFees); if (availableFees > 0) { diff --git a/test/DeliveryHelper.t.sol b/test/DeliveryHelper.t.sol index bf77d5ec..5ea0036d 100644 --- a/test/DeliveryHelper.t.sol +++ b/test/DeliveryHelper.t.sol @@ -190,7 +190,7 @@ contract DeliveryHelperTest is SetupTest { vm.stopPrank(); bytes memory bytesInput = abi.encode( - IFeesManager.incrementFeesDeposited.selector, + IFeesManager.incrementCreditsDeposited.selector, appGateway_, fees_.chainSlug, fees_.token, @@ -201,7 +201,7 @@ contract DeliveryHelperTest is SetupTest { abi.encode(address(feesManager), evmxSlug, signatureNonce, bytesInput) ); bytes memory sig = _createSignature(digest, watcherPrivateKey); - feesManager.incrementFeesDeposited( + feesManager.incrementCreditsDeposited( appGateway_, fees_.chainSlug, fees_.token, diff --git a/test/FeesTest.t.sol b/test/FeesTest.t.sol index 6c9b7118..3383b127 100644 --- a/test/FeesTest.t.sol +++ b/test/FeesTest.t.sol @@ -46,7 +46,7 @@ contract FeesTest is DeliveryHelperTest { ); uint256 transmitterReceiverBalanceBefore = feesConfig.feesTokenUSDC.balanceOf(receiver); - uint256 withdrawAmount = feesManager.getMaxFeesAvailableForWithdraw(transmitterEOA); + uint256 withdrawAmount = feesManager.getMaxCreditsAvailableForWithdraw(transmitterEOA); vm.startPrank(transmitterEOA); uint40 requestCount = deliveryHelper.withdrawTransmitterFees( feesChainSlug, From 250bc8f9190302d802f947ffa17d033c45120f1a Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 23 Apr 2025 16:34:00 +0530 Subject: [PATCH 083/108] fix: app gateway id --- .../protocol/payload-delivery/app-gateway/RequestQueue.sol | 2 +- test/SetupTest.t.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index b49ae310..8085cf8e 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -133,7 +133,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 diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 9849031e..f6636df2 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -75,7 +75,7 @@ contract SetupTest is Test { ERC1967Factory public proxyFactory; event Initialized(uint64 version); - event FinalizeRequested(bytes32 payloadId, PayloadParams payloadParams); + event FinalizeRequested(bytes32 digest, PayloadParams payloadParams); //////////////////////////////////// Setup //////////////////////////////////// From 0613efa61f1e0559ecd35f6de170514e5b6cf7db Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 23 Apr 2025 19:12:15 +0530 Subject: [PATCH 084/108] feat: wrap unwrap fees --- .../protocol/payload-delivery/FeesManager.sol | 30 ++++++++++++++----- .../core/WatcherPrecompile.sol | 7 ++--- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 7210ffea..119664ff 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -11,7 +11,7 @@ import "../../interfaces/IFeesManager.sol"; import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; import {NotAuctionManager, InvalidWatcherSignature, NonceUsed} from "../utils/common/Errors.sol"; import {Bid, CallType, Parallel, WriteFinality, QueuePayloadParams, IsPlug, PayloadSubmitParams, RequestMetadata, UserCredits} from "../utils/common/Structs.sol"; -import {console} from "forge-std/console.sol"; + abstract contract FeesManagerStorage is IFeesManager { // slots [0-49] reserved for gap uint256[50] _gap_before; @@ -44,7 +44,6 @@ abstract contract FeesManagerStorage is IFeesManager { /// @dev requestCount => RequestFee mapping(uint40 => uint256) public requestCountCredits; - // @dev amount uint256 public watcherPrecompileCredits; @@ -125,6 +124,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol error AppGatewayNotWhitelisted(); error InvalidAmount(); + constructor() { _disableInitializers(); // disable for implementation } @@ -173,6 +173,23 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol emit CreditsDepositedUpdated(chainSlug_, depositTo_, token_, amount_); } + function wrap() external payable { + UserCredits storage userCredit = userCredits[msg.sender]; + userCredit.totalCredits += msg.value; + emit CreditsWrapped(msg.sender, msg.value); + } + + function unwrap(uint256 amount_) external { + UserCredits storage userCredit = userCredits[msg.sender]; + if (userCredit.totalCredits < amount_) revert InsufficientCreditsAvailable(); + userCredit.totalCredits -= amount_; + + // todo: if contract balance not enough, take from our pool? + if (address(this).balance < amount_) revert InsufficientBalance(); + payable(msg.sender).transfer(amount_); + emit CreditsUnwrapped(msg.sender, amount_); + } + function isUserCreditsEnough( address consumeFrom_, address appGateway_, @@ -408,10 +425,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol function getMaxCreditsAvailableForWithdraw(address transmitter_) public view returns (uint256) { uint256 watcherFees = watcherPrecompileLimits().getTotalFeesRequired(0, 1, 0, 1); uint256 transmitterCredits = userCredits[transmitter_].totalCredits; - return - transmitterCredits > watcherFees - ? transmitterCredits - watcherFees - : 0; + return transmitterCredits > watcherFees ? transmitterCredits - watcherFees : 0; } function _getSwitchboard(uint32 chainSlug_) internal view returns (address) { @@ -442,8 +456,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol } /// @notice hook called by watcher precompile when request is finished - function onRequestComplete(uint40 requestCount_, bytes memory) external { - } + function onRequestComplete(uint40 requestCount_, bytes memory) external {} function _queue(uint32 chainSlug_, bytes memory payload_) internal { QueuePayloadParams memory queuePayloadParams = _createQueuePayloadParams( @@ -452,6 +465,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol ); deliveryHelper__().queue(queuePayloadParams); } + function _getFeesPlugAddress(uint32 chainSlug_) internal view returns (address) { return watcherPrecompileConfig().feesPlug(chainSlug_); } diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 9213a0d0..9a85d88f 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -175,7 +175,6 @@ contract WatcherPrecompile is RequestHandler { if (r.middleware != msg.sender) revert InvalidCaller(); r.isRequestCancelled = true; - emit RequestCancelledFromGateway(requestCount); } @@ -313,9 +312,9 @@ contract WatcherPrecompile is RequestHandler { ) revert InvalidCallerTriggered(); IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileCreditsFromAddress( - watcherPrecompileLimits__.callBackFees(), - appGateway - ); + watcherPrecompileLimits__.callBackFees(), + appGateway + ); appGatewayCaller = appGateway; appGatewayCalled[params_[i].triggerId] = true; From a8856eb7bdf44f7cc36b6c12e34cc47ed2c2e5e6 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 23 Apr 2025 19:26:25 +0530 Subject: [PATCH 085/108] fix: cache trigger prefix --- contracts/protocol/socket/SocketUtils.sol | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index e862b6a1..9384e5c9 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -18,6 +18,8 @@ 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; @@ -31,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_); } @@ -107,12 +111,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++); } ////////////////////////////////////////////// From 930caf840b2756039f3f3f92b974a62afaab24a2 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 23 Apr 2025 19:32:27 +0530 Subject: [PATCH 086/108] feat: extra data in digest --- contracts/protocol/socket/SocketUtils.sol | 3 ++- contracts/protocol/utils/common/Structs.sol | 1 + .../protocol/watcherPrecompile/core/WatcherPrecompileCore.sol | 4 +++- test/SetupTest.t.sol | 3 ++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index 9384e5c9..bbcd22b3 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -65,7 +65,8 @@ abstract contract SocketUtils is SocketConfig { executeParams_.payload, executeParams_.target, appGatewayId_, - executeParams_.prevDigestsHash + executeParams_.prevDigestsHash, + executeParams_.extraData ) ); } diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index c6e3f29d..03bf96f3 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -229,6 +229,7 @@ struct ExecuteParams { uint40 batchCount; uint40 payloadCount; bytes32 prevDigestsHash; // should be id? hash of hashes + bytes extraData; } /// @notice Struct containing fee amounts and status diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 1c3881dc..d5c968d0 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -133,7 +133,9 @@ abstract contract WatcherPrecompileCore is params_.payload, params_.target, params_.appGatewayId, - params_.prevDigestsHash + params_.prevDigestsHash, + // later can take it form app gateway in forwarder, no use right now + bytes("") ) ); } diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index f6636df2..7db4c388 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -361,7 +361,8 @@ contract SetupTest is Test { requestCount: payloadParams.dump.getRequestCount(), batchCount: payloadParams.dump.getBatchCount(), payloadCount: payloadParams.dump.getPayloadCount(), - prevDigestsHash: payloadParams.prevDigestsHash + prevDigestsHash: payloadParams.prevDigestsHash, + extraData: bytes("") }); value = payloadParams.value; From 2ef4bccfb6ad1ef7b4761ab104cf18dddd486f26 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 23 Apr 2025 23:41:32 +0530 Subject: [PATCH 087/108] fix: tests --- contracts/protocol/socket/Socket.sol | 6 ++++++ contracts/protocol/socket/SocketUtils.sol | 2 +- contracts/protocol/utils/common/Structs.sol | 1 + test/SetupTest.t.sol | 1 + test/apps/Counter.t.sol | 2 +- 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index f197a541..f4b44bee 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -48,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 @@ -70,6 +74,8 @@ contract Socket is SocketUtils { ) 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 diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index bbcd22b3..7000b771 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -59,7 +59,7 @@ abstract contract SocketUtils is SocketConfig { transmitter_, payloadId_, executeParams_.deadline, - CallType.WRITE, + executeParams_.callType, executeParams_.gasLimit, executeParams_.value, executeParams_.payload, diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 03bf96f3..4b2ba690 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -220,6 +220,7 @@ struct RequestMetadata { } struct ExecuteParams { + CallType callType; uint256 deadline; uint256 gasLimit; uint256 value; diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 7db4c388..39d03809 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -353,6 +353,7 @@ contract SetupTest is Test { transmitterSig = _createSignature(transmitterDigest, transmitterPrivateKey); params = ExecuteParams({ + callType: payloadParams.dump.getCallType(), deadline: payloadParams.deadline, gasLimit: payloadParams.gasLimit, value: payloadParams.value, diff --git a/test/apps/Counter.t.sol b/test/apps/Counter.t.sol index 243abcfc..cb9b8290 100644 --- a/test/apps/Counter.t.sol +++ b/test/apps/Counter.t.sol @@ -27,7 +27,7 @@ contract CounterTest is DeliveryHelperTest { requestCount = _deploy(chainSlug, IAppGateway(counterGateway), contractIds); } - function testCounterDeployment() external { + function testCounterDeployment1() external { deploySetup(); deployCounterApp(arbChainSlug); From ca5d7d5a3d575e0bbae8bb7275c8d8277789b9e5 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 23 Apr 2025 23:44:34 +0530 Subject: [PATCH 088/108] fix: comments --- contracts/protocol/utils/common/Structs.sol | 4 ++-- .../protocol/watcherPrecompile/core/WatcherPrecompileCore.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 4b2ba690..96c5e72d 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -137,7 +137,7 @@ struct DigestParams { bytes payload; address target; bytes32 appGatewayId; - bytes32 prevDigestsHash; // should be id? hash of hashes + bytes32 prevDigestsHash; } struct QueuePayloadParams { @@ -229,7 +229,7 @@ struct ExecuteParams { uint40 requestCount; uint40 batchCount; uint40 payloadCount; - bytes32 prevDigestsHash; // should be id? hash of hashes + bytes32 prevDigestsHash; bytes extraData; } diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index d5c968d0..a6cc0400 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -117,6 +117,7 @@ abstract contract WatcherPrecompileCore is // ================== Helper functions ================== /// @notice Calculates the digest hash of payload parameters + /// @dev extraData is empty for now, not needed for this EVMx /// @param params_ The payload parameters to calculate the digest for /// @return digest The calculated digest hash /// @dev This function creates a keccak256 hash of the payload parameters @@ -134,7 +135,6 @@ abstract contract WatcherPrecompileCore is params_.target, params_.appGatewayId, params_.prevDigestsHash, - // later can take it form app gateway in forwarder, no use right now bytes("") ) ); From 02e9067539598ccdda901d6f5845725f0d54e9a9 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 24 Apr 2025 17:21:42 +0530 Subject: [PATCH 089/108] fix: tests --- contracts/interfaces/IFeesManager.sol | 41 ++++++++------- .../interfaces/IWatcherPrecompileLimits.sol | 2 +- .../protocol/payload-delivery/FeesManager.sol | 50 ++++++++++++++++--- .../app-gateway/FeesHelpers.sol | 5 +- contracts/protocol/socket/Socket.sol | 6 ++- contracts/protocol/socket/SocketConfig.sol | 3 +- .../WatcherPrecompileLimits.sol | 8 +-- .../core/WatcherPrecompile.sol | 6 +-- .../core/WatcherPrecompileCore.sol | 8 ++- script/helpers/AppGatewayFeeBalance.s.sol | 2 +- test/DeliveryHelper.t.sol | 24 ++++----- test/FeesTest.t.sol | 9 +--- test/SetupTest.t.sol | 2 +- test/mock/MockFastSwitchboard.sol | 4 +- 14 files changed, 102 insertions(+), 68 deletions(-) diff --git a/contracts/interfaces/IFeesManager.sol b/contracts/interfaces/IFeesManager.sol index f0fadb1b..4dc24fc5 100644 --- a/contracts/interfaces/IFeesManager.sol +++ b/contracts/interfaces/IFeesManager.sol @@ -20,28 +20,14 @@ interface IFeesManager { function unblockAndAssignCredits(uint40 requestCount_, address transmitter_) external; - function withdrawFees( - address appGateway_, - uint32 chainSlug_, - address token_, - uint256 amount_, - address receiver_ - ) external; - function assignWatcherPrecompileCreditsFromRequestCount( uint256 fees_, uint40 requestCount_ ) external; - function assignWatcherPrecompileCreditsFromAddress(uint256 fees_, address consumeFrom_) external; - - function incrementCreditsDeposited( - address depositTo_, - uint32 chainSlug_, - address token_, - uint256 amount_, - uint256 signatureNonce_, - bytes memory signature_ + function assignWatcherPrecompileCreditsFromAddress( + uint256 fees_, + address consumeFrom_ ) external; function whitelistAppGatewayWithSignature( @@ -58,5 +44,24 @@ interface IFeesManager { uint256 amount_ ) external returns (PayloadSubmitParams[] memory); - function getMaxCreditsAvailableForWithdraw(address transmitter_) external view returns (uint256); + function getMaxCreditsAvailableForWithdraw( + address transmitter_ + ) external view returns (uint256); + + function withdrawCredits( + address originAppGatewayOrUser_, + uint32 chainSlug_, + address token_, + uint256 amount_, + address receiver_ + ) external; + + function depositCredits( + address depositTo_, + uint32 chainSlug_, + address token_, + uint256 amount_, + uint256 signatureNonce_, + bytes memory signature_ + ) external; } diff --git a/contracts/interfaces/IWatcherPrecompileLimits.sol b/contracts/interfaces/IWatcherPrecompileLimits.sol index 9179706a..02899a72 100644 --- a/contracts/interfaces/IWatcherPrecompileLimits.sol +++ b/contracts/interfaces/IWatcherPrecompileLimits.sol @@ -57,7 +57,7 @@ interface IWatcherPrecompileLimits { function queryFees() external view returns (uint256); function finalizeFees() external view returns (uint256); - function scheduleFees() external view returns (uint256); + function timeoutFees() external view returns (uint256); function callBackFees() external view returns (uint256); /// @notice Emitted when limit parameters are updated diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 119664ff..34a6ac5e 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -4,10 +4,8 @@ pragma solidity ^0.8.21; import {Ownable} from "solady/auth/Ownable.sol"; import "solady/utils/Initializable.sol"; import "solady/utils/ECDSA.sol"; - import {IFeesPlug} from "../../interfaces/IFeesPlug.sol"; import "../../interfaces/IFeesManager.sol"; - import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; import {NotAuctionManager, InvalidWatcherSignature, NonceUsed} from "../utils/common/Errors.sol"; import {Bid, CallType, Parallel, WriteFinality, QueuePayloadParams, IsPlug, PayloadSubmitParams, RequestMetadata, UserCredits} from "../utils/common/Structs.sol"; @@ -82,7 +80,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @param appGateway The app gateway address /// @param token The token address /// @param amount The new amount deposited - event CreditsDepositedUpdated( + event CreditsDeposited( uint32 indexed chainSlug, address indexed appGateway, address indexed token, @@ -110,6 +108,13 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol address token, address consumeFrom ); + + /// @notice Emitted when credits are wrapped + event CreditsWrapped(address indexed consumeFrom, uint256 amount); + + /// @notice Emitted when credits are unwrapped + event CreditsUnwrapped(address indexed consumeFrom, uint256 amount); + /// @notice Error thrown when insufficient fees are available error InsufficientCreditsAvailable(); /// @notice Error thrown when no fees are available for a transmitter @@ -124,6 +129,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol error AppGatewayNotWhitelisted(); error InvalidAmount(); + error InsufficientBalance(); constructor() { _disableInitializers(); // disable for implementation @@ -164,13 +170,32 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol address depositTo_, uint32 chainSlug_, address token_, - uint256 amount_ - ) external payable { - if (msg.value != amount_) revert InvalidAmount(); + uint256 amount_, + uint256 signatureNonce_, + bytes memory signature_ + ) external { + if (isNonceUsed[signatureNonce_]) revert NonceUsed(); + isNonceUsed[signatureNonce_] = true; + + // check signature + bytes32 digest = keccak256( + abi.encode( + depositTo_, + chainSlug_, + token_, + amount_, + address(this), + evmxSlug, + signatureNonce_ + ) + ); + + if (_recoverSigner(digest, signature_) != owner()) revert InvalidWatcherSignature(); + UserCredits storage userCredit = userCredits[depositTo_]; userCredit.totalCredits += amount_; tokenPoolBalances[chainSlug_][token_] += amount_; - emit CreditsDepositedUpdated(chainSlug_, depositTo_, token_, amount_); + emit CreditsDeposited(chainSlug_, depositTo_, token_, amount_); } function wrap() external payable { @@ -220,7 +245,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol isApproved ) ); - if (ECDSA.recover(digest, signature_) != consumeFrom) revert InvalidUserSignature(); + if (_recoverSigner(digest, signature_) != consumeFrom) revert InvalidUserSignature(); isAppGatewayWhitelisted[consumeFrom][appGateway] = isApproved; userNonce[consumeFrom]++; @@ -469,4 +494,13 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol function _getFeesPlugAddress(uint32 chainSlug_) internal view returns (address) { return watcherPrecompileConfig().feesPlug(chainSlug_); } + + function _recoverSigner( + bytes32 digest_, + bytes memory signature_ + ) internal view returns (address signer) { + bytes32 digest = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", digest_)); + // recovered signer is checked for the valid roles later + signer = ECDSA.recover(digest, signature_); + } } diff --git a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol index d7834d8e..8b37a58d 100644 --- a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol +++ b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol @@ -20,7 +20,8 @@ abstract contract FeesHelpers is RequestQueue { revert OnlyAppGateway(); } if (requests[requestCount_].winningBid.transmitter != address(0)) revert WinningBidExists(); - if (requests[requestCount_].maxFees >= newMaxFees_) revert NewMaxFeesLowerThanCurrent(requests[requestCount_].maxFees, newMaxFees_); + if (requests[requestCount_].maxFees >= newMaxFees_) + revert NewMaxFeesLowerThanCurrent(requests[requestCount_].maxFees, newMaxFees_); requests[requestCount_].maxFees = newMaxFees_; emit FeesIncreased(appGateway, requestCount_, newMaxFees_); } @@ -39,7 +40,7 @@ abstract contract FeesHelpers is RequestQueue { address auctionManager_, uint256 fees_ ) external returns (uint40) { - IFeesManager(addressResolver__.feesManager()).withdrawFees( + IFeesManager(addressResolver__.feesManager()).withdrawCredits( msg.sender, chainSlug_, token_, diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index ced0edf4..021150fd 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -137,9 +137,11 @@ 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); } diff --git a/contracts/protocol/socket/SocketConfig.sol b/contracts/protocol/socket/SocketConfig.sol index b47a2754..5f8c0a8d 100644 --- a/contracts/protocol/socket/SocketConfig.sol +++ b/contracts/protocol/socket/SocketConfig.sol @@ -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; @@ -63,7 +63,6 @@ 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_); diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index 1ea1c2da..01a084da 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -46,7 +46,7 @@ contract WatcherPrecompileLimits is // slot 157: fees uint256 public queryFees; uint256 public finalizeFees; - uint256 public scheduleFees; + uint256 public timeoutFees; uint256 public callBackFees; /// @notice Emitted when the default limit and rate per second are set @@ -173,8 +173,8 @@ contract WatcherPrecompileLimits is finalizeFees = finalizeFees_; } - function setScheduleFees(uint256 scheduleFees_) external onlyOwner { - scheduleFees = scheduleFees_; + function setTimeoutFees(uint256 timeoutFees_) external onlyOwner { + timeoutFees = timeoutFees_; } function setCallBackFees(uint256 callBackFees_) external onlyOwner { @@ -191,7 +191,7 @@ contract WatcherPrecompileLimits is totalFees += callbackCount_ * callBackFees; totalFees += queryCount_ * queryFees; totalFees += finalizeCount_ * finalizeFees; - totalFees += scheduleCount_ * scheduleFees; + totalFees += scheduleCount_ * timeoutFees; return totalFees; } diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 9a85d88f..48cae8fe 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -312,9 +312,9 @@ contract WatcherPrecompile is RequestHandler { ) revert InvalidCallerTriggered(); IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileCreditsFromAddress( - watcherPrecompileLimits__.callBackFees(), - appGateway - ); + watcherPrecompileLimits__.callBackFees(), + appGateway + ); appGatewayCaller = appGateway; appGatewayCalled[params_[i].triggerId] = true; diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 0d787502..ce146eaf 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -36,7 +36,7 @@ abstract contract WatcherPrecompileCore is ) internal returns (bytes32 timeoutId) { if (delayInSeconds_ > maxTimeoutDelayInSeconds) revert TimeoutDelayTooLarge(); - _consumeCallbackFeesFromAddress(watcherPrecompileLimits__.scheduleFees(), msg.sender); + _consumeCallbackFeesFromAddress(watcherPrecompileLimits__.timeoutFees(), msg.sender); uint256 executeAt = block.timestamp + delayInSeconds_; timeoutId = _encodeTimeoutId(); @@ -259,10 +259,8 @@ abstract contract WatcherPrecompileCore is function _consumeCallbackFeesFromRequestCount(uint256 fees_, uint40 requestCount_) internal { // for callbacks in all precompiles uint256 feesToConsume = fees_ + watcherPrecompileLimits__.callBackFees(); - IFeesManager(addressResolver__.feesManager()).assignWatcherPrecompileCreditsFromRequestCount( - feesToConsume, - requestCount_ - ); + IFeesManager(addressResolver__.feesManager()) + .assignWatcherPrecompileCreditsFromRequestCount(feesToConsume, requestCount_); } function _consumeCallbackFeesFromAddress(uint256 fees_, address consumeFrom_) internal { diff --git a/script/helpers/AppGatewayFeeBalance.s.sol b/script/helpers/AppGatewayFeeBalance.s.sol index d2ba38d8..8e80861f 100644 --- a/script/helpers/AppGatewayFeeBalance.s.sol +++ b/script/helpers/AppGatewayFeeBalance.s.sol @@ -18,7 +18,7 @@ contract CheckDepositedFees is Script { console.log("totalCredits fees:", totalCredits); console.log("blockedCredits fees:", blockedCredits); - uint256 availableFees = feesManager.getAvailableFees(appGateway); + uint256 availableFees = feesManager.getAvailableCredits(appGateway); console.log("Available fees:", availableFees); } } diff --git a/test/DeliveryHelper.t.sol b/test/DeliveryHelper.t.sol index d4c91351..03e005a0 100644 --- a/test/DeliveryHelper.t.sol +++ b/test/DeliveryHelper.t.sol @@ -189,25 +189,25 @@ contract DeliveryHelperTest is SetupTest { socketConfig.feesPlug.depositToFeeAndNative(fees_.token, appGateway_, fees_.amount); vm.stopPrank(); - bytes memory bytesInput = abi.encode( - IFeesManager.incrementCreditsDeposited.selector, - appGateway_, - fees_.chainSlug, - fees_.token, - fees_.amount - ); - bytes32 digest = keccak256( - abi.encode(address(feesManager), evmxSlug, signatureNonce, bytesInput) + abi.encode( + appGateway_, + fees_.chainSlug, + fees_.token, + fees_.amount, + address(feesManager), + evmxSlug, + signatureNonce + ) ); - bytes memory sig = _createSignature(digest, watcherPrivateKey); - feesManager.incrementCreditsDeposited( + + feesManager.depositCredits( appGateway_, fees_.chainSlug, fees_.token, fees_.amount, signatureNonce++, - sig + _createSignature(digest, watcherPrivateKey) ); } diff --git a/test/FeesTest.t.sol b/test/FeesTest.t.sol index 3383b127..1f6a0ba0 100644 --- a/test/FeesTest.t.sol +++ b/test/FeesTest.t.sol @@ -70,7 +70,6 @@ contract FeesTest is DeliveryHelperTest { } function testWithdrawFeeTokensAppGateway() public { - uint256 receiverBalanceBefore = feesConfig.feesTokenUSDC.balanceOf(receiver); uint256 withdrawAmount = 0.5 ether; @@ -82,7 +81,6 @@ contract FeesTest is DeliveryHelperTest { ); executeRequest(new bytes[](0)); - assertEq( receiverBalanceBefore + withdrawAmount, feesConfig.feesTokenUSDC.balanceOf(receiver), @@ -90,9 +88,7 @@ contract FeesTest is DeliveryHelperTest { ); } - - function testWithdrawFeeTokensUser() public { - + function testWithdrawFeeTokensUser() public { depositUSDCFees( user, OnChainFees({ @@ -111,12 +107,11 @@ contract FeesTest is DeliveryHelperTest { address(feesConfig.feesTokenUSDC), withdrawAmount, user, - address(auctionManager), + address(auctionManager), maxFees ); executeRequest(new bytes[](0)); - assertEq( receiverBalanceBefore + withdrawAmount, feesConfig.feesTokenUSDC.balanceOf(user), diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index 322ac45d..cf4a6ca2 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -202,7 +202,7 @@ contract SetupTest is Test { watcherPrecompileLimits.setCallBackFees(1); watcherPrecompileLimits.setFinalizeFees(1); watcherPrecompileLimits.setQueryFees(1); - watcherPrecompileLimits.setScheduleFees(1); + watcherPrecompileLimits.setTimeoutFees(1); vm.stopPrank(); } diff --git a/test/mock/MockFastSwitchboard.sol b/test/mock/MockFastSwitchboard.sol index e831d02c..3341fab8 100644 --- a/test/mock/MockFastSwitchboard.sol +++ b/test/mock/MockFastSwitchboard.sol @@ -21,11 +21,11 @@ contract MockFastSwitchboard is ISwitchboard { owner = owner_; } - function attest(bytes32 , bytes calldata ) external { + function attest(bytes32, bytes calldata) external { // TODO: implement } - function allowPayload(bytes32 , bytes32) external pure returns (bool) { + function allowPayload(bytes32, bytes32) external pure returns (bool) { // digest has enough attestations return true; } From f1a6ce3b05942f8f5e401a8947bfe02ff4102780 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 24 Apr 2025 22:59:36 +0530 Subject: [PATCH 090/108] fix: payable deposit credit --- contracts/interfaces/IFeesManager.sol | 3 +-- .../protocol/payload-delivery/FeesManager.sol | 14 +++++++------- test/DeliveryHelper.t.sol | 3 +-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/contracts/interfaces/IFeesManager.sol b/contracts/interfaces/IFeesManager.sol index 4dc24fc5..02701602 100644 --- a/contracts/interfaces/IFeesManager.sol +++ b/contracts/interfaces/IFeesManager.sol @@ -60,8 +60,7 @@ interface IFeesManager { address depositTo_, uint32 chainSlug_, address token_, - uint256 amount_, uint256 signatureNonce_, bytes memory signature_ - ) external; + ) external payable; } diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 34a6ac5e..acc816c1 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -163,27 +163,27 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol /// @notice Adds the fees deposited for an app gateway on a chain /// @param depositTo_ The app gateway address - /// @param amount_ The amount deposited // @dev only callable by watcher precompile // @dev will need tokenAmount_ and creditAmount_ when introduce tokens except stables function depositCredits( address depositTo_, uint32 chainSlug_, address token_, - uint256 amount_, uint256 signatureNonce_, bytes memory signature_ - ) external { + ) external payable { if (isNonceUsed[signatureNonce_]) revert NonceUsed(); isNonceUsed[signatureNonce_] = true; + uint256 amount = msg.value; + // check signature bytes32 digest = keccak256( abi.encode( depositTo_, chainSlug_, token_, - amount_, + amount, address(this), evmxSlug, signatureNonce_ @@ -193,9 +193,9 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol if (_recoverSigner(digest, signature_) != owner()) revert InvalidWatcherSignature(); UserCredits storage userCredit = userCredits[depositTo_]; - userCredit.totalCredits += amount_; - tokenPoolBalances[chainSlug_][token_] += amount_; - emit CreditsDeposited(chainSlug_, depositTo_, token_, amount_); + userCredit.totalCredits += amount; + tokenPoolBalances[chainSlug_][token_] += amount; + emit CreditsDeposited(chainSlug_, depositTo_, token_, amount); } function wrap() external payable { diff --git a/test/DeliveryHelper.t.sol b/test/DeliveryHelper.t.sol index 03e005a0..e8a689ad 100644 --- a/test/DeliveryHelper.t.sol +++ b/test/DeliveryHelper.t.sol @@ -201,11 +201,10 @@ contract DeliveryHelperTest is SetupTest { ) ); - feesManager.depositCredits( + feesManager.depositCredits{value: fees_.amount}( appGateway_, fees_.chainSlug, fees_.token, - fees_.amount, signatureNonce++, _createSignature(digest, watcherPrivateKey) ); From 91dbe9d444fbb498a52f474fcc0ea63f4893beda Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 24 Apr 2025 23:34:46 +0530 Subject: [PATCH 091/108] fix: remove approval data from middleware and watcher --- contracts/base/AppGatewayBase.sol | 25 ++++++++------- contracts/interfaces/IMiddleware.sol | 3 +- .../protocol/payload-delivery/FeesManager.sol | 2 +- .../app-gateway/FeesHelpers.sol | 5 +-- .../app-gateway/RequestQueue.sol | 31 +++++++------------ contracts/protocol/utils/common/Structs.sol | 1 - test/apps/Counter.t.sol | 2 +- 7 files changed, 32 insertions(+), 37 deletions(-) diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index 572ee516..64fc82ad 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -25,29 +25,33 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { mapping(bytes32 => mapping(uint32 => address)) public override forwarderAddresses; mapping(bytes32 => bytes) public creationCodeWithArgs; + address public consumeFrom; + /// @notice Modifier to treat functions async modifier async(bytes memory feesApprovalData_) { - _preAsync(); + _preAsync(feesApprovalData_); _; - _postAsync(feesApprovalData_); + _postAsync(); } - function _postAsync(bytes memory feesApprovalData_) internal { + function _postAsync() internal { isAsyncModifierSet = false; - if (feesApprovalData_.length == 0) { - feesApprovalData_ = abi.encode(address(this), address(this), true, new bytes(0)); - } - // todo: cache the feesApprovalData for next async in same request - deliveryHelper__().batch(maxFees, auctionManager, feesApprovalData_, onCompleteData); + + deliveryHelper__().batch(maxFees, auctionManager, consumeFrom, onCompleteData); _markValidPromises(); onCompleteData = bytes(""); } - function _preAsync() internal { + function _preAsync(bytes memory feesApprovalData_) internal { isAsyncModifierSet = true; _clearOverrides(); deliveryHelper__().clearQueue(); addressResolver__.clearPromises(); + + if (feesApprovalData_.length > 0) { + (consumeFrom, , ) = IFeesManager(addressResolver__.feesManager()) + .whitelistAppGatewayWithSignature(feesApprovalData_); + } else consumeFrom = address(this); } /// @notice Modifier to ensure only valid promises can call the function @@ -189,8 +193,7 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { return address(0); } - onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]) - .getOnChainAddress(); + onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]).getOnChainAddress(); } //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/contracts/interfaces/IMiddleware.sol b/contracts/interfaces/IMiddleware.sol index 00099830..6bbd5a3d 100644 --- a/contracts/interfaces/IMiddleware.sol +++ b/contracts/interfaces/IMiddleware.sol @@ -25,13 +25,12 @@ interface IMiddleware { /// @notice Batches a request /// @param fees_ The fees for the request /// @param auctionManager_ The address of the auction manager - /// @param feesApprovalData_ the data to be passed to the fees manager /// @param onCompleteData_ The data to be passed to the onComplete callback /// @return requestCount The request id function batch( uint256 fees_, address auctionManager_, - bytes memory feesApprovalData_, + address consumeFrom_, bytes memory onCompleteData_ ) external returns (uint40 requestCount); diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index acc816c1..b2e5b75e 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -246,9 +246,9 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol ) ); if (_recoverSigner(digest, signature_) != consumeFrom) revert InvalidUserSignature(); - isAppGatewayWhitelisted[consumeFrom][appGateway] = isApproved; userNonce[consumeFrom]++; + return (consumeFrom, appGateway, isApproved); } diff --git a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol index 8b37a58d..f1845865 100644 --- a/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol +++ b/contracts/protocol/payload-delivery/app-gateway/FeesHelpers.sol @@ -10,6 +10,7 @@ abstract contract FeesHelpers is RequestQueue { uint256[50] _gap_batch_async; error NewMaxFeesLowerThanCurrent(uint256 current, uint256 new_); + /// @notice Increases the fees for a request if no bid is placed /// @param requestCount_ The ID of the request /// @param newMaxFees_ The new maximum fees @@ -47,8 +48,7 @@ abstract contract FeesHelpers is RequestQueue { amount_, receiver_ ); - bytes memory feesApprovalData = abi.encode(msg.sender, msg.sender, true, bytes("")); - return _batch(msg.sender, auctionManager_, fees_, feesApprovalData, bytes("")); + return _batch(msg.sender, auctionManager_, msg.sender, fees_, bytes("")); } /// @notice Withdraws fees to a specified receiver @@ -89,6 +89,7 @@ abstract contract FeesHelpers is RequestQueue { // same transmitter can execute requests without auction watcherPrecompile__().startProcessingRequest(requestCount, transmitter); } + /// @notice Returns the fees for a request /// @param requestCount_ The ID of the request /// @return fees The fees data diff --git a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol index 835b0193..2fe73e94 100644 --- a/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol +++ b/contracts/protocol/payload-delivery/app-gateway/RequestQueue.sol @@ -26,11 +26,11 @@ abstract contract RequestQueue is DeliveryUtils { function batch( uint256 maxFees_, address auctionManager_, - bytes memory feesApprovalData_, + address consumeFrom_, bytes memory onCompleteData_ ) external returns (uint40 requestCount) { address appGateway = _getCoreAppGateway(msg.sender); - return _batch(appGateway, auctionManager_, maxFees_, feesApprovalData_, onCompleteData_); + return _batch(appGateway, auctionManager_, consumeFrom_, maxFees_, onCompleteData_); } /// @notice Initiates a batch of payloads @@ -40,8 +40,8 @@ abstract contract RequestQueue is DeliveryUtils { function _batch( address appGateway_, address auctionManager_, + address consumeFrom_, uint256 maxFees_, - bytes memory feesApprovalData_, bytes memory onCompleteData_ ) internal returns (uint40 requestCount) { if (queuePayloadParams.length == 0) return 0; @@ -50,7 +50,6 @@ abstract contract RequestQueue is DeliveryUtils { appGateway: appGateway_, auctionManager: _getAuctionManager(auctionManager_), maxFees: maxFees_, - feesApprovalData: feesApprovalData_, onCompleteData: onCompleteData_, onlyReadRequests: false, queryCount: 0, @@ -69,18 +68,14 @@ abstract contract RequestQueue is DeliveryUtils { params.queryCount = queryCount; params.finalizeCount = finalizeCount; - address consumeFrom = _checkBatch( - params.appGateway, - params.feesApprovalData, - params.maxFees - ); + _checkBatch(consumeFrom_, params.appGateway, params.maxFees); - return _submitBatchRequest(payloadSubmitParamsArray, consumeFrom, params); + return _submitBatchRequest(payloadSubmitParamsArray, consumeFrom_, params); } function _submitBatchRequest( PayloadSubmitParams[] memory payloadSubmitParamsArray, - address consumeFrom, + address consumeFrom_, BatchParams memory params ) internal returns (uint40 requestCount) { RequestMetadata memory requestMetadata = RequestMetadata({ @@ -90,7 +85,7 @@ abstract contract RequestQueue is DeliveryUtils { winningBid: Bid({fee: 0, transmitter: address(0), extraData: new bytes(0)}), onCompleteData: params.onCompleteData, onlyReadRequests: params.onlyReadRequests, - consumeFrom: consumeFrom, + consumeFrom: consumeFrom_, queryCount: params.queryCount, finalizeCount: params.finalizeCount }); @@ -127,24 +122,22 @@ abstract contract RequestQueue is DeliveryUtils { ? IAddressResolver(addressResolver__).defaultAuctionManager() : auctionManager_; } + function _checkBatch( + address consumeFrom_, address appGateway_, - bytes memory feesApprovalData_, uint256 maxFees_ - ) internal returns (address consumeFrom) { + ) internal view { if (queuePayloadParams.length > REQUEST_PAYLOAD_COUNT_LIMIT) revert RequestPayloadCountLimitExceeded(); - (consumeFrom, , ) = IFeesManager(addressResolver__.feesManager()) - .whitelistAppGatewayWithSignature(feesApprovalData_); + if ( !IFeesManager(addressResolver__.feesManager()).isUserCreditsEnough( - consumeFrom, + consumeFrom_, appGateway_, maxFees_ ) ) revert InsufficientFees(); - - return consumeFrom; } /// @notice Creates an array of payload details diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 8e63a0ea..5926ea9d 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -55,7 +55,6 @@ struct BatchParams { address appGateway; address auctionManager; uint256 maxFees; - bytes feesApprovalData; bytes onCompleteData; bool onlyReadRequests; uint256 queryCount; diff --git a/test/apps/Counter.t.sol b/test/apps/Counter.t.sol index 5e34404f..8980a314 100644 --- a/test/apps/Counter.t.sol +++ b/test/apps/Counter.t.sol @@ -34,7 +34,7 @@ contract CounterTest is DeliveryHelperTest { requestCount = _deploy(chainSlug, IAppGateway(counterGateway), contractIds); } - function testCounterDeployment() external { + function testCounterDeployment1() external { deploySetup(); deployCounterApp(arbChainSlug); From eb419c325e3609adc14a5cc86d94305f321be704 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 24 Apr 2025 23:40:11 +0530 Subject: [PATCH 092/108] feat: modifier with consume from in case gateways resolve it --- contracts/base/AppGatewayBase.sol | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index 64fc82ad..4b7e9950 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -34,6 +34,15 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { _postAsync(); } + // todo: can't overload modifier with same name, can rename later + /// @notice Modifier to treat functions async with consume from address + modifier asyncWithConsume(address consumeFrom_) { + _preAsync(new bytes(0)); + consumeFrom = consumeFrom_; + _; + _postAsync(); + } + function _postAsync() internal { isAsyncModifierSet = false; @@ -48,6 +57,10 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { deliveryHelper__().clearQueue(); addressResolver__.clearPromises(); + _handleFeesApproval(feesApprovalData_); + } + + function _handleFeesApproval(bytes memory feesApprovalData_) internal { if (feesApprovalData_.length > 0) { (consumeFrom, , ) = IFeesManager(addressResolver__.feesManager()) .whitelistAppGatewayWithSignature(feesApprovalData_); From 32dbc2b7000ca3c07445d30c5f7d9bd82bec5162 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 25 Apr 2025 20:26:51 +0530 Subject: [PATCH 093/108] fix: optimise contract size --- .../watcherPrecompile/core/RequestHandler.sol | 24 ++-- .../core/WatcherPrecompile.sol | 113 ++++++++++-------- .../core/WatcherPrecompileCore.sol | 13 +- .../core/WatcherPrecompileStorage.sol | 4 + 4 files changed, 77 insertions(+), 77 deletions(-) diff --git a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol index 1cb77e73..25399dc8 100644 --- a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol +++ b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol @@ -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_); @@ -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; @@ -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; @@ -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(); diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 48cae8fe..b517aa90 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -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 ================== @@ -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_); } @@ -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_), @@ -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_), @@ -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_), @@ -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); } } @@ -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_), @@ -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_), @@ -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; @@ -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); + } + } } diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 402532bc..9c82e2b7 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -32,10 +32,9 @@ abstract contract WatcherPrecompileCore is /// @return timeoutId The unique identifier for the timeout request function _setTimeout( uint256 delayInSeconds_, - bytes calldata payload_ + bytes memory payload_ ) internal returns (bytes32 timeoutId) { if (delayInSeconds_ > maxTimeoutDelayInSeconds) revert TimeoutDelayTooLarge(); - _consumeCallbackFeesFromAddress(watcherPrecompileLimits__.timeoutFees(), msg.sender); uint256 executeAt = block.timestamp + delayInSeconds_; @@ -52,9 +51,6 @@ abstract contract WatcherPrecompileCore is payload_ ); - // consumes limit for SCHEDULE precompile - watcherPrecompileLimits__.consumeLimit(_getCoreAppGateway(msg.sender), SCHEDULE, 1); - // emits event for watcher to track timeout and resolve when timeout is reached emit TimeoutRequested(timeoutId, msg.sender, payload_, executeAt); } @@ -201,12 +197,7 @@ abstract contract WatcherPrecompileCore is function _encodeTimeoutId() internal returns (bytes32) { // Encode timeout ID by bit-shifting and combining: // EVMx chainSlug (32 bits) | watcher precompile address (160 bits) | counter (64 bits) - return - bytes32( - (uint256(evmxSlug) << 224) | - (uint256(uint160(address(this))) << 64) | - payloadCounter++ - ); + return bytes32(timeoutIdPrefix | payloadCounter++); } /// @notice Creates a payload ID from the given parameters diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol index 12dd3d53..d90706f9 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileStorage.sol @@ -47,6 +47,10 @@ abstract contract WatcherPrecompileStorage is IWatcherPrecompile { address public appGatewayCaller; // slot 54 + /// @notice The prefix for timeout IDs + uint256 public timeoutIdPrefix; + + // slot 55 /// @notice Maps nonce to whether it has been used /// @dev Used to prevent replay attacks with signature nonces /// @dev signatureNonce => isValid From d8e2bad11684ebabf839104caf81a955383076c9 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 25 Apr 2025 20:36:33 +0530 Subject: [PATCH 094/108] feat: optimise watcher --- .../WatcherPrecompileConfig.sol | 7 ++-- .../watcherPrecompile/core/RequestHandler.sol | 2 +- .../watcherPrecompile/core/WatcherIdUtils.sol | 32 +++++++++++++++++ .../core/WatcherPrecompile.sol | 2 +- .../core/WatcherPrecompileCore.sol | 34 ++++--------------- .../core/WatcherPrecompileUtils.sol | 12 ------- 6 files changed, 44 insertions(+), 45 deletions(-) create mode 100644 contracts/protocol/watcherPrecompile/core/WatcherIdUtils.sol delete mode 100644 contracts/protocol/watcherPrecompile/core/WatcherPrecompileUtils.sol diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index 22c85d4f..3d9b4f09 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -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 @@ -16,8 +16,7 @@ contract WatcherPrecompileConfig is IWatcherPrecompileConfig, Initializable, Ownable, - AddressResolverUtil, - WatcherPrecompileUtils + AddressResolverUtil { // slots 0-50 (51) reserved for addr resolver util @@ -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(); } diff --git a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol index 25399dc8..d6f620eb 100644 --- a/contracts/protocol/watcherPrecompile/core/RequestHandler.sol +++ b/contracts/protocol/watcherPrecompile/core/RequestHandler.sol @@ -50,7 +50,7 @@ abstract contract RequestHandler is WatcherPrecompileCore { } uint40 localPayloadCount = payloadCounter++; - bytes32 payloadId = _createPayloadId( + bytes32 payloadId = WatcherIdUtils.createPayloadId( requestCount, batchCount, localPayloadCount, diff --git a/contracts/protocol/watcherPrecompile/core/WatcherIdUtils.sol b/contracts/protocol/watcherPrecompile/core/WatcherIdUtils.sol new file mode 100644 index 00000000..5121b3da --- /dev/null +++ b/contracts/protocol/watcherPrecompile/core/WatcherIdUtils.sol @@ -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_) + ); + } +} diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index b517aa90..13e72e43 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -268,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, diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 9c82e2b7..60aad863 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -3,11 +3,12 @@ pragma solidity ^0.8.21; import "./WatcherPrecompileStorage.sol"; import {ECDSA} from "solady/utils/ECDSA.sol"; -import {AccessControl} from "../../utils/AccessControl.sol"; +import {Ownable} from "solady/auth/Ownable.sol"; + import "solady/utils/Initializable.sol"; import {AddressResolverUtil} from "../../utils/AddressResolverUtil.sol"; import {IFeesManager} from "../../../interfaces/IFeesManager.sol"; -import "./WatcherPrecompileUtils.sol"; +import "./WatcherIdUtils.sol"; /// @title WatcherPrecompileCore /// @notice Core functionality for the WatcherPrecompile system @@ -17,9 +18,8 @@ abstract contract WatcherPrecompileCore is IWatcherPrecompile, WatcherPrecompileStorage, Initializable, - AccessControl, - AddressResolverUtil, - WatcherPrecompileUtils + Ownable, + AddressResolverUtil { using PayloadHeaderDecoder for bytes32; @@ -98,7 +98,7 @@ abstract contract WatcherPrecompileCore is params_.value, params_.payload, params_.target, - _encodeAppGatewayId(params_.appGateway), + WatcherIdUtils.encodeAppGatewayId(params_.appGateway), prevDigestsHash ); @@ -169,7 +169,7 @@ abstract contract WatcherPrecompileCore is p.value, p.payload, p.target, - _encodeAppGatewayId(p.appGateway), + WatcherIdUtils.encodeAppGatewayId(p.appGateway), p.prevDigestsHash ); prevDigestsHash = keccak256(abi.encodePacked(prevDigestsHash, getDigest(digestParams))); @@ -200,26 +200,6 @@ abstract contract WatcherPrecompileCore is return bytes32(timeoutIdPrefix | payloadCounter++); } - /// @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_) - ); - } - /// @notice Verifies that a watcher signature is valid /// @param inputData_ The input data to verify /// @param signatureNonce_ The nonce of the signature diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileUtils.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileUtils.sol deleted file mode 100644 index c47786e8..00000000 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileUtils.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.22; - -abstract contract WatcherPrecompileUtils { - 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_))); - } -} From fbf9e4932de3ca161d2fba7583c7ee0724b1373a Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 25 Apr 2025 20:40:56 +0530 Subject: [PATCH 095/108] fix: remove timeout id from params --- contracts/protocol/utils/common/Structs.sol | 1 - .../core/WatcherPrecompileCore.sol | 14 ++++---------- test/mock/MockWatcherPrecompile.sol | 1 - 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 301cca0c..2b9f3838 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -103,7 +103,6 @@ struct TriggerParams { } // timeout: struct TimeoutRequest { - bytes32 timeoutId; address target; uint256 delayInSeconds; uint256 executeAt; diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 60aad863..85fc60f7 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -40,16 +40,10 @@ abstract contract WatcherPrecompileCore is uint256 executeAt = block.timestamp + delayInSeconds_; timeoutId = _encodeTimeoutId(); - // stores timeout request for watcher to track and resolve when timeout is reached - timeoutRequests[timeoutId] = TimeoutRequest( - timeoutId, - msg.sender, - delayInSeconds_, - executeAt, - 0, - false, - payload_ - ); + timeoutRequests[timeoutId].target = msg.sender; + timeoutRequests[timeoutId].delayInSeconds = delayInSeconds_; + timeoutRequests[timeoutId].executeAt = executeAt; + timeoutRequests[timeoutId].payload = payload_; // emits event for watcher to track timeout and resolve when timeout is reached emit TimeoutRequested(timeoutId, msg.sender, payload_, executeAt); diff --git a/test/mock/MockWatcherPrecompile.sol b/test/mock/MockWatcherPrecompile.sol index dfd3711b..3cd1681c 100644 --- a/test/mock/MockWatcherPrecompile.sol +++ b/test/mock/MockWatcherPrecompile.sol @@ -73,7 +73,6 @@ contract MockWatcherPrecompile { uint256 executeAt = block.timestamp + delayInSeconds_; bytes32 timeoutId = _encodeTimeoutId(); timeoutRequests[timeoutId] = TimeoutRequest( - timeoutId, msg.sender, delayInSeconds_, executeAt, From 712611239574ff665512c84178dc3a14ed57f26e Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 25 Apr 2025 20:43:18 +0530 Subject: [PATCH 096/108] chore: publish socket-protocol --- package.json | 2 +- publish.sh | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100755 publish.sh diff --git a/package.json b/package.json index 80d12f1f..2610da7a 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "publishConfig": { "access": "public" }, - "version": "1.1.9", + "version": "1.1.11", "description": "socket protocol", "scripts": { "build": "yarn abi && tsc --project lib.tsconfig.json", diff --git a/publish.sh b/publish.sh deleted file mode 100755 index 6d4331ee..00000000 --- a/publish.sh +++ /dev/null @@ -1,2 +0,0 @@ -yarn build -yarn publish \ No newline at end of file From a7b634313b27673d8561dd13bd73e33da65f03b6 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 29 Apr 2025 17:32:02 +0530 Subject: [PATCH 097/108] wip: event fixes --- contracts/protocol/socket/Socket.sol | 1 + .../core/WatcherPrecompileCore.sol | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 5564153b..16852906 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -190,6 +190,7 @@ contract Socket is SocketUtils { triggerId = _encodeTriggerId(); emit AppGatewayCallRequested( triggerId, + plugConfig.appGatewayId, plugConfig.switchboard, plug_, // gets the overrides from the plug diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 85fc60f7..8b11fcae 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -115,6 +115,21 @@ abstract contract WatcherPrecompileCore is payloads[params_.payloadId].prevDigestsHash = _getPreviousDigestsHash( params_.payloadHeader.getBatchCount() ); + payloads[params_.payloadId].digest = getDigest( + DigestParams( + watcherPrecompileConfig__.sockets(params_.payloadHeader.getChainSlug()), + params_.transmitter, + params_.payloadId, + params_.deadline, + params_.payloadHeader.getCallType(), + params_.gasLimit, + params_.value, + params_.payload, + params_.target, + WatcherIdUtils.encodeAppGatewayId(params_.appGateway), + payloads[params_.payloadId].prevDigestsHash + ) + ); emit QueryRequested(params_); } From 746c0ca2182b62a57c00a646f61d262d09e8153f Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 30 Apr 2025 17:19:34 +0530 Subject: [PATCH 098/108] feat: memory to calldata --- contracts/interfaces/ISocket.sol | 6 ++-- contracts/interfaces/ISocketBatcher.sol | 2 +- contracts/protocol/socket/Socket.sol | 28 +++++++++++-------- contracts/protocol/socket/SocketBatcher.sol | 2 +- contracts/protocol/socket/SocketUtils.sol | 10 +++---- contracts/protocol/utils/common/Structs.sol | 22 +++++++-------- .../watcherPrecompile/core/WatcherIdUtils.sol | 2 +- test/SetupTest.t.sol | 4 +-- test/apps/Counter.t.sol | 2 +- test/mock/MockSocket.sol | 6 ++-- 10 files changed, 44 insertions(+), 40 deletions(-) diff --git a/contracts/interfaces/ISocket.sol b/contracts/interfaces/ISocket.sol index 58fa8dab..8c5ac2db 100644 --- a/contracts/interfaces/ISocket.sol +++ b/contracts/interfaces/ISocket.sol @@ -52,9 +52,9 @@ interface ISocket { * @notice executes a payload */ function execute( - ExecuteParams memory executeParams_, - TransmissionParams memory transmissionParams_ - ) external payable returns (bool, bool, bytes memory); + ExecuteParams calldata executeParams_, + TransmissionParams calldata transmissionParams_ + ) external payable returns (bool, bytes memory); /** * @notice sets the config specific to the plug diff --git a/contracts/interfaces/ISocketBatcher.sol b/contracts/interfaces/ISocketBatcher.sol index aba5a990..b8cb2270 100644 --- a/contracts/interfaces/ISocketBatcher.sol +++ b/contracts/interfaces/ISocketBatcher.sol @@ -23,5 +23,5 @@ interface ISocketBatcher { bytes calldata proof_, bytes calldata transmitterSignature_, address refundAddress_ - ) external payable returns (bool, bool, bytes memory); + ) external payable returns (bool, bytes memory); } diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 5564153b..3c299bb8 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -19,7 +19,7 @@ contract Socket is SocketUtils { mapping(bytes32 => bytes32) public payloadIdToDigest; // @notice buffer to account for gas used by current contract execution - uint256 public constant GAS_LIMIT_BUFFER = 105; + uint256 private constant GAS_LIMIT_BUFFER = 105; //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// @@ -69,9 +69,9 @@ contract Socket is SocketUtils { * @notice Executes a payload that has been delivered by transmitters and authenticated by switchboards */ function execute( - ExecuteParams memory executeParams_, - TransmissionParams memory transmissionParams_ - ) external payable returns (bool, bool, bytes memory) { + ExecuteParams calldata executeParams_, + TransmissionParams calldata transmissionParams_ + ) external payable returns (bool, bytes memory) { // check if the deadline has passed if (executeParams_.deadline < block.timestamp) revert DeadlinePassed(); // check if the call type is valid @@ -83,6 +83,7 @@ contract Socket is SocketUtils { if (msg.value < executeParams_.value + transmissionParams_.socketFees) revert InsufficientMsgValue(); + bytes32 payloadId = _createPayloadId(plugConfig.switchboard, executeParams_); // validate the execution status @@ -107,6 +108,7 @@ contract Socket is SocketUtils { // verify the digest _verify(digest, payloadId, plugConfig.switchboard); + return _execute(payloadId, executeParams_, transmissionParams_); } @@ -129,15 +131,16 @@ contract Socket is SocketUtils { */ function _execute( bytes32 payloadId_, - ExecuteParams memory executeParams_, - TransmissionParams memory transmissionParams_ - ) internal returns (bool, bool, bytes memory) { + ExecuteParams calldata executeParams_, + TransmissionParams calldata transmissionParams_ + ) internal returns (bool success, bytes memory returnData) { // check if the gas limit is sufficient // 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, bool exceededMaxCopy, bytes memory returnData) = executeParams_ + bool exceededMaxCopy; + (success, exceededMaxCopy, returnData) = executeParams_ .target .tryCall( executeParams_.value, @@ -148,6 +151,7 @@ contract Socket is SocketUtils { if (success) { emit ExecutionSuccess(payloadId_, exceededMaxCopy, returnData); + if (address(socketFeeManager) != address(0)) { socketFeeManager.payAndCheckFees{value: transmissionParams_.socketFees}( executeParams_, @@ -156,20 +160,20 @@ contract Socket is SocketUtils { } } else { payloadExecuted[payloadId_] = ExecutionStatus.Reverted; + address receiver = transmissionParams_.refundAddress == address(0) ? msg.sender : transmissionParams_.refundAddress; SafeTransferLib.forceSafeTransferETH(receiver, msg.value); - emit ExecutionFailed(payloadId_, exceededMaxCopy, returnData); } - - return (success, exceededMaxCopy, returnData); + return (success, returnData); } function _validateExecutionStatus(bytes32 payloadId_) internal { if (payloadExecuted[payloadId_] == ExecutionStatus.Executed) revert PayloadAlreadyExecuted(payloadExecuted[payloadId_]); + payloadExecuted[payloadId_] = ExecutionStatus.Executed; } @@ -180,7 +184,7 @@ contract Socket is SocketUtils { * @notice To trigger to a connected remote chain. Should only be called by a plug. */ function _triggerAppGateway(address plug_) internal returns (bytes32 triggerId) { - PlugConfig storage plugConfig = _plugConfigs[plug_]; + PlugConfig memory plugConfig = _plugConfigs[plug_]; // if no sibling plug is found for the given chain slug, revert // sends the trigger to connected app gateway diff --git a/contracts/protocol/socket/SocketBatcher.sol b/contracts/protocol/socket/SocketBatcher.sol index ad37696f..d996a722 100644 --- a/contracts/protocol/socket/SocketBatcher.sol +++ b/contracts/protocol/socket/SocketBatcher.sol @@ -41,7 +41,7 @@ contract SocketBatcher is ISocketBatcher, Ownable { bytes calldata proof_, bytes calldata transmitterSignature_, address refundAddress_ - ) external payable returns (bool, bool, bytes memory) { + ) external payable returns (bool, bytes memory) { ISwitchboard(switchboard_).attest(digest_, proof_); return socket__.execute{value: msg.value}( diff --git a/contracts/protocol/socket/SocketUtils.sol b/contracts/protocol/socket/SocketUtils.sol index 7000b771..ccd8db5d 100644 --- a/contracts/protocol/socket/SocketUtils.sol +++ b/contracts/protocol/socket/SocketUtils.sol @@ -50,7 +50,7 @@ abstract contract SocketUtils is SocketConfig { address transmitter_, bytes32 payloadId_, bytes32 appGatewayId_, - ExecuteParams memory executeParams_ + ExecuteParams calldata executeParams_ ) internal view returns (bytes32) { return keccak256( @@ -78,7 +78,7 @@ abstract contract SocketUtils is SocketConfig { */ function _createPayloadId( address switchboard_, - ExecuteParams memory executeParams_ + ExecuteParams calldata executeParams_ ) internal view returns (bytes32) { return keccak256( @@ -86,8 +86,8 @@ abstract contract SocketUtils is SocketConfig { executeParams_.requestCount, executeParams_.batchCount, executeParams_.payloadCount, - switchboard_, - chainSlug + chainSlug, + switchboard_ ) ); } @@ -100,7 +100,7 @@ abstract contract SocketUtils is SocketConfig { */ function _recoverSigner( bytes32 digest_, - bytes memory signature_ + bytes calldata signature_ ) internal view returns (address signer) { bytes32 digest = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", digest_)); // recovered signer is checked for the valid roles later diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 2b9f3838..43fef165 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -118,8 +118,8 @@ struct ResolvedPromises { // AM struct Bid { - address transmitter; uint256 fee; + address transmitter; bytes extraData; } @@ -233,28 +233,28 @@ struct RequestParams { } struct RequestMetadata { + bool onlyReadRequests; + address consumeFrom; address appGateway; address auctionManager; uint256 maxFees; - Bid winningBid; - bytes onCompleteData; - bool onlyReadRequests; - address consumeFrom; uint256 queryCount; uint256 finalizeCount; + Bid winningBid; + bytes onCompleteData; } struct ExecuteParams { CallType callType; - uint256 deadline; - uint256 gasLimit; - uint256 value; - bytes payload; - address target; uint40 requestCount; uint40 batchCount; uint40 payloadCount; + uint256 deadline; + uint256 gasLimit; + uint256 value; bytes32 prevDigestsHash; + address target; + bytes payload; bytes extraData; } @@ -269,6 +269,6 @@ struct PayloadIdParams { uint40 requestCount; uint40 batchCount; uint40 payloadCount; - address switchboard; uint32 chainSlug; + address switchboard; } diff --git a/contracts/protocol/watcherPrecompile/core/WatcherIdUtils.sol b/contracts/protocol/watcherPrecompile/core/WatcherIdUtils.sol index 5121b3da..1d2ed762 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherIdUtils.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherIdUtils.sol @@ -26,7 +26,7 @@ library WatcherIdUtils { ) internal pure returns (bytes32) { return keccak256( - abi.encode(requestCount_, batchCount_, payloadCount_, switchboard_, chainSlug_) + abi.encode(requestCount_, batchCount_, payloadCount_, chainSlug_, switchboard_) ); } } diff --git a/test/SetupTest.t.sol b/test/SetupTest.t.sol index fbd05066..4735ab79 100644 --- a/test/SetupTest.t.sol +++ b/test/SetupTest.t.sol @@ -256,7 +256,7 @@ contract SetupTest is Test { isLastPayload ); } else { - (, , bytes memory returnData) = _uploadProofAndExecute(payloadParams); + (, bytes memory returnData) = _uploadProofAndExecute(payloadParams); _resolveAndExpectFinalizeRequested( payloadParams.payloadId, payloadParams, @@ -270,7 +270,7 @@ contract SetupTest is Test { function _uploadProofAndExecute( PayloadParams memory payloadParams - ) internal returns (bool, bool, bytes memory) { + ) internal returns (bool, bytes memory) { (bytes memory watcherProof, bytes32 digest) = _generateWatcherProof(payloadParams); _writeProof(payloadParams.payloadId, watcherProof); diff --git a/test/apps/Counter.t.sol b/test/apps/Counter.t.sol index b2ec64ad..f2c58c2c 100644 --- a/test/apps/Counter.t.sol +++ b/test/apps/Counter.t.sol @@ -34,7 +34,7 @@ contract CounterTest is DeliveryHelperTest { requestCount = _deploy(chainSlug, counterGateway, contractIds); } - function testCounterDeployment1() external { + function testCounterDeployment() external { deploySetup(); deployCounterApp(arbChainSlug); diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index b4e0f483..d74d1da1 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -107,9 +107,9 @@ contract MockSocket is ISocket { * @notice Executes a payload that has been delivered by transmitters and authenticated by switchboards */ function execute( - ExecuteParams memory executeParams_, - TransmissionParams memory transmissionParams_ - ) external payable override returns (bool, bool, bytes memory) { + ExecuteParams calldata executeParams_, + TransmissionParams calldata transmissionParams_ + ) external payable override returns (bool, bytes memory) { // execute payload // return // _execute( From df9d7bb1404b525d782daa0335baef5db51534c4 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 30 Apr 2025 17:24:29 +0530 Subject: [PATCH 099/108] fix: remove query digest --- .../core/WatcherPrecompileCore.sol | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol index 8b11fcae..85fc60f7 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompileCore.sol @@ -115,21 +115,6 @@ abstract contract WatcherPrecompileCore is payloads[params_.payloadId].prevDigestsHash = _getPreviousDigestsHash( params_.payloadHeader.getBatchCount() ); - payloads[params_.payloadId].digest = getDigest( - DigestParams( - watcherPrecompileConfig__.sockets(params_.payloadHeader.getChainSlug()), - params_.transmitter, - params_.payloadId, - params_.deadline, - params_.payloadHeader.getCallType(), - params_.gasLimit, - params_.value, - params_.payload, - params_.target, - WatcherIdUtils.encodeAppGatewayId(params_.appGateway), - payloads[params_.payloadId].prevDigestsHash - ) - ); emit QueryRequested(params_); } From 2e90f145bb2676796b571c86d710519ab46586b5 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 30 Apr 2025 17:35:49 +0530 Subject: [PATCH 100/108] fix: build --- contracts/interfaces/ISocket.sol | 1 + test/Inbox.t.sol | 2 ++ test/mock/MockSocket.sol | 1 + 3 files changed, 4 insertions(+) diff --git a/contracts/interfaces/ISocket.sol b/contracts/interfaces/ISocket.sol index 58fa8dab..8b05b875 100644 --- a/contracts/interfaces/ISocket.sol +++ b/contracts/interfaces/ISocket.sol @@ -42,6 +42,7 @@ interface ISocket { */ event AppGatewayCallRequested( bytes32 triggerId, + bytes32 appGatewayId, address switchboard, address plug, bytes overrides, diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index 45caa25f..2dfb73c1 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -12,6 +12,7 @@ contract TriggerTest is DeliveryHelperTest { event AppGatewayCallRequested( bytes32 triggerId, + bytes32 appGatewayId, address switchboard, address plug, bytes overrides, @@ -78,6 +79,7 @@ contract TriggerTest is DeliveryHelperTest { vm.expectEmit(true, true, true, true); emit AppGatewayCallRequested( triggerId, + _encodeAppGatewayId(address(gateway)), address(arbConfig.switchboard), address(counter), bytes(""), diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index b4e0f483..6a86473a 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -96,6 +96,7 @@ contract MockSocket is ISocket { triggerId = _encodeTriggerId(plugConfig.appGatewayId); emit AppGatewayCallRequested( triggerId, + plugConfig.appGatewayId, address(plugConfig.switchboard__), msg.sender, overrides, From 7412f04330b764970a6c7e69140fcfb0d3f1ce65 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 1 May 2025 20:39:36 +0530 Subject: [PATCH 101/108] fix: event and fees sign --- contracts/protocol/payload-delivery/FeesManager.sol | 10 +--------- contracts/protocol/utils/common/Structs.sol | 2 +- src/enums.ts | 1 + src/events.ts | 1 + 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index b2e5b75e..22a6f70b 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -179,15 +179,7 @@ contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResol // check signature bytes32 digest = keccak256( - abi.encode( - depositTo_, - chainSlug_, - token_, - amount, - address(this), - evmxSlug, - signatureNonce_ - ) + abi.encode(depositTo_, chainSlug_, token_, amount, address(this), evmxSlug) ); if (_recoverSigner(digest, signature_) != owner()) revert InvalidWatcherSignature(); diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 2b9f3838..e65db7ae 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -95,7 +95,7 @@ struct PlugConfig { //trigger: struct TriggerParams { bytes32 triggerId; - bytes32 params; + bytes32 overrides; address plug; bytes32 appGatewayId; uint32 chainSlug; diff --git a/src/enums.ts b/src/enums.ts index 87a79cd5..689543ce 100644 --- a/src/enums.ts +++ b/src/enums.ts @@ -11,6 +11,7 @@ export enum Events { ExecutionFailed = "ExecutionFailed", PlugConnected = "PlugConnected", AppGatewayCallRequested = "AppGatewayCallRequested", + AppGatewayCallFailed = "AppGatewayCallFailed", // FeesPlug FeesDeposited = "FeesDeposited", diff --git a/src/events.ts b/src/events.ts index 1893f5b0..b12fa610 100644 --- a/src/events.ts +++ b/src/events.ts @@ -11,6 +11,7 @@ export const feesPlugEvents = [Events.FeesDeposited]; export const watcherPrecompileEvents = [ Events.CalledAppGateway, + Events.AppGatewayCallFailed, Events.RequestSubmitted, Events.QueryRequested, Events.FinalizeRequested, From ff52d0733a3a33d347b552d2e917417770e2c14a Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 1 May 2025 20:40:35 +0530 Subject: [PATCH 102/108] fix: tests --- test/DeliveryHelper.t.sol | 3 +-- test/Inbox.t.sol | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/test/DeliveryHelper.t.sol b/test/DeliveryHelper.t.sol index e1dc4574..85d4b42b 100644 --- a/test/DeliveryHelper.t.sol +++ b/test/DeliveryHelper.t.sol @@ -200,8 +200,7 @@ contract DeliveryHelperTest is SetupTest { fees_.token, fees_.amount, address(feesManager), - evmxSlug, - signatureNonce + evmxSlug ) ); diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index 2dfb73c1..f680f6de 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -94,7 +94,7 @@ contract TriggerTest is DeliveryHelperTest { appGatewayId: _encodeAppGatewayId(address(gateway)), plug: address(counter), payload: payload, - params: bytes32(0) + overrides: bytes32(0) }); bytes memory watcherSignature = _createWatcherSignature( From 5bd85fa02aa34560dbc5e31b4033cbedf9183731 Mon Sep 17 00:00:00 2001 From: Akash Date: Thu, 1 May 2025 23:23:09 +0530 Subject: [PATCH 103/108] fix : deploy scripts --- Errors.md | 159 ++++--- EventTopics.md | 237 +++++----- FunctionSignatures.md | 406 ++++++++++-------- contracts/base/AppGatewayBase.sol | 3 +- contracts/helpers/TestUSDC.sol | 41 ++ contracts/protocol/socket/Socket.sol | 14 +- .../WatcherPrecompileConfig.sol | 8 + .../WatcherPrecompileLimits.sol | 12 + .../core/WatcherPrecompile.sol | 6 +- deployments/dev_addresses.json | 74 ++-- deployments/dev_verification.json | 60 ++- hardhat-scripts/config/config.ts | 37 +- hardhat-scripts/constants/enums.ts | 2 + hardhat-scripts/deploy/1.deploy.ts | 110 ++++- hardhat-scripts/deploy/4.connect.ts | 36 +- 15 files changed, 743 insertions(+), 462 deletions(-) create mode 100644 contracts/helpers/TestUSDC.sol diff --git a/Errors.md b/Errors.md index 018c142e..e374a824 100644 --- a/Errors.md +++ b/Errors.md @@ -8,28 +8,21 @@ ## interfaces/IWatcherPrecompile.sol -| Error | Signature | -| --------------------------- | ------------ | -| `InvalidChainSlug()` | `0xbff6b106` | -| `InvalidConnection()` | `0x63228f29` | -| `InvalidTransmitter()` | `0x58a70a0a` | -| `InvalidTimeoutRequest()` | `0x600ca372` | -| `InvalidPayloadId()` | `0xfa0b8c86` | -| `InvalidCaller()` | `0x48f5c3ed` | -| `InvalidGateway()` | `0xfc9dfe85` | -| `InvalidSwitchboard()` | `0xf63c9e4d` | -| `RequestAlreadyCancelled()` | `0xc70f47d8` | -| `RequestCancelled()` | `0xe3cf2258` | -| `AlreadyStarted()` | `0x1fbde445` | -| `InvalidLevelNumber()` | `0x5022f14b` | - -## interfaces/IWatcherPrecompileLimits.sol - -| Error | Signature | -| ------------------------------------------------ | ------------ | -| `ActionNotSupported(address,bytes32)` | `0xa219158f` | -| `NotDeliveryHelper()` | `0x29029c67` | -| `LimitExceeded(address,bytes32,uint256,uint256)` | `0x80bb2621` | +| Error | Signature | +| ------------------------------------- | ------------ | +| `InvalidChainSlug()` | `0xbff6b106` | +| `InvalidConnection()` | `0x63228f29` | +| `InvalidTimeoutRequest()` | `0x600ca372` | +| `InvalidPayloadId()` | `0xfa0b8c86` | +| `InvalidCaller()` | `0x48f5c3ed` | +| `InvalidGateway()` | `0xfc9dfe85` | +| `InvalidSwitchboard()` | `0xf63c9e4d` | +| `RequestAlreadyCancelled()` | `0xc70f47d8` | +| `RequestCancelled()` | `0xe3cf2258` | +| `AlreadyStarted()` | `0x1fbde445` | +| `RequestNotProcessing()` | `0x07ba8aaa` | +| `InvalidLevelNumber()` | `0x5022f14b` | +| `DeadlineNotPassedForOnChainRevert()` | `0x7006aa10` | ## protocol/AddressResolver.sol @@ -46,12 +39,6 @@ | `PromiseAlreadySetUp()` | `0x927c53d5` | | `PromiseRevertFailed()` | `0x0175b9de` | -## protocol/Forwarder.sol - -| Error | Signature | -| ------------------------ | ------------ | -| `AsyncModifierNotUsed()` | `0xb9521e1a` | - ## protocol/payload-delivery/AuctionManager.sol | Error | Signature | @@ -61,44 +48,50 @@ ## protocol/payload-delivery/ContractFactoryPlug.sol -| Error | Signature | -| ------------------------- | ------------ | -| `DeploymentFailed()` | `0x30116425` | -| `ExecutionFailed()` | `0xacfdb444` | -| `information(bool,bytes)` | `0x1a5c6d63` | +| Error | Signature | +| -------------------------- | ------------ | +| `DeploymentFailed()` | `0x30116425` | +| `ExecutionFailed()` | `0xacfdb444` | +| `information(bool,,bytes)` | `0x3a82a1f3` | ## protocol/payload-delivery/FeesManager.sol -| Error | Signature | -| ----------------------------- | ------------ | -| `InsufficientFeesAvailable()` | `0x51488f54` | -| `NoFeesForTransmitter()` | `0x248bac55` | -| `NoFeesBlocked()` | `0x116d68f9` | -| `InvalidWatcherSignature()` | `0x5029f14f` | -| `NonceUsed()` | `0x1f6d5aef` | -| `InvalidCaller()` | `0x48f5c3ed` | +| Error | Signature | +| -------------------------------- | ------------ | +| `InsufficientCreditsAvailable()` | `0xe61dc0aa` | +| `NoFeesForTransmitter()` | `0x248bac55` | +| `NoCreditsBlocked()` | `0xada9eb4c` | +| `InvalidCaller()` | `0x48f5c3ed` | +| `InvalidUserSignature()` | `0xe3fb657c` | +| `AppGatewayNotWhitelisted()` | `0x84e5309f` | +| `InvalidAmount()` | `0x2c5211c6` | +| `InsufficientBalance()` | `0xf4d678b8` | ## protocol/payload-delivery/FeesPlug.sol -| Error | Signature | -| ----------------------------------- | ------------ | -| `FeesAlreadyPaid()` | `0xd3b1ad69` | -| `InsufficientTokenBalance(address)` | `0x642faafa` | -| `InvalidDepositAmount()` | `0xfe9ba5cd` | -| `TokenNotWhitelisted(address)` | `0xea3bff2e` | +| Error | Signature | +| --------------------------------------------------- | ------------ | +| `InsufficientTokenBalance(address,uint256,uint256)` | `0xebd6ced9` | +| `InvalidDepositAmount()` | `0xfe9ba5cd` | +| `TokenNotWhitelisted(address)` | `0xea3bff2e` | ## protocol/payload-delivery/app-gateway/DeliveryUtils.sol -| Error | Signature | -| ----------------------- | ------------ | -| `AllPayloadsExecuted()` | `0x6bc43bfe` | -| `NotFromForwarder()` | `0xe83aa6bd` | -| `CallFailed(bytes32)` | `0xe22e3683` | -| `PayloadTooLarge()` | `0x492f620d` | -| `OnlyAppGateway()` | `0xfec944ea` | -| `WinningBidExists()` | `0xe8733654` | -| `InsufficientFees()` | `0x8d53e553` | -| `ReadOnlyRequests()` | `0x5f16b0e6` | +| Error | Signature | +| ------------------------------------ | ------------ | +| `PayloadTooLarge()` | `0x492f620d` | +| `OnlyAppGateway()` | `0xfec944ea` | +| `WinningBidExists()` | `0xe8733654` | +| `InsufficientFees()` | `0x8d53e553` | +| `ReadOnlyRequests()` | `0x5f16b0e6` | +| `RequestPayloadCountLimitExceeded()` | `0xcbef144b` | +| `MaxMsgValueLimitExceeded()` | `0x97b4e8ce` | + +## protocol/payload-delivery/app-gateway/FeesHelpers.sol + +| Error | Signature | +| --------------------------------------------- | ------------ | +| `NewMaxFeesLowerThanCurrent(uint256,uint256)` | `0x1345dda1` | ## protocol/socket/Socket.sol @@ -109,6 +102,8 @@ | `LowGasLimit()` | `0xd38edae0` | | `InvalidSlug()` | `0x290a8315` | | `DeadlinePassed()` | `0x70f65caa` | +| `InsufficientMsgValue()` | `0x78f38f76` | +| `ReadOnlyCall()` | `0xcf8fd6f1` | ## protocol/socket/SocketConfig.sol @@ -119,11 +114,12 @@ | `SwitchboardExists()` | `0x2dff8555` | | `SwitchboardExistsOrDisabled()` | `0x1c7d2487` | -## protocol/socket/SocketUtils.sol +## protocol/socket/SocketFeeManager.sol -| Error | Signature | -| ---------------------- | ------------ | -| `InvalidTransmitter()` | `0x58a70a0a` | +| Error | Signature | +| -------------------- | ------------ | +| `InsufficientFees()` | `0x8d53e553` | +| `FeeTooLow()` | `0x732f9413` | ## protocol/socket/switchboard/FastSwitchboard.sol @@ -132,12 +128,6 @@ | `AlreadyAttested()` | `0x35d90805` | | `WatcherNotFound()` | `0xa278e4ad` | -## protocol/socket/switchboard/SwitchboardBase.sol - -| Error | Signature | -| ---------------- | ------------ | -| `InvalidNonce()` | `0x756688fe` | - ## protocol/utils/AccessControl.sol | Error | Signature | @@ -156,17 +146,8 @@ | Error | Signature | | ---------------------------- | ------------ | -| `NotAuthorized()` | `0xea8e4eb5` | -| `NotBridge()` | `0x7fea9dc5` | | `NotSocket()` | `0xc59f8f7c` | -| `ConnectorUnavailable()` | `0xb1efb84a` | -| `InvalidTokenContract()` | `0x29bdfb34` | -| `ZeroAddressReceiver()` | `0x96bbcf1e` | | `ZeroAddress()` | `0xd92e233d` | -| `ZeroAmount()` | `0x1f2a2005` | -| `InsufficientFunds()` | `0x356680b7` | -| `InvalidSigner()` | `0x815e1d64` | -| `InvalidFunction()` | `0xdb2079c3` | | `TimeoutDelayTooLarge()` | `0xc10bfe64` | | `TimeoutAlreadyResolved()` | `0x7dc8be06` | | `ResolvingTimeoutTooEarly()` | `0x28fd4c50` | @@ -174,13 +155,13 @@ | `FeesAlreadyPaid()` | `0xd3b1ad69` | | `NotAuctionManager()` | `0x87944c26` | | `CallFailed()` | `0x3204506f` | -| `PlugDisconnected()` | `0xe741bafb` | +| `PlugNotFound()` | `0x5f1ac76a` | | `InvalidAppGateway()` | `0x82ded261` | | `AppGatewayAlreadyCalled()` | `0xb224683f` | | `InvalidInboxCaller()` | `0x4f1aa61e` | +| `InvalidCallerTriggered()` | `0x3292d247` | | `PromisesNotResolved()` | `0xb91dbe7d` | | `InvalidPromise()` | `0x45f2d176` | -| `InvalidIndex()` | `0x63df8171` | | `InvalidTransmitter()` | `0x58a70a0a` | | `FeesNotSet()` | `0x2a831034` | | `InvalidTokenAddress()` | `0x1eb00b06` | @@ -191,12 +172,22 @@ | `BidExceedsMaxFees()` | `0x4c923f3c` | | `LowerBidAlreadyExists()` | `0xaaa1f709` | | `AsyncModifierNotUsed()` | `0xb9521e1a` | +| `InvalidIndex()` | `0x63df8171` | +| `RequestAlreadyExecuted()` | `0xd6f1f946` | +| `NoAsyncPromiseFound()` | `0xa2928f68` | +| `PromiseCallerMismatch()` | `0x2b87f115` | +| `RequestCountMismatch()` | `0x98bbcbff` | +| `DeliveryHelperNotSet()` | `0x07e6c946` | ## protocol/watcherPrecompile/WatcherPrecompileConfig.sol -| Error | Signature | -| --------------------------- | ------------ | -| `InvalidGateway()` | `0xfc9dfe85` | -| `InvalidSwitchboard()` | `0xf63c9e4d` | -| `NonceUsed()` | `0x1f6d5aef` | -| `InvalidWatcherSignature()` | `0x5029f14f` | +| Error | Signature | +| ---------------------- | ------------ | +| `InvalidGateway()` | `0xfc9dfe85` | +| `InvalidSwitchboard()` | `0xf63c9e4d` | + +## protocol/watcherPrecompile/WatcherPrecompileLimits.sol + +| Error | Signature | +| ---------------------------- | ------------ | +| `WatcherFeesNotSet(bytes32)` | `0x1ce1de3f` | diff --git a/EventTopics.md b/EventTopics.md index 28b83f5a..ada88250 100644 --- a/EventTopics.md +++ b/EventTopics.md @@ -8,19 +8,31 @@ | `Deployed` | `(proxy: address, implementation: address, admin: address)` | `0xc95935a66d15e0da5e412aca0ad27ae891d20b2fb91cf3994b6a3bf2b8178082` | | `Upgraded` | `(proxy: address, implementation: address)` | `0x5d611f318680d00598bb735d61bacf0c514c6b50e1e5ad30040a4df2b12791c7` | +## TestUSDC + +| Event | Arguments | Topic | +| ---------- | ----------------------------------------------------- | -------------------------------------------------------------------- | +| `Approval` | `(owner: address, spender: address, amount: uint256)` | `0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925` | +| `Transfer` | `(from: address, to: address, amount: uint256)` | `0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef` | + ## AddressResolver -| Event | Arguments | Topic | -| ---------------------------- | ----------------------------------------------------------- | -------------------------------------------------------------------- | -| `AddressSet` | `(name: bytes32, oldAddress: address, newAddress: address)` | `0x9ef0e8c8e52743bb38b83b17d9429141d494b8041ca6d616a6c77cebae9cd8b7` | -| `AsyncPromiseDeployed` | `(newAsyncPromise: address, salt: bytes32)` | `0xb6c5491cf83e09749b1a4dd6a9f07b0e925fcb0a915ac8c2b40e8ab28191c270` | -| `ForwarderDeployed` | `(newForwarder: address, salt: bytes32)` | `0x4dbbecb9cf9c8b93da9743a2b48ea52efe68d69230ab1c1b711891d9d223b29f` | -| `ImplementationUpdated` | `(contractName: string, newImplementation: address)` | `0xa1e41aa2c2f3f20d9b63ac06b634d2788768d6034f3d9192cdf7d07374bb16f4` | -| `Initialized` | `(version: uint64)` | `0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2` | -| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | -| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | -| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | -| `PlugAdded` | `(appGateway: address, chainSlug: uint32, plug: address)` | `0x2cb8d865028f9abf3dc064724043264907615fadc8615a3699a85edb66472273` | +| Event | Arguments | Topic | +| ------------------------------ | ----------------------------------------------------------- | -------------------------------------------------------------------- | +| `AddressSet` | `(name: bytes32, oldAddress: address, newAddress: address)` | `0x9ef0e8c8e52743bb38b83b17d9429141d494b8041ca6d616a6c77cebae9cd8b7` | +| `AsyncPromiseDeployed` | `(newAsyncPromise: address, salt: bytes32)` | `0xb6c5491cf83e09749b1a4dd6a9f07b0e925fcb0a915ac8c2b40e8ab28191c270` | +| `ContractsToGatewaysUpdated` | `(contractAddress_: address, appGateway_: address)` | `0xb870bb0c6b5ea24214ae6c653af6c2a8b6240d5838f82132703ee5c069b14b4c` | +| `DefaultAuctionManagerUpdated` | `(defaultAuctionManager_: address)` | `0x60f296739208a505ead7fb622df0f76b7791b824481b120a2300bdaf85e3e3d6` | +| `DeliveryHelperUpdated` | `(deliveryHelper_: address)` | `0xc792471d30bbabcf9dc9fdba5bfa74f8872ff3c28f6e65e122bdb82a71b83c1c` | +| `FeesManagerUpdated` | `(feesManager_: address)` | `0x94e67aa1341a65767dfde81e62fd265bfbade1f5744bfd3cd73f99a6eca0572a` | +| `ForwarderDeployed` | `(newForwarder: address, salt: bytes32)` | `0x4dbbecb9cf9c8b93da9743a2b48ea52efe68d69230ab1c1b711891d9d223b29f` | +| `ImplementationUpdated` | `(contractName: string, newImplementation: address)` | `0xa1e41aa2c2f3f20d9b63ac06b634d2788768d6034f3d9192cdf7d07374bb16f4` | +| `Initialized` | `(version: uint64)` | `0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2` | +| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | +| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | +| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | +| `PlugAdded` | `(appGateway: address, chainSlug: uint32, plug: address)` | `0x2cb8d865028f9abf3dc064724043264907615fadc8615a3699a85edb66472273` | +| `WatcherPrecompileUpdated` | `(watcherPrecompile_: address)` | `0xb00972c0b5c3d3d9ddc6d6a6db612abeb109653a3424d5d972510fa20bff4972` | ## AsyncPromise @@ -38,10 +50,11 @@ | Event | Arguments | Topic | | ---------------------------- | ------------------------------------------- | -------------------------------------------------------------------- | -| `AuctionEnded` | `(requestCount: uint40, winningBid: tuple)` | `0x9cc96c8b9e588c26f8beae57fe7fbb59113b82865578b54ff3f025317dcd6895` | +| `AuctionEndDelaySecondsSet` | `(auctionEndDelaySeconds: uint256)` | `0xf38f0d9dc8459cf5426728c250d115196a4c065ebc1a6c29da24764a8c0da722` | +| `AuctionEnded` | `(requestCount: uint40, winningBid: tuple)` | `0xede4ec1efc469fac10dcb4930f70be4cd21f3700ed61c91967c19a7cd7c0d86e` | | `AuctionRestarted` | `(requestCount: uint40)` | `0x071867b21946ec4655665f0d4515d3757a5a52f144c762ecfdfb11e1da542b82` | | `AuctionStarted` | `(requestCount: uint40)` | `0xcd040613cf8ef0cfcaa3af0d711783e827a275fc647c116b74595bf17cb9364f` | -| `BidPlaced` | `(requestCount: uint40, bid: tuple)` | `0xd3dc2f289bc8a88faaaf6a3f4f800dd0eac760a653b067ef749771252a1343b3` | +| `BidPlaced` | `(requestCount: uint40, bid: tuple)` | `0x7f79485e4c9aeea5d4899bc6f7c63b22ac1f4c01d2d28c801e94732fee657b5d` | | `Initialized` | `(version: uint64)` | `0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2` | | `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | | `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | @@ -63,48 +76,54 @@ ## FeesManager -| Event | Arguments | Topic | -| ---------------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------------------------- | -| `FeesBlocked` | `(requestCount: uint40, chainSlug: uint32, token: address, amount: uint256)` | `0xbb23ad39130b455188189b8de52b55fa41a7ea8ee8413dc28ced31e543d0df0c` | -| `FeesDepositedUpdated` | `(chainSlug: uint32, appGateway: address, token: address, amount: uint256)` | `0xe82dece33ef85114446a366b7d94538d641968e3ec87bf9f2f5a957ace1086e7` | -| `FeesUnblocked` | `(requestCount: uint40, appGateway: address)` | `0xc8b27128d97a92b6664c696ac891afaa87c9fc7d7c7cda17d892237589ebd4fc` | -| `FeesUnblockedAndAssigned` | `(requestCount: uint40, transmitter: address, amount: uint256)` | `0x04d2986fb321499f6bc8263ff6e65d823570e186dcdc16c04c6b388ccd0f29a8` | -| `Initialized` | `(version: uint64)` | `0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2` | -| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | -| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | -| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | -| `TransmitterFeesUpdated` | `(requestCount: uint40, transmitter: address, amount: uint256)` | `0x9839a0f8408a769f0f3bb89025b64a6cff279673c77d2de3ab8d59b1841fcd5f` | +| Event | Arguments | Topic | +| ----------------------------------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| `CreditsBlocked` | `(requestCount: uint40, consumeFrom: address, amount: uint256)` | `0xf037c15aef41440aa823cf1fdeaea332105d8b23d52557f6670189b5d76f1eed` | +| `CreditsDeposited` | `(chainSlug: uint32, appGateway: address, token: address, amount: uint256)` | `0x7254d040844de2dac4225a23f81bb54acb13d1eadb6e8b369dd251d36a9e8552` | +| `CreditsUnblocked` | `(requestCount: uint40, appGateway: address)` | `0x45db29ef2701319155cac058aa2f56ce1f73e0e238161d3db9f8c9a47655210d` | +| `CreditsUnblockedAndAssigned` | `(requestCount: uint40, transmitter: address, amount: uint256)` | `0x6f3d11270d1df9aff1aa04d1ea7797a3a572586a31437acc415ac853f625050c` | +| `CreditsUnwrapped` | `(consumeFrom: address, amount: uint256)` | `0xdcc9473b722b4c953617ab373840b365298a520bc7f20ce94fa7314f4a857774` | +| `CreditsWrapped` | `(consumeFrom: address, amount: uint256)` | `0x40246503613721eb4acf4020c6c56b6a16e5d08713316db0bea5210e8819c592` | +| `Initialized` | `(version: uint64)` | `0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2` | +| `InsufficientWatcherPrecompileCreditsAvailable` | `(chainSlug: uint32, token: address, consumeFrom: address)` | `0xd50bc02f94b9ef4a8aff7438da15a69e443956f56b6aa007cf2c584215e87493` | +| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | +| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | +| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | +| `TransmitterCreditsUpdated` | `(requestCount: uint40, transmitter: address, amount: uint256)` | `0x24790626bfbe84d1358ce3e8cb0ff6cfc9eb7ea16e597f43ab607107baf889e3` | +| `WatcherPrecompileCreditsAssigned` | `(amount: uint256, consumeFrom: address)` | `0x87eddb69736f41b812366535a59efc79b1997f2d237240d7176d210397012e1b` | ## FeesPlug -| Event | Arguments | Topic | -| ---------------------------- | -------------------------------------------------------- | -------------------------------------------------------------------- | -| `ConnectorPlugDisconnected` | `()` | `0xc2af098c82dba3c4b00be8bda596d62d13b98a87b42626fefa67e0bb0e198fdd` | -| `FeesDeposited` | `(appGateway: address, token: address, amount: uint256)` | `0x0fd38537e815732117cfdab41ba9b6d3eb2c5799d44039c100c05fc9c112f235` | -| `FeesWithdrawn` | `(token: address, amount: uint256, receiver: address)` | `0x87044da2612407bc001bb0985725dcc651a0dc71eaabfd1d7e8617ca85a8c19c` | -| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | -| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | -| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | -| `RoleGranted` | `(role: bytes32, grantee: address)` | `0x2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f3` | -| `RoleRevoked` | `(role: bytes32, revokee: address)` | `0x155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a52` | -| `TokenRemovedFromWhitelist` | `(token: address)` | `0xdd2e6d9f52cbe8f695939d018b7d4a216dc613a669876163ac548b916489d917` | -| `TokenWhitelisted` | `(token: address)` | `0x6a65f90b1a644d2faac467a21e07e50e3f8fa5846e26231d30ae79a417d3d262` | +| Event | Arguments | Topic | +| ---------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| `ConnectorPlugDisconnected` | `()` | `0xc2af098c82dba3c4b00be8bda596d62d13b98a87b42626fefa67e0bb0e198fdd` | +| `FeesDeposited` | `(token: address, receiver: address, feeAmount: uint256, nativeAmount: uint256)` | `0xeb4e1b24b7fe377de69f80f7380bda5ba4b43176c6a4d300a3be9009c49f4228` | +| `FeesWithdrawn` | `(token: address, receiver: address, amount: uint256)` | `0x5e110f8bc8a20b65dcc87f224bdf1cc039346e267118bae2739847f07321ffa8` | +| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | +| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | +| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | +| `RoleGranted` | `(role: bytes32, grantee: address)` | `0x2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f3` | +| `RoleRevoked` | `(role: bytes32, revokee: address)` | `0x155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a52` | +| `TokenRemovedFromWhitelist` | `(token: address)` | `0xdd2e6d9f52cbe8f695939d018b7d4a216dc613a669876163ac548b916489d917` | +| `TokenWhitelisted` | `(token: address)` | `0x6a65f90b1a644d2faac467a21e07e50e3f8fa5846e26231d30ae79a417d3d262` | ## Socket -| Event | Arguments | Topic | -| ---------------------------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | -| `AppGatewayCallRequested` | `(callId: bytes32, chainSlug: uint32, plug: address, appGateway: address, params: bytes32, payload: bytes)` | `0x392cb36fae7bd0470268c65b15c32a745b37168c4ccd13348c59bd9170f3b3e8` | -| `ExecutionFailed` | `(payloadId: bytes32, returnData: bytes)` | `0xd255d8a333980d77af4f9179384057def133983cb02db3e1fdb70c4dc14102e8` | -| `ExecutionSuccess` | `(payloadId: bytes32, returnData: bytes)` | `0xc54787fbe087097b182e713f16d3443ad2e67cbe6732628451dd3695a11814c2` | -| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | -| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | -| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | -| `PlugConnected` | `(plug: address, appGateway: address, switchboard: address)` | `0x99c37c6da3bd69c6d59967915f8339f11a0a17fed28c615efb19457fdec0d7db` | -| `RoleGranted` | `(role: bytes32, grantee: address)` | `0x2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f3` | -| `RoleRevoked` | `(role: bytes32, revokee: address)` | `0x155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a52` | -| `SwitchboardAdded` | `(switchboard: address)` | `0x1595852923edfbbf906f09fc8523e4cfb022a194773c4d1509446b614146ee88` | -| `SwitchboardDisabled` | `(switchboard: address)` | `0x1b4ee41596b4e754e5665f01ed6122b356f7b36ea0a02030804fac7fa0fdddfc` | +| Event | Arguments | Topic | +| ---------------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| `AppGatewayCallRequested` | `(triggerId: bytes32, appGatewayId: bytes32, switchboard: address, plug: address, overrides: bytes, payload: bytes)` | `0x5c88d65ab8ba22a57e582bd8ddfa9801cc0ca6be6cb3182baaedc705a612419e` | +| `ExecutionFailed` | `(payloadId: bytes32, exceededMaxCopy: bool, returnData: bytes)` | `0x385334bc68a32c4d164625189adc7633e6074eb1b837fb4d11d768245151e4ce` | +| `ExecutionSuccess` | `(payloadId: bytes32, exceededMaxCopy: bool, returnData: bytes)` | `0x324d63a433b21a12b90e79cd2ba736b2a5238be6165e03b750fa4a7d5193d5d9` | +| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | +| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | +| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | +| `PlugConnected` | `(plug: address, appGatewayId: bytes32, switchboard: address)` | `0x90c5924e27cfb6e3a688e729083681f30494ae2615ae14aac3bc807a0c436a88` | +| `RoleGranted` | `(role: bytes32, grantee: address)` | `0x2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f3` | +| `RoleRevoked` | `(role: bytes32, revokee: address)` | `0x155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a52` | +| `SocketFeeManagerUpdated` | `(oldSocketFeeManager: address, newSocketFeeManager: address)` | `0xdcb02e10d5220346a4638aa2826eaab1897306623bc40a427049e4ebd12255b4` | +| `SwitchboardAdded` | `(switchboard: address)` | `0x1595852923edfbbf906f09fc8523e4cfb022a194773c4d1509446b614146ee88` | +| `SwitchboardDisabled` | `(switchboard: address)` | `0x1b4ee41596b4e754e5665f01ed6122b356f7b36ea0a02030804fac7fa0fdddfc` | +| `SwitchboardEnabled` | `(switchboard: address)` | `0x6909a9974e3eec619bc479ba882d30a5ef1219b72ab1ce6a354516e91be317b8` | ## SocketBatcher @@ -114,76 +133,92 @@ | `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | | `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | -## WatcherPrecompile +## SocketFeeManager -| Event | Arguments | Topic | -| ---------------------------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | -| `CalledAppGateway` | `(callId: bytes32, chainSlug: uint32, plug: address, appGateway: address, params: bytes32, payload: bytes)` | `0x255bcf22d238fe60f6611670cd7919d2bc890283be2fdaf6d2ad3411e777e33c` | -| `FinalizeRequested` | `(digest: bytes32, params: tuple)` | `0x5bc623895e2e50e307b4c3ba21df61ddfe68de0e084bb85eb1d42d4596532589` | -| `Finalized` | `(payloadId: bytes32, proof: bytes)` | `0x7e6e3e411317567fb9eabe3eb86768c3e33c46e38a50790726e916939b4918d6` | -| `Initialized` | `(version: uint64)` | `0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2` | -| `MarkedRevert` | `(payloadId: bytes32, isRevertingOnchain: bool)` | `0xcf1fd844cb4d32cbebb5ca6ce4ac834fe98da3ddac44deb77fffd22ad933824c` | -| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | -| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | -| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | -| `PromiseNotResolved` | `(payloadId: bytes32, asyncPromise: address)` | `0xbcf0d0c678940566e9e64f0c871439395bd5fb5c39bca3547b126fe6ee467937` | -| `PromiseResolved` | `(payloadId: bytes32, asyncPromise: address)` | `0x1b1b5810494fb3e17f7c46547e6e67cd6ad3e6001ea6fb7d12ea0241ba13c4ba` | -| `QueryRequested` | `(params: tuple)` | `0xca81bf0029a549d7e6e3a9c668a717472f4330a6a5ec4350304a9e79bf437345` | -| `RequestSubmitted` | `(middleware: address, requestCount: uint40, payloadParamsArray: tuple[])` | `0xb856562fcff2119ba754f0486f47c06087ebc1842bff464faf1b2a1f8d273b1d` | -| `RoleGranted` | `(role: bytes32, grantee: address)` | `0x2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f3` | -| `RoleRevoked` | `(role: bytes32, revokee: address)` | `0x155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a52` | -| `TimeoutRequested` | `(timeoutId: bytes32, target: address, payload: bytes, executeAt: uint256)` | `0xdf94fed77e41734b8a17815476bbbf88e2db15d762f42a30ddb9d7870f2fb858` | -| `TimeoutResolved` | `(timeoutId: bytes32, target: address, payload: bytes, executedAt: uint256)` | `0x221462ec065e22637f794ec3a7edb17b2f04bec88f0546dda308bc37a83801b8` | +| Event | Arguments | Topic | +| ---------------------------- | ---------------------------------------- | -------------------------------------------------------------------- | +| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | +| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | +| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | +| `RoleGranted` | `(role: bytes32, grantee: address)` | `0x2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f3` | +| `RoleRevoked` | `(role: bytes32, revokee: address)` | `0x155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a52` | +| `SocketFeesUpdated` | `(oldFees: uint256, newFees: uint256)` | `0xcbd4d756fb6198bbcc2e4013cce929f504ad46e9d97c543ef9a8dfea3e407053` | ## WatcherPrecompileConfig | Event | Arguments | Topic | | ---------------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | | `Initialized` | `(version: uint64)` | `0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2` | +| `IsValidPlugSet` | `(appGateway: address, chainSlug: uint32, plug: address, isValid: bool)` | `0x61cccc7387868fc741379c7acd9dd346e0ca2e5c067dc5b156fbbc55b1c2fcf5` | | `OnChainContractSet` | `(chainSlug: uint32, socket: address, contractFactoryPlug: address, feesPlug: address)` | `0xd24cf816377e3c571e7bc798dd43d3d5fc78c32f7fc94b42898b0d37c5301a4e` | | `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | | `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | | `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | -| `PlugAdded` | `(appGateway: address, chainSlug: uint32, plug: address)` | `0x2cb8d865028f9abf3dc064724043264907615fadc8615a3699a85edb66472273` | -| `RoleGranted` | `(role: bytes32, grantee: address)` | `0x2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f3` | -| `RoleRevoked` | `(role: bytes32, revokee: address)` | `0x155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a52` | +| `PlugAdded` | `(appGatewayId: bytes32, chainSlug: uint32, plug: address)` | `0x7b3e14230a721c4737d275f9a63b92c44cb657bcfddbe6fe9b4d9cd9bd8d4a95` | | `SwitchboardSet` | `(chainSlug: uint32, sbType: bytes32, switchboard: address)` | `0x6273f161f4a795e66ef3585d9b4442ef3796b32337157fdfb420b5281e4cf2e3` | ## WatcherPrecompileLimits -| Event | Arguments | Topic | -| ---------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------- | -| `AppGatewayActivated` | `(appGateway: address, maxLimit: uint256, ratePerSecond: uint256)` | `0x44628d7d5628b9fbc2c84ea9bf3bd3987fa9cde8d2b28e2d5ceb451f916cb8b9` | -| `Initialized` | `(version: uint64)` | `0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2` | -| `LimitParamsUpdated` | `(updates: tuple[])` | `0x81576b12f4d507fd0543afd25a86785573a595334c2c7eb8ca8ec1b0a56a55b3` | -| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | -| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | -| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | -| `RoleGranted` | `(role: bytes32, grantee: address)` | `0x2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f3` | -| `RoleRevoked` | `(role: bytes32, revokee: address)` | `0x155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a52` | +| Event | Arguments | Topic | +| --------------------------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------- | +| `AppGatewayActivated` | `(appGateway: address, maxLimit: uint256, ratePerSecond: uint256)` | `0x44628d7d5628b9fbc2c84ea9bf3bd3987fa9cde8d2b28e2d5ceb451f916cb8b9` | +| `CallBackFeesSet` | `(callBackFees: uint256)` | `0x667c97afffb32265f3b4e026d31b81dc223275ff8bb9819e67012197f5799faf` | +| `DefaultLimitAndRatePerSecondSet` | `(defaultLimit: uint256, defaultRatePerSecond: uint256)` | `0x39def16be1ce80876ad0b0936cfdf88b8be7a1790b6c1da16ba8bdee53367e8e` | +| `FinalizeFeesSet` | `(finalizeFees: uint256)` | `0x0b710f92aabbdda2e8c347f802353f34ef27845d79db79efb4884e8790a0d5fb` | +| `Initialized` | `(version: uint64)` | `0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2` | +| `LimitParamsUpdated` | `(updates: tuple[])` | `0x81576b12f4d507fd0543afd25a86785573a595334c2c7eb8ca8ec1b0a56a55b3` | +| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | +| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | +| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | +| `QueryFeesSet` | `(queryFees: uint256)` | `0x19569faa0df733d4b0806372423e828b05a5257eb7652da812b90f662bed5cfb` | +| `TimeoutFeesSet` | `(timeoutFees: uint256)` | `0xe8a5b23529bc11019d6df86a1ee0d043571d464902a3fa98e7e3e67dbd5981ca` | ## DeliveryHelper -| Event | Arguments | Topic | -| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | -| `AuctionEnded` | `(requestCount: uint40, winningBid: tuple)` | `0x9cc96c8b9e588c26f8beae57fe7fbb59113b82865578b54ff3f025317dcd6895` | -| `BidTimeoutUpdated` | `(newBidTimeout: uint256)` | `0xd4552e666d0e4e343fb2b13682972a8f0c7f1a86e252d6433b356f0c0e817c3d` | -| `CallBackReverted` | `(requestCount_: uint40, payloadId_: bytes32)` | `0xcecb2641ea89470f68bf9f852d731e123505424e4dcfd770c7ea9e2e25326b1b` | -| `FeesIncreased` | `(appGateway: address, requestCount: uint40, newMaxFees: uint256)` | `0x63ee9e9e84d216b804cb18f51b7f7511254b0c1f11304b7a3aa34d57511aa6dc` | -| `Initialized` | `(version: uint64)` | `0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2` | -| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | -| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | -| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | -| `PayloadSubmitted` | `(requestCount: uint40, appGateway: address, payloadSubmitParams: tuple[], fees: tuple, auctionManager: address, onlyReadRequests: bool)` | `0x204c4de167e7a12fc9ad8231fa3d877639ed95a66bd19e1a55d1f68088d4c784` | -| `RequestCancelled` | `(requestCount: uint40)` | `0xff191657769be72fc08def44c645014c60d18cb24b9ca05c9a33406a28253245` | +| Event | Arguments | Topic | +| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| `BidTimeoutUpdated` | `(newBidTimeout: uint256)` | `0xd4552e666d0e4e343fb2b13682972a8f0c7f1a86e252d6433b356f0c0e817c3d` | +| `ChainMaxMsgValueLimitsUpdated` | `(chainSlugs: uint32[], maxMsgValueLimits: uint256[])` | `0x17e47f6f0fa0e79831bee11b7c29adc45d9a7bd25acd70b91e4b2bad0f544352` | +| `FeesIncreased` | `(appGateway: address, requestCount: uint40, newMaxFees: uint256)` | `0x63ee9e9e84d216b804cb18f51b7f7511254b0c1f11304b7a3aa34d57511aa6dc` | +| `Initialized` | `(version: uint64)` | `0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2` | +| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | +| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | +| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | +| `PayloadSubmitted` | `(requestCount: uint40, appGateway: address, payloadSubmitParams: tuple[], fees: uint256, auctionManager: address, onlyReadRequests: bool)` | `0xc6455dba7c07a5e75c7189040ae9e3478162f333a96365b283b434fd0e32c6b3` | +| `RequestCancelled` | `(requestCount: uint40)` | `0xff191657769be72fc08def44c645014c60d18cb24b9ca05c9a33406a28253245` | ## FastSwitchboard -| Event | Arguments | Topic | -| ---------------------------- | ---------------------------------------- | -------------------------------------------------------------------- | -| `Attested` | `(digest_: bytes32, watcher: address)` | `0x3d83c7bc55c269e0bc853ddc0d7b9fca30216ecc43779acb4e36b7e0ad1c71e4` | -| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | -| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | -| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | -| `RoleGranted` | `(role: bytes32, grantee: address)` | `0x2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f3` | -| `RoleRevoked` | `(role: bytes32, revokee: address)` | `0x155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a52` | +| Event | Arguments | Topic | +| ---------------------------- | ----------------------------------------- | -------------------------------------------------------------------- | +| `Attested` | `(payloadId_: bytes32, watcher: address)` | `0x3d83c7bc55c269e0bc853ddc0d7b9fca30216ecc43779acb4e36b7e0ad1c71e4` | +| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | +| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | +| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | +| `RoleGranted` | `(role: bytes32, grantee: address)` | `0x2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f3` | +| `RoleRevoked` | `(role: bytes32, revokee: address)` | `0x155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a52` | + +## WatcherPrecompile + +| Event | Arguments | Topic | +| ----------------------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| `AppGatewayCallFailed` | `(triggerId: bytes32)` | `0xcaf8475fdade8465ea31672463949e6cf1797fdcdd11eeddbbaf857e1e5907b7` | +| `CalledAppGateway` | `(triggerId: bytes32)` | `0xf659ffb3875368f54fb4ab8f5412ac4518af79701a48076f7a58d4448e4bdd0b` | +| `ExpiryTimeSet` | `(expiryTime: uint256)` | `0x07e837e13ad9a34715a6bd45f49bbf12de19f06df79cb0be12b3a7d7f2397fa9` | +| `FinalizeRequested` | `(digest: bytes32, params: tuple)` | `0x5bc623895e2e50e307b4c3ba21df61ddfe68de0e084bb85eb1d42d4596532589` | +| `Finalized` | `(payloadId: bytes32, proof: bytes)` | `0x7e6e3e411317567fb9eabe3eb86768c3e33c46e38a50790726e916939b4918d6` | +| `Initialized` | `(version: uint64)` | `0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2` | +| `MarkedRevert` | `(payloadId: bytes32, isRevertingOnchain: bool)` | `0xcf1fd844cb4d32cbebb5ca6ce4ac834fe98da3ddac44deb77fffd22ad933824c` | +| `MaxTimeoutDelayInSecondsSet` | `(maxTimeoutDelayInSeconds: uint256)` | `0x3564638b089495c19e7359a040be083841e11da34c22a29ea8d602c8a9805fec` | +| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | +| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | +| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | +| `PromiseNotResolved` | `(payloadId: bytes32, asyncPromise: address)` | `0xbcf0d0c678940566e9e64f0c871439395bd5fb5c39bca3547b126fe6ee467937` | +| `PromiseResolved` | `(payloadId: bytes32, asyncPromise: address)` | `0x1b1b5810494fb3e17f7c46547e6e67cd6ad3e6001ea6fb7d12ea0241ba13c4ba` | +| `QueryRequested` | `(params: tuple)` | `0xca81bf0029a549d7e6e3a9c668a717472f4330a6a5ec4350304a9e79bf437345` | +| `RequestCancelledFromGateway` | `(requestCount: uint40)` | `0x333619ca4a2a9c4ee292aafa3c37215d88afe358afee4a575cfed21d743091c6` | +| `RequestSubmitted` | `(middleware: address, requestCount: uint40, payloadParamsArray: tuple[])` | `0xb856562fcff2119ba754f0486f47c06087ebc1842bff464faf1b2a1f8d273b1d` | +| `TimeoutRequested` | `(timeoutId: bytes32, target: address, payload: bytes, executeAt: uint256)` | `0xdf94fed77e41734b8a17815476bbbf88e2db15d762f42a30ddb9d7870f2fb858` | +| `TimeoutResolved` | `(timeoutId: bytes32, target: address, payload: bytes, executedAt: uint256, returnData: bytes)` | `0x61122416680ac7038ca053afc2c26983f2c524e5003b1f4d9dea095fbc8f6905` | +| `WatcherPrecompileConfigSet` | `(watcherPrecompileConfig: address)` | `0xdc19bca647582b3fbf69a6ffacabf56b4f7a4551d2d0944843712f2d0987a8e5` | +| `WatcherPrecompileLimitsSet` | `(watcherPrecompileLimits: address)` | `0xcec7ba89301793a37efb418279f17f8dd77e5959e9f3fbcbc54e40615a14bd8e` | diff --git a/FunctionSignatures.md b/FunctionSignatures.md index 557f95f5..17183218 100644 --- a/FunctionSignatures.md +++ b/FunctionSignatures.md @@ -15,6 +15,25 @@ | `upgrade` | `0x99a88ec4` | | `upgradeAndCall` | `0x9623609d` | +## TestUSDC + +| Function | Signature | +| ------------------ | ------------ | +| `DOMAIN_SEPARATOR` | `0x3644e515` | +| `allowance` | `0xdd62ed3e` | +| `approve` | `0x095ea7b3` | +| `balanceOf` | `0x70a08231` | +| `decimals` | `0x313ce567` | +| `mint` | `0x40c10f19` | +| `name` | `0x06fdde03` | +| `nonces` | `0x7ecebe00` | +| `owner` | `0x8da5cb5b` | +| `permit` | `0xd505accf` | +| `symbol` | `0x95d89b41` | +| `totalSupply` | `0x18160ddd` | +| `transfer` | `0xa9059cbb` | +| `transferFrom` | `0x23b872dd` | + ## AddressResolver | Function | Signature | @@ -83,6 +102,8 @@ | `getOnChainAddress` | `0x9da48789` | | `initialize` | `0x647c576c` | | `latestAsyncPromise` | `0xb8a8ba52` | +| `latestPromiseCaller` | `0xdfe580a8` | +| `latestRequestCount` | `0x198b9a47` | | `onChainAddress` | `0x8bd0b363` | | `then` | `0x0bf2ba15` | | `watcherPrecompileConfig` | `0x8618a912` | @@ -91,51 +112,52 @@ ## AuctionManager -| Function | Signature | -| ---------------------------- | ------------ | -| `addressResolver__` | `0x6a750469` | -| `auctionClosed` | `0x6862ebb0` | -| `auctionEndDelaySeconds` | `0x9087dfdb` | -| `auctionStarted` | `0x7c9c5bb8` | -| `bid` | `0xfcdf49c2` | -| `cancelOwnershipHandover` | `0x54d1f13d` | -| `completeOwnershipHandover` | `0xf04e283e` | -| `deliveryHelper__` | `0xc031dfb4` | -| `endAuction` | `0x1212e653` | -| `evmxSlug` | `0x8bae77c2` | -| `expireBid` | `0x1dd5022c` | -| `grantRole` | `0x2f2ff15d` | -| `hasRole` | `0x91d14854` | -| `initialize` | `0x5f24043b` | -| `maxReAuctionCount` | `0xc367b376` | -| `owner` | `0x8da5cb5b` | -| `ownershipHandoverExpiresAt` | `0xfee81cf4` | -| `reAuctionCount` | `0x9b4b22d3` | -| `renounceOwnership` | `0x715018a6` | -| `requestOwnershipHandover` | `0x25692962` | -| `revokeRole` | `0xd547741f` | -| `setAuctionEndDelaySeconds` | `0x88606b1a` | -| `transferOwnership` | `0xf2fde38b` | -| `watcherPrecompileConfig` | `0x8618a912` | -| `watcherPrecompileLimits` | `0xa71cd97d` | -| `watcherPrecompile__` | `0x1de360c3` | -| `whitelistedTransmitters` | `0xc2f1bf5d` | -| `winningBids` | `0x9133f232` | +| Function | Signature | +| -------------------------------- | ------------ | +| `addressResolver__` | `0x6a750469` | +| `auctionClosed` | `0x6862ebb0` | +| `auctionEndDelaySeconds` | `0x9087dfdb` | +| `auctionStarted` | `0x7c9c5bb8` | +| `bid` | `0xfcdf49c2` | +| `cancelOwnershipHandover` | `0x54d1f13d` | +| `completeOwnershipHandover` | `0xf04e283e` | +| `deliveryHelper__` | `0xc031dfb4` | +| `endAuction` | `0x1212e653` | +| `evmxSlug` | `0x8bae77c2` | +| `expireBid` | `0x1dd5022c` | +| `getTransmitterMaxFeesAvailable` | `0xa70f18ea` | +| `grantRole` | `0x2f2ff15d` | +| `hasRole` | `0x91d14854` | +| `initialize` | `0x5f24043b` | +| `maxReAuctionCount` | `0xc367b376` | +| `owner` | `0x8da5cb5b` | +| `ownershipHandoverExpiresAt` | `0xfee81cf4` | +| `reAuctionCount` | `0x9b4b22d3` | +| `renounceOwnership` | `0x715018a6` | +| `requestOwnershipHandover` | `0x25692962` | +| `revokeRole` | `0xd547741f` | +| `setAuctionEndDelaySeconds` | `0x88606b1a` | +| `transferOwnership` | `0xf2fde38b` | +| `watcherPrecompileConfig` | `0x8618a912` | +| `watcherPrecompileLimits` | `0xa71cd97d` | +| `watcherPrecompile__` | `0x1de360c3` | +| `winningBids` | `0x9133f232` | ## ContractFactoryPlug | Function | Signature | | ---------------------------- | ------------ | -| `appGateway` | `0xb82bb881` | +| `appGatewayId` | `0x1c335f49` | | `cancelOwnershipHandover` | `0x54d1f13d` | | `completeOwnershipHandover` | `0xf04e283e` | -| `connectSocket` | `0x052615a6` | -| `deployContract` | `0x35041492` | +| `connectSocket` | `0x258d19c8` | +| `deployContract` | `0xa0695389` | | `getAddress` | `0x94ca2cb5` | | `grantRole` | `0x2f2ff15d` | | `hasRole` | `0x91d14854` | -| `initSocket` | `0x59c92b64` | +| `initSocket` | `0xa07d8545` | | `isSocketInitialized` | `0x9a7d9a9b` | +| `overrides` | `0x4a85f041` | | `owner` | `0x8da5cb5b` | | `ownershipHandoverExpiresAt` | `0xfee81cf4` | | `renounceOwnership` | `0x715018a6` | @@ -147,53 +169,64 @@ ## FeesManager -| Function | Signature | -| ---------------------------- | ------------ | -| `addressResolver__` | `0x6a750469` | -| `appGatewayFeeBalances` | `0x46a312be` | -| `blockFees` | `0x1c0ac675` | -| `cancelOwnershipHandover` | `0x54d1f13d` | -| `completeOwnershipHandover` | `0xf04e283e` | -| `deliveryHelper__` | `0xc031dfb4` | -| `evmxSlug` | `0x8bae77c2` | -| `feesCounter` | `0xb94f4778` | -| `getAvailableFees` | `0xe3d07506` | -| `incrementFeesDeposited` | `0x4f88fe32` | -| `initialize` | `0x6f6186bd` | -| `isFeesEnough` | `0x7d274c6a` | -| `isNonceUsed` | `0x5d00bb12` | -| `owner` | `0x8da5cb5b` | -| `ownershipHandoverExpiresAt` | `0xfee81cf4` | -| `renounceOwnership` | `0x715018a6` | -| `requestCountBlockedFees` | `0xd09604ed` | -| `requestOwnershipHandover` | `0x25692962` | -| `sbType` | `0x745de344` | -| `transferOwnership` | `0xf2fde38b` | -| `transmitterCredits` | `0xefb4cdea` | -| `unblockAndAssignFees` | `0x3c5366a2` | -| `unblockFees` | `0xc1867a4b` | -| `watcherPrecompileConfig` | `0x8618a912` | -| `watcherPrecompileLimits` | `0xa71cd97d` | -| `watcherPrecompile__` | `0x1de360c3` | -| `withdrawFees` | `0xe1a69364` | -| `withdrawTransmitterFees` | `0x8c047bbd` | +| Function | Signature | +| ------------------------------------------------ | ------------ | +| `addressResolver__` | `0x6a750469` | +| `assignWatcherPrecompileCreditsFromAddress` | `0xd699b6c8` | +| `assignWatcherPrecompileCreditsFromRequestCount` | `0x7a483022` | +| `blockCredits` | `0x2d64fc91` | +| `cancelOwnershipHandover` | `0x54d1f13d` | +| `completeOwnershipHandover` | `0xf04e283e` | +| `deliveryHelper__` | `0xc031dfb4` | +| `depositCredits` | `0x211998c8` | +| `evmxSlug` | `0x8bae77c2` | +| `feesCounter` | `0xb94f4778` | +| `getAvailableCredits` | `0xb065a8e5` | +| `getMaxCreditsAvailableForWithdraw` | `0x6ef9efe2` | +| `getWithdrawTransmitterCreditsPayloadParams` | `0x27be4536` | +| `initialize` | `0x6f6186bd` | +| `isAppGatewayWhitelisted` | `0x2a83b813` | +| `isNonceUsed` | `0x5d00bb12` | +| `isUserCreditsEnough` | `0x5bee3a67` | +| `onRequestComplete` | `0x5ed1f959` | +| `owner` | `0x8da5cb5b` | +| `ownershipHandoverExpiresAt` | `0xfee81cf4` | +| `renounceOwnership` | `0x715018a6` | +| `requestCountCredits` | `0x0c9b8a49` | +| `requestOwnershipHandover` | `0x25692962` | +| `sbType` | `0x745de344` | +| `tokenPoolBalances` | `0x2eda3bfd` | +| `transferOwnership` | `0xf2fde38b` | +| `unblockAndAssignCredits` | `0x01958181` | +| `unblockCredits` | `0xa0b32314` | +| `unwrap` | `0xde0e9a3e` | +| `userCredits` | `0x20babb92` | +| `userNonce` | `0x2e04b8e7` | +| `watcherPrecompileConfig` | `0x8618a912` | +| `watcherPrecompileCredits` | `0x052e1f6a` | +| `watcherPrecompileLimits` | `0xa71cd97d` | +| `watcherPrecompile__` | `0x1de360c3` | +| `whitelistAppGatewayWithSignature` | `0x28cbf83d` | +| `whitelistAppGateways` | `0x6c2499e3` | +| `withdrawCredits` | `0xc22f8cf3` | +| `wrap` | `0xd46eb119` | ## FeesPlug | Function | Signature | | ---------------------------- | ------------ | -| `appGateway` | `0xb82bb881` | -| `balanceOf` | `0x70a08231` | +| `appGatewayId` | `0x1c335f49` | | `cancelOwnershipHandover` | `0x54d1f13d` | | `completeOwnershipHandover` | `0xf04e283e` | -| `connectSocket` | `0x052615a6` | -| `deposit` | `0x8340f549` | -| `distributeFee` | `0x7aeee972` | -| `feesRedeemed` | `0x58f8782b` | +| `connectSocket` | `0x258d19c8` | +| `depositToFee` | `0xef0db49f` | +| `depositToFeeAndNative` | `0x5f952be8` | +| `depositToNative` | `0xe2665889` | | `grantRole` | `0x2f2ff15d` | | `hasRole` | `0x91d14854` | -| `initSocket` | `0x59c92b64` | +| `initSocket` | `0xa07d8545` | | `isSocketInitialized` | `0x9a7d9a9b` | +| `overrides` | `0x4a85f041` | | `owner` | `0x8da5cb5b` | | `ownershipHandoverExpiresAt` | `0xfee81cf4` | | `removeTokenFromWhitelist` | `0x306275be` | @@ -205,40 +238,45 @@ | `transferOwnership` | `0xf2fde38b` | | `whitelistToken` | `0x6247f6f2` | | `whitelistedTokens` | `0xdaf9c210` | -| `withdrawFees` | `0x9ba372c2` | +| `withdrawFees` | `0xe55dc4e6` | ## Socket | Function | Signature | | ---------------------------- | ------------ | -| `callAppGateway` | `0x31ed7099` | -| `callCounter` | `0xc0f9882e` | | `cancelOwnershipHandover` | `0x54d1f13d` | | `chainSlug` | `0xb349ba65` | | `completeOwnershipHandover` | `0xf04e283e` | -| `connect` | `0x295058ef` | +| `connect` | `0xb3bde1aa` | | `disableSwitchboard` | `0xe545b261` | -| `execute` | `0x2c6571a9` | +| `enableSwitchboard` | `0xf97a498a` | +| `execute` | `0x68ef086b` | | `getPlugConfig` | `0xf9778ee0` | | `grantRole` | `0x2f2ff15d` | | `hasRole` | `0x91d14854` | | `isValidSwitchboard` | `0xb2d67675` | +| `maxCopyBytes` | `0x212249d4` | | `owner` | `0x8da5cb5b` | | `ownershipHandoverExpiresAt` | `0xfee81cf4` | | `payloadExecuted` | `0x3eaeac3d` | +| `payloadIdToDigest` | `0x7c8552b2` | | `registerSwitchboard` | `0x74f5b1fc` | | `renounceOwnership` | `0x715018a6` | | `requestOwnershipHandover` | `0x25692962` | | `rescueFunds` | `0x6ccae054` | | `revokeRole` | `0xd547741f` | +| `setMaxCopyBytes` | `0x4fc7d6e9` | +| `setSocketFeeManager` | `0x25bd97e5` | +| `socketFeeManager` | `0xde5b8838` | | `transferOwnership` | `0xf2fde38b` | +| `triggerCounter` | `0x8b0021de` | | `version` | `0x54fd4d50` | ## SocketBatcher | Function | Signature | | ---------------------------- | ------------ | -| `attestAndExecute` | `0x841f0228` | +| `attestAndExecute` | `0xa11d3bdc` | | `cancelOwnershipHandover` | `0x54d1f13d` | | `completeOwnershipHandover` | `0xf04e283e` | | `owner` | `0x8da5cb5b` | @@ -249,64 +287,25 @@ | `socket__` | `0xc6a261d2` | | `transferOwnership` | `0xf2fde38b` | -## WatcherPrecompile +## SocketFeeManager -| Function | Signature | -| ----------------------------- | ------------ | -| `addressResolver__` | `0x6a750469` | -| `appGatewayCalled` | `0xc6767cf1` | -| `batchPayloadIds` | `0x02b74f98` | -| `callAppGateways` | `0xdede3465` | -| `cancelOwnershipHandover` | `0x54d1f13d` | -| `cancelRequest` | `0x50ad0779` | -| `completeOwnershipHandover` | `0xf04e283e` | -| `deliveryHelper__` | `0xc031dfb4` | -| `evmxSlug` | `0x8bae77c2` | -| `expiryTime` | `0x99bc0aea` | -| `finalize` | `0x7ffecf2e` | -| `finalized` | `0x81c051de` | -| `getBatchPayloadIds` | `0xfd83cd1f` | -| `getBatches` | `0xcb95b7b3` | -| `getCurrentRequestCount` | `0x5715abbb` | -| `getDigest` | `0xeba9500e` | -| `getPayloadParams` | `0xae5eeb77` | -| `getRequestParams` | `0x71263d0d` | -| `grantRole` | `0x2f2ff15d` | -| `hasRole` | `0x91d14854` | -| `initialize` | `0xb7dc6b77` | -| `isNonceUsed` | `0x5d00bb12` | -| `isPromiseExecuted` | `0x17a2cdf0` | -| `markRevert` | `0x1c75dad5` | -| `maxTimeoutDelayInSeconds` | `0x46fbc9d7` | -| `nextBatchCount` | `0x333a3963` | -| `nextRequestCount` | `0xfef72893` | -| `owner` | `0x8da5cb5b` | -| `ownershipHandoverExpiresAt` | `0xfee81cf4` | -| `payloadCounter` | `0x550ce1d5` | -| `payloads` | `0x58722672` | -| `query` | `0x16ad71bc` | -| `renounceOwnership` | `0x715018a6` | -| `requestBatchIds` | `0xf865c4a7` | -| `requestOwnershipHandover` | `0x25692962` | -| `requestParams` | `0x5ce2d853` | -| `resolvePromises` | `0xccb1caff` | -| `resolveTimeout` | `0xa67c0781` | -| `revokeRole` | `0xd547741f` | -| `setExpiryTime` | `0x30fc4cff` | -| `setMaxTimeoutDelayInSeconds` | `0x65d480fc` | -| `setTimeout` | `0x9c29ec74` | -| `startProcessingRequest` | `0x77290f24` | -| `submitRequest` | `0x16b47482` | -| `timeoutCounter` | `0x94f6522e` | -| `timeoutRequests` | `0xcdf85751` | -| `transferOwnership` | `0xf2fde38b` | -| `updateTransmitter` | `0xb228a22c` | -| `watcherPrecompileConfig` | `0x8618a912` | -| `watcherPrecompileConfig__` | `0xa816cbd9` | -| `watcherPrecompileLimits` | `0xa71cd97d` | -| `watcherPrecompileLimits__` | `0xb2ad6c48` | -| `watcherPrecompile__` | `0x1de360c3` | -| `watcherProofs` | `0x3fa3166b` | +| Function | Signature | +| ---------------------------- | ------------ | +| `cancelOwnershipHandover` | `0x54d1f13d` | +| `completeOwnershipHandover` | `0xf04e283e` | +| `getMinSocketFees` | `0xd383b688` | +| `grantRole` | `0x2f2ff15d` | +| `hasRole` | `0x91d14854` | +| `owner` | `0x8da5cb5b` | +| `ownershipHandoverExpiresAt` | `0xfee81cf4` | +| `payAndCheckFees` | `0xbaa56229` | +| `renounceOwnership` | `0x715018a6` | +| `requestOwnershipHandover` | `0x25692962` | +| `rescueFunds` | `0x6ccae054` | +| `revokeRole` | `0xd547741f` | +| `setSocketFees` | `0x47a406f6` | +| `socketFees` | `0xab1b33a8` | +| `transferOwnership` | `0xf2fde38b` | ## WatcherPrecompileConfig @@ -320,8 +319,6 @@ | `evmxSlug` | `0x8bae77c2` | | `feesPlug` | `0xd1ba159d` | | `getPlugConfigs` | `0x8a028c38` | -| `grantRole` | `0x2f2ff15d` | -| `hasRole` | `0x91d14854` | | `initialize` | `0x6ecf2b22` | | `isNonceUsed` | `0x5d00bb12` | | `isValidPlug` | `0xec8aef74` | @@ -329,62 +326,66 @@ | `ownershipHandoverExpiresAt` | `0xfee81cf4` | | `renounceOwnership` | `0x715018a6` | | `requestOwnershipHandover` | `0x25692962` | -| `revokeRole` | `0xd547741f` | -| `setAppGateways` | `0xbdf0b455` | +| `setAppGateways` | `0xdd8539d4` | | `setIsValidPlug` | `0xb3a6bbcf` | | `setOnChainContracts` | `0x33fa78c2` | | `setSwitchboard` | `0x61706f1e` | | `sockets` | `0xb44a23ab` | | `switchboards` | `0xaa539546` | | `transferOwnership` | `0xf2fde38b` | -| `verifyConnections` | `0xe283ce7b` | +| `verifyConnections` | `0xf269ab50` | | `watcherPrecompileConfig` | `0x8618a912` | | `watcherPrecompileLimits` | `0xa71cd97d` | | `watcherPrecompile__` | `0x1de360c3` | ## WatcherPrecompileLimits -| Function | Signature | -| ---------------------------- | ------------ | -| `LIMIT_DECIMALS` | `0x1e65497d` | -| `addressResolver__` | `0x6a750469` | -| `cancelOwnershipHandover` | `0x54d1f13d` | -| `completeOwnershipHandover` | `0xf04e283e` | -| `consumeLimit` | `0xc22f5a13` | -| `defaultLimit` | `0xe26b013b` | -| `defaultRatePerSecond` | `0x16d7acdf` | -| `deliveryHelper__` | `0xc031dfb4` | -| `getCurrentLimit` | `0x1a065507` | -| `getLimitParams` | `0x2ff81ee0` | -| `grantRole` | `0x2f2ff15d` | -| `hasRole` | `0x91d14854` | -| `initialize` | `0x1794bb3c` | -| `owner` | `0x8da5cb5b` | -| `ownershipHandoverExpiresAt` | `0xfee81cf4` | -| `renounceOwnership` | `0x715018a6` | -| `requestOwnershipHandover` | `0x25692962` | -| `revokeRole` | `0xd547741f` | -| `setDefaultLimit` | `0x995284b1` | -| `setDefaultRatePerSecond` | `0xa44df657` | -| `transferOwnership` | `0xf2fde38b` | -| `updateLimitParams` | `0x01b2a5a0` | -| `watcherPrecompileConfig` | `0x8618a912` | -| `watcherPrecompileLimits` | `0xa71cd97d` | -| `watcherPrecompile__` | `0x1de360c3` | +| Function | Signature | +| --------------------------------- | ------------ | +| `addressResolver__` | `0x6a750469` | +| `callBackFees` | `0xf9554ecc` | +| `cancelOwnershipHandover` | `0x54d1f13d` | +| `completeOwnershipHandover` | `0xf04e283e` | +| `consumeLimit` | `0xc22f5a13` | +| `defaultLimit` | `0xe26b013b` | +| `defaultRatePerSecond` | `0x16d7acdf` | +| `deliveryHelper__` | `0xc031dfb4` | +| `finalizeFees` | `0x09207879` | +| `getCurrentLimit` | `0x1a065507` | +| `getLimitParams` | `0x2ff81ee0` | +| `getTotalFeesRequired` | `0x964500b5` | +| `initialize` | `0x1794bb3c` | +| `limitDecimals` | `0xee185533` | +| `owner` | `0x8da5cb5b` | +| `ownershipHandoverExpiresAt` | `0xfee81cf4` | +| `queryFees` | `0xcfcbafb6` | +| `renounceOwnership` | `0x715018a6` | +| `requestOwnershipHandover` | `0x25692962` | +| `setCallBackFees` | `0x622be814` | +| `setDefaultLimitAndRatePerSecond` | `0x7e434156` | +| `setFinalizeFees` | `0xbce0a88c` | +| `setQueryFees` | `0x877135d7` | +| `setTimeoutFees` | `0x571db4f9` | +| `timeoutFees` | `0xeab12f7e` | +| `transferOwnership` | `0xf2fde38b` | +| `updateLimitParams` | `0x01b2a5a0` | +| `watcherPrecompileConfig` | `0x8618a912` | +| `watcherPrecompileLimits` | `0xa71cd97d` | +| `watcherPrecompile__` | `0x1de360c3` | ## DeliveryHelper | Function | Signature | | ------------------------------ | ------------ | | `addressResolver__` | `0x6a750469` | -| `batch` | `0x039cc50a` | +| `batch` | `0xd9307dd8` | | `bidTimeout` | `0x94090d0b` | | `cancelOwnershipHandover` | `0x54d1f13d` | | `cancelRequest` | `0x50ad0779` | +| `chainMaxMsgValueLimit` | `0x01d1e126` | | `clearQueue` | `0xf22cb874` | | `completeOwnershipHandover` | `0xf04e283e` | | `deliveryHelper__` | `0xc031dfb4` | -| `endTimeout` | `0x9c3bb867` | | `finishRequest` | `0xeab148c0` | | `getDeliveryHelperPlugAddress` | `0xb709bd9f` | | `getFees` | `0xfbf4ec4b` | @@ -400,19 +401,21 @@ | `requestOwnershipHandover` | `0x25692962` | | `requests` | `0xb71a5e58` | | `saltCounter` | `0xa04c6809` | -| `startRequestProcessing` | `0xf61474a9` | +| `startRequestProcessing` | `0x5ca2100f` | | `transferOwnership` | `0xf2fde38b` | | `updateBidTimeout` | `0xa29f83d1` | +| `updateChainMaxMsgValueLimits` | `0x0b32de76` | | `watcherPrecompileConfig` | `0x8618a912` | | `watcherPrecompileLimits` | `0xa71cd97d` | | `watcherPrecompile__` | `0x1de360c3` | -| `withdrawTo` | `0x74c33667` | +| `withdrawTo` | `0x2ba9d5bb` | +| `withdrawTransmitterFees` | `0x38ff6dd2` | ## FastSwitchboard | Function | Signature | | ---------------------------- | ------------ | -| `allowPacket` | `0x21e9ec80` | +| `allowPayload` | `0x31c23f66` | | `attest` | `0x63671b60` | | `cancelOwnershipHandover` | `0x54d1f13d` | | `chainSlug` | `0xb349ba65` | @@ -420,7 +423,6 @@ | `grantRole` | `0x2f2ff15d` | | `hasRole` | `0x91d14854` | | `isAttested` | `0xc13c2396` | -| `nextNonce` | `0x0cd55abf` | | `owner` | `0x8da5cb5b` | | `ownershipHandoverExpiresAt` | `0xfee81cf4` | | `registerSwitchboard` | `0x74f5b1fc` | @@ -430,3 +432,63 @@ | `revokeRole` | `0xd547741f` | | `socket__` | `0xc6a261d2` | | `transferOwnership` | `0xf2fde38b` | + +## WatcherPrecompile + +| Function | Signature | +| ----------------------------- | ------------ | +| `addressResolver__` | `0x6a750469` | +| `appGatewayCalled` | `0xc6767cf1` | +| `appGatewayCaller` | `0x712b193a` | +| `batchPayloadIds` | `0x02b74f98` | +| `callAppGateways` | `0xaef0b33d` | +| `cancelOwnershipHandover` | `0x54d1f13d` | +| `cancelRequest` | `0x50ad0779` | +| `completeOwnershipHandover` | `0xf04e283e` | +| `deliveryHelper__` | `0xc031dfb4` | +| `evmxSlug` | `0x8bae77c2` | +| `expiryTime` | `0x99bc0aea` | +| `finalize` | `0x7ffecf2e` | +| `finalized` | `0x81c051de` | +| `getBatchPayloadIds` | `0xfd83cd1f` | +| `getBatches` | `0xcb95b7b3` | +| `getCurrentRequestCount` | `0x5715abbb` | +| `getDigest` | `0xa7993154` | +| `getPayloadParams` | `0xae5eeb77` | +| `getRequestParams` | `0x71263d0d` | +| `initialize` | `0xb7dc6b77` | +| `isNonceUsed` | `0x5d00bb12` | +| `isPromiseExecuted` | `0x17a2cdf0` | +| `markRevert` | `0x1c75dad5` | +| `maxTimeoutDelayInSeconds` | `0x46fbc9d7` | +| `nextBatchCount` | `0x333a3963` | +| `nextRequestCount` | `0xfef72893` | +| `owner` | `0x8da5cb5b` | +| `ownershipHandoverExpiresAt` | `0xfee81cf4` | +| `payloadCounter` | `0x550ce1d5` | +| `payloads` | `0x58722672` | +| `query` | `0x16ad71bc` | +| `renounceOwnership` | `0x715018a6` | +| `requestBatchIds` | `0xf865c4a7` | +| `requestMetadata` | `0x875b3f7e` | +| `requestOwnershipHandover` | `0x25692962` | +| `requestParams` | `0x5ce2d853` | +| `resolvePromises` | `0xccb1caff` | +| `resolveTimeout` | `0xa67c0781` | +| `setExpiryTime` | `0x30fc4cff` | +| `setMaxTimeoutDelayInSeconds` | `0x65d480fc` | +| `setTimeout` | `0x9c29ec74` | +| `setWatcherPrecompileConfig` | `0x794edeb4` | +| `setWatcherPrecompileLimits` | `0x712a6f07` | +| `startProcessingRequest` | `0x77290f24` | +| `submitRequest` | `0x16b47482` | +| `timeoutIdPrefix` | `0x96ec119f` | +| `timeoutRequests` | `0xcdf85751` | +| `transferOwnership` | `0xf2fde38b` | +| `updateTransmitter` | `0xb228a22c` | +| `watcherPrecompileConfig` | `0x8618a912` | +| `watcherPrecompileConfig__` | `0xa816cbd9` | +| `watcherPrecompileLimits` | `0xa71cd97d` | +| `watcherPrecompileLimits__` | `0xb2ad6c48` | +| `watcherPrecompile__` | `0x1de360c3` | +| `watcherProofs` | `0x3fa3166b` | diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index 4b7e9950..f1a936f4 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -206,7 +206,8 @@ abstract contract AppGatewayBase is AddressResolverUtil, IAppGateway { return address(0); } - onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]).getOnChainAddress(); + onChainAddress = IForwarder(forwarderAddresses[contractId_][chainSlug_]) + .getOnChainAddress(); } //////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/contracts/helpers/TestUSDC.sol b/contracts/helpers/TestUSDC.sol new file mode 100644 index 00000000..02528a07 --- /dev/null +++ b/contracts/helpers/TestUSDC.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; + +import "solady/tokens/ERC20.sol"; + +contract TestUSDC is ERC20 { + address public immutable owner; + string private _name; + string private _symbol; + uint8 private _decimals; + + constructor( + string memory name_, + string memory symbol_, + uint8 decimals_, + address owner_, + uint256 initialSupply_ + ) { + owner = owner_; + _name = name_; + _symbol = symbol_; + _decimals = decimals_; + _mint(owner_, initialSupply_); + } + + function name() public view override returns (string memory) { + return _name; + } + + function symbol() public view override returns (string memory) { + return _symbol; + } + + function decimals() public view override returns (uint8) { + return _decimals; + } + + function mint(address to, uint256 amount) external { + _mint(to, amount); + } +} diff --git a/contracts/protocol/socket/Socket.sol b/contracts/protocol/socket/Socket.sol index 78f20288..2b39f518 100644 --- a/contracts/protocol/socket/Socket.sol +++ b/contracts/protocol/socket/Socket.sol @@ -140,14 +140,12 @@ contract Socket is SocketUtils { // NOTE: external un-trusted call bool exceededMaxCopy; - (success, exceededMaxCopy, returnData) = executeParams_ - .target - .tryCall( - executeParams_.value, - executeParams_.gasLimit, - maxCopyBytes, - executeParams_.payload - ); + (success, exceededMaxCopy, returnData) = executeParams_.target.tryCall( + executeParams_.value, + executeParams_.gasLimit, + maxCopyBytes, + executeParams_.payload + ); if (success) { emit ExecutionSuccess(payloadId_, exceededMaxCopy, returnData); diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index 3d9b4f09..81083086 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -85,6 +85,13 @@ contract WatcherPrecompileConfig is address feesPlug ); + /// @notice Emitted when a valid plug is set for an app gateway + /// @param appGateway The address of the app gateway + /// @param chainSlug The identifier of the network + /// @param plug The address of the plug + /// @param isValid Whether the plug is valid + event IsValidPlugSet(address appGateway, uint32 chainSlug, address plug, bool isValid); + error InvalidGateway(); error InvalidSwitchboard(); @@ -162,6 +169,7 @@ contract WatcherPrecompileConfig is /// @param isValid_ Whether the plug is valid function setIsValidPlug(uint32 chainSlug_, address plug_, bool isValid_) external { isValidPlug[msg.sender][chainSlug_][plug_] = isValid_; + emit IsValidPlugSet(msg.sender, chainSlug_, plug_, isValid_); } /// @notice Retrieves the configuration for a specific plug on a network diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index 01a084da..2c01968e 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -51,6 +51,14 @@ contract WatcherPrecompileLimits is /// @notice Emitted when the default limit and rate per second are set event DefaultLimitAndRatePerSecondSet(uint256 defaultLimit, uint256 defaultRatePerSecond); + /// @notice Emitted when the query fees are set + event QueryFeesSet(uint256 queryFees); + /// @notice Emitted when the finalize fees are set + event FinalizeFeesSet(uint256 finalizeFees); + /// @notice Emitted when the timeout fees are set + event TimeoutFeesSet(uint256 timeoutFees); + /// @notice Emitted when the call back fees are set + event CallBackFeesSet(uint256 callBackFees); error WatcherFeesNotSet(bytes32 limitType); @@ -167,18 +175,22 @@ contract WatcherPrecompileLimits is function setQueryFees(uint256 queryFees_) external onlyOwner { queryFees = queryFees_; + emit QueryFeesSet(queryFees_); } function setFinalizeFees(uint256 finalizeFees_) external onlyOwner { finalizeFees = finalizeFees_; + emit FinalizeFeesSet(finalizeFees_); } function setTimeoutFees(uint256 timeoutFees_) external onlyOwner { timeoutFees = timeoutFees_; + emit TimeoutFeesSet(timeoutFees_); } function setCallBackFees(uint256 callBackFees_) external onlyOwner { callBackFees = callBackFees_; + emit CallBackFeesSet(callBackFees_); } function getTotalFeesRequired( diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 13e72e43..2898354c 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -278,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; diff --git a/deployments/dev_addresses.json b/deployments/dev_addresses.json index a5ecf711..326f1ff2 100644 --- a/deployments/dev_addresses.json +++ b/deployments/dev_addresses.json @@ -1,52 +1,38 @@ { - "84532": { - "ContractFactoryPlug": "0xFEFdbD9CB5bdC58Aa8B6B455923454275392838c", - "FastSwitchboard": "0xCc9f995DAE2D3Cf3C0763e353986E97ab78801b4", - "FeesPlug": "0xBBff19fBEAd971f39F1B298De985D7ADfb57b784", - "Socket": "0x89e66357C5F101C56b4F9F97cf378Cc32A21438a", - "SocketBatcher": "0x3bABE35428D9E2B87d678289f837F805dAB0db3A", - "startBlock": 23827713 - }, "421614": { - "ContractFactoryPlug": "0x10BFD064258ac890957Ca65A85E7997a923Fb551", - "FastSwitchboard": "0xB0feE9136102B99A5722C693C8eb538C8c8d013B", - "FeesPlug": "0x0Ae8458F80a50a4ad706680605c88e050AaC8A89", - "Socket": "0xA99E6020d973C5956dBD727eDB45cDA5F7C4ca99", - "SocketBatcher": "0x3F95273234164329aec7DE66A037a80eb5a8F393", - "startBlock": 136387501 + "ContractFactoryPlug": "0x9dEa956E863a44a24c1CE068460E6190EA438602", + "FastSwitchboard": "0xb4C5A231614639801F421Ca386388f65576F3c81", + "FeesPlug": "0xFF3639fD99515b40d8f6bC02AC1Fead65CAd93fD", + "Socket": "0xBd0436A6E0dee11e9359767142Ed6bD7B48ba258", + "SocketBatcher": "0x1683c3AB4954852f71f400AbDeF98112a066ee44", + "startBlock": 148461456, + "TestUSDC": "0x3f134B047620590a0b7a2f98d5F9B07D32d4D927" }, "7625382": { - "AddressResolver": "0x491DE197D50aaa4F59090DBDa4b5Aa6C2E14A10D", - "AddressResolverImpl": "0x2736Fc0AA0fa5Ca595Ae5F27Ec807B912A5b1D0f", - "AuctionManager": "0x7187372e1fF5CB8E8B84Ee02F25033fc42B3D033", - "AuctionManagerImpl": "0xaE4624b006D7730f22De1F7df5b1C0b960262AE3", - "DeliveryHelper": "0x8b4a6cee33f18D757d7F3613476b135E3B889303", - "DeliveryHelperImpl": "0xE693bEc40e39223749AC351156E713b7256541B0", - "ERC1967Factory": "0x122beAFCfc2E99D825322a78EAFD8a11fa2d9E0b", - "FeesManager": "0x7c0922C2AfaBF9098F55710E8D8E3c8F81bCB8ee", - "FeesManagerImpl": "0x2c50B6e519e0705A396E5c8652E5D447F37f9796", - "startBlock": 2491495, - "WatcherPrecompile": "0x6E4c1afEcc1AbFF69dF3bC6bFf81eF7A34a343d5", - "WatcherPrecompileConfig": "0x95f86089D4015e6874031B3583dE8795C3b217Fd", - "WatcherPrecompileConfigImpl": "0xE4a2eBcE3Bdcaa1861c01ddc6465b90311B749e4", - "WatcherPrecompileImpl": "0x233539d6BBf231660652AF00B5d6E850E892946a", - "WatcherPrecompileLimits": "0x34212a2Cf92D2863F35877Ed835B7e28b8287764", - "WatcherPrecompileLimitsImpl": "0xdfeA8cb793b84b3f046d2426259e0eC237Ec9bF3" - }, - "11155111": { - "ContractFactoryPlug": "0xdd7b56968a3505D1A10181F2880cAA65a89E4750", - "FastSwitchboard": "0xb34DB19C7FAeECf14B3D0336C6E34f7dc1336968", - "FeesPlug": "0xfBa932AaE9Ae2777aEC77832FC0C2D9059C8E905", - "Socket": "0xF86B89B5c689c170BfD2734254228D6d2db5a672", - "SocketBatcher": "0x466b51bcc1799A4cb8F629C46640ab3aA2608F75", - "startBlock": 23827730 + "AddressResolver": "0x9EAa5b231c1DF8c5660dDC5fE7cD06a9D86e26f0", + "AddressResolverImpl": "0x4b067469E7865d9959E0E658BA77ec4b0F28FB15", + "AuctionManager": "0x2b1F13479D9D2E53E1487169b9C9073958710170", + "AuctionManagerImpl": "0x23364acdd298EBB3Dfd3c1835C5ACd7f77E3E2bD", + "DeliveryHelper": "0xe557Ba90Dd94Aa0Dd49DD6467f1e7d196C6Bd179", + "DeliveryHelperImpl": "0x915eA695e03265998cd455B4df2A1EAeb1b61e74", + "ERC1967Factory": "0xb30eBbA571721923175751AE7aF5aC90cfb1E892", + "FeesManager": "0xBf9529b5aA4a6a047Ff65CfAE9613A274C479143", + "FeesManagerImpl": "0xB577c29F7Cbb1bBB314dD8E74059Aa5BF72838b0", + "startBlock": 5537530, + "WatcherPrecompile": "0x68430a7f22125dd8b566f774AED05e868CA70fEA", + "WatcherPrecompileConfig": "0xD0F77272a5F0208f20c836bB4eeddbCE1e4aef9d", + "WatcherPrecompileConfigImpl": "0x060b4a50EcCC9Cf329005c94406dd2886676F759", + "WatcherPrecompileImpl": "0x1BCe40d84499Db8E7Bc65277A32f0abd56588CC7", + "WatcherPrecompileLimits": "0x4A645F050166FaBdA7ce44BE90B0A61073C19696", + "WatcherPrecompileLimitsImpl": "0x4Fd04B0D4903e630F169BB228be52750E6B5331a" }, "11155420": { - "ContractFactoryPlug": "0xDd73B7C6Bc8Cb4FFB3F0b460FF0985A91622f7E1", - "FastSwitchboard": "0x0CdaCA0B9F9bFfF1D9B660173BB8BE46c4fB71Da", - "FeesPlug": "0x95D672aaF036887aABEe72a6496D583317e6e48B", - "Socket": "0xDad99e0D8bA0ffe4955523ea0BCbe8F3DE3446D1", - "SocketBatcher": "0xb8D652C7D1dfA7578c6F119712c7b4c812BB3b57", - "startBlock": 25810565 + "ContractFactoryPlug": "0x95Be4D8500e3e5C970802c64b0755027d4Fc5C9F", + "FastSwitchboard": "0xe7858f1dc202f5E9C9B3ee6db052F45164a88534", + "FeesPlug": "0x01bDCAB43c423D08BaCe87ED716280536dAB3eF3", + "Socket": "0xE09CC429e77EE5DBeF68f3796b2A33BBDF39C03C", + "SocketBatcher": "0x9175d90706a2b17f0aE025ce5A6C76e64850c2f5", + "startBlock": 27158986, + "TestUSDC": "0x3D7515519beab77B541497626CFB7E764F6887CD" } } diff --git a/deployments/dev_verification.json b/deployments/dev_verification.json index cc9bb657..1bd53705 100644 --- a/deployments/dev_verification.json +++ b/deployments/dev_verification.json @@ -41,11 +41,63 @@ "0x89e66357C5F101C56b4F9F97cf378Cc32A21438a", "Socket", "contracts/protocol/socket/Socket.sol", - [84532, "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", "EVMX"] + [ + 84532, + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", + "EVMX" + ] ] ], "421614": [], "7625382": [ + [ + "0x23364acdd298EBB3Dfd3c1835C5ACd7f77E3E2bD", + "AuctionManager", + "contracts/protocol/payload-delivery/AuctionManager.sol", + [] + ], + [ + "0x915eA695e03265998cd455B4df2A1EAeb1b61e74", + "DeliveryHelper", + "contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol", + [] + ], + [ + "0xB577c29F7Cbb1bBB314dD8E74059Aa5BF72838b0", + "FeesManager", + "contracts/protocol/payload-delivery/FeesManager.sol", + [] + ], + [ + "0x1BCe40d84499Db8E7Bc65277A32f0abd56588CC7", + "WatcherPrecompile", + "contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol", + [] + ], + [ + "0x060b4a50EcCC9Cf329005c94406dd2886676F759", + "WatcherPrecompileConfig", + "contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol", + [] + ], + [ + "0x4Fd04B0D4903e630F169BB228be52750E6B5331a", + "WatcherPrecompileLimits", + "contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol", + [] + ], + [ + "0x4b067469E7865d9959E0E658BA77ec4b0F28FB15", + "AddressResolver", + "contracts/protocol/AddressResolver.sol", + [] + ], + [ + "0xb30eBbA571721923175751AE7aF5aC90cfb1E892", + "ERC1967Factory", + "lib/solady/src/utils/ERC1967Factory.sol", + [] + ], [ "0x2c50B6e519e0705A396E5c8652E5D447F37f9796", "FeesManager", @@ -155,7 +207,11 @@ "0xF86B89B5c689c170BfD2734254228D6d2db5a672", "Socket", "contracts/protocol/socket/Socket.sol", - [11155111, "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", "EVMX"] + [ + 11155111, + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", + "EVMX" + ] ] ], "11155420": [] diff --git a/hardhat-scripts/config/config.ts b/hardhat-scripts/config/config.ts index d7236e79..cdd6ad33 100644 --- a/hardhat-scripts/config/config.ts +++ b/hardhat-scripts/config/config.ts @@ -1,6 +1,6 @@ import { config as dotenvConfig } from "dotenv"; dotenvConfig(); -import { ethers } from "ethers"; +import { ethers, utils } from "ethers"; import { ChainSlug, DeploymentMode } from "../../src"; export const mode = process.env.DEPLOYMENT_MODE as @@ -26,8 +26,8 @@ export const logConfig = () => { export const chains: Array = [ ChainSlug.ARBITRUM_SEPOLIA, ChainSlug.OPTIMISM_SEPOLIA, - ChainSlug.BASE_SEPOLIA, - ChainSlug.SEPOLIA, + // ChainSlug.BASE_SEPOLIA, + // ChainSlug.SEPOLIA, ]; export const EVM_CHAIN_ID_MAP: Record = { [DeploymentMode.LOCAL]: 7625382, @@ -35,13 +35,34 @@ export const EVM_CHAIN_ID_MAP: Record = { [DeploymentMode.STAGE]: 43, [DeploymentMode.PROD]: 3605, }; -export const auctionEndDelaySeconds = 0; +// Addresses export const watcher = "0xb62505feacC486e809392c65614Ce4d7b051923b"; export const transmitter = "0x138e9840861C983DC0BB9b3e941FB7C0e9Ade320"; -export const MAX_FEES = ethers.utils.parseEther("0.001"); + +// Chain config export const EVMX_CHAIN_ID = EVM_CHAIN_ID_MAP[mode]; +export const MAX_FEES = ethers.utils.parseEther("0.001"); + +// Auction parameters +export const auctionEndDelaySeconds = 0; +export const BID_TIMEOUT = 600; // 10 minutes +export const EXPIRY_TIME = 300; // 5 minutes +export const MAX_RE_AUCTION_COUNT = 5; + +// TestUSDC +export const TEST_USDC_NAME = "testUSDC"; +export const TEST_USDC_SYMBOL = "testUSDC"; +export const TEST_USDC_INITIAL_SUPPLY = ethers.utils.parseEther( + "1000000000000000000000000" +); +export const TEST_USDC_DECIMALS = 6; + +// Watcher Precompile Fees +export const QUERY_FEES = utils.parseEther("0.000001"); +export const FINALIZE_FEES = utils.parseEther("0.000001"); +export const TIMEOUT_FEES = utils.parseEther("0.000001"); +export const CALLBACK_FEES = utils.parseEther("0.000001"); + +// Other constants export const DEFAULT_MAX_LIMIT = 100; -export const BID_TIMEOUT = 600; -export const EXPIRY_TIME = 300; export const UPGRADE_VERSION = 1; -export const MAX_RE_AUCTION_COUNT = 5; diff --git a/hardhat-scripts/constants/enums.ts b/hardhat-scripts/constants/enums.ts index d7707602..011570b2 100644 --- a/hardhat-scripts/constants/enums.ts +++ b/hardhat-scripts/constants/enums.ts @@ -4,6 +4,8 @@ export enum CORE_CONTRACTS { FastSwitchboard = "FastSwitchboard", FeesPlug = "FeesPlug", ContractFactoryPlug = "ContractFactoryPlug", + TestUSDC = "TestUSDC", + SocketFeeManager = "SocketFeeManager", } export enum EVMxCoreContracts { diff --git a/hardhat-scripts/deploy/1.deploy.ts b/hardhat-scripts/deploy/1.deploy.ts index bc3c6715..892ac0a9 100644 --- a/hardhat-scripts/deploy/1.deploy.ts +++ b/hardhat-scripts/deploy/1.deploy.ts @@ -1,6 +1,6 @@ import { ChainAddressesObj, ChainSlug } from "../../src"; import { config } from "dotenv"; -import { Contract, Signer, utils, Wallet } from "ethers"; +import { BigNumber, Contract, Signer, utils, Wallet } from "ethers"; import { formatEther } from "ethers/lib/utils"; import { ethers } from "hardhat"; import { @@ -13,6 +13,7 @@ import { import { DeployParams, getAddresses, + getInstance, getOrDeploy, storeAddresses, } from "../utils"; @@ -27,6 +28,14 @@ import { DEFAULT_MAX_LIMIT, MAX_RE_AUCTION_COUNT, mode, + TEST_USDC_INITIAL_SUPPLY, + TEST_USDC_DECIMALS, + TEST_USDC_NAME, + TEST_USDC_SYMBOL, + QUERY_FEES, + FINALIZE_FEES, + TIMEOUT_FEES, + CALLBACK_FEES, } from "../config/config"; config(); @@ -131,7 +140,7 @@ const deployEVMxContracts = async () => { deployUtils = await deployContractWithProxy( EVMxCoreContracts.WatcherPrecompile, - `contracts/protocol/watcherPrecompile/WatcherPrecompile.sol`, + `contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol`, [ EVMxOwner, addressResolver.address, @@ -215,6 +224,44 @@ const deployEVMxContracts = async () => { deployUtils.signer ); + let watcherPrecompileLimits = await getInstance( + EVMxCoreContracts.WatcherPrecompileLimits, + deployUtils.addresses[EVMxCoreContracts.WatcherPrecompileLimits] + ); + watcherPrecompileLimits = watcherPrecompileLimits.connect( + deployUtils.signer + ); + await updateContractSettings( + watcherPrecompileLimits, + "queryFees", + "setQueryFees", + QUERY_FEES, + deployUtils.signer + ); + + await updateContractSettings( + watcherPrecompileLimits, + "finalizeFees", + "setFinalizeFees", + FINALIZE_FEES, + deployUtils.signer + ); + + await updateContractSettings( + watcherPrecompileLimits, + "timeoutFees", + "setTimeoutFees", + TIMEOUT_FEES, + deployUtils.signer + ); + + await updateContractSettings( + watcherPrecompileLimits, + "callBackFees", + "setCallBackFees", + CALLBACK_FEES, + deployUtils.signer + ); deployUtils.addresses.startBlock = (deployUtils.addresses.startBlock ? deployUtils.addresses.startBlock @@ -288,6 +335,23 @@ const deploySocketContracts = async () => { ); deployUtils.addresses[contractName] = sb.address; + contractName = CORE_CONTRACTS.TestUSDC; + + const testUSDC: Contract = await getOrDeploy( + contractName, + contractName, + `contracts/helpers/${contractName}.sol`, + [ + TEST_USDC_NAME, + TEST_USDC_SYMBOL, + TEST_USDC_DECIMALS, + socketOwner, + TEST_USDC_INITIAL_SUPPLY, + ], + deployUtils + ); + deployUtils.addresses[contractName] = testUSDC.address; + contractName = CORE_CONTRACTS.FeesPlug; const feesPlug: Contract = await getOrDeploy( contractName, @@ -298,6 +362,8 @@ const deploySocketContracts = async () => { ); deployUtils.addresses[contractName] = feesPlug.address; + await whitelistToken(feesPlug, testUSDC.address, deployUtils.signer); + contractName = CORE_CONTRACTS.ContractFactoryPlug; const contractFactoryPlug: Contract = await getOrDeploy( contractName, @@ -328,24 +394,20 @@ const deploySocketContracts = async () => { } }; -async function initializeSigVerifier( - contract: Contract, - getterMethod: string, - setterMethod: string, - requiredAddress: string, - initParams: any[], +async function whitelistToken( + feesPlug: Contract, + tokenAddress: string, signer: Signer ) { - const currentValue = await contract.connect(signer)[getterMethod](); - - if (currentValue.toLowerCase() !== requiredAddress.toLowerCase()) { - console.log({ - setterMethod, - current: currentValue, - required: requiredAddress, - }); - const tx = await contract.connect(signer)[setterMethod](...initParams); - console.log(`Setting ${getterMethod} for ${contract.address} to`, tx.hash); + const isWhitelisted = await feesPlug + .connect(signer) + .whitelistedTokens(tokenAddress); + if (!isWhitelisted) { + const tx = await feesPlug.connect(signer).whitelistToken(tokenAddress); + console.log( + `Whitelisting token ${tokenAddress} for ${feesPlug.address}`, + tx.hash + ); await tx.wait(); } } @@ -354,18 +416,22 @@ async function updateContractSettings( contract: Contract, getterMethod: string, setterMethod: string, - requiredAddress: string, + requiredValue: string | BigNumber, signer: Signer ) { const currentValue = await contract.connect(signer)[getterMethod](); - if (currentValue.toLowerCase() !== requiredAddress.toLowerCase()) { + if ( + (typeof currentValue === "string" && + currentValue.toLowerCase() !== String(requiredValue).toLowerCase()) || + (BigNumber.isBigNumber(currentValue) && currentValue !== requiredValue) + ) { console.log({ setterMethod, current: currentValue, - required: requiredAddress, + required: requiredValue, }); - const tx = await contract.connect(signer)[setterMethod](requiredAddress); + const tx = await contract.connect(signer)[setterMethod](requiredValue); console.log(`Setting ${getterMethod} for ${contract.address} to`, tx.hash); await tx.wait(); } diff --git a/hardhat-scripts/deploy/4.connect.ts b/hardhat-scripts/deploy/4.connect.ts index 1b0bfd84..dd51974e 100644 --- a/hardhat-scripts/deploy/4.connect.ts +++ b/hardhat-scripts/deploy/4.connect.ts @@ -16,22 +16,25 @@ import { getWatcherSigner, signWatcherMessage } from "../utils/sign"; const plugs = [CORE_CONTRACTS.ContractFactoryPlug, CORE_CONTRACTS.FeesPlug]; export type AppGatewayConfig = { plug: string; - appGateway: string; + appGatewayId: string; switchboard: string; chainSlug: number; }; // Maps plug contracts to their corresponding app gateways -export const getAppGateway = (plug: string, addresses: DeploymentAddresses) => { +export const getAppGatewayId = ( + plug: string, + addresses: DeploymentAddresses +) => { let address: string = ""; switch (plug) { case CORE_CONTRACTS.ContractFactoryPlug: address = addresses?.[EVMX_CHAIN_ID]?.[EVMxCoreContracts.DeliveryHelper]; if (!address) throw new Error(`DeliveryHelper not found on EVMX`); - return address; + return ethers.utils.hexZeroPad(address, 32); case CORE_CONTRACTS.FeesPlug: address = addresses?.[EVMX_CHAIN_ID]?.[EVMxCoreContracts.FeesManager]; if (!address) throw new Error(`FeesManager not found on EVMX`); - return address; + return ethers.utils.hexZeroPad(address, 32); default: throw new Error(`Unknown plug: ${plug}`); } @@ -52,13 +55,13 @@ export const checkIfAddressExists = (address: string, name: string) => { export const isConfigSetOnSocket = async ( plug: Contract, socket: Contract, - appGateway: string, + appGatewayId: string, switchboard: string ) => { const plugConfigRegistered = await socket.getPlugConfig(plug.address); return ( - plugConfigRegistered.appGateway.toLowerCase() === - appGateway.toLowerCase() && + plugConfigRegistered.appGatewayId.toLowerCase() === + appGatewayId.toLowerCase() && plugConfigRegistered.switchboard.toLowerCase() === switchboard.toLowerCase() ); }; @@ -84,22 +87,21 @@ async function connectPlug( // Get switchboard and app gateway addresses const switchboard = addr[CORE_CONTRACTS.FastSwitchboard]; checkIfAddressExists(switchboard, "Switchboard"); - const appGateway = getAppGateway(plugContract, addresses); - checkIfAddressExists(appGateway, "AppGateway"); + const appGatewayId = getAppGatewayId(plugContract, addresses); // Check if config is already set - if (await isConfigSetOnSocket(plug, socket, appGateway, switchboard)) { + if (await isConfigSetOnSocket(plug, socket, appGatewayId, switchboard)) { console.log(`${plugContract} Socket Config on ${chain} already set!`); return; } // Connect the plug const tx = await plug.functions["connectSocket"]( - appGateway, + appGatewayId, socket.address, switchboard ); console.log( - `Connecting ${plugContract} on ${chain} to ${appGateway} tx hash: ${tx.hash}` + `Connecting ${plugContract} on ${chain} to ${appGatewayId} tx hash: ${tx.hash}` ); await tx.wait(); } @@ -160,17 +162,17 @@ export const updateConfigEVMx = async () => { const addr = addresses[chain]!; for (const plugContract of plugs) { - const appGateway = getAppGateway(plugContract, addresses); + const appGatewayId = getAppGatewayId(plugContract, addresses); const switchboard = addr[CORE_CONTRACTS.FastSwitchboard]; checkIfAddressExists(switchboard, "Switchboard"); - checkIfAddressExists(appGateway, "AppGateway"); + checkIfAddressExists(appGatewayId, "AppGateway"); if ( await isConfigSetOnEVMx( watcherPrecompileConfig, chain, addr[plugContract], - appGateway, + appGatewayId, switchboard ) ) { @@ -179,7 +181,7 @@ export const updateConfigEVMx = async () => { } appConfigs.push({ plug: addr[plugContract], - appGateway, + appGatewayId: appGatewayId, switchboard: addr[CORE_CONTRACTS.FastSwitchboard], chainSlug: chain, }); @@ -193,7 +195,7 @@ export const updateConfigEVMx = async () => { const encodedMessage = ethers.utils.defaultAbiCoder.encode( [ "bytes4", - "tuple(address plug,address appGateway,address switchboard,uint32 chainSlug)[]", + "tuple(address plug,bytes32 appGatewayId,address switchboard,uint32 chainSlug)[]", ], [ watcherPrecompileConfig.interface.getSighash("setAppGateways"), From 34295d8eacb46dda4b868b5fa5828a2ab831cda5 Mon Sep 17 00:00:00 2001 From: Akash Date: Fri, 2 May 2025 00:01:43 +0530 Subject: [PATCH 104/108] chore: version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2610da7a..3ed39c3b 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "publishConfig": { "access": "public" }, - "version": "1.1.11", + "version": "1.1.12", "description": "socket protocol", "scripts": { "build": "yarn abi && tsc --project lib.tsconfig.json", From 6de98d40979b6235c0cc4dd1a9ed2a5ad469947b Mon Sep 17 00:00:00 2001 From: Akash Date: Fri, 2 May 2025 17:39:56 +0530 Subject: [PATCH 105/108] fix : deployment --- EventTopics.md | 24 ++++---- .../protocol/payload-delivery/FeesPlug.sol | 19 ++++--- deployments/dev_addresses.json | 4 +- deployments/dev_verification.json | 57 ++++++++++++++++--- hardhat-scripts/deploy/1.deploy.ts | 3 +- hardhat-scripts/deploy/4.connect.ts | 25 ++++++-- hardhat-scripts/deploy/6.setupEnv.ts | 4 ++ package.json | 2 +- script/counter/DeployEVMxCounterApp.s.sol | 2 +- .../helpers/PayFeesInArbitrumTestUSDC.s.sol | 35 ++++++++++++ 10 files changed, 137 insertions(+), 38 deletions(-) create mode 100644 script/helpers/PayFeesInArbitrumTestUSDC.s.sol diff --git a/EventTopics.md b/EventTopics.md index ada88250..cb976877 100644 --- a/EventTopics.md +++ b/EventTopics.md @@ -94,18 +94,18 @@ ## FeesPlug -| Event | Arguments | Topic | -| ---------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------- | -| `ConnectorPlugDisconnected` | `()` | `0xc2af098c82dba3c4b00be8bda596d62d13b98a87b42626fefa67e0bb0e198fdd` | -| `FeesDeposited` | `(token: address, receiver: address, feeAmount: uint256, nativeAmount: uint256)` | `0xeb4e1b24b7fe377de69f80f7380bda5ba4b43176c6a4d300a3be9009c49f4228` | -| `FeesWithdrawn` | `(token: address, receiver: address, amount: uint256)` | `0x5e110f8bc8a20b65dcc87f224bdf1cc039346e267118bae2739847f07321ffa8` | -| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | -| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | -| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | -| `RoleGranted` | `(role: bytes32, grantee: address)` | `0x2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f3` | -| `RoleRevoked` | `(role: bytes32, revokee: address)` | `0x155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a52` | -| `TokenRemovedFromWhitelist` | `(token: address)` | `0xdd2e6d9f52cbe8f695939d018b7d4a216dc613a669876163ac548b916489d917` | -| `TokenWhitelisted` | `(token: address)` | `0x6a65f90b1a644d2faac467a21e07e50e3f8fa5846e26231d30ae79a417d3d262` | +| Event | Arguments | Topic | +| ---------------------------- | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------- | +| `ConnectorPlugDisconnected` | `()` | `0xc2af098c82dba3c4b00be8bda596d62d13b98a87b42626fefa67e0bb0e198fdd` | +| `FeesDeposited` | `(token: address, receiver: address, creditAmount: uint256, nativeAmount: uint256)` | `0xeb4e1b24b7fe377de69f80f7380bda5ba4b43176c6a4d300a3be9009c49f4228` | +| `FeesWithdrawn` | `(token: address, receiver: address, amount: uint256)` | `0x5e110f8bc8a20b65dcc87f224bdf1cc039346e267118bae2739847f07321ffa8` | +| `OwnershipHandoverCanceled` | `(pendingOwner: address)` | `0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92` | +| `OwnershipHandoverRequested` | `(pendingOwner: address)` | `0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d` | +| `OwnershipTransferred` | `(oldOwner: address, newOwner: address)` | `0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0` | +| `RoleGranted` | `(role: bytes32, grantee: address)` | `0x2ae6a113c0ed5b78a53413ffbb7679881f11145ccfba4fb92e863dfcd5a1d2f3` | +| `RoleRevoked` | `(role: bytes32, revokee: address)` | `0x155aaafb6329a2098580462df33ec4b7441b19729b9601c5fc17ae1cf99a8a52` | +| `TokenRemovedFromWhitelist` | `(token: address)` | `0xdd2e6d9f52cbe8f695939d018b7d4a216dc613a669876163ac548b916489d917` | +| `TokenWhitelisted` | `(token: address)` | `0x6a65f90b1a644d2faac467a21e07e50e3f8fa5846e26231d30ae79a417d3d262` | ## Socket diff --git a/contracts/protocol/payload-delivery/FeesPlug.sol b/contracts/protocol/payload-delivery/FeesPlug.sol index 98ae9a46..4935afa9 100644 --- a/contracts/protocol/payload-delivery/FeesPlug.sol +++ b/contracts/protocol/payload-delivery/FeesPlug.sol @@ -27,7 +27,12 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { error TokenNotWhitelisted(address token_); /// @notice Event emitted when fees are deposited - event FeesDeposited(address token, address receiver, uint256 feeAmount, uint256 nativeAmount); + event FeesDeposited( + address token, + address receiver, + uint256 creditAmount, + uint256 nativeAmount + ); /// @notice Event emitted when fees are withdrawn event FeesWithdrawn(address token, address receiver, uint256 amount); /// @notice Event emitted when a token is whitelisted @@ -73,8 +78,8 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { uint256 amount_ ) external override { uint256 nativeAmount_ = amount_ / 10; - uint256 feeAmount_ = amount_ - nativeAmount_; - _deposit(token_, receiver_, feeAmount_, nativeAmount_); + uint256 creditAmount_ = amount_ - nativeAmount_; + _deposit(token_, receiver_, creditAmount_, nativeAmount_); } function depositToNative(address token_, address receiver_, uint256 amount_) external override { @@ -83,19 +88,19 @@ contract FeesPlug is IFeesPlug, PlugBase, AccessControl { /// @notice Deposits funds /// @param token_ The token address - /// @param feeAmount_ The amount of fees + /// @param creditAmount_ The amount of fees /// @param nativeAmount_ The amount of native tokens /// @param receiver_ The receiver address function _deposit( address token_, address receiver_, - uint256 feeAmount_, + uint256 creditAmount_, uint256 nativeAmount_ ) internal { - uint256 totalAmount_ = feeAmount_ + nativeAmount_; + uint256 totalAmount_ = creditAmount_ + nativeAmount_; if (!whitelistedTokens[token_]) revert TokenNotWhitelisted(token_); SafeTransferLib.safeTransferFrom(token_, msg.sender, address(this), totalAmount_); - emit FeesDeposited(receiver_, token_, feeAmount_, nativeAmount_); + emit FeesDeposited(token_, receiver_, creditAmount_, nativeAmount_); } /// @notice Adds a token to the whitelist diff --git a/deployments/dev_addresses.json b/deployments/dev_addresses.json index 326f1ff2..ee2e60eb 100644 --- a/deployments/dev_addresses.json +++ b/deployments/dev_addresses.json @@ -2,7 +2,7 @@ "421614": { "ContractFactoryPlug": "0x9dEa956E863a44a24c1CE068460E6190EA438602", "FastSwitchboard": "0xb4C5A231614639801F421Ca386388f65576F3c81", - "FeesPlug": "0xFF3639fD99515b40d8f6bC02AC1Fead65CAd93fD", + "FeesPlug": "0x2fc70A464588b3f692D5C500Bfe3A2F2165911aD", "Socket": "0xBd0436A6E0dee11e9359767142Ed6bD7B48ba258", "SocketBatcher": "0x1683c3AB4954852f71f400AbDeF98112a066ee44", "startBlock": 148461456, @@ -29,7 +29,7 @@ "11155420": { "ContractFactoryPlug": "0x95Be4D8500e3e5C970802c64b0755027d4Fc5C9F", "FastSwitchboard": "0xe7858f1dc202f5E9C9B3ee6db052F45164a88534", - "FeesPlug": "0x01bDCAB43c423D08BaCe87ED716280536dAB3eF3", + "FeesPlug": "0x29b77ecEf2D163528d1F4A235c0795daDD2DA1Bf", "Socket": "0xE09CC429e77EE5DBeF68f3796b2A33BBDF39C03C", "SocketBatcher": "0x9175d90706a2b17f0aE025ce5A6C76e64850c2f5", "startBlock": 27158986, diff --git a/deployments/dev_verification.json b/deployments/dev_verification.json index 1bd53705..3f734147 100644 --- a/deployments/dev_verification.json +++ b/deployments/dev_verification.json @@ -41,14 +41,29 @@ "0x89e66357C5F101C56b4F9F97cf378Cc32A21438a", "Socket", "contracts/protocol/socket/Socket.sol", + [84532, "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", "EVMX"] + ] + ], + "421614": [ + [ + "0x092194e4Cd90d950ED91bD216472A18cCA7cd8F7", + "FeesPlug", + "contracts/protocol/payload-delivery/FeesPlug.sol", [ - 84532, - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", - "EVMX" + "0xBd0436A6E0dee11e9359767142Ed6bD7B48ba258", + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" + ] + ], + [ + "0x44f182553Ccdd82f95b592a935Bd7Dd90FF7F292", + "FeesPlug", + "contracts/protocol/payload-delivery/FeesPlug.sol", + [ + "0xBd0436A6E0dee11e9359767142Ed6bD7B48ba258", + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" ] ] ], - "421614": [], "7625382": [ [ "0x23364acdd298EBB3Dfd3c1835C5ACd7f77E3E2bD", @@ -207,12 +222,36 @@ "0xF86B89B5c689c170BfD2734254228D6d2db5a672", "Socket", "contracts/protocol/socket/Socket.sol", + [11155111, "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", "EVMX"] + ] + ], + "11155420": [ + [ + "0x29b77ecEf2D163528d1F4A235c0795daDD2DA1Bf", + "FeesPlug", + "contracts/protocol/payload-delivery/FeesPlug.sol", [ - 11155111, - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", - "EVMX" + "0xE09CC429e77EE5DBeF68f3796b2A33BBDF39C03C", + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" + ] + ], + [ + "0x78775DCd56fBdb903e4d83AE51924797a74AD49d", + "FeesPlug", + "contracts/protocol/payload-delivery/FeesPlug.sol", + [ + "0xE09CC429e77EE5DBeF68f3796b2A33BBDF39C03C", + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" + ] + ], + [ + "0x8eD0cebe57236Bf85b0a74951CeE74357B1d381D", + "FeesPlug", + "contracts/protocol/payload-delivery/FeesPlug.sol", + [ + "0xE09CC429e77EE5DBeF68f3796b2A33BBDF39C03C", + "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" ] ] - ], - "11155420": [] + ] } diff --git a/hardhat-scripts/deploy/1.deploy.ts b/hardhat-scripts/deploy/1.deploy.ts index 892ac0a9..3063c4e8 100644 --- a/hardhat-scripts/deploy/1.deploy.ts +++ b/hardhat-scripts/deploy/1.deploy.ts @@ -424,7 +424,8 @@ async function updateContractSettings( if ( (typeof currentValue === "string" && currentValue.toLowerCase() !== String(requiredValue).toLowerCase()) || - (BigNumber.isBigNumber(currentValue) && currentValue !== requiredValue) + (BigNumber.isBigNumber(currentValue) && + currentValue.toString() !== requiredValue.toString()) ) { console.log({ setterMethod, diff --git a/hardhat-scripts/deploy/4.connect.ts b/hardhat-scripts/deploy/4.connect.ts index dd51974e..71223808 100644 --- a/hardhat-scripts/deploy/4.connect.ts +++ b/hardhat-scripts/deploy/4.connect.ts @@ -1,4 +1,4 @@ -import { Contract, ethers, Wallet } from "ethers"; +import { constants, Contract, ethers, Wallet } from "ethers"; import { ChainAddressesObj, ChainSlug } from "../../src"; import { chains, EVMX_CHAIN_ID, mode } from "../config"; import { @@ -47,10 +47,24 @@ export const checkIfAddressExists = (address: string, name: string) => { address == "0x" || address.length != 42 ) { - throw Error(`${name} not found`); + throw Error(`${name} not found : ${address}`); } return address; }; +export const checkIfAppGatewayIdExists = ( + appGatewayId: string, + name: string +) => { + if ( + appGatewayId == constants.HashZero || + !appGatewayId || + appGatewayId == "0x" || + appGatewayId.length != 66 + ) { + throw Error(`${name} not found : ${appGatewayId}`); + } + return appGatewayId; +}; export const isConfigSetOnSocket = async ( plug: Contract, @@ -88,6 +102,7 @@ async function connectPlug( const switchboard = addr[CORE_CONTRACTS.FastSwitchboard]; checkIfAddressExists(switchboard, "Switchboard"); const appGatewayId = getAppGatewayId(plugContract, addresses); + checkIfAppGatewayIdExists(appGatewayId, "AppGatewayId"); // Check if config is already set if (await isConfigSetOnSocket(plug, socket, appGatewayId, switchboard)) { console.log(`${plugContract} Socket Config on ${chain} already set!`); @@ -128,12 +143,12 @@ export const isConfigSetOnEVMx = async ( watcher: Contract, chain: number, plug: string, - appGateway: string, + appGatewayId: string, switchboard: string ) => { const plugConfigRegistered = await watcher.getPlugConfigs(chain, plug); return ( - plugConfigRegistered[0].toLowerCase() === appGateway?.toLowerCase() && + plugConfigRegistered[0].toLowerCase() === appGatewayId?.toLowerCase() && plugConfigRegistered[1].toLowerCase() === switchboard.toLowerCase() ); }; @@ -165,7 +180,7 @@ export const updateConfigEVMx = async () => { const appGatewayId = getAppGatewayId(plugContract, addresses); const switchboard = addr[CORE_CONTRACTS.FastSwitchboard]; checkIfAddressExists(switchboard, "Switchboard"); - checkIfAddressExists(appGatewayId, "AppGateway"); + checkIfAppGatewayIdExists(appGatewayId, "AppGatewayId"); if ( await isConfigSetOnEVMx( diff --git a/hardhat-scripts/deploy/6.setupEnv.ts b/hardhat-scripts/deploy/6.setupEnv.ts index 6f9a7f83..1fe5cc72 100644 --- a/hardhat-scripts/deploy/6.setupEnv.ts +++ b/hardhat-scripts/deploy/6.setupEnv.ts @@ -39,6 +39,10 @@ const updatedLines = lines.map((line) => { return `ARBITRUM_FEES_PLUG=${ latestAddresses[ChainSlug.ARBITRUM_SEPOLIA]["FeesPlug"] }`; + } else if (line.startsWith("ARBITRUM_TEST_USDC=")) { + return `ARBITRUM_TEST_USDC=${ + latestAddresses[ChainSlug.ARBITRUM_SEPOLIA]["TestUSDC"] + }`; } return line; // Return the line unchanged if it doesn't match any of the above }); diff --git a/package.json b/package.json index 3ed39c3b..4600ccf9 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "publishConfig": { "access": "public" }, - "version": "1.1.12", + "version": "1.1.14", "description": "socket protocol", "scripts": { "build": "yarn abi && tsc --project lib.tsconfig.json", diff --git a/script/counter/DeployEVMxCounterApp.s.sol b/script/counter/DeployEVMxCounterApp.s.sol index 85755639..42c09af7 100644 --- a/script/counter/DeployEVMxCounterApp.s.sol +++ b/script/counter/DeployEVMxCounterApp.s.sol @@ -17,7 +17,7 @@ contract CounterDeploy is Script { vm.startBroadcast(deployerPrivateKey); // Setting fee payment on Arbitrum Sepolia - uint256 fees = 0.00001 ether; + uint256 fees = 10 ether; CounterAppGateway gateway = new CounterAppGateway(addressResolver, fees); diff --git a/script/helpers/PayFeesInArbitrumTestUSDC.s.sol b/script/helpers/PayFeesInArbitrumTestUSDC.s.sol new file mode 100644 index 00000000..5b280f39 --- /dev/null +++ b/script/helpers/PayFeesInArbitrumTestUSDC.s.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {FeesPlug} from "../../contracts/protocol/payload-delivery/FeesPlug.sol"; +import {ETH_ADDRESS} from "../../contracts/protocol/utils/common/Constants.sol"; +import {TestUSDC} from "../../contracts/helpers/TestUSDC.sol"; +// source .env && forge script script/helpers/PayFeesInArbitrumETH.s.sol --broadcast --skip-simulation +contract DepositFees is Script { + function run() external { + uint256 amount = 100000000; + vm.createSelectFork(vm.envString("ARBITRUM_SEPOLIA_RPC")); + + uint256 privateKey = vm.envUint("SPONSOR_KEY"); + vm.startBroadcast(privateKey); + FeesPlug feesPlug = FeesPlug(payable(vm.envAddress("ARBITRUM_FEES_PLUG"))); + address appGateway = vm.envAddress("APP_GATEWAY"); + TestUSDC testUSDCContract = TestUSDC(vm.envAddress("ARBITRUM_TEST_USDC")); + + // mint test USDC to sender + testUSDCContract.mint(vm.addr(privateKey), amount); + // approve fees plug to spend test USDC + testUSDCContract.approve(address(feesPlug), amount); + + address sender = vm.addr(privateKey); + console.log("Sender address:", sender); + uint256 balance = testUSDCContract.balanceOf(sender); + console.log("Sender balance in wei:", balance); + console.log("App Gateway:", appGateway); + console.log("Fees Plug:", address(feesPlug)); + uint feesAmount = 0.001 ether; + feesPlug.depositToFeeAndNative(address(testUSDCContract), appGateway, feesAmount); + } +} From 504d48a92082d146545106eb471df9b559aa1839 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 2 May 2025 17:41:39 +0530 Subject: [PATCH 106/108] fix: override type --- contracts/protocol/utils/common/Structs.sol | 2 +- test/Inbox.t.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/protocol/utils/common/Structs.sol b/contracts/protocol/utils/common/Structs.sol index 74e7f50c..2f93bf17 100644 --- a/contracts/protocol/utils/common/Structs.sol +++ b/contracts/protocol/utils/common/Structs.sol @@ -95,10 +95,10 @@ struct PlugConfig { //trigger: struct TriggerParams { bytes32 triggerId; - bytes32 overrides; address plug; bytes32 appGatewayId; uint32 chainSlug; + bytes overrides; bytes payload; } // timeout: diff --git a/test/Inbox.t.sol b/test/Inbox.t.sol index f680f6de..c9306913 100644 --- a/test/Inbox.t.sol +++ b/test/Inbox.t.sol @@ -94,7 +94,7 @@ contract TriggerTest is DeliveryHelperTest { appGatewayId: _encodeAppGatewayId(address(gateway)), plug: address(counter), payload: payload, - overrides: bytes32(0) + overrides: bytes("") }); bytes memory watcherSignature = _createWatcherSignature( From cfe15e348282f71e56399ed275e167f0260c1d69 Mon Sep 17 00:00:00 2001 From: Akash Date: Fri, 2 May 2025 21:31:36 +0530 Subject: [PATCH 107/108] fix: removed finalize --- FunctionSignatures.md | 3 +-- contracts/interfaces/IWatcherPrecompile.sol | 5 ----- .../watcherPrecompile/core/WatcherPrecompile.sol | 14 -------------- deployments/dev_addresses.json | 4 ++-- deployments/dev_verification.json | 15 ++++++--------- package.json | 2 +- 6 files changed, 10 insertions(+), 33 deletions(-) diff --git a/FunctionSignatures.md b/FunctionSignatures.md index 17183218..e3ef596c 100644 --- a/FunctionSignatures.md +++ b/FunctionSignatures.md @@ -441,14 +441,13 @@ | `appGatewayCalled` | `0xc6767cf1` | | `appGatewayCaller` | `0x712b193a` | | `batchPayloadIds` | `0x02b74f98` | -| `callAppGateways` | `0xaef0b33d` | +| `callAppGateways` | `0x5c38ded5` | | `cancelOwnershipHandover` | `0x54d1f13d` | | `cancelRequest` | `0x50ad0779` | | `completeOwnershipHandover` | `0xf04e283e` | | `deliveryHelper__` | `0xc031dfb4` | | `evmxSlug` | `0x8bae77c2` | | `expiryTime` | `0x99bc0aea` | -| `finalize` | `0x7ffecf2e` | | `finalized` | `0x81c051de` | | `getBatchPayloadIds` | `0xfd83cd1f` | | `getBatches` | `0xcb95b7b3` | diff --git a/contracts/interfaces/IWatcherPrecompile.sol b/contracts/interfaces/IWatcherPrecompile.sol index 1265f677..0262a220 100644 --- a/contracts/interfaces/IWatcherPrecompile.sol +++ b/contracts/interfaces/IWatcherPrecompile.sol @@ -132,11 +132,6 @@ interface IWatcherPrecompile { bytes calldata signature_ ) external; - function finalize( - PayloadParams memory params_, - address transmitter_ - ) external returns (bytes32 digest); - function query(PayloadParams memory params_) external; function finalized( diff --git a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol index 2898354c..878ef213 100644 --- a/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol @@ -99,20 +99,6 @@ contract WatcherPrecompile is RequestHandler { ); } - // ================== Finalize functions ================== - - /// @notice Finalizes a payload request, requests the watcher to release the proofs to execute on chain - /// @param params_ The payload parameters - /// @param transmitter_ The address of the transmitter - /// @return The digest hash of the finalized payload - /// @dev This function finalizes a payload request and requests the watcher to release the proofs - function finalize( - PayloadParams memory params_, - address transmitter_ - ) external returns (bytes32) { - return _finalize(params_, transmitter_); - } - // ================== Query functions ================== /// @notice Creates a new query request diff --git a/deployments/dev_addresses.json b/deployments/dev_addresses.json index ee2e60eb..72af1b71 100644 --- a/deployments/dev_addresses.json +++ b/deployments/dev_addresses.json @@ -19,10 +19,10 @@ "FeesManager": "0xBf9529b5aA4a6a047Ff65CfAE9613A274C479143", "FeesManagerImpl": "0xB577c29F7Cbb1bBB314dD8E74059Aa5BF72838b0", "startBlock": 5537530, - "WatcherPrecompile": "0x68430a7f22125dd8b566f774AED05e868CA70fEA", + "WatcherPrecompile": "0xB0CC4C4a6706E265306daCa279Ce60D1052b2782", "WatcherPrecompileConfig": "0xD0F77272a5F0208f20c836bB4eeddbCE1e4aef9d", "WatcherPrecompileConfigImpl": "0x060b4a50EcCC9Cf329005c94406dd2886676F759", - "WatcherPrecompileImpl": "0x1BCe40d84499Db8E7Bc65277A32f0abd56588CC7", + "WatcherPrecompileImpl": "0xe1CA4da421C52161B4EecCE6F5Cb2937554e2958", "WatcherPrecompileLimits": "0x4A645F050166FaBdA7ce44BE90B0A61073C19696", "WatcherPrecompileLimitsImpl": "0x4Fd04B0D4903e630F169BB228be52750E6B5331a" }, diff --git a/deployments/dev_verification.json b/deployments/dev_verification.json index 3f734147..cb1975f3 100644 --- a/deployments/dev_verification.json +++ b/deployments/dev_verification.json @@ -65,6 +65,12 @@ ] ], "7625382": [ + [ + "0xe1CA4da421C52161B4EecCE6F5Cb2937554e2958", + "WatcherPrecompile", + "contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol", + [] + ], [ "0x23364acdd298EBB3Dfd3c1835C5ACd7f77E3E2bD", "AuctionManager", @@ -226,15 +232,6 @@ ] ], "11155420": [ - [ - "0x29b77ecEf2D163528d1F4A235c0795daDD2DA1Bf", - "FeesPlug", - "contracts/protocol/payload-delivery/FeesPlug.sol", - [ - "0xE09CC429e77EE5DBeF68f3796b2A33BBDF39C03C", - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" - ] - ], [ "0x78775DCd56fBdb903e4d83AE51924797a74AD49d", "FeesPlug", diff --git a/package.json b/package.json index 4600ccf9..86bf9215 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "publishConfig": { "access": "public" }, - "version": "1.1.14", + "version": "1.1.15", "description": "socket protocol", "scripts": { "build": "yarn abi && tsc --project lib.tsconfig.json", From 9c1be2368e90c8122c04db28408cc3c588b4305a Mon Sep 17 00:00:00 2001 From: Akash Date: Fri, 2 May 2025 23:37:20 +0530 Subject: [PATCH 108/108] fix: stage deploy --- deployments/stage_addresses.json | 82 +++++------ deployments/stage_verification.json | 128 ++---------------- hardhat-scripts/config/config.ts | 33 ++++- hardhat-scripts/constants/roles.ts | 1 + hardhat-scripts/deploy/1.deploy.ts | 62 +++++++++ hardhat-scripts/deploy/2.roles.ts | 6 +- script/admin/RescueFunds.s.sol | 70 ++++++++++ .../helpers/PayFeesInArbitrumTestUSDC.s.sol | 8 +- setupInfraContracts.sh | 1 + 9 files changed, 222 insertions(+), 169 deletions(-) create mode 100644 script/admin/RescueFunds.s.sol diff --git a/deployments/stage_addresses.json b/deployments/stage_addresses.json index 61a298c8..d950e823 100644 --- a/deployments/stage_addresses.json +++ b/deployments/stage_addresses.json @@ -1,52 +1,54 @@ { "43": { - "AddressResolver": "0x4846430BB142385e581C894AE92a4CF0722aEC21", - "AddressResolverImpl": "0x0F13F50f7dED1da9A4845366C2AB5120a1A17549", - "AuctionManager": "0x222574Dab9bb404Cb49a9445CD4d9555e8B52Cf5", - "AuctionManagerImpl": "0xbe1b573aa1B6ddD1A7a27aE0Aa6A38FA5d26fc67", - "DeliveryHelper": "0x436Ea32C3198500d113B006dBdc6fF1Bebd10769", - "DeliveryHelperImpl": "0x40f7fdE05bFa9F7c9B55a582B0783352856BCd03", - "ERC1967Factory": "0xF362fdCAbbd1f58AAd998d4c7Aef4020365092C8", - "FeesManager": "0x9745623Aaa299500F93d2B1B4Efb7b3EC5e60FFc", - "FeesManagerImpl": "0x71956F006Ec5434581D3Fd5E7224BB3bae231907", - "startBlock": 2368668, - "WatcherPrecompile": "0xEbdA834fAc9ca4B86AdD442083c1650f8497EdCb", - "WatcherPrecompileConfig": "0x4D38091442c78B4cb2bB22AFF61552bc72d4BF8e", - "WatcherPrecompileConfigImpl": "0xDf9d7b339Db52Fc58f2c72ffAd3a87201FB16b30", - "WatcherPrecompileImpl": "0xbAeF84edEae864Ff22Bd9c9912AdfF84aD490d82", - "WatcherPrecompileLimits": "0x0CF9B01E7d1ef769D160F53289244f74197B4149", - "WatcherPrecompileLimitsImpl": "0x0bA474851A0703eC69964FB8264304AF357cd16D" + "AddressResolver": "0x21a9AFDfbEb0399D4a12f3AA1324042Be2B57F8e", + "AddressResolverImpl": "0x794b92C2Ade7D33Fb34d138B13014C63aB27CBC0", + "AuctionManager": "0x87E15a6f9Cbe482f67683Ec3f7294f12d221C8bA", + "AuctionManagerImpl": "0xfddb38811a0774E66ABD5F3Ae960bFB7E7415029", + "DeliveryHelper": "0xb399b60C22A32512a24F01C4401f43BfF979A49F", + "DeliveryHelperImpl": "0xa07e38cAB46eAA358C3653C63219f1009e8F7789", + "ERC1967Factory": "0x98ea7A5601f203DE56d86BDCA69fC3019377D6B1", + "FeesManager": "0x30e07016eB24570629Bc8765CA307394Af90B27C", + "FeesManagerImpl": "0x9F10A0c71178dbD4d049f2C04fD0e34966134b9e", + "startBlock": 5480301, + "WatcherPrecompile": "0x426509517074E0fBf15F8aAB2472711FB456C58C", + "WatcherPrecompileConfig": "0x49094ECAF26d8295BcBD73b0Ff17215348E7b253", + "WatcherPrecompileConfigImpl": "0xd69E17Ce715f49Cd2B16C64cf75201A56Ce0E90d", + "WatcherPrecompileImpl": "0xB423eE3bffc3604F96B59cF419C48AE05b8E9d0b", + "WatcherPrecompileLimits": "0x54B315eC6c7059b19164BC0e5335643d5bBAad4f", + "WatcherPrecompileLimitsImpl": "0x0e26C8CFCABC04c642696A625664553e2C183bbe" }, "84532": { - "ContractFactoryPlug": "0x5AF9cA0Ce2Bc991FcE955f8c993fb0A5464B289F", - "FastSwitchboard": "0x0594497C89ECF66bC67204EE89770C4e799De3f9", - "FeesPlug": "0x0EBC6E395503eF135b7a45FfC7d42C2A2bc56D54", - "Socket": "0x92562Ae6526aB8B4fFF9Fa8ECAb6db67f0753693", - "SocketBatcher": "0xE4036898F51842E7DdFD36Dc8eAa7D4B207c5DEe", - "startBlock": 23664081 + "ContractFactoryPlug": "0x8e51D99Bf353Ebba50080D0452546fd2EBAd86A4", + "FastSwitchboard": "0x4C114115755c39dB81a06fBfEb4b08302Abe7beE", + "FeesPlug": "0x9161a99deD597fe519E03D319053CA1669118dDA", + "Socket": "0x36Ae239a92faee6aFF4df9749d592dA7c00717Be", + "SocketBatcher": "0x8fa361816874a11a66D02EC84b28E1A931B4035e", + "startBlock": 25218634, + "TestUSDC": "0xfD51918C0572512901fFA79F822c99A475d22BB4" }, "421614": { - "ContractFactoryPlug": "0x5F710Ac554DD22819F9411fa554265EEf827247d", - "FastSwitchboard": "0xBD0158415Eb99B5e1dBA1e4E534f916ba82380f9", - "FeesPlug": "0x9E263f6c7C199d9c147E30764A8cae1175184CB8", - "Socket": "0x22c1275677E600e2b049B69D929E2ccAAf4B880E", - "SocketBatcher": "0x92f85fe2CbB2D9ab577E4D167E629095497325b9", - "startBlock": 136470235 + "ContractFactoryPlug": "0x65C066BE05CB4622393fADc1Bf3dE8eEdEcB3817", + "FastSwitchboard": "0xF121f4B97F7C902eeD4b188B08052Da9A1FD5aBe", + "FeesPlug": "0xDfE94B9b14de382Ed13C8A7F387884808D0f7E0b", + "Socket": "0xDAB25fB82cc1b1611Fb9016FB50222dBFcD1BCf5", + "SocketBatcher": "0x4e7163Ce9F7F335138fB32827d6f99f174060897", + "startBlock": 148801970, + "TestUSDC": "0xa03Cbf13f331aF7c0fD7F2E28E6Cbc13F879E3F3" }, "11155111": { - "ContractFactoryPlug": "0xd36C1Dcb65CB09b7fCFABf153D7cdd42312C782E", - "FastSwitchboard": "0x36AC527afA283c95EA7dD11c8E93225d9F139028", - "FeesPlug": "0xc4008CCB59413cC2745d33549e5BE16A2d1DD061", - "Socket": "0x899AE7770eFb9714aF717d03c0d577e41d78ed48", - "SocketBatcher": "0xE90649F3BA488D91c7e8E3025F639F435Fa85243", - "startBlock": 23664095 + "FastSwitchboard": "0x1eFD3AF2317B9E6E7492718878f69De747C9e7c3", + "FeesPlug": "0xfE555AD869ac24305471F0755976c556425E8D23", + "Socket": "0xae59BA0Bd0D92232B3B6304185448C9Fe5445f4d", + "SocketBatcher": "0xdaE4538FbbEf41B2Feb5c79DD2fFC9720AF13d7b", + "TestUSDC": "0xbcaDE56f86a819994d0F66b98e921C484bE6FE4e" }, "11155420": { - "ContractFactoryPlug": "0x6320Ff773a4E01Cb8EB849EA906F17Cf6c48Ff9c", - "FastSwitchboard": "0xd94741a4654953817faEe228739a6d10C0683839", - "FeesPlug": "0x89634ecFea933aFaD5d3D6557b13cb8D466313d2", - "Socket": "0x2420B85D7e126d1948a4602f0c78a685655292Bd", - "SocketBatcher": "0xBD6770182fB47DD77924aDf3F200246Ab851f9c2", - "startBlock": 25646940 + "ContractFactoryPlug": "0x469B536c5Df15948c8759FEEE5DB1c17790d4152", + "FastSwitchboard": "0xd8bCd4b4Bc4b0f5cb279B6FAdCEd733614f34F51", + "FeesPlug": "0x6734a30B8f2d210faefa5aeD4E11b674C59641F1", + "Socket": "0x11fbd3a7031b28607973fc44d4d24B26DEfac886", + "SocketBatcher": "0x2c2060f5586751676fC2Af96cc8bE9BF0c7A8770", + "startBlock": 27201458, + "TestUSDC": "0xa0E1738a9Fc0698789866e09d7A335d30128C5C5" } } diff --git a/deployments/stage_verification.json b/deployments/stage_verification.json index 61c8987d..88f93e4b 100644 --- a/deployments/stage_verification.json +++ b/deployments/stage_verification.json @@ -1,162 +1,56 @@ { "43": [ [ - "0x71956F006Ec5434581D3Fd5E7224BB3bae231907", - "FeesManager", - "contracts/protocol/payload-delivery/FeesManager.sol", - [] - ], - [ - "0xbAeF84edEae864Ff22Bd9c9912AdfF84aD490d82", - "WatcherPrecompile", - "contracts/protocol/watcherPrecompile/WatcherPrecompile.sol", - [] - ], - [ - "0x02Bd15aa48BAE9A92E23a3F30Be0c1bD253970Cf", - "WatcherPrecompile", - "contracts/protocol/watcherPrecompile/WatcherPrecompile.sol", - [] - ], - [ - "0xbe1b573aa1B6ddD1A7a27aE0Aa6A38FA5d26fc67", + "0xfddb38811a0774E66ABD5F3Ae960bFB7E7415029", "AuctionManager", "contracts/protocol/payload-delivery/AuctionManager.sol", [] ], [ - "0x40f7fdE05bFa9F7c9B55a582B0783352856BCd03", + "0xa07e38cAB46eAA358C3653C63219f1009e8F7789", "DeliveryHelper", "contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol", [] ], [ - "0xd151bD217704F72f717C2111207e6Bb33B609f61", + "0x9F10A0c71178dbD4d049f2C04fD0e34966134b9e", "FeesManager", "contracts/protocol/payload-delivery/FeesManager.sol", [] ], [ - "0x2916aC09Be088427E1a25968DA332F1F1eFa62d2", + "0xB423eE3bffc3604F96B59cF419C48AE05b8E9d0b", "WatcherPrecompile", - "contracts/protocol/watcherPrecompile/WatcherPrecompile.sol", + "contracts/protocol/watcherPrecompile/core/WatcherPrecompile.sol", [] ], [ - "0xDf9d7b339Db52Fc58f2c72ffAd3a87201FB16b30", + "0xd69E17Ce715f49Cd2B16C64cf75201A56Ce0E90d", "WatcherPrecompileConfig", "contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol", [] ], [ - "0x0bA474851A0703eC69964FB8264304AF357cd16D", + "0x0e26C8CFCABC04c642696A625664553e2C183bbe", "WatcherPrecompileLimits", "contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol", [] ], [ - "0x0F13F50f7dED1da9A4845366C2AB5120a1A17549", + "0x794b92C2Ade7D33Fb34d138B13014C63aB27CBC0", "AddressResolver", "contracts/protocol/AddressResolver.sol", [] ], [ - "0xF362fdCAbbd1f58AAd998d4c7Aef4020365092C8", + "0x98ea7A5601f203DE56d86BDCA69fC3019377D6B1", "ERC1967Factory", "lib/solady/src/utils/ERC1967Factory.sol", [] ] ], - "84532": [ - [ - "0x5AF9cA0Ce2Bc991FcE955f8c993fb0A5464B289F", - "ContractFactoryPlug", - "contracts/protocol/payload-delivery/ContractFactoryPlug.sol", - [ - "0x92562Ae6526aB8B4fFF9Fa8ECAb6db67f0753693", - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" - ] - ], - [ - "0x0EBC6E395503eF135b7a45FfC7d42C2A2bc56D54", - "FeesPlug", - "contracts/protocol/payload-delivery/FeesPlug.sol", - [ - "0x92562Ae6526aB8B4fFF9Fa8ECAb6db67f0753693", - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" - ] - ], - [ - "0x0594497C89ECF66bC67204EE89770C4e799De3f9", - "FastSwitchboard", - "contracts/protocol/socket/switchboard/FastSwitchboard.sol", - [ - 84532, - "0x92562Ae6526aB8B4fFF9Fa8ECAb6db67f0753693", - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" - ] - ], - [ - "0xE4036898F51842E7DdFD36Dc8eAa7D4B207c5DEe", - "SocketBatcher", - "contracts/protocol/socket/SocketBatcher.sol", - [ - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", - "0x92562Ae6526aB8B4fFF9Fa8ECAb6db67f0753693" - ] - ], - [ - "0x92562Ae6526aB8B4fFF9Fa8ECAb6db67f0753693", - "Socket", - "contracts/protocol/socket/Socket.sol", - [84532, "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", "EVMX"] - ] - ], + "84532": [], "421614": [], - "11155111": [ - [ - "0xd36C1Dcb65CB09b7fCFABf153D7cdd42312C782E", - "ContractFactoryPlug", - "contracts/protocol/payload-delivery/ContractFactoryPlug.sol", - [ - "0x899AE7770eFb9714aF717d03c0d577e41d78ed48", - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" - ] - ], - [ - "0xc4008CCB59413cC2745d33549e5BE16A2d1DD061", - "FeesPlug", - "contracts/protocol/payload-delivery/FeesPlug.sol", - [ - "0x899AE7770eFb9714aF717d03c0d577e41d78ed48", - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" - ] - ], - [ - "0x36AC527afA283c95EA7dD11c8E93225d9F139028", - "FastSwitchboard", - "contracts/protocol/socket/switchboard/FastSwitchboard.sol", - [ - 11155111, - "0x899AE7770eFb9714aF717d03c0d577e41d78ed48", - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18" - ] - ], - [ - "0xE90649F3BA488D91c7e8E3025F639F435Fa85243", - "SocketBatcher", - "contracts/protocol/socket/SocketBatcher.sol", - [ - "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", - "0x899AE7770eFb9714aF717d03c0d577e41d78ed48" - ] - ], - [ - "0x899AE7770eFb9714aF717d03c0d577e41d78ed48", - "Socket", - "contracts/protocol/socket/Socket.sol", - [11155111, "0x3339Cf48f1F9cf31b6F8c2664d144c7444eBBB18", "EVMX"] - ] - ], + "11155111": [], "11155420": [] } diff --git a/hardhat-scripts/config/config.ts b/hardhat-scripts/config/config.ts index cdd6ad33..e0964075 100644 --- a/hardhat-scripts/config/config.ts +++ b/hardhat-scripts/config/config.ts @@ -23,12 +23,31 @@ export const logConfig = () => { ); }; -export const chains: Array = [ - ChainSlug.ARBITRUM_SEPOLIA, - ChainSlug.OPTIMISM_SEPOLIA, - // ChainSlug.BASE_SEPOLIA, - // ChainSlug.SEPOLIA, -]; +export const getChains = () => { + switch (mode) { + case DeploymentMode.LOCAL: + return [ChainSlug.ARBITRUM_SEPOLIA, ChainSlug.OPTIMISM_SEPOLIA]; + case DeploymentMode.DEV: + return [ChainSlug.ARBITRUM_SEPOLIA, ChainSlug.OPTIMISM_SEPOLIA]; + case DeploymentMode.STAGE: + return [ + ChainSlug.OPTIMISM_SEPOLIA, + ChainSlug.ARBITRUM_SEPOLIA, + ChainSlug.BASE_SEPOLIA, + ]; + case DeploymentMode.PROD: + return [ + ChainSlug.OPTIMISM_SEPOLIA, + ChainSlug.ARBITRUM_SEPOLIA, + ChainSlug.BASE_SEPOLIA, + ChainSlug.SEPOLIA, + ]; + default: + throw new Error(`Invalid deployment mode: ${mode}`); + } +}; + +export const chains: Array = getChains(); export const EVM_CHAIN_ID_MAP: Record = { [DeploymentMode.LOCAL]: 7625382, [DeploymentMode.DEV]: 7625382, @@ -48,7 +67,7 @@ export const auctionEndDelaySeconds = 0; export const BID_TIMEOUT = 600; // 10 minutes export const EXPIRY_TIME = 300; // 5 minutes export const MAX_RE_AUCTION_COUNT = 5; - +export const AUCTION_MANAGER_FUNDING_AMOUNT = ethers.utils.parseEther("100"); // TestUSDC export const TEST_USDC_NAME = "testUSDC"; export const TEST_USDC_SYMBOL = "testUSDC"; diff --git a/hardhat-scripts/constants/roles.ts b/hardhat-scripts/constants/roles.ts index 99ff3d91..ec875446 100644 --- a/hardhat-scripts/constants/roles.ts +++ b/hardhat-scripts/constants/roles.ts @@ -4,4 +4,5 @@ export enum ROLES { GOVERNANCE_ROLE = "GOVERNANCE_ROLE", WATCHER_ROLE = "WATCHER_ROLE", TRANSMITTER_ROLE = "TRANSMITTER_ROLE", + SWITCHBOARD_DISABLER_ROLE = "SWITCHBOARD_DISABLER_ROLE", } diff --git a/hardhat-scripts/deploy/1.deploy.ts b/hardhat-scripts/deploy/1.deploy.ts index 3063c4e8..fdb51e5a 100644 --- a/hardhat-scripts/deploy/1.deploy.ts +++ b/hardhat-scripts/deploy/1.deploy.ts @@ -6,6 +6,7 @@ import { ethers } from "hardhat"; import { CORE_CONTRACTS, DeploymentAddresses, + ETH_ADDRESS, EVMxCoreContracts, FAST_SWITCHBOARD_TYPE, IMPLEMENTATION_SLOT, @@ -36,6 +37,7 @@ import { FINALIZE_FEES, TIMEOUT_FEES, CALLBACK_FEES, + AUCTION_MANAGER_FUNDING_AMOUNT, } from "../config/config"; config(); @@ -262,6 +264,16 @@ const deployEVMxContracts = async () => { CALLBACK_FEES, deployUtils.signer ); + + const feesManager = await getInstance( + EVMxCoreContracts.FeesManager, + deployUtils.addresses[EVMxCoreContracts.FeesManager] + ); + await fundAuctionManager( + feesManager.connect(deployUtils.signer), + deployUtils.addresses[EVMxCoreContracts.AuctionManager], + deployUtils.signer + ); deployUtils.addresses.startBlock = (deployUtils.addresses.startBlock ? deployUtils.addresses.startBlock @@ -277,6 +289,56 @@ const deployEVMxContracts = async () => { } }; +export const fundAuctionManager = async ( + feesManager: Contract, + auctionManagerAddress: string, + watcherSigner: Signer +) => { + const currentCredits = await feesManager.getAvailableCredits( + auctionManagerAddress + ); + console.log("Current credits:", currentCredits.toString()); + if (currentCredits.gte(BigNumber.from(AUCTION_MANAGER_FUNDING_AMOUNT))) { + console.log( + `Auction manager ${auctionManagerAddress} already has credits, skipping funding` + ); + return; + } + const signatureNonce = Date.now(); + const digest = ethers.utils.keccak256( + ethers.utils.defaultAbiCoder.encode( + ["address", "uint32", "address", "uint256", "address", "uint32"], + [ + auctionManagerAddress, + EVMX_CHAIN_ID, + ETH_ADDRESS, + AUCTION_MANAGER_FUNDING_AMOUNT, + feesManager.address, + EVMX_CHAIN_ID, + ] + ) + ); + const signature = await watcherSigner.signMessage( + ethers.utils.arrayify(digest) + ); + const tx = await feesManager + .connect(watcherSigner) + .depositCredits( + auctionManagerAddress, + EVMX_CHAIN_ID, + ETH_ADDRESS, + signatureNonce, + signature, + { + value: AUCTION_MANAGER_FUNDING_AMOUNT, + } + ); + console.log( + `Funding auction manager ${auctionManagerAddress} with ${AUCTION_MANAGER_FUNDING_AMOUNT} ETH, txHash: `, + tx.hash + ); + await tx.wait(); +}; const deploySocketContracts = async () => { try { let addresses: DeploymentAddresses; diff --git a/hardhat-scripts/deploy/2.roles.ts b/hardhat-scripts/deploy/2.roles.ts index ae578b73..8ef83acc 100644 --- a/hardhat-scripts/deploy/2.roles.ts +++ b/hardhat-scripts/deploy/2.roles.ts @@ -20,7 +20,11 @@ import { ROLES } from "../constants/roles"; import { getWatcherSigner, getSocketSigner } from "../utils/sign"; export const REQUIRED_ROLES = { FastSwitchboard: [ROLES.WATCHER_ROLE, ROLES.RESCUE_ROLE], - Socket: [ROLES.GOVERNANCE_ROLE, ROLES.RESCUE_ROLE], + Socket: [ + ROLES.GOVERNANCE_ROLE, + ROLES.RESCUE_ROLE, + ROLES.SWITCHBOARD_DISABLER_ROLE, + ], FeesPlug: [ROLES.RESCUE_ROLE], ContractFactoryPlug: [ROLES.RESCUE_ROLE], }; diff --git a/script/admin/RescueFunds.s.sol b/script/admin/RescueFunds.s.sol new file mode 100644 index 00000000..dbd5a3dd --- /dev/null +++ b/script/admin/RescueFunds.s.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import {Script} from "forge-std/Script.sol"; +import {console} from "forge-std/console.sol"; +import {FeesPlug} from "../../contracts/protocol/payload-delivery/FeesPlug.sol"; + +contract RescueFundsScript is Script { + address constant NATIVE_TOKEN = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + + struct ChainConfig { + address feesPlug; + string rpc; + string name; + } + + function rescueFromChain( + ChainConfig memory config, + address sender, + uint256 deployerKey + ) internal { + uint256 fork = vm.createFork(config.rpc); + vm.selectFork(fork); + uint256 balance = address(config.feesPlug).balance; + + if (balance > 0) { + console.log("%s Fees Plug Balance:", config.name); + console.log(balance); + + vm.startBroadcast(deployerKey); + FeesPlug(config.feesPlug).rescueFunds(NATIVE_TOKEN, sender, balance); + vm.stopBroadcast(); + } + } + + function run() external { + uint256 deployerPrivateKey = vm.envUint("SPONSOR_KEY"); + address sender = vm.envAddress("SENDER_ADDRESS"); + + ChainConfig[] memory chains = new ChainConfig[](4); + + chains[0] = ChainConfig({ + feesPlug: vm.envAddress("ARBITRUM_FEES_PLUG"), + rpc: vm.envString("ARBITRUM_SEPOLIA_RPC"), + name: "Arbitrum" + }); + + chains[1] = ChainConfig({ + feesPlug: vm.envAddress("BASE_FEES_PLUG"), + rpc: vm.envString("BASE_SEPOLIA_RPC"), + name: "Base" + }); + + chains[2] = ChainConfig({ + feesPlug: vm.envAddress("OPTIMISM_FEES_PLUG"), + rpc: vm.envString("OPTIMISM_SEPOLIA_RPC"), + name: "Optimism" + }); + + chains[3] = ChainConfig({ + feesPlug: vm.envAddress("SEPOLIA_FEES_PLUG"), + rpc: vm.envString("SEPOLIA_RPC"), + name: "Sepolia" + }); + + for (uint i = 0; i < chains.length; i++) { + rescueFromChain(chains[i], sender, deployerPrivateKey); + } + } +} diff --git a/script/helpers/PayFeesInArbitrumTestUSDC.s.sol b/script/helpers/PayFeesInArbitrumTestUSDC.s.sol index 5b280f39..c7fe8111 100644 --- a/script/helpers/PayFeesInArbitrumTestUSDC.s.sol +++ b/script/helpers/PayFeesInArbitrumTestUSDC.s.sol @@ -9,7 +9,7 @@ import {TestUSDC} from "../../contracts/helpers/TestUSDC.sol"; // source .env && forge script script/helpers/PayFeesInArbitrumETH.s.sol --broadcast --skip-simulation contract DepositFees is Script { function run() external { - uint256 amount = 100000000; + uint256 feesAmount = 100000000; vm.createSelectFork(vm.envString("ARBITRUM_SEPOLIA_RPC")); uint256 privateKey = vm.envUint("SPONSOR_KEY"); @@ -19,9 +19,9 @@ contract DepositFees is Script { TestUSDC testUSDCContract = TestUSDC(vm.envAddress("ARBITRUM_TEST_USDC")); // mint test USDC to sender - testUSDCContract.mint(vm.addr(privateKey), amount); + testUSDCContract.mint(vm.addr(privateKey), feesAmount); // approve fees plug to spend test USDC - testUSDCContract.approve(address(feesPlug), amount); + testUSDCContract.approve(address(feesPlug), feesAmount); address sender = vm.addr(privateKey); console.log("Sender address:", sender); @@ -29,7 +29,7 @@ contract DepositFees is Script { console.log("Sender balance in wei:", balance); console.log("App Gateway:", appGateway); console.log("Fees Plug:", address(feesPlug)); - uint feesAmount = 0.001 ether; + console.log("Fees Amount:", feesAmount); feesPlug.depositToFeeAndNative(address(testUSDCContract), appGateway, feesAmount); } } diff --git a/setupInfraContracts.sh b/setupInfraContracts.sh index dee0dde8..9d916681 100644 --- a/setupInfraContracts.sh +++ b/setupInfraContracts.sh @@ -12,3 +12,4 @@ time npx hardhat run hardhat-scripts/misc-scripts/errorCodes.ts --no-compile time npx hardhat run hardhat-scripts/misc-scripts/eventTopics.ts --no-compile time npx hardhat run hardhat-scripts/misc-scripts/functionSigs.ts --no-compile time npx hardhat run hardhat-scripts/verify/verify.ts --no-compile +yarn lint \ No newline at end of file