From cf49f4aff62be537f0dc8ce4297c62aef4d10b0e Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 25 Feb 2025 15:10:54 +0400 Subject: [PATCH 01/12] feat: separate storages --- contracts/protocol/AddressResolver.sol | 10 +-- contracts/protocol/AsyncPromise.sol | 10 +-- contracts/protocol/Forwarder.sol | 8 ++- .../{app-gateway => }/AuctionManager.sol | 41 +++++------ .../{app-gateway => }/FeesManager.sol | 29 +++++--- .../app-gateway/DeliveryHelper.sol | 6 +- .../app-gateway/DeliveryHelperStorage.sol | 28 ++++++++ .../app-gateway/QueueAsync.sol | 44 ++++-------- contracts/protocol/utils/common/Errors.sol | 8 +++ .../watcherPrecompile/WatcherPrecompile.sol | 21 ------ .../WatcherPrecompileConfig.sol | 27 ------- .../WatcherPrecompileLimits.sol | 24 +------ .../WatcherPrecompileStorage.sol | 71 +++++++++++++++++++ script/AppGatewayFeeBalance.s.sol | 2 +- .../WithdrawFeesArbitrumFeesPlug.s.sol | 2 +- test/DeliveryHelper.t.sol | 4 +- 16 files changed, 181 insertions(+), 154 deletions(-) rename contracts/protocol/payload-delivery/{app-gateway => }/AuctionManager.sol (85%) rename contracts/protocol/payload-delivery/{app-gateway => }/FeesManager.sol (95%) create mode 100644 contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol create mode 100644 contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol diff --git a/contracts/protocol/AddressResolver.sol b/contracts/protocol/AddressResolver.sol index 28050c26..6f804a0d 100644 --- a/contracts/protocol/AddressResolver.sol +++ b/contracts/protocol/AddressResolver.sol @@ -9,10 +9,7 @@ import "../interfaces/IAddressResolver.sol"; import {Forwarder} from "./Forwarder.sol"; import {AsyncPromise} from "./AsyncPromise.sol"; -/// @title AddressResolver Contract -/// @notice This contract is responsible for fetching latest core addresses and deploying Forwarder and AsyncPromise contracts. -/// @dev Inherits the Ownable contract and implements the IAddressResolver interface. -contract AddressResolver is Ownable, IAddressResolver, Initializable { +abstract contract AddressResolverStorage is IAddressResolver { IWatcherPrecompile public override watcherPrecompile__; address public override deliveryHelper; address public override feesManager; @@ -34,7 +31,12 @@ contract AddressResolver is Ownable, IAddressResolver, Initializable { mapping(address => address) public override contractsToGateways; // gateway to contract map mapping(address => address) public override gatewaysToContracts; +} +/// @title AddressResolver Contract +/// @notice This contract is responsible for fetching latest core addresses and deploying Forwarder and AsyncPromise contracts. +/// @dev Inherits the Ownable contract and implements the IAddressResolver interface. +contract AddressResolver is AddressResolverStorage, Ownable, Initializable { /// @notice Error thrown if AppGateway contract was already set by a different address error InvalidCaller(address contractAddress_); diff --git a/contracts/protocol/AsyncPromise.sol b/contracts/protocol/AsyncPromise.sol index 99f5e08e..b8d4b8f6 100644 --- a/contracts/protocol/AsyncPromise.sol +++ b/contracts/protocol/AsyncPromise.sol @@ -15,10 +15,7 @@ enum AsyncPromiseState { RESOLVED } -/// @title AsyncPromise -/// @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 IPromise, Initializable, AddressResolverUtil { +abstract contract AsyncPromiseStorage is IPromise { /// @notice The callback selector to be called on the invoker. bytes4 public callbackSelector; @@ -37,7 +34,12 @@ contract AsyncPromise is IPromise, Initializable, AddressResolverUtil { /// @notice The callback data to be used when the promise is resolved. bytes public callbackData; +} +/// @title AsyncPromise +/// @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 { /// @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 diff --git a/contracts/protocol/Forwarder.sol b/contracts/protocol/Forwarder.sol index 0c45a45c..22a4b95f 100644 --- a/contracts/protocol/Forwarder.sol +++ b/contracts/protocol/Forwarder.sol @@ -9,9 +9,7 @@ import "./AsyncPromise.sol"; import "../interfaces/IForwarder.sol"; import "solady/utils/Initializable.sol"; -/// @title Forwarder Contract -/// @notice This contract acts as a forwarder for async calls to the on-chain contracts. -contract Forwarder is IForwarder, Initializable { +abstract contract ForwarderStorage is IForwarder { /// @notice chain id uint32 public chainSlug; @@ -23,7 +21,11 @@ contract Forwarder is IForwarder, Initializable { /// @notice caches the latest async promise address for the last call address public latestAsyncPromise; +} +/// @title Forwarder Contract +/// @notice This contract acts as a forwarder for async calls to the on-chain contracts. +contract Forwarder is ForwarderStorage, Initializable { error AsyncModifierNotUsed(); constructor() { diff --git a/contracts/protocol/payload-delivery/app-gateway/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol similarity index 85% rename from contracts/protocol/payload-delivery/app-gateway/AuctionManager.sol rename to contracts/protocol/payload-delivery/AuctionManager.sol index f5933c02..bb86ba37 100644 --- a/contracts/protocol/payload-delivery/app-gateway/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -1,18 +1,19 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {Ownable} from "solady/auth/Ownable.sol"; import {ECDSA} from "solady/utils/ECDSA.sol"; -import {AddressResolverUtil} from "../../utils/AddressResolverUtil.sol"; -import {Fees, Bid, PayloadBatch} from "../../utils/common/Structs.sol"; -import {IDeliveryHelper} from "../../../interfaces/IDeliveryHelper.sol"; -import {IFeesManager} from "../../../interfaces/IFeesManager.sol"; -import {IAuctionManager} from "../../../interfaces/IAuctionManager.sol"; import "solady/utils/Initializable.sol"; +import {Ownable} from "solady/auth/Ownable.sol"; -/// @title AuctionManager -/// @notice Contract for managing auctions and placing bids -contract AuctionManager is AddressResolverUtil, Ownable, IAuctionManager, Initializable { +import {IDeliveryHelper} from "../../interfaces/IDeliveryHelper.sol"; +import {IFeesManager} from "../../interfaces/IFeesManager.sol"; +import {IAuctionManager} from "../../interfaces/IAuctionManager.sol"; + +import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; +import {Fees, Bid, PayloadBatch} from "../utils/common/Structs.sol"; +import {AuctionClosed, AuctionAlreadyStarted, BidExceedsMaxFees, LowerBidAlreadyExists, InvalidTransmitter} from "../utils/common/Errors.sol"; + +abstract contract AuctionManagerStorage is IAuctionManager { uint32 public evmxChainSlug; mapping(bytes32 => Bid) public winningBids; // asyncId => auction status @@ -20,19 +21,15 @@ contract AuctionManager is AddressResolverUtil, Ownable, IAuctionManager, Initia mapping(bytes32 => bool) public override auctionStarted; uint256 public auctionEndDelaySeconds; +} - /// @notice Error thrown when trying to start or bid a closed auction - error AuctionClosed(); - /// @notice Error thrown when trying to start an ongoing auction - error AuctionAlreadyStarted(); - /// @notice Error thrown if fees exceed the maximum set fees - error BidExceedsMaxFees(); - /// @notice Error thrown if winning bid is assigned to an invalid transmitter - error InvalidTransmitter(); - /// @notice Error thrown if a lower bid already exists - error LowerBidAlreadyExists(); - +/// @title AuctionManager +/// @notice Contract for managing auctions and placing bids +contract AuctionManager is AuctionManagerStorage, AddressResolverUtil, Ownable, Initializable { event AuctionRestarted(bytes32 asyncId); + event AuctionStarted(bytes32 asyncId); + event AuctionEnded(bytes32 asyncId, Bid winningBid); + event BidPlaced(bytes32 asyncId, Bid bid); constructor() { _disableInitializers(); // disable for implementation @@ -55,10 +52,6 @@ contract AuctionManager is AddressResolverUtil, Ownable, IAuctionManager, Initia auctionEndDelaySeconds = auctionEndDelaySeconds_; } - event AuctionStarted(bytes32 asyncId); - event AuctionEnded(bytes32 asyncId, Bid winningBid); - event BidPlaced(bytes32 asyncId, Bid bid); - function setAuctionEndDelaySeconds(uint256 auctionEndDelaySeconds_) external onlyOwner { auctionEndDelaySeconds = auctionEndDelaySeconds_; } diff --git a/contracts/protocol/payload-delivery/app-gateway/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol similarity index 95% rename from contracts/protocol/payload-delivery/app-gateway/FeesManager.sol rename to contracts/protocol/payload-delivery/FeesManager.sol index 8c6873de..ec364b4f 100644 --- a/contracts/protocol/payload-delivery/app-gateway/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -4,20 +4,17 @@ pragma solidity ^0.8.0; import {Ownable} from "solady/auth/Ownable.sol"; import "solady/utils/Initializable.sol"; -import {AddressResolverUtil} from "../../../protocol/utils/AddressResolverUtil.sol"; -import {Bid, Fees, PayloadDetails, CallType, FinalizeParams, PayloadBatch, Parallel} from "../../../protocol/utils/common/Structs.sol"; -import {IDeliveryHelper} from "../../../interfaces/IDeliveryHelper.sol"; -import {FORWARD_CALL, DISTRIBUTE_FEE, DEPLOY, WITHDRAW} from "../../../protocol/utils/common/Constants.sol"; -import {IFeesPlug} from "../../../interfaces/IFeesPlug.sol"; -import {IFeesManager} from "../../../interfaces/IFeesManager.sol"; +import {IDeliveryHelper} from "../../interfaces/IDeliveryHelper.sol"; +import {IFeesPlug} from "../../interfaces/IFeesPlug.sol"; +import {IFeesManager} from "../../interfaces/IFeesManager.sol"; -import {NotAuctionManager} from "../../../protocol/utils/common/Errors.sol"; +import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; +import {FORWARD_CALL, DISTRIBUTE_FEE, DEPLOY, WITHDRAW} from "../utils/common/Constants.sol"; +import {NotAuctionManager} from "../utils/common/Errors.sol"; +import {Bid, Fees, PayloadDetails, CallType, FinalizeParams, Parallel} from "../utils/common/Structs.sol"; -/// @title FeesManager -/// @notice Contract for managing fees -contract FeesManager is IFeesManager, AddressResolverUtil, Ownable, Initializable { +contract FeesManagerStorage { uint256 public feesCounter; - /// @notice Struct containing fee amounts and status struct TokenBalance { uint256 deposited; // Amount deposited @@ -36,7 +33,17 @@ contract FeesManager is IFeesManager, AddressResolverUtil, Ownable, Initializabl /// @notice Mapping to track fees to be distributed to transmitters /// @dev transmitter => chainSlug => token => amount mapping(address => mapping(uint32 => mapping(address => uint256))) public transmitterFees; +} +/// @title FeesManager +/// @notice Contract for managing fees +contract FeesManager is + IFeesManager, + FeesManagerStorage, + AddressResolverUtil, + Ownable, + Initializable +{ /// @notice Emitted when fees are blocked for a batch /// @param asyncId The batch identifier /// @param chainSlug The chain identifier diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol index e0a85af0..4eace8ca 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol @@ -11,9 +11,6 @@ import "./BatchAsync.sol"; contract DeliveryHelper is BatchAsync, Initializable { event CallBackReverted(bytes32 asyncId_, bytes32 payloadId_); - uint64 public version; - - bytes32[] public tempPayloadIds; constructor() { _disableInitializers(); // disable for implementation @@ -28,7 +25,6 @@ contract DeliveryHelper is BatchAsync, Initializable { uint256 bidTimeout_ ) public reinitializer(1) { _setAddressResolver(addressResolver_); - version = 1; bidTimeout = bidTimeout_; _initializeOwner(owner_); } @@ -44,7 +40,7 @@ contract DeliveryHelper is BatchAsync, Initializable { if (!isRestarted) return _process(asyncId_, false); - // Refinalize all payloads in the batch if a new transmitter is assigned + // Re-finalize all payloads in the batch if a new transmitter is assigned bytes32[] memory payloadIds = _payloadBatches[asyncId_].lastBatchOfPayloads; for (uint256 i = 0; i < payloadIds.length; i++) { watcherPrecompile__().refinalize( diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol new file mode 100644 index 00000000..958b2d82 --- /dev/null +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.21; + +import {CallParams, PayloadDetails, PayloadBatch} from "../../../protocol/utils/common/Structs.sol"; +import {IDeliveryHelper} from "../../../interfaces/IDeliveryHelper.sol"; + +/// @title DeliveryHelperStorage +/// @notice Storage contract for DeliveryHelper +abstract contract DeliveryHelperStorage is IDeliveryHelper { + uint256 public saltCounter; + uint256 public asyncCounter; + uint256 public bidTimeout; + + /// @notice The call parameters array + CallParams[] public callParamsArray; + + bytes32[] public tempPayloadIds; + + /// @notice The mapping of valid promises + mapping(address => bool) public isValidPromise; + // payloadId => asyncId + mapping(bytes32 => bytes32) public payloadIdToBatchHash; + mapping(bytes32 => PayloadDetails) public payloadIdToPayloadDetails; + // asyncId => PayloadDetails[] + mapping(bytes32 => PayloadDetails[]) public payloadBatchDetails; + // asyncId => PayloadBatch + mapping(bytes32 => PayloadBatch) internal _payloadBatches; +} diff --git a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol index 85f13c65..28586381 100644 --- a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol @@ -9,42 +9,18 @@ import {IPromise} from "../../../interfaces/IPromise.sol"; import {IAppDeployer} from "../../../interfaces/IAppDeployer.sol"; import {IAddressResolver} from "../../../interfaces/IAddressResolver.sol"; import {IContractFactoryPlug} from "../../../interfaces/IContractFactoryPlug.sol"; -import {IDeliveryHelper} from "../../../interfaces/IDeliveryHelper.sol"; import {Ownable} from "solady/auth/Ownable.sol"; +import {DeliveryHelperStorage} from "./DeliveryHelperStorage.sol"; /// @notice Abstract contract for managing asynchronous payloads -abstract contract QueueAsync is AddressResolverUtil, IDeliveryHelper, Ownable { - uint256 public saltCounter; - uint256 public asyncCounter; - - uint256 public bidTimeout; - - /// @notice The call parameters array - CallParams[] public callParamsArray; - /// @notice The mapping of valid promises - mapping(address => bool) public isValidPromise; - - // payloadId => asyncId - mapping(bytes32 => bytes32) public payloadIdToBatchHash; - mapping(bytes32 => PayloadDetails) public payloadIdToPayloadDetails; - - // asyncId => PayloadBatch - mapping(bytes32 => PayloadBatch) internal _payloadBatches; - +abstract contract QueueAsync is + DeliveryHelperStorage, + AddressResolverUtil, + Ownable +{ event PayloadBatchCancelled(bytes32 asyncId); event BidTimeoutUpdated(uint256 newBidTimeout); - function payloadBatches(bytes32 asyncId_) external view override returns (PayloadBatch memory) { - return _payloadBatches[asyncId_]; - } - - function getPayloadDetails(bytes32 payloadId_) external view returns (PayloadDetails memory) { - return payloadIdToPayloadDetails[payloadId_]; - } - - // asyncId => PayloadDetails[] - mapping(bytes32 => PayloadDetails[]) public payloadBatchDetails; - modifier onlyPromises() { if (!isValidPromise[msg.sender]) revert InvalidPromise(); _; @@ -55,6 +31,14 @@ abstract contract QueueAsync is AddressResolverUtil, IDeliveryHelper, Ownable { _; } + function payloadBatches(bytes32 asyncId_) external view override returns (PayloadBatch memory) { + return _payloadBatches[asyncId_]; + } + + function getPayloadDetails(bytes32 payloadId_) external view returns (PayloadDetails memory) { + return payloadIdToPayloadDetails[payloadId_]; + } + /// @notice Clears the call parameters array function clearQueue() public { delete callParamsArray; diff --git a/contracts/protocol/utils/common/Errors.sol b/contracts/protocol/utils/common/Errors.sol index f129a2a2..9a1fe4ca 100644 --- a/contracts/protocol/utils/common/Errors.sol +++ b/contracts/protocol/utils/common/Errors.sol @@ -31,3 +31,11 @@ error FeesNotSet(); error InvalidTokenAddress(); error InvalidWatcherSignature(); error NonceUsed(); +/// @notice Error thrown when trying to start or bid a closed auction +error AuctionClosed(); +/// @notice Error thrown when trying to start an ongoing auction +error AuctionAlreadyStarted(); +/// @notice Error thrown if fees exceed the maximum set fees +error BidExceedsMaxFees(); +/// @notice Error thrown if a lower bid already exists +error LowerBidAlreadyExists(); diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index 38be81ab..eaecfbd6 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -6,31 +6,10 @@ import "../../interfaces/IAppGateway.sol"; import "../../interfaces/IPromise.sol"; import "../../interfaces/IFeesManager.sol"; import "solady/utils/Initializable.sol"; -import {PayloadDigestParams, AsyncRequest, FinalizeParams, TimeoutRequest, CallFromChainParams} from "../utils/common/Structs.sol"; /// @title WatcherPrecompile /// @notice Contract that handles payload verification, execution and app configurations contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { - uint256 public maxTimeoutDelayInSeconds; - /// @notice Counter for tracking payload requests - uint256 public payloadCounter; - /// @notice The expiry time for the payload - uint256 public expiryTime; - - /// @notice Mapping to store async requests - /// @dev payloadId => AsyncRequest struct - mapping(bytes32 => AsyncRequest) public asyncRequests; - /// @notice Mapping to store timeout requests - /// @dev timeoutId => TimeoutRequest struct - mapping(bytes32 => TimeoutRequest) public timeoutRequests; - /// @notice Mapping to store watcher proofs - /// @dev payloadId => proof bytes - mapping(bytes32 => bytes) public watcherProofs; - - /// @notice Mapping to store if appGateway has been called with trigger from on-chain Inbox - /// @dev callId => bool - mapping(bytes32 => bool) public appGatewayCalled; - /// @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/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index f5940af3..a8bba140 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -8,33 +8,6 @@ import {ECDSA} from "solady/utils/ECDSA.sol"; /// @notice Configuration contract for the Watcher Precompile system /// @dev Handles the mapping between networks, plugs, and app gateways for payload execution abstract contract WatcherPrecompileConfig is WatcherPrecompileLimits { - /// @notice Maps network and plug to their configuration - /// @dev chainSlug => plug => PlugConfig - mapping(uint32 => mapping(address => PlugConfig)) internal _plugConfigs; - - /// @notice Maps chain slug to their associated switchboard - /// @dev chainSlug => sb type => switchboard address - mapping(uint32 => mapping(bytes32 => address)) public switchboards; - - /// @notice Maps chain slug to their associated socket - /// @dev chainSlug => socket address - mapping(uint32 => address) public sockets; - - /// @notice Maps chain slug to their associated contract factory plug - /// @dev chainSlug => contract factory plug address - mapping(uint32 => address) public contractFactoryPlug; - - /// @notice Maps chain slug to their associated fees plug - /// @dev chainSlug => fees plug address - mapping(uint32 => address) public feesPlug; - - /// @notice Maps nonce to whether it has been used - /// @dev signatureNonce => isValid - mapping(uint256 => bool) public isNonceUsed; - - // appGateway => chainSlug => plug => isValid - 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 chainSlug The identifier of the destination network diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index cc6d44ab..ac07d848 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -3,36 +3,18 @@ pragma solidity ^0.8.21; import {AccessControl} from "../utils/AccessControl.sol"; import {Gauge} from "../utils/Gauge.sol"; -import {LimitParams, UpdateLimitParams} from "../utils/common/Structs.sol"; import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; import {QUERY, FINALIZE, SCHEDULE} from "../utils/common/Constants.sol"; -import "../../interfaces/IWatcherPrecompile.sol"; import {WATCHER_ROLE} from "../utils/common/AccessRoles.sol"; import {TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidInboxCaller, ResolvingTimeoutTooEarly, CallFailed, AppGatewayAlreadyCalled, InvalidWatcherSignature, NonceUsed} from "../utils/common/Errors.sol"; +import "./WatcherPrecompileStorage.sol"; abstract contract WatcherPrecompileLimits is + WatcherPrecompileStorage, Gauge, AddressResolverUtil, - AccessControl, - IWatcherPrecompile + AccessControl { - /// @notice Number of decimals used in limit calculations - uint256 public constant LIMIT_DECIMALS = 18; - - /// @notice Default limit value for any app gateway - uint256 public defaultLimit; - /// @notice Rate at which limit replenishes per second - uint256 public defaultRatePerSecond; - - /// @notice The chain slug of the watcher precompile - uint32 public evmxChainSlug; - - // appGateway => limitType => receivingLimitParams - mapping(address => mapping(bytes32 => LimitParams)) internal _limitParams; - - // Mapping to track active app gateways - mapping(address => bool) private _activeAppGateways; - //////////////////////////////////////////////////////// ////////////////////// EVENTS ////////////////////////// //////////////////////////////////////////////////////// diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol new file mode 100644 index 00000000..44b0839e --- /dev/null +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {IWatcherPrecompile} from "../../interfaces/IWatcherPrecompile.sol"; +import {ResolvedPromises, AppGatewayConfig, LimitParams, UpdateLimitParams, PlugConfig, PayloadDigestParams, AsyncRequest, FinalizeParams, TimeoutRequest, CallFromChainParams} from "../utils/common/Structs.sol"; + +abstract contract WatcherPrecompileStorage is IWatcherPrecompile { + /// @notice Number of decimals used in limit calculations + uint256 public constant LIMIT_DECIMALS = 18; + + /// @notice Default limit value for any app gateway + uint256 public defaultLimit; + /// @notice Rate at which limit replenishes per second + uint256 public defaultRatePerSecond; + + /// @notice The chain slug of the watcher precompile + uint32 public evmxChainSlug; + + // appGateway => limitType => receivingLimitParams + mapping(address => mapping(bytes32 => LimitParams)) internal _limitParams; + + // Mapping to track active app gateways + mapping(address => bool) internal _activeAppGateways; + + /// @notice Maps network and plug to their configuration + /// @dev chainSlug => plug => PlugConfig + mapping(uint32 => mapping(address => PlugConfig)) internal _plugConfigs; + + /// @notice Maps chain slug to their associated switchboard + /// @dev chainSlug => sb type => switchboard address + mapping(uint32 => mapping(bytes32 => address)) public switchboards; + + /// @notice Maps chain slug to their associated socket + /// @dev chainSlug => socket address + mapping(uint32 => address) public sockets; + + /// @notice Maps chain slug to their associated contract factory plug + /// @dev chainSlug => contract factory plug address + mapping(uint32 => address) public contractFactoryPlug; + + /// @notice Maps chain slug to their associated fees plug + /// @dev chainSlug => fees plug address + mapping(uint32 => address) public feesPlug; + + /// @notice Maps nonce to whether it has been used + /// @dev signatureNonce => isValid + mapping(uint256 => bool) public isNonceUsed; + + // appGateway => chainSlug => plug => isValid + mapping(address => mapping(uint32 => mapping(address => bool))) public isValidPlug; + + uint256 public maxTimeoutDelayInSeconds; + /// @notice Counter for tracking payload requests + uint256 public payloadCounter; + /// @notice The expiry time for the payload + uint256 public expiryTime; + + /// @notice Mapping to store async requests + /// @dev payloadId => AsyncRequest struct + mapping(bytes32 => AsyncRequest) public asyncRequests; + /// @notice Mapping to store timeout requests + /// @dev timeoutId => TimeoutRequest struct + mapping(bytes32 => TimeoutRequest) public timeoutRequests; + /// @notice Mapping to store watcher proofs + /// @dev payloadId => proof bytes + mapping(bytes32 => bytes) public watcherProofs; + + /// @notice Mapping to store if appGateway has been called with trigger from on-chain Inbox + /// @dev callId => bool + mapping(bytes32 => bool) public appGatewayCalled; +} diff --git a/script/AppGatewayFeeBalance.s.sol b/script/AppGatewayFeeBalance.s.sol index 70934724..cb773904 100644 --- a/script/AppGatewayFeeBalance.s.sol +++ b/script/AppGatewayFeeBalance.s.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; -import {FeesManager} from "../contracts/protocol/payload-delivery/app-gateway/FeesManager.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"; diff --git a/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol b/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol index 098f58bc..a7dbc9f1 100644 --- a/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol +++ b/script/counter/WithdrawFeesArbitrumFeesPlug.s.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import {Script} from "forge-std/Script.sol"; import {console} from "forge-std/console.sol"; -import {FeesManager} from "../../contracts/protocol/payload-delivery/app-gateway/FeesManager.sol"; +import {FeesManager} from "../../contracts/protocol/payload-delivery/FeesManager.sol"; import {ETH_ADDRESS} from "../../contracts/protocol/utils/common/Constants.sol"; import {CounterAppGateway} from "../../contracts/apps/counter/CounterAppGateway.sol"; diff --git a/test/DeliveryHelper.t.sol b/test/DeliveryHelper.t.sol index cb9e0161..2d6f923c 100644 --- a/test/DeliveryHelper.t.sol +++ b/test/DeliveryHelper.t.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.0; import "../contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol"; -import "../contracts/protocol/payload-delivery/app-gateway/FeesManager.sol"; -import "../contracts/protocol/payload-delivery/app-gateway/AuctionManager.sol"; +import "../contracts/protocol/payload-delivery/FeesManager.sol"; +import "../contracts/protocol/payload-delivery/AuctionManager.sol"; import "../contracts/protocol/Forwarder.sol"; import "../contracts/interfaces/IAppDeployer.sol"; From 4b910292d6880e0aeb0312b2332022a1bd7323d4 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 25 Feb 2025 19:46:36 +0400 Subject: [PATCH 02/12] fix: remove extra imports and rearrange --- contracts/protocol/Forwarder.sol | 1 - .../protocol/payload-delivery/FeesManager.sol | 3 +-- .../app-gateway/BatchAsync.sol | 9 --------- .../app-gateway/DeliveryHelper.sol | 5 ----- .../app-gateway/DeliveryHelperStorage.sol | 13 ++++++++++++- .../app-gateway/QueueAsync.sol | 19 +++++-------------- .../watcherPrecompile/WatcherPrecompile.sol | 3 --- .../WatcherPrecompileLimits.sol | 2 -- .../WatcherPrecompileStorage.sol | 6 ++++++ 9 files changed, 24 insertions(+), 37 deletions(-) diff --git a/contracts/protocol/Forwarder.sol b/contracts/protocol/Forwarder.sol index 22a4b95f..14ae74f4 100644 --- a/contracts/protocol/Forwarder.sol +++ b/contracts/protocol/Forwarder.sol @@ -5,7 +5,6 @@ import "../interfaces/IAddressResolver.sol"; import "../interfaces/IDeliveryHelper.sol"; import "../interfaces/IAppGateway.sol"; import "../interfaces/IPromise.sol"; -import "./AsyncPromise.sol"; import "../interfaces/IForwarder.sol"; import "solady/utils/Initializable.sol"; diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index ec364b4f..310d496f 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -4,12 +4,11 @@ pragma solidity ^0.8.0; import {Ownable} from "solady/auth/Ownable.sol"; import "solady/utils/Initializable.sol"; -import {IDeliveryHelper} from "../../interfaces/IDeliveryHelper.sol"; import {IFeesPlug} from "../../interfaces/IFeesPlug.sol"; import {IFeesManager} from "../../interfaces/IFeesManager.sol"; import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; -import {FORWARD_CALL, DISTRIBUTE_FEE, DEPLOY, WITHDRAW} from "../utils/common/Constants.sol"; +import {WITHDRAW} from "../utils/common/Constants.sol"; import {NotAuctionManager} from "../utils/common/Errors.sol"; import {Bid, Fees, PayloadDetails, CallType, FinalizeParams, Parallel} from "../utils/common/Structs.sol"; diff --git a/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol b/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol index db2cf1e4..fa6cd3a4 100644 --- a/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol @@ -3,15 +3,6 @@ pragma solidity ^0.8.21; import "./QueueAsync.sol"; -import {IDeliveryHelper} from "../../../interfaces/IDeliveryHelper.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 {Bid, PayloadBatch, Fees, PayloadDetails} from "../../../protocol/utils/common/Structs.sol"; -import {FORWARD_CALL, DISTRIBUTE_FEE, DEPLOY, WITHDRAW, QUERY, FINALIZE} from "../../../protocol/utils/common/Constants.sol"; - /// @title BatchAsync /// @notice Abstract contract for managing asynchronous payload batches abstract contract BatchAsync is QueueAsync { diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol index 4eace8ca..3df8d2e7 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol @@ -2,11 +2,6 @@ pragma solidity ^0.8.0; import "solady/utils/Initializable.sol"; - -import {IAppGateway} from "../../../interfaces/IAppGateway.sol"; -import {Bid, PayloadBatch, Fees, PayloadDetails, FinalizeParams} from "../../../protocol/utils/common/Structs.sol"; -import {DISTRIBUTE_FEE, DEPLOY} from "../../../protocol/utils/common/Constants.sol"; -import {PromisesNotResolved, InvalidTransmitter} from "../../../protocol/utils/common/Errors.sol"; import "./BatchAsync.sol"; contract DeliveryHelper is BatchAsync, Initializable { diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol index 958b2d82..58755ec9 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol @@ -1,8 +1,19 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.21; -import {CallParams, PayloadDetails, PayloadBatch} from "../../../protocol/utils/common/Structs.sol"; import {IDeliveryHelper} from "../../../interfaces/IDeliveryHelper.sol"; +import {IPromise} from "../../../interfaces/IPromise.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 {CallParams, Fees, PayloadDetails, CallType, Bid, PayloadBatch, Parallel, IsPlug, FinalizeParams} from "../../utils/common/Structs.sol"; +import {NotAuctionManager, InvalidPromise, InvalidIndex, PromisesNotResolved, InvalidTransmitter} from "../../utils/common/Errors.sol"; +import {FORWARD_CALL, DISTRIBUTE_FEE, DEPLOY, WITHDRAW, QUERY, FINALIZE} from "../../utils/common/Constants.sol"; /// @title DeliveryHelperStorage /// @notice Storage contract for DeliveryHelper diff --git a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol index 28586381..1d12c82b 100644 --- a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol @@ -1,23 +1,14 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.21; -import {AddressResolverUtil} from "../../../protocol/utils/AddressResolverUtil.sol"; -import {CallParams, Fees, PayloadDetails, CallType, Bid, PayloadBatch, Parallel, IsPlug} from "../../../protocol/utils/common/Structs.sol"; -import {NotAuctionManager, InvalidPromise, InvalidIndex} from "../../../protocol/utils/common/Errors.sol"; -import {AsyncPromise} from "../../AsyncPromise.sol"; -import {IPromise} from "../../../interfaces/IPromise.sol"; -import {IAppDeployer} from "../../../interfaces/IAppDeployer.sol"; -import {IAddressResolver} from "../../../interfaces/IAddressResolver.sol"; -import {IContractFactoryPlug} from "../../../interfaces/IContractFactoryPlug.sol"; import {Ownable} from "solady/auth/Ownable.sol"; -import {DeliveryHelperStorage} from "./DeliveryHelperStorage.sol"; + +import {AddressResolverUtil} from "../../utils/AddressResolverUtil.sol"; + +import "./DeliveryHelperStorage.sol"; /// @notice Abstract contract for managing asynchronous payloads -abstract contract QueueAsync is - DeliveryHelperStorage, - AddressResolverUtil, - Ownable -{ +abstract contract QueueAsync is DeliveryHelperStorage, AddressResolverUtil, Ownable { event PayloadBatchCancelled(bytes32 asyncId); event BidTimeoutUpdated(uint256 newBidTimeout); diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index eaecfbd6..6d5c2aab 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -2,9 +2,6 @@ pragma solidity ^0.8.21; import "./WatcherPrecompileConfig.sol"; -import "../../interfaces/IAppGateway.sol"; -import "../../interfaces/IPromise.sol"; -import "../../interfaces/IFeesManager.sol"; import "solady/utils/Initializable.sol"; /// @title WatcherPrecompile diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index ac07d848..05fc7565 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -4,9 +4,7 @@ pragma solidity ^0.8.21; import {AccessControl} from "../utils/AccessControl.sol"; import {Gauge} from "../utils/Gauge.sol"; import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; -import {QUERY, FINALIZE, SCHEDULE} from "../utils/common/Constants.sol"; import {WATCHER_ROLE} from "../utils/common/AccessRoles.sol"; -import {TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidInboxCaller, ResolvingTimeoutTooEarly, CallFailed, AppGatewayAlreadyCalled, InvalidWatcherSignature, NonceUsed} from "../utils/common/Errors.sol"; import "./WatcherPrecompileStorage.sol"; abstract contract WatcherPrecompileLimits is diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol index 44b0839e..c530e489 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol @@ -2,6 +2,12 @@ pragma solidity ^0.8.0; import {IWatcherPrecompile} from "../../interfaces/IWatcherPrecompile.sol"; +import {IAppGateway} from "../../interfaces/IAppGateway.sol"; +import {IFeesManager} from "../../interfaces/IFeesManager.sol"; +import {IPromise} from "../../interfaces/IPromise.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, UpdateLimitParams, PlugConfig, PayloadDigestParams, AsyncRequest, FinalizeParams, TimeoutRequest, CallFromChainParams} from "../utils/common/Structs.sol"; abstract contract WatcherPrecompileStorage is IWatcherPrecompile { From 5468244713035960240f37cd4321114f681382d4 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 25 Feb 2025 19:58:53 +0400 Subject: [PATCH 03/12] feat: add gaps --- README.md | 2 ++ contracts/protocol/AddressResolver.sol | 5 ++++- contracts/protocol/AsyncPromise.sol | 3 +++ contracts/protocol/Forwarder.sol | 4 ++++ .../protocol/payload-delivery/AuctionManager.sol | 6 +++++- .../protocol/payload-delivery/FeesManager.sol | 14 ++++++-------- .../app-gateway/DeliveryHelperStorage.sol | 4 ++++ .../payload-delivery/app-gateway/QueueAsync.sol | 4 +--- contracts/protocol/utils/AddressResolverUtil.sol | 2 +- .../watcherPrecompile/WatcherPrecompileStorage.sol | 4 ++++ 10 files changed, 34 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index dd8337b0..7807e036 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,5 @@ Find more information at [docs](https://docs.socket.tech) - update version after every change - never remove code - inherited contracts should have gaps at the end to avoid storage collision +- write tests for migration checking slots after the change +- diff --git a/contracts/protocol/AddressResolver.sol b/contracts/protocol/AddressResolver.sol index 6f804a0d..64c5bb21 100644 --- a/contracts/protocol/AddressResolver.sol +++ b/contracts/protocol/AddressResolver.sol @@ -10,6 +10,7 @@ import {Forwarder} from "./Forwarder.sol"; import {AsyncPromise} from "./AsyncPromise.sol"; abstract contract AddressResolverStorage is IAddressResolver { + uint256[50] _gap_before; IWatcherPrecompile public override watcherPrecompile__; address public override deliveryHelper; address public override feesManager; @@ -31,12 +32,14 @@ abstract contract AddressResolverStorage is IAddressResolver { mapping(address => address) public override contractsToGateways; // gateway to contract map mapping(address => address) public override gatewaysToContracts; + + uint256[50] _gap_after; } /// @title AddressResolver Contract /// @notice This contract is responsible for fetching latest core addresses and deploying Forwarder and AsyncPromise contracts. /// @dev Inherits the Ownable contract and implements the IAddressResolver interface. -contract AddressResolver is AddressResolverStorage, Ownable, Initializable { +contract AddressResolver is AddressResolverStorage, Initializable, Ownable { /// @notice Error thrown if AppGateway contract was already set by a different address error InvalidCaller(address contractAddress_); diff --git a/contracts/protocol/AsyncPromise.sol b/contracts/protocol/AsyncPromise.sol index b8d4b8f6..268f27cd 100644 --- a/contracts/protocol/AsyncPromise.sol +++ b/contracts/protocol/AsyncPromise.sol @@ -16,6 +16,7 @@ enum AsyncPromiseState { } abstract contract AsyncPromiseStorage is IPromise { + uint256[50] _gap_before; /// @notice The callback selector to be called on the invoker. bytes4 public callbackSelector; @@ -34,6 +35,8 @@ abstract contract AsyncPromiseStorage is IPromise { /// @notice The callback data to be used when the promise is resolved. bytes public callbackData; + + uint256[50] _gap_after; } /// @title AsyncPromise diff --git a/contracts/protocol/Forwarder.sol b/contracts/protocol/Forwarder.sol index 14ae74f4..9cfb4d03 100644 --- a/contracts/protocol/Forwarder.sol +++ b/contracts/protocol/Forwarder.sol @@ -9,6 +9,8 @@ import "../interfaces/IForwarder.sol"; import "solady/utils/Initializable.sol"; abstract contract ForwarderStorage is IForwarder { + uint256[50] _gap_before; + /// @notice chain id uint32 public chainSlug; @@ -20,6 +22,8 @@ abstract contract ForwarderStorage is IForwarder { /// @notice caches the latest async promise address for the last call address public latestAsyncPromise; + + uint256[50] _gap_after; } /// @title Forwarder Contract diff --git a/contracts/protocol/payload-delivery/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol index bb86ba37..c24054ce 100644 --- a/contracts/protocol/payload-delivery/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -14,6 +14,8 @@ import {Fees, Bid, PayloadBatch} from "../utils/common/Structs.sol"; import {AuctionClosed, AuctionAlreadyStarted, BidExceedsMaxFees, LowerBidAlreadyExists, InvalidTransmitter} from "../utils/common/Errors.sol"; abstract contract AuctionManagerStorage is IAuctionManager { + uint256[50] _gap_before; + uint32 public evmxChainSlug; mapping(bytes32 => Bid) public winningBids; // asyncId => auction status @@ -21,11 +23,13 @@ abstract contract AuctionManagerStorage is IAuctionManager { mapping(bytes32 => bool) public override auctionStarted; uint256 public auctionEndDelaySeconds; + + uint256[50] _gap_after; } /// @title AuctionManager /// @notice Contract for managing auctions and placing bids -contract AuctionManager is AuctionManagerStorage, AddressResolverUtil, Ownable, Initializable { +contract AuctionManager is AuctionManagerStorage, Initializable, Ownable, AddressResolverUtil { event AuctionRestarted(bytes32 asyncId); event AuctionStarted(bytes32 asyncId); event AuctionEnded(bytes32 asyncId, Bid winningBid); diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index 310d496f..df482fb5 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -12,7 +12,9 @@ import {WITHDRAW} from "../utils/common/Constants.sol"; import {NotAuctionManager} from "../utils/common/Errors.sol"; import {Bid, Fees, PayloadDetails, CallType, FinalizeParams, Parallel} from "../utils/common/Structs.sol"; -contract FeesManagerStorage { +abstract contract FeesManagerStorage is IFeesManager { + uint256[50] _gap_before; + uint256 public feesCounter; /// @notice Struct containing fee amounts and status struct TokenBalance { @@ -32,17 +34,13 @@ contract FeesManagerStorage { /// @notice Mapping to track fees to be distributed to transmitters /// @dev transmitter => chainSlug => token => amount mapping(address => mapping(uint32 => mapping(address => uint256))) public transmitterFees; + + uint256[50] _gap_after; } /// @title FeesManager /// @notice Contract for managing fees -contract FeesManager is - IFeesManager, - FeesManagerStorage, - AddressResolverUtil, - Ownable, - Initializable -{ +contract FeesManager is FeesManagerStorage, Initializable, Ownable, AddressResolverUtil { /// @notice Emitted when fees are blocked for a batch /// @param asyncId The batch identifier /// @param chainSlug The chain identifier diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol index 58755ec9..49f1966b 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol @@ -18,6 +18,8 @@ import {FORWARD_CALL, DISTRIBUTE_FEE, DEPLOY, WITHDRAW, QUERY, FINALIZE} from ". /// @title DeliveryHelperStorage /// @notice Storage contract for DeliveryHelper abstract contract DeliveryHelperStorage is IDeliveryHelper { + uint256[50] _gap_before; + uint256 public saltCounter; uint256 public asyncCounter; uint256 public bidTimeout; @@ -36,4 +38,6 @@ abstract contract DeliveryHelperStorage is IDeliveryHelper { mapping(bytes32 => PayloadDetails[]) public payloadBatchDetails; // asyncId => PayloadBatch mapping(bytes32 => PayloadBatch) internal _payloadBatches; + + uint256[50] _gap_after; } diff --git a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol index 1d12c82b..c4a31bf9 100644 --- a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol @@ -8,7 +8,7 @@ import {AddressResolverUtil} from "../../utils/AddressResolverUtil.sol"; import "./DeliveryHelperStorage.sol"; /// @notice Abstract contract for managing asynchronous payloads -abstract contract QueueAsync is DeliveryHelperStorage, AddressResolverUtil, Ownable { +abstract contract QueueAsync is DeliveryHelperStorage, Ownable, AddressResolverUtil { event PayloadBatchCancelled(bytes32 asyncId); event BidTimeoutUpdated(uint256 newBidTimeout); @@ -159,6 +159,4 @@ abstract contract QueueAsync is DeliveryHelperStorage, AddressResolverUtil, Owna function getAsyncBatchDetails(bytes32 asyncId_) external view returns (PayloadBatch memory) { return _payloadBatches[asyncId_]; } - - uint256[49] __gap; } diff --git a/contracts/protocol/utils/AddressResolverUtil.sol b/contracts/protocol/utils/AddressResolverUtil.sol index a0dcd789..ad85e214 100644 --- a/contracts/protocol/utils/AddressResolverUtil.sol +++ b/contracts/protocol/utils/AddressResolverUtil.sol @@ -67,5 +67,5 @@ abstract contract AddressResolverUtil { } // for proxy contracts - uint256[49] __gap_resolver_util; + uint256[50] __gap_resolver_util; } diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol index c530e489..0a073602 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol @@ -11,6 +11,8 @@ import {TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidInboxCaller, Resolv import {ResolvedPromises, AppGatewayConfig, LimitParams, UpdateLimitParams, PlugConfig, PayloadDigestParams, AsyncRequest, FinalizeParams, TimeoutRequest, CallFromChainParams} from "../utils/common/Structs.sol"; abstract contract WatcherPrecompileStorage is IWatcherPrecompile { + uint256[50] _gap_before; + /// @notice Number of decimals used in limit calculations uint256 public constant LIMIT_DECIMALS = 18; @@ -74,4 +76,6 @@ abstract contract WatcherPrecompileStorage is IWatcherPrecompile { /// @notice Mapping to store if appGateway has been called with trigger from on-chain Inbox /// @dev callId => bool mapping(bytes32 => bool) public appGatewayCalled; + + uint256[50] _gap_after; } From fdf3538361ff84ce0b9397a1bfef388e40a6159f Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 25 Feb 2025 20:07:20 +0400 Subject: [PATCH 04/12] fix: gaps in inherited contracts --- .../protocol/payload-delivery/app-gateway/BatchAsync.sol | 2 ++ .../payload-delivery/app-gateway/DeliveryHelper.sol | 3 +-- .../protocol/payload-delivery/app-gateway/QueueAsync.sol | 5 ++++- contracts/protocol/utils/AccessControl.sol | 2 ++ contracts/protocol/utils/Gauge.sol | 2 ++ .../protocol/watcherPrecompile/WatcherPrecompile.sol | 3 +-- .../watcherPrecompile/WatcherPrecompileConfig.sol | 2 +- .../watcherPrecompile/WatcherPrecompileLimits.sol | 8 +++++--- 8 files changed, 18 insertions(+), 9 deletions(-) diff --git a/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol b/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol index fa6cd3a4..a5eda5a1 100644 --- a/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol @@ -310,4 +310,6 @@ abstract contract BatchAsync is QueueAsync { ); _deliverPayload(payloadDetailsArray, fees_, auctionManager_, new bytes(0)); } + + uint256[50] _gap_batch_async; } diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol index 3df8d2e7..e7796e8b 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol @@ -1,10 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "solady/utils/Initializable.sol"; import "./BatchAsync.sol"; -contract DeliveryHelper is BatchAsync, Initializable { +contract DeliveryHelper is BatchAsync { event CallBackReverted(bytes32 asyncId_, bytes32 payloadId_); constructor() { diff --git a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol index c4a31bf9..bf9b0046 100644 --- a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol @@ -2,13 +2,14 @@ pragma solidity ^0.8.21; import {Ownable} from "solady/auth/Ownable.sol"; +import "solady/utils/Initializable.sol"; import {AddressResolverUtil} from "../../utils/AddressResolverUtil.sol"; import "./DeliveryHelperStorage.sol"; /// @notice Abstract contract for managing asynchronous payloads -abstract contract QueueAsync is DeliveryHelperStorage, Ownable, AddressResolverUtil { +abstract contract QueueAsync is DeliveryHelperStorage, Initializable, Ownable, AddressResolverUtil { event PayloadBatchCancelled(bytes32 asyncId); event BidTimeoutUpdated(uint256 newBidTimeout); @@ -159,4 +160,6 @@ abstract contract QueueAsync is DeliveryHelperStorage, Ownable, AddressResolverU function getAsyncBatchDetails(bytes32 asyncId_) external view returns (PayloadBatch memory) { return _payloadBatches[asyncId_]; } + + uint256[50] _gap_queue_async; } diff --git a/contracts/protocol/utils/AccessControl.sol b/contracts/protocol/utils/AccessControl.sol index 1f050375..6086e354 100644 --- a/contracts/protocol/utils/AccessControl.sol +++ b/contracts/protocol/utils/AccessControl.sol @@ -105,4 +105,6 @@ abstract contract AccessControl is Ownable { function _hasRole(bytes32 role_, address address_) internal view returns (bool) { return _permits[role_][address_]; } + + uint256[50] _gap_access_control; } diff --git a/contracts/protocol/utils/Gauge.sol b/contracts/protocol/utils/Gauge.sol index 1aa4cebe..a9a6a7dc 100644 --- a/contracts/protocol/utils/Gauge.sol +++ b/contracts/protocol/utils/Gauge.sol @@ -41,4 +41,6 @@ abstract contract Gauge { revert LimitReached(); } } + + uint256[50] _gap_gauge; } diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol index 6d5c2aab..1d5fb8ed 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompile.sol @@ -2,11 +2,10 @@ pragma solidity ^0.8.21; import "./WatcherPrecompileConfig.sol"; -import "solady/utils/Initializable.sol"; /// @title WatcherPrecompile /// @notice Contract that handles payload verification, execution and app configurations -contract WatcherPrecompile is WatcherPrecompileConfig, Initializable { +contract WatcherPrecompile is WatcherPrecompileConfig { /// @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/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index a8bba140..cd63d06f 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -122,5 +122,5 @@ abstract contract WatcherPrecompileConfig is WatcherPrecompileLimits { if (signer != owner()) revert InvalidWatcherSignature(); } - uint256[49] __gap_config; + uint256[50] _gap_watcher_precompile_config; } diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index 05fc7565..14034ca5 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -6,12 +6,14 @@ import {Gauge} from "../utils/Gauge.sol"; import {AddressResolverUtil} from "../utils/AddressResolverUtil.sol"; import {WATCHER_ROLE} from "../utils/common/AccessRoles.sol"; import "./WatcherPrecompileStorage.sol"; +import "solady/utils/Initializable.sol"; abstract contract WatcherPrecompileLimits is WatcherPrecompileStorage, + Initializable, + AccessControl, Gauge, - AddressResolverUtil, - AccessControl + AddressResolverUtil { //////////////////////////////////////////////////////// ////////////////////// EVENTS ////////////////////////// @@ -164,5 +166,5 @@ abstract contract WatcherPrecompileLimits is defaultRatePerSecond = defaultRatePerSecond_; } - uint256[49] __gap; + uint256[50] _gap_watcher_precompile_limits; } From 57a81b596a3b4663f799829decc355676572c0de Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 26 Feb 2025 13:25:20 +0400 Subject: [PATCH 05/12] feat: change data type --- contracts/interfaces/IDeliveryHelper.sol | 2 +- .../app-gateway/DeliveryHelper.sol | 2 +- .../app-gateway/DeliveryHelperStorage.sol | 22 ++++++++++++++----- .../app-gateway/QueueAsync.sol | 4 +++- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/contracts/interfaces/IDeliveryHelper.sol b/contracts/interfaces/IDeliveryHelper.sol index d6cd4fd3..2f9d15ae 100644 --- a/contracts/interfaces/IDeliveryHelper.sol +++ b/contracts/interfaces/IDeliveryHelper.sol @@ -13,7 +13,7 @@ interface IDeliveryHelper { Bid winningBid // Replaced winningTransmitter and winningBid with Bid struct ); - function bidTimeout() external view returns (uint256); + function bidTimeout() external view returns (uint128); function payloadBatches(bytes32) external view returns (PayloadBatch memory); diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol index e7796e8b..df7cb9bc 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelper.sol @@ -16,7 +16,7 @@ contract DeliveryHelper is BatchAsync { function initialize( address addressResolver_, address owner_, - uint256 bidTimeout_ + uint128 bidTimeout_ ) public reinitializer(1) { _setAddressResolver(addressResolver_); bidTimeout = bidTimeout_; diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol index 49f1966b..118444ff 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol @@ -20,22 +20,34 @@ import {FORWARD_CALL, DISTRIBUTE_FEE, DEPLOY, WITHDRAW, QUERY, FINALIZE} from ". abstract contract DeliveryHelperStorage is IDeliveryHelper { uint256[50] _gap_before; + // slot 50 uint256 public saltCounter; - uint256 public asyncCounter; - uint256 public bidTimeout; - /// @notice The call parameters array - CallParams[] public callParamsArray; + // slot 51 + uint128 public asyncCounter; + uint128 public bidTimeout; + // slot 52 bytes32[] public tempPayloadIds; + // slot 53 + /// @notice The call parameters array + CallParams[] public callParamsArray; + + // slot 54 /// @notice The mapping of valid promises mapping(address => bool) public isValidPromise; - // payloadId => asyncId + + // slot 55 - payloadIdToBatchHash mapping(bytes32 => bytes32) public payloadIdToBatchHash; + // slot 56 - payloadIdToPayloadDetails mapping(bytes32 => PayloadDetails) public payloadIdToPayloadDetails; + + // slot 57 // asyncId => PayloadDetails[] mapping(bytes32 => PayloadDetails[]) public payloadBatchDetails; + + // slot 58 // asyncId => PayloadBatch mapping(bytes32 => PayloadBatch) internal _payloadBatches; diff --git a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol index bf9b0046..ce859cec 100644 --- a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol @@ -75,6 +75,8 @@ abstract contract QueueAsync is DeliveryHelperStorage, Initializable, Ownable, A function _createPayloadDetailsArray( bytes32 sbType_ ) internal returns (PayloadDetails[] memory payloadDetailsArray) { + if (callParamsArray.length == 0) return payloadDetailsArray; + payloadDetailsArray = new PayloadDetails[](callParamsArray.length); for (uint256 i = 0; i < callParamsArray.length; i++) { CallParams memory params = callParamsArray[i]; @@ -140,7 +142,7 @@ abstract contract QueueAsync is DeliveryHelperStorage, Initializable, Ownable, A /// @notice Updates the bid timeout /// @param newBidTimeout_ The new bid timeout value - function updateBidTimeout(uint256 newBidTimeout_) external onlyOwner { + function updateBidTimeout(uint128 newBidTimeout_) external onlyOwner { bidTimeout = newBidTimeout_; emit BidTimeoutUpdated(newBidTimeout_); } From 2bf154967748fd3ad64a6a9e80d2fb8464359f8f Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 26 Feb 2025 14:21:31 +0400 Subject: [PATCH 06/12] doc: added slots --- contracts/protocol/AddressResolver.sol | 25 +- contracts/protocol/AsyncPromise.sol | 11 + contracts/protocol/Forwarder.sol | 6 + .../payload-delivery/AuctionManager.sol | 12 + .../protocol/payload-delivery/FeesManager.sol | 11 +- .../app-gateway/BatchAsync.sol | 1 + .../app-gateway/DeliveryHelperStorage.sol | 6 +- .../app-gateway/QueueAsync.sol | 3 + .../protocol/utils/AddressResolverUtil.sol | 3 +- contracts/protocol/utils/Gauge.sol | 1 + .../WatcherPrecompileConfig.sol | 1 + .../WatcherPrecompileLimits.sol | 8 + .../WatcherPrecompileStorage.sol | 22 ++ test/mock/MockSocket.sol | 181 +++++++++++++ test/mock/MockWatcherPrecompile.sol | 245 ++++++++++++++++++ 15 files changed, 528 insertions(+), 8 deletions(-) create mode 100644 test/mock/MockSocket.sol create mode 100644 test/mock/MockWatcherPrecompile.sol diff --git a/contracts/protocol/AddressResolver.sol b/contracts/protocol/AddressResolver.sol index 64c5bb21..fe87dbe9 100644 --- a/contracts/protocol/AddressResolver.sol +++ b/contracts/protocol/AddressResolver.sol @@ -10,29 +10,46 @@ import {Forwarder} from "./Forwarder.sol"; import {AsyncPromise} from "./AsyncPromise.sol"; abstract contract AddressResolverStorage is IAddressResolver { + // slots [0-49] reserved for gap uint256[50] _gap_before; + + // slot 50 IWatcherPrecompile public override watcherPrecompile__; + + // slot 51 address public override deliveryHelper; + + // slot 52 address public override feesManager; - // Beacons for managing upgrades + // slot 53 UpgradeableBeacon public forwarderBeacon; + + // slot 54 UpgradeableBeacon public asyncPromiseBeacon; + // slot 55 address public forwarderImplementation; + + // slot 56 address public asyncPromiseImplementation; - // Array to store promises + // slot 57 address[] internal _promises; + // slot 58 uint256 public asyncPromiseCounter; + + // slot 59 uint64 public version; - // contracts to gateway map + // slot 60 mapping(address => address) public override contractsToGateways; - // gateway to contract map + + // slot 61 mapping(address => address) public override gatewaysToContracts; + // slots [62-111] reserved for gap uint256[50] _gap_after; } diff --git a/contracts/protocol/AsyncPromise.sol b/contracts/protocol/AsyncPromise.sol index 268f27cd..869601c3 100644 --- a/contracts/protocol/AsyncPromise.sol +++ b/contracts/protocol/AsyncPromise.sol @@ -16,27 +16,38 @@ enum AsyncPromiseState { } abstract contract AsyncPromiseStorage is IPromise { + // slots [0-49] reserved for gap uint256[50] _gap_before; + + // slot 50 /// @notice The callback selector to be called on the invoker. bytes4 public callbackSelector; + // slot 51 /// @notice Indicates whether the promise has been resolved. bool public override resolved; + // slot 52 /// @notice The current state of the async promise. AsyncPromiseState public state; + // slot 53 /// @notice The local contract which initiated the async call. /// @dev The callback will be executed on this address address public localInvoker; + // slot 54 /// @notice The forwarder address which can call the callback address public forwarder; + // slot 55 /// @notice The callback data to be used when the promise is resolved. bytes public callbackData; + // slots [56-105] reserved for gap uint256[50] _gap_after; + + // slots 106-157 reserved for addr resolver util } /// @title AsyncPromise diff --git a/contracts/protocol/Forwarder.sol b/contracts/protocol/Forwarder.sol index 9cfb4d03..13785b7b 100644 --- a/contracts/protocol/Forwarder.sol +++ b/contracts/protocol/Forwarder.sol @@ -9,20 +9,26 @@ import "../interfaces/IForwarder.sol"; import "solady/utils/Initializable.sol"; abstract contract ForwarderStorage is IForwarder { + // slots [0-49] reserved for gap uint256[50] _gap_before; + // slot 50 /// @notice chain id uint32 public chainSlug; + // slot 51 /// @notice on-chain address associated with this forwarder address public onChainAddress; + // slot 52 /// @notice address resolver contract address for imp addresses address public addressResolver; + // slot 53 /// @notice caches the latest async promise address for the last call address public latestAsyncPromise; + // slots [54-103] reserved for gap uint256[50] _gap_after; } diff --git a/contracts/protocol/payload-delivery/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol index c24054ce..c7e2b260 100644 --- a/contracts/protocol/payload-delivery/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -14,17 +14,29 @@ import {Fees, Bid, PayloadBatch} from "../utils/common/Structs.sol"; import {AuctionClosed, AuctionAlreadyStarted, BidExceedsMaxFees, LowerBidAlreadyExists, InvalidTransmitter} from "../utils/common/Errors.sol"; abstract contract AuctionManagerStorage is IAuctionManager { + // slots [0-49] reserved for gap uint256[50] _gap_before; + // slot 50 uint32 public evmxChainSlug; + + // slot 51 mapping(bytes32 => Bid) public winningBids; + + // slot 52 // asyncId => auction status mapping(bytes32 => bool) public override auctionClosed; + + // slot 53 mapping(bytes32 => bool) public override auctionStarted; + // slot 54 uint256 public auctionEndDelaySeconds; + // slots [55-104] reserved for gap uint256[50] _gap_after; + + // slots 105-156 reserved for addr resolver util } /// @title AuctionManager diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index df482fb5..007e2e09 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -13,30 +13,39 @@ import {NotAuctionManager} from "../utils/common/Errors.sol"; import {Bid, Fees, PayloadDetails, CallType, FinalizeParams, Parallel} from "../utils/common/Structs.sol"; abstract contract FeesManagerStorage is IFeesManager { + // slots [0-49] reserved for gap uint256[50] _gap_before; + // slot 50 uint256 public feesCounter; + /// @notice Struct containing fee amounts and status struct TokenBalance { uint256 deposited; // Amount deposited uint256 blocked; // Amount blocked } + // slot 51 /// @notice Master mapping tracking all fee information /// @dev appGateway => chainSlug => token => TokenBalance mapping(address => mapping(uint32 => mapping(address => TokenBalance))) public appGatewayFeeBalances; + // slot 52 /// @notice Mapping to track blocked fees for each async id /// @dev asyncId => Fees mapping(bytes32 => Fees) public asyncIdBlockedFees; + // slot 53 /// @notice Mapping to track fees to be distributed to transmitters /// @dev transmitter => chainSlug => token => amount mapping(address => mapping(uint32 => mapping(address => uint256))) public transmitterFees; + // slots [54-103] reserved for gap uint256[50] _gap_after; -} + + // slots 104-153 reserved for addr resolver util + } /// @title FeesManager /// @notice Contract for managing fees diff --git a/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol b/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol index a5eda5a1..634709e8 100644 --- a/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol @@ -311,5 +311,6 @@ abstract contract BatchAsync is QueueAsync { _deliverPayload(payloadDetailsArray, fees_, auctionManager_, new bytes(0)); } + // slots [211-260] reserved for gap uint256[50] _gap_batch_async; } diff --git a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol index 118444ff..1801385a 100644 --- a/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol +++ b/contracts/protocol/payload-delivery/app-gateway/DeliveryHelperStorage.sol @@ -18,6 +18,7 @@ import {FORWARD_CALL, DISTRIBUTE_FEE, DEPLOY, WITHDRAW, QUERY, FINALIZE} from ". /// @title DeliveryHelperStorage /// @notice Storage contract for DeliveryHelper abstract contract DeliveryHelperStorage is IDeliveryHelper { + // slots [0-49] reserved for gap uint256[50] _gap_before; // slot 50 @@ -40,16 +41,17 @@ abstract contract DeliveryHelperStorage is IDeliveryHelper { // slot 55 - payloadIdToBatchHash mapping(bytes32 => bytes32) public payloadIdToBatchHash; - // slot 56 - payloadIdToPayloadDetails + // slot 56 - payloadIdToPayloadDetails mapping(bytes32 => PayloadDetails) public payloadIdToPayloadDetails; // slot 57 // asyncId => PayloadDetails[] mapping(bytes32 => PayloadDetails[]) public payloadBatchDetails; - + // slot 58 // asyncId => PayloadBatch mapping(bytes32 => PayloadBatch) internal _payloadBatches; + // slots [59-108] reserved for gap uint256[50] _gap_after; } diff --git a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol index ce859cec..9b9d36ab 100644 --- a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol @@ -10,6 +10,8 @@ import "./DeliveryHelperStorage.sol"; /// @notice Abstract contract for managing asynchronous payloads abstract contract QueueAsync is DeliveryHelperStorage, Initializable, Ownable, AddressResolverUtil { + // slots [0-108] reserved for delivery helper storage and [109-160] reserved for addr resolver util + event PayloadBatchCancelled(bytes32 asyncId); event BidTimeoutUpdated(uint256 newBidTimeout); @@ -163,5 +165,6 @@ abstract contract QueueAsync is DeliveryHelperStorage, Initializable, Ownable, A return _payloadBatches[asyncId_]; } + // slots [161-210] reserved for gap uint256[50] _gap_queue_async; } diff --git a/contracts/protocol/utils/AddressResolverUtil.sol b/contracts/protocol/utils/AddressResolverUtil.sol index ad85e214..7862aab6 100644 --- a/contracts/protocol/utils/AddressResolverUtil.sol +++ b/contracts/protocol/utils/AddressResolverUtil.sol @@ -11,6 +11,7 @@ import "../../interfaces/IWatcherPrecompile.sol"; abstract contract AddressResolverUtil { /// @notice The address resolver contract reference /// @dev Used to look up system contract addresses + // slot 0 IAddressResolver public addressResolver__; /// @notice Error thrown when an invalid address attempts to call the Payload Delivery only function @@ -66,6 +67,6 @@ abstract contract AddressResolverUtil { if (appGateway == address(0)) appGateway = originAppGateway_; } - // for proxy contracts + // slots 1-50 reserved for future use uint256[50] __gap_resolver_util; } diff --git a/contracts/protocol/utils/Gauge.sol b/contracts/protocol/utils/Gauge.sol index a9a6a7dc..3fde631f 100644 --- a/contracts/protocol/utils/Gauge.sol +++ b/contracts/protocol/utils/Gauge.sol @@ -42,5 +42,6 @@ abstract contract Gauge { } } + // slot 0-49: gap for future storage variables uint256[50] _gap_gauge; } diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index cd63d06f..1acad489 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -122,5 +122,6 @@ abstract contract WatcherPrecompileConfig is WatcherPrecompileLimits { if (signer != owner()) revert InvalidWatcherSignature(); } + // slot 324-374: gap for future storage variables uint256[50] _gap_watcher_precompile_config; } diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index 14034ca5..e21eaff6 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -15,6 +15,13 @@ abstract contract WatcherPrecompileLimits is Gauge, AddressResolverUtil { + // Slots from parent contracts: + // slot 0-119: watcher precompile storage + // 0 slots for initializable and ownable + // slots 120-170: access control + // slots 171-221: gauge + // slots 222-272: address resolver util + //////////////////////////////////////////////////////// ////////////////////// EVENTS ////////////////////////// //////////////////////////////////////////////////////// @@ -166,5 +173,6 @@ abstract contract WatcherPrecompileLimits is defaultRatePerSecond = defaultRatePerSecond_; } + // slots 273-323: gap for future storage variables uint256[50] _gap_watcher_precompile_limits; } diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol index 0a073602..640b90b0 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol @@ -11,71 +11,93 @@ import {TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidInboxCaller, Resolv import {ResolvedPromises, AppGatewayConfig, LimitParams, UpdateLimitParams, PlugConfig, PayloadDigestParams, AsyncRequest, FinalizeParams, TimeoutRequest, CallFromChainParams} from "../utils/common/Structs.sol"; abstract contract WatcherPrecompileStorage is IWatcherPrecompile { + // slot 0-49: gap for future storage variables uint256[50] _gap_before; + // slot 50: constant LIMIT_DECIMALS (doesn't use storage) /// @notice Number of decimals used in limit calculations uint256 public constant LIMIT_DECIMALS = 18; + // slot 51: defaultLimit /// @notice Default limit value for any app gateway uint256 public defaultLimit; + // slot 52: defaultRatePerSecond /// @notice Rate at which limit replenishes per second uint256 public defaultRatePerSecond; + // slot 53: evmxChainSlug /// @notice The chain slug of the watcher precompile uint32 public evmxChainSlug; + // slot 54: _limitParams // appGateway => limitType => receivingLimitParams mapping(address => mapping(bytes32 => LimitParams)) internal _limitParams; + // slot 55: _activeAppGateways // Mapping to track active app gateways mapping(address => bool) internal _activeAppGateways; + // slot 56: _plugConfigs /// @notice Maps network and plug to their configuration /// @dev chainSlug => plug => PlugConfig mapping(uint32 => mapping(address => PlugConfig)) internal _plugConfigs; + // slot 57: switchboards /// @notice Maps chain slug to their associated switchboard /// @dev chainSlug => sb type => switchboard address mapping(uint32 => mapping(bytes32 => address)) public switchboards; + // slot 58: sockets /// @notice Maps chain slug to their associated socket /// @dev chainSlug => socket address mapping(uint32 => address) public sockets; + // slot 59: contractFactoryPlug /// @notice Maps chain slug to their associated contract factory plug /// @dev chainSlug => contract factory plug address mapping(uint32 => address) public contractFactoryPlug; + // slot 60: feesPlug /// @notice Maps chain slug to their associated fees plug /// @dev chainSlug => fees plug address mapping(uint32 => address) public feesPlug; + // slot 61: isNonceUsed /// @notice Maps nonce to whether it has been used /// @dev signatureNonce => isValid mapping(uint256 => bool) public isNonceUsed; + // slot 62: isValidPlug // appGateway => chainSlug => plug => isValid mapping(address => mapping(uint32 => mapping(address => bool))) public isValidPlug; + // slot 63: maxTimeoutDelayInSeconds uint256 public maxTimeoutDelayInSeconds; + // slot 64: payloadCounter /// @notice Counter for tracking payload requests uint256 public payloadCounter; + // slot 65: expiryTime /// @notice The expiry time for the payload uint256 public expiryTime; + // slot 66: asyncRequests /// @notice Mapping to store async requests /// @dev payloadId => AsyncRequest struct mapping(bytes32 => AsyncRequest) public asyncRequests; + // slot 67: timeoutRequests /// @notice Mapping to store timeout requests /// @dev timeoutId => TimeoutRequest struct mapping(bytes32 => TimeoutRequest) public timeoutRequests; + // slot 68: watcherProofs /// @notice Mapping to store watcher proofs /// @dev payloadId => proof bytes mapping(bytes32 => bytes) public watcherProofs; + // slot 69: appGatewayCalled /// @notice Mapping to store if appGateway has been called with trigger from on-chain Inbox /// @dev callId => bool mapping(bytes32 => bool) public appGatewayCalled; + // slots 70-119: gap for future storage variables uint256[50] _gap_after; } diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol new file mode 100644 index 00000000..d6a4c96e --- /dev/null +++ b/test/mock/MockSocket.sol @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.21; + +import {PlugDisconnected, InvalidAppGateway} from "../protocol/utils/common/Errors.sol"; +import "../interfaces/ISwitchboard.sol"; +import "../interfaces/ISocket.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 + */ +contract MockSocket is ISocket { + struct PlugConfig { + // address of the sibling plug on the remote chain + address appGateway; + // switchboard instance for the plug connection + ISwitchboard switchboard__; + } + + // plug => (appGateway, switchboard__) + mapping(address => PlugConfig) internal _plugConfigs; + + function getPlugConfig( + address plugAddress_ + ) external view returns (address appGateway, address switchboard__) { + PlugConfig memory _plugConfig = _plugConfigs[plugAddress_]; + return (_plugConfig.appGateway, address(_plugConfig.switchboard__)); + } + + function connect(address appGateway_, address switchboard_) external override {} + + function registerSwitchboard() external override {} + + //////////////////////////////////////////////////////// + ////////////////////// ERRORS ////////////////////////// + //////////////////////////////////////////////////////// + /** + * @dev Error emitted when proof is invalid + */ + + /** + * @dev Error emitted when a payload has already been executed + */ + error PayloadAlreadyExecuted(); + /** + * @dev Error emitted when the executor is not valid + */ + /** + * @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 + */ + error LowGasLimit(); + error InvalidSlug(); + + //////////////////////////////////////////////////////////// + ////////////////////// State Vars ////////////////////////// + //////////////////////////////////////////////////////////// + uint64 public callCounter; + uint32 public chainSlug; + + enum ExecutionStatus { + NotExecuted, + Executed, + Reverted + } + + /** + * @dev keeps track of whether a payload has been executed or not using payload id + */ + mapping(bytes32 => ExecutionStatus) public payloadExecuted; + + constructor(uint32 chainSlug_, address, address, address, string memory) { + chainSlug = chainSlug_; + } + + //////////////////////////////////////////////////////// + ////////////////////// 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]; + // 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 + */ + function execute( + address, + ExecuteParams memory params_, + bytes memory + ) external payable returns (bytes memory) { + // execute payload + return + _execute(params_.target, params_.payloadId, params_.executionGasLimit, params_.payload); + } + + //////////////////////////////////////////////////////// + ////////////////// INTERNAL FUNCS ////////////////////// + //////////////////////////////////////////////////////// + + function _verify( + bytes32 digest_, + bytes32 payloadId_, + 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(); + } + + /** + * This function assumes localPlug_ will have code while executing. As the payload + * execution failure is not blocking the system, it is not necessary to check if + * code exists in the given address. + */ + function _execute( + address, + bytes32 payloadId_, + uint256, + bytes memory + ) internal returns (bytes memory) { + bytes memory returnData = hex"00010203"; + emit ExecutionSuccess(payloadId_, returnData); + return returnData; + } + + /** + * @dev Decodes the switchboard address from a given payload id. + * @param id_ The ID of the msg 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_) { + switchboard_ = address(uint160(uint256(id_) >> 64)); + } + + /** + * @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. + */ + function _decodeChainSlug(bytes32 id_) internal pure returns (uint32 chainSlug_) { + chainSlug_ = uint32(uint256(id_) >> 224); + } + + // 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) { + return + bytes32( + (uint256(chainSlug) << 224) | (uint256(uint160(appGateway_)) << 64) | callCounter++ + ); + } +} diff --git a/test/mock/MockWatcherPrecompile.sol b/test/mock/MockWatcherPrecompile.sol new file mode 100644 index 00000000..93f9b3c5 --- /dev/null +++ b/test/mock/MockWatcherPrecompile.sol @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.21; + +import "../interfaces/IAppGateway.sol"; +import "../interfaces/IWatcherPrecompile.sol"; +import "../interfaces/IPromise.sol"; + +import {PayloadDigestParams, AsyncRequest, FinalizeParams, TimeoutRequest, CallFromChainParams, PlugConfig, ResolvedPromises, AppGatewayConfig} from "../protocol/utils/common/Structs.sol"; +import {QUERY, FINALIZE, SCHEDULE} from "../protocol/utils/common/Constants.sol"; +import {TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidInboxCaller, ResolvingTimeoutTooEarly, CallFailed, AppGatewayAlreadyCalled} from "../protocol/utils/common/Errors.sol"; +import "solady/utils/ERC1967Factory.sol"; + +/// @title WatcherPrecompile +/// @notice Contract that handles payload verification, execution and app configurations +contract MockWatcherPrecompile { + uint256 public maxTimeoutDelayInSeconds = 24 * 60 * 60; // 24 hours + /// @notice Counter for tracking query requests + uint256 public queryCounter; + /// @notice Counter for tracking payload execution requests + uint256 public payloadCounter; + /// @notice Counter for tracking timeout requests + uint256 public timeoutCounter; + /// @notice Mapping to store async requests + /// @dev payloadId => AsyncRequest struct + mapping(bytes32 => AsyncRequest) public asyncRequests; + /// @notice Mapping to store timeout requests + /// @dev timeoutId => TimeoutRequest struct + mapping(bytes32 => TimeoutRequest) public timeoutRequests; + + mapping(uint32 => mapping(address => PlugConfig)) internal _plugConfigs; + + /// @notice Error thrown when an invalid chain slug is provided + error InvalidChainSlug(); + error InvalidTransmitter(); + + event CalledAppGateway( + bytes32 callId, + uint32 chainSlug, + address plug, + address appGateway, + bytes32 params, + bytes payload + ); + + /// @notice Emitted when a new query is requested + /// @param chainSlug The identifier of the destination chain + /// @param targetAddress The address of the target contract + /// @param payloadId The unique identifier for the query + /// @param payload The query data + event QueryRequested(uint32 chainSlug, address targetAddress, bytes32 payloadId, bytes payload); + + /// @notice Emitted when a finalize request is made + /// @param payloadId The unique identifier for the request + /// @param asyncRequest The async request details + event FinalizeRequested(bytes32 indexed payloadId, AsyncRequest asyncRequest); + + /// @notice Emitted when a request is finalized + /// @param payloadId The unique identifier for the request + /// @param asyncRequest The async request details + /// @param proof The proof from the watcher + event Finalized(bytes32 indexed payloadId, AsyncRequest asyncRequest, bytes proof); + + /// @notice Emitted when a promise is resolved + /// @param payloadId The unique identifier for the resolved promise + event PromiseResolved(bytes32 indexed payloadId); + + event TimeoutRequested( + bytes32 timeoutId, + address target, + bytes payload, + uint256 executeAt // Epoch time when the task should execute + ); + + /// @notice Emitted when a timeout is resolved + /// @param timeoutId The unique identifier for the timeout + /// @param target The target address for the timeout + /// @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); + + /// @notice Contract constructor + /// @param _owner Address of the contract owner + constructor(address _owner, address addressResolver_) {} + + // ================== Timeout functions ================== + + /// @notice Sets a timeout for a payload execution on app gateway + /// @param payload_ The payload data + /// @param delayInSeconds_ The delay in seconds + function setTimeout(bytes calldata payload_, uint256 delayInSeconds_) external { + uint256 executeAt = block.timestamp + delayInSeconds_; + bytes32 timeoutId = _encodeTimeoutId(timeoutCounter++); + timeoutRequests[timeoutId] = TimeoutRequest( + timeoutId, + msg.sender, + delayInSeconds_, + executeAt, + 0, + false, + payload_ + ); + emit TimeoutRequested(timeoutId, msg.sender, payload_, executeAt); + } + + /// @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 + function resolveTimeout(bytes32 timeoutId) external { + TimeoutRequest storage timeoutRequest = timeoutRequests[timeoutId]; + + (bool success, ) = address(timeoutRequest.target).call(timeoutRequest.payload); + if (!success) revert CallFailed(); + emit TimeoutResolved( + timeoutId, + timeoutRequest.target, + timeoutRequest.payload, + block.timestamp + ); + } + + // ================== Finalize functions ================== + + /// @notice Finalizes a payload request, requests the watcher to release the proofs to execute on chain + /// @param params_ The finalization parameters + /// @return payloadId The unique identifier for the finalized request + /// @return digest The digest of the payload parameters + function finalize( + FinalizeParams memory params_ + ) external returns (bytes32 payloadId, bytes32 digest) { + digest = keccak256(abi.encode(block.timestamp)); + // Generate a unique payload ID by combining chain, target, and counter + payloadId = encodePayloadId( + params_.payloadDetails.chainSlug, + params_.payloadDetails.target, + payloadCounter++ + ); + address[] memory next = new address[](1); + emit FinalizeRequested( + payloadId, + AsyncRequest( + msg.sender, + address(0), + address(0), + params_.payloadDetails.target, + address(0), + 0, + block.timestamp + 1000, + params_.asyncId, + bytes32(0), + bytes(""), + next + ) + ); + } + + // ================== Query functions ================== + /// @notice Creates a new query request + /// @param chainSlug The identifier of the destination chain + /// @param targetAddress The address of the target contract + /// @param payload The query payload data + /// @return payloadId The unique identifier for the query + function query( + uint32 chainSlug, + address targetAddress, + address[] memory, + bytes memory payload + ) public returns (bytes32 payloadId) { + payloadId = bytes32(queryCounter++); + emit QueryRequested(chainSlug, targetAddress, payloadId, payload); + } + + /// @notice Marks a request as finalized with a proof + /// @param payloadId_ The unique identifier of the request + /// @param proof_ The watcher's proof + /// @dev Only callable by the contract owner + function finalized(bytes32 payloadId_, bytes calldata proof_) external { + emit Finalized(payloadId_, asyncRequests[payloadId_], proof_); + } + + /// @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 + function resolvePromises(ResolvedPromises[] calldata resolvedPromises_) external { + for (uint256 i = 0; i < resolvedPromises_.length; i++) { + emit PromiseResolved(resolvedPromises_[i].payloadId); + } + } + + // ================== On-Chain Inbox ================== + + function callAppGateways(CallFromChainParams[] calldata params_) external { + for (uint256 i = 0; i < params_.length; i++) { + emit CalledAppGateway( + params_[i].callId, + params_[i].chainSlug, + params_[i].plug, + params_[i].appGateway, + params_[i].params, + params_[i].payload + ); + } + } + + /// @notice Encodes a unique payload ID from chain slug, plug address, and counter + /// @param chainSlug_ The identifier of the chain + /// @param plug_ The plug address + /// @param counter_ The current counter value + /// @return The encoded payload ID as bytes32 + /// @dev Reverts if chainSlug is 0 + function encodePayloadId( + uint32 chainSlug_, + address plug_, + uint256 counter_ + ) internal view returns (bytes32) { + if (chainSlug_ == 0) revert InvalidChainSlug(); + (, address switchboard) = getPlugConfigs(chainSlug_, plug_); + // Encode payload ID by bit-shifting and combining: + // chainSlug (32 bits) | switchboard address (160 bits) | counter (64 bits) + + return + bytes32( + (uint256(chainSlug_) << 224) | (uint256(uint160(switchboard)) << 64) | counter_ + ); + } + + function _encodeTimeoutId(uint256 timeoutCounter_) internal view returns (bytes32) { + // watcher address (160 bits) | counter (64 bits) + return bytes32((uint256(uint160(address(this))) << 64) | timeoutCounter_); + } + + /// @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 + /// @dev Returns zero addresses if configuration doesn't exist + function getPlugConfigs( + uint32 chainSlug_, + address plug_ + ) public view returns (address, address) { + return ( + _plugConfigs[chainSlug_][plug_].appGateway, + _plugConfigs[chainSlug_][plug_].switchboard + ); + } +} From 8668471ac8a98f56fe004c71b9b257d6688a159d Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 27 Feb 2025 12:28:31 +0400 Subject: [PATCH 07/12] fix: slot comments --- .../WatcherPrecompileStorage.sol | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol index 640b90b0..a8f11fd7 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol @@ -14,90 +14,89 @@ abstract contract WatcherPrecompileStorage is IWatcherPrecompile { // slot 0-49: gap for future storage variables uint256[50] _gap_before; - // slot 50: constant LIMIT_DECIMALS (doesn't use storage) /// @notice Number of decimals used in limit calculations uint256 public constant LIMIT_DECIMALS = 18; - // slot 51: defaultLimit + // slot 50: defaultLimit /// @notice Default limit value for any app gateway uint256 public defaultLimit; - // slot 52: defaultRatePerSecond + // slot 51: defaultRatePerSecond /// @notice Rate at which limit replenishes per second uint256 public defaultRatePerSecond; - // slot 53: evmxChainSlug + // slot 52: evmxSlug /// @notice The chain slug of the watcher precompile - uint32 public evmxChainSlug; + uint32 public evmxSlug; - // slot 54: _limitParams + // slot 53: _limitParams // appGateway => limitType => receivingLimitParams mapping(address => mapping(bytes32 => LimitParams)) internal _limitParams; - // slot 55: _activeAppGateways + // slot 54: _activeAppGateways // Mapping to track active app gateways mapping(address => bool) internal _activeAppGateways; - // slot 56: _plugConfigs + // slot 55: _plugConfigs /// @notice Maps network and plug to their configuration /// @dev chainSlug => plug => PlugConfig mapping(uint32 => mapping(address => PlugConfig)) internal _plugConfigs; - // slot 57: switchboards + // slot 56: switchboards /// @notice Maps chain slug to their associated switchboard /// @dev chainSlug => sb type => switchboard address mapping(uint32 => mapping(bytes32 => address)) public switchboards; - // slot 58: sockets + // slot 57: sockets /// @notice Maps chain slug to their associated socket /// @dev chainSlug => socket address mapping(uint32 => address) public sockets; - // slot 59: contractFactoryPlug + // slot 58: contractFactoryPlug /// @notice Maps chain slug to their associated contract factory plug /// @dev chainSlug => contract factory plug address mapping(uint32 => address) public contractFactoryPlug; - // slot 60: feesPlug + // slot 59: feesPlug /// @notice Maps chain slug to their associated fees plug /// @dev chainSlug => fees plug address mapping(uint32 => address) public feesPlug; - // slot 61: isNonceUsed + // slot 60: isNonceUsed /// @notice Maps nonce to whether it has been used /// @dev signatureNonce => isValid mapping(uint256 => bool) public isNonceUsed; - // slot 62: isValidPlug + // slot 61: isValidPlug // appGateway => chainSlug => plug => isValid mapping(address => mapping(uint32 => mapping(address => bool))) public isValidPlug; - // slot 63: maxTimeoutDelayInSeconds + // slot 62: maxTimeoutDelayInSeconds uint256 public maxTimeoutDelayInSeconds; - // slot 64: payloadCounter + // slot 63: payloadCounter /// @notice Counter for tracking payload requests uint256 public payloadCounter; - // slot 65: expiryTime + // slot 64: expiryTime /// @notice The expiry time for the payload uint256 public expiryTime; - // slot 66: asyncRequests + // slot 65: asyncRequests /// @notice Mapping to store async requests /// @dev payloadId => AsyncRequest struct mapping(bytes32 => AsyncRequest) public asyncRequests; - // slot 67: timeoutRequests + // slot 66: timeoutRequests /// @notice Mapping to store timeout requests /// @dev timeoutId => TimeoutRequest struct mapping(bytes32 => TimeoutRequest) public timeoutRequests; - // slot 68: watcherProofs + // slot 67: watcherProofs /// @notice Mapping to store watcher proofs /// @dev payloadId => proof bytes mapping(bytes32 => bytes) public watcherProofs; - // slot 69: appGatewayCalled + // slot 68: appGatewayCalled /// @notice Mapping to store if appGateway has been called with trigger from on-chain Inbox /// @dev callId => bool mapping(bytes32 => bool) public appGatewayCalled; - // slots 70-119: gap for future storage variables - uint256[50] _gap_after; + // slots 69-119: gap for future storage variables + uint256[51] _gap_after; } From 86ff37ffbc65447532f98719a47a6844649f9b02 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 27 Feb 2025 12:28:56 +0400 Subject: [PATCH 08/12] fix: comment --- contracts/protocol/payload-delivery/FeesManager.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index b5bc3781..c43e8a39 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -45,7 +45,7 @@ abstract contract FeesManagerStorage is IFeesManager { // slot 54 /// @notice Mapping to track nonce to whether it has been used - /// @dev signatureNonce => isValid + /// @dev signatureNonce => isNonceUsed mapping(uint256 => bool) public isNonceUsed; // slots [55-104] reserved for gap From 578a685a010586706e4d8cc17dc45d4618e1325b Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 27 Feb 2025 12:33:56 +0400 Subject: [PATCH 09/12] fix: moved gaps to top --- .../protocol/payload-delivery/app-gateway/BatchAsync.sol | 6 +++--- .../protocol/payload-delivery/app-gateway/QueueAsync.sol | 5 ++--- contracts/protocol/utils/AccessControl.sol | 5 +++-- contracts/protocol/utils/AddressResolverUtil.sol | 6 +++--- contracts/protocol/utils/Gauge.sol | 6 +++--- .../protocol/watcherPrecompile/WatcherPrecompileConfig.sol | 6 +++--- .../protocol/watcherPrecompile/WatcherPrecompileLimits.sol | 5 ++--- 7 files changed, 19 insertions(+), 20 deletions(-) diff --git a/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol b/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol index 634709e8..578b47aa 100644 --- a/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol @@ -6,6 +6,9 @@ import "./QueueAsync.sol"; /// @title BatchAsync /// @notice Abstract contract for managing asynchronous payload batches abstract contract BatchAsync is QueueAsync { + // slots [211-260] reserved for gap + uint256[50] _gap_batch_async; + /// @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 @@ -310,7 +313,4 @@ abstract contract BatchAsync is QueueAsync { ); _deliverPayload(payloadDetailsArray, fees_, auctionManager_, new bytes(0)); } - - // slots [211-260] reserved for gap - uint256[50] _gap_batch_async; } diff --git a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol index 9b9d36ab..34284598 100644 --- a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol @@ -11,6 +11,8 @@ import "./DeliveryHelperStorage.sol"; /// @notice Abstract contract for managing asynchronous payloads abstract contract QueueAsync is DeliveryHelperStorage, Initializable, Ownable, AddressResolverUtil { // slots [0-108] reserved for delivery helper storage and [109-160] reserved for addr resolver util + // slots [161-210] reserved for gap + uint256[50] _gap_queue_async; event PayloadBatchCancelled(bytes32 asyncId); event BidTimeoutUpdated(uint256 newBidTimeout); @@ -164,7 +166,4 @@ abstract contract QueueAsync is DeliveryHelperStorage, Initializable, Ownable, A function getAsyncBatchDetails(bytes32 asyncId_) external view returns (PayloadBatch memory) { return _payloadBatches[asyncId_]; } - - // slots [161-210] reserved for gap - uint256[50] _gap_queue_async; } diff --git a/contracts/protocol/utils/AccessControl.sol b/contracts/protocol/utils/AccessControl.sol index 6086e354..d96dabaa 100644 --- a/contracts/protocol/utils/AccessControl.sol +++ b/contracts/protocol/utils/AccessControl.sol @@ -15,6 +15,9 @@ abstract contract AccessControl is Ownable { */ mapping(bytes32 => mapping(address => bool)) private _permits; + // slots 0-49: gap for future storage variables + uint256[50] _gap_access_control; + /** * @dev Emitted when a role is granted to an address. */ @@ -105,6 +108,4 @@ abstract contract AccessControl is Ownable { function _hasRole(bytes32 role_, address address_) internal view returns (bool) { return _permits[role_][address_]; } - - uint256[50] _gap_access_control; } diff --git a/contracts/protocol/utils/AddressResolverUtil.sol b/contracts/protocol/utils/AddressResolverUtil.sol index 7862aab6..38005e15 100644 --- a/contracts/protocol/utils/AddressResolverUtil.sol +++ b/contracts/protocol/utils/AddressResolverUtil.sol @@ -14,6 +14,9 @@ abstract contract AddressResolverUtil { // slot 0 IAddressResolver public addressResolver__; + // slots 1-50 reserved for future use + uint256[50] __gap_resolver_util; + /// @notice Error thrown when an invalid address attempts to call the Payload Delivery only function error OnlyPayloadDelivery(); /// @notice Error thrown when an invalid address attempts to call the Watcher only function @@ -66,7 +69,4 @@ abstract contract AddressResolverUtil { appGateway = addressResolver__.contractsToGateways(originAppGateway_); if (appGateway == address(0)) appGateway = originAppGateway_; } - - // slots 1-50 reserved for future use - uint256[50] __gap_resolver_util; } diff --git a/contracts/protocol/utils/Gauge.sol b/contracts/protocol/utils/Gauge.sol index 3fde631f..0a0d7b59 100644 --- a/contracts/protocol/utils/Gauge.sol +++ b/contracts/protocol/utils/Gauge.sol @@ -4,6 +4,9 @@ import {LimitParams} from "../utils/common/Structs.sol"; import {LimitReached} from "../utils/common/Errors.sol"; abstract contract Gauge { + // slot 0-49: gap for future storage variables + uint256[50] _gap_gauge; + function _getCurrentLimit(LimitParams storage params_) internal view returns (uint256 _limit) { uint256 timeElapsed = block.timestamp - params_.lastUpdateTimestamp; uint256 limitIncrease = timeElapsed * params_.ratePerSecond; @@ -41,7 +44,4 @@ abstract contract Gauge { revert LimitReached(); } } - - // slot 0-49: gap for future storage variables - uint256[50] _gap_gauge; } diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index d80f4a9f..8ea88b68 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -8,6 +8,9 @@ import {ECDSA} from "solady/utils/ECDSA.sol"; /// @notice Configuration contract for the Watcher Precompile system /// @dev Handles the mapping between networks, plugs, and app gateways for payload execution abstract contract WatcherPrecompileConfig is WatcherPrecompileLimits { + // slot 324-374: gap for future storage variables + uint256[50] _gap_watcher_precompile_config; + /// @notice Emitted when a new plug is configured for an app gateway /// @param appGateway The address of the app gateway /// @param chainSlug The identifier of the destination network @@ -123,7 +126,4 @@ abstract contract WatcherPrecompileConfig is WatcherPrecompileLimits { address signer = ECDSA.recover(digest, signature_); if (signer != owner()) revert InvalidWatcherSignature(); } - - // slot 324-374: gap for future storage variables - uint256[50] _gap_watcher_precompile_config; } diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index e21eaff6..9a01a7f2 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -21,6 +21,8 @@ abstract contract WatcherPrecompileLimits is // slots 120-170: access control // slots 171-221: gauge // slots 222-272: address resolver util + // slots 273-323: gap for future storage variables + uint256[50] _gap_watcher_precompile_limits; //////////////////////////////////////////////////////// ////////////////////// EVENTS ////////////////////////// @@ -172,7 +174,4 @@ abstract contract WatcherPrecompileLimits is function setDefaultRatePerSecond(uint256 defaultRatePerSecond_) external onlyOwner { defaultRatePerSecond = defaultRatePerSecond_; } - - // slots 273-323: gap for future storage variables - uint256[50] _gap_watcher_precompile_limits; } From 4c7cb800b793b5d8841b94fe3d77ab761fda1ed9 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 27 Feb 2025 12:35:12 +0400 Subject: [PATCH 10/12] fix: build --- test/mock/MockSocket.sol | 6 +++--- test/mock/MockWatcherPrecompile.sol | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/mock/MockSocket.sol b/test/mock/MockSocket.sol index d6a4c96e..98c87f09 100644 --- a/test/mock/MockSocket.sol +++ b/test/mock/MockSocket.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.21; -import {PlugDisconnected, InvalidAppGateway} from "../protocol/utils/common/Errors.sol"; -import "../interfaces/ISwitchboard.sol"; -import "../interfaces/ISocket.sol"; +import {PlugDisconnected, InvalidAppGateway} from "../../contracts/protocol/utils/common/Errors.sol"; +import "../../contracts/interfaces/ISwitchboard.sol"; +import "../../contracts/interfaces/ISocket.sol"; /** * @title SocketDst diff --git a/test/mock/MockWatcherPrecompile.sol b/test/mock/MockWatcherPrecompile.sol index 93f9b3c5..d1886b89 100644 --- a/test/mock/MockWatcherPrecompile.sol +++ b/test/mock/MockWatcherPrecompile.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.21; -import "../interfaces/IAppGateway.sol"; -import "../interfaces/IWatcherPrecompile.sol"; -import "../interfaces/IPromise.sol"; +import "../../contracts/interfaces/IAppGateway.sol"; +import "../../contracts/interfaces/IWatcherPrecompile.sol"; +import "../../contracts/interfaces/IPromise.sol"; -import {PayloadDigestParams, AsyncRequest, FinalizeParams, TimeoutRequest, CallFromChainParams, PlugConfig, ResolvedPromises, AppGatewayConfig} from "../protocol/utils/common/Structs.sol"; -import {QUERY, FINALIZE, SCHEDULE} from "../protocol/utils/common/Constants.sol"; -import {TimeoutDelayTooLarge, TimeoutAlreadyResolved, InvalidInboxCaller, ResolvingTimeoutTooEarly, CallFailed, AppGatewayAlreadyCalled} from "../protocol/utils/common/Errors.sol"; +import {PayloadDigestParams, AsyncRequest, FinalizeParams, TimeoutRequest, CallFromChainParams, 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 "solady/utils/ERC1967Factory.sol"; /// @title WatcherPrecompile From 99c08ea8450dc9d0fcb08aad4bccc66495089495 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 27 Feb 2025 12:56:53 +0400 Subject: [PATCH 11/12] fix: slot comments --- .../protocol/payload-delivery/AuctionManager.sol | 2 +- .../protocol/payload-delivery/FeesManager.sol | 14 ++++++++------ .../payload-delivery/app-gateway/BatchAsync.sol | 2 +- .../payload-delivery/app-gateway/QueueAsync.sol | 4 ++-- contracts/protocol/utils/AccessControl.sol | 3 ++- .../watcherPrecompile/WatcherPrecompileConfig.sol | 2 +- .../watcherPrecompile/WatcherPrecompileLimits.sol | 8 ++++---- .../watcherPrecompile/WatcherPrecompileStorage.sol | 4 ++-- 8 files changed, 21 insertions(+), 18 deletions(-) diff --git a/contracts/protocol/payload-delivery/AuctionManager.sol b/contracts/protocol/payload-delivery/AuctionManager.sol index c0209183..dd12f475 100644 --- a/contracts/protocol/payload-delivery/AuctionManager.sol +++ b/contracts/protocol/payload-delivery/AuctionManager.sol @@ -36,7 +36,7 @@ abstract contract AuctionManagerStorage is IAuctionManager { // slots [55-104] reserved for gap uint256[50] _gap_after; - // slots 105-156 reserved for addr resolver util + // slots 105-155 reserved for addr resolver util } /// @title AuctionManager diff --git a/contracts/protocol/payload-delivery/FeesManager.sol b/contracts/protocol/payload-delivery/FeesManager.sol index c43e8a39..58a9323f 100644 --- a/contracts/protocol/payload-delivery/FeesManager.sol +++ b/contracts/protocol/payload-delivery/FeesManager.sol @@ -19,6 +19,8 @@ abstract contract FeesManagerStorage is IFeesManager { // slot 50 uint256 public feesCounter; + + // slot 51 uint32 public evmxSlug; /// @notice Struct containing fee amounts and status @@ -27,31 +29,31 @@ abstract contract FeesManagerStorage is IFeesManager { uint256 blocked; // Amount blocked } - // slot 51 + // slot 52 /// @notice Master mapping tracking all fee information /// @dev appGateway => chainSlug => token => TokenBalance mapping(address => mapping(uint32 => mapping(address => TokenBalance))) public appGatewayFeeBalances; - // slot 52 + // slot 53 /// @notice Mapping to track blocked fees for each async id /// @dev asyncId => Fees mapping(bytes32 => Fees) public asyncIdBlockedFees; - // slot 53 + // slot 54 /// @notice Mapping to track fees to be distributed to transmitters /// @dev transmitter => chainSlug => token => amount mapping(address => mapping(uint32 => mapping(address => uint256))) public transmitterFees; - // slot 54 + // slot 55 /// @notice Mapping to track nonce to whether it has been used /// @dev signatureNonce => isNonceUsed mapping(uint256 => bool) public isNonceUsed; - // slots [55-104] reserved for gap + // slots [56-105] reserved for gap uint256[50] _gap_after; - // slots 105-154 reserved for addr resolver util + // slots 106-156 reserved for addr resolver util } /// @title FeesManager diff --git a/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol b/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol index 578b47aa..f4415f3c 100644 --- a/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/BatchAsync.sol @@ -6,7 +6,7 @@ import "./QueueAsync.sol"; /// @title BatchAsync /// @notice Abstract contract for managing asynchronous payload batches abstract contract BatchAsync is QueueAsync { - // slots [211-260] reserved for gap + // slots [210-259] reserved for gap uint256[50] _gap_batch_async; /// @notice Error thrown when attempting to executed payloads after all have been executed diff --git a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol index 34284598..c08b0f93 100644 --- a/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/protocol/payload-delivery/app-gateway/QueueAsync.sol @@ -10,8 +10,8 @@ import "./DeliveryHelperStorage.sol"; /// @notice Abstract contract for managing asynchronous payloads abstract contract QueueAsync is DeliveryHelperStorage, Initializable, Ownable, AddressResolverUtil { - // slots [0-108] reserved for delivery helper storage and [109-160] reserved for addr resolver util - // slots [161-210] reserved for gap + // slots [0-108] reserved for delivery helper storage and [109-159] reserved for addr resolver util + // slots [160-209] reserved for gap uint256[50] _gap_queue_async; event PayloadBatchCancelled(bytes32 asyncId); diff --git a/contracts/protocol/utils/AccessControl.sol b/contracts/protocol/utils/AccessControl.sol index d96dabaa..c25f0645 100644 --- a/contracts/protocol/utils/AccessControl.sol +++ b/contracts/protocol/utils/AccessControl.sol @@ -12,10 +12,11 @@ import "solady/auth/Ownable.sol"; abstract contract AccessControl is Ownable { /** * @dev A mapping of roles to a mapping of addresses to boolean values indicating whether or not they have the role. + * @dev slot 0 */ mapping(bytes32 => mapping(address => bool)) private _permits; - // slots 0-49: gap for future storage variables + // slots 1-50: gap for future storage variables uint256[50] _gap_access_control; /** diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol index 8ea88b68..ca5e6fac 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileConfig.sol @@ -8,7 +8,7 @@ import {ECDSA} from "solady/utils/ECDSA.sol"; /// @notice Configuration contract for the Watcher Precompile system /// @dev Handles the mapping between networks, plugs, and app gateways for payload execution abstract contract WatcherPrecompileConfig is WatcherPrecompileLimits { - // slot 324-374: gap for future storage variables + // slot 322-371: gap for future storage variables uint256[50] _gap_watcher_precompile_config; /// @notice Emitted when a new plug is configured for an app gateway diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index 9a01a7f2..6f2cbcd8 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -18,10 +18,10 @@ abstract contract WatcherPrecompileLimits is // Slots from parent contracts: // slot 0-119: watcher precompile storage // 0 slots for initializable and ownable - // slots 120-170: access control - // slots 171-221: gauge - // slots 222-272: address resolver util - // slots 273-323: gap for future storage variables + // slots 120-170: access control (gap + 1) + // slots 171-220: gauge (gap) + // slots 221-271: address resolver util (gap + 1) + // slots 272-321: gap for future storage variables uint256[50] _gap_watcher_precompile_limits; //////////////////////////////////////////////////////// diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol index a8f11fd7..e5142824 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileStorage.sol @@ -97,6 +97,6 @@ abstract contract WatcherPrecompileStorage is IWatcherPrecompile { /// @dev callId => bool mapping(bytes32 => bool) public appGatewayCalled; - // slots 69-119: gap for future storage variables - uint256[51] _gap_after; + // slots 69-118: gap for future storage variables + uint256[50] _gap_after; } From f8eb3fb6dc6c258eb19a3320d4a0a99088d338b2 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Thu, 27 Feb 2025 14:43:48 +0400 Subject: [PATCH 12/12] test: slot --- contracts/protocol/AsyncPromise.sol | 18 ++--- .../WatcherPrecompileLimits.sol | 10 +-- test/Storage.t.sol | 75 +++++++++++++++++++ 3 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 test/Storage.t.sol diff --git a/contracts/protocol/AsyncPromise.sol b/contracts/protocol/AsyncPromise.sol index 869601c3..4f5b0664 100644 --- a/contracts/protocol/AsyncPromise.sol +++ b/contracts/protocol/AsyncPromise.sol @@ -20,34 +20,32 @@ abstract contract AsyncPromiseStorage is IPromise { uint256[50] _gap_before; // slot 50 + // bytes1 /// @notice The callback selector to be called on the invoker. bytes4 public callbackSelector; - - // slot 51 + // bytes4 /// @notice Indicates whether the promise has been resolved. bool public override resolved; - - // slot 52 + // bytes8 /// @notice The current state of the async promise. AsyncPromiseState public state; - - // slot 53 + // bytes20 /// @notice The local contract which initiated the async call. /// @dev The callback will be executed on this address address public localInvoker; - // slot 54 + // slot 51 /// @notice The forwarder address which can call the callback address public forwarder; - // slot 55 + // slot 52 /// @notice The callback data to be used when the promise is resolved. bytes public callbackData; - // slots [56-105] reserved for gap + // slots [53-102] reserved for gap uint256[50] _gap_after; - // slots 106-157 reserved for addr resolver util + // slots 103-154 reserved for addr resolver util } /// @title AsyncPromise diff --git a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol index 6f2cbcd8..7f62d344 100644 --- a/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol +++ b/contracts/protocol/watcherPrecompile/WatcherPrecompileLimits.sol @@ -16,12 +16,12 @@ abstract contract WatcherPrecompileLimits is AddressResolverUtil { // Slots from parent contracts: - // slot 0-119: watcher precompile storage + // slot 0-118: watcher precompile storage // 0 slots for initializable and ownable - // slots 120-170: access control (gap + 1) - // slots 171-220: gauge (gap) - // slots 221-271: address resolver util (gap + 1) - // slots 272-321: gap for future storage variables + // 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; //////////////////////////////////////////////////////// diff --git a/test/Storage.t.sol b/test/Storage.t.sol new file mode 100644 index 00000000..d5e91d60 --- /dev/null +++ b/test/Storage.t.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./DeliveryHelper.t.sol"; + +contract StorageTest is DeliveryHelperTest { + DeliveryHelper public deliveryHelperImpl; + + function setUp() public { + setUpDeliveryHelper(); + } + + function testAddressResolverSlot() public { + // Test AddressResolver version at slot 59 + bytes32 versionSlot = vm.load(address(addressResolver), bytes32(uint256(59))); + assertEq(uint64(uint256(versionSlot)), 1); + } + + function testWatcherPrecompileSlot() public { + // Test AddressResolver address at slot 109 in WatcherPrecompile + bytes32 slotValue = vm.load(address(watcherPrecompile), bytes32(uint256(52))); + assertEq(uint256(slotValue), evmxSlug); + + slotValue = vm.load(address(watcherPrecompile), bytes32(uint256(220))); + assertEq(address(uint160(uint256(slotValue))), address(addressResolver)); + } + + function testFeesManagerSlot() public { + bytes32 slotValue = vm.load(address(feesManager), bytes32(uint256(51))); + assertEq(uint32(uint256(slotValue)), evmxSlug); + + slotValue = vm.load(address(feesManager), bytes32(uint256(106))); + assertEq(address(uint160(uint256(slotValue))), address(addressResolver)); + } + + function testAuctionManagerSlot() public { + bytes32 slotValue = vm.load(address(auctionManager), bytes32(uint256(50))); + assertEq(uint32(uint256(slotValue)), evmxSlug); + + slotValue = vm.load(address(auctionManager), bytes32(uint256(105))); + assertEq(address(uint160(uint256(slotValue))), address(addressResolver)); + } + + function testForwarderSlot() public { + address forwarder = addressResolver.getOrDeployForwarderContract( + address(this), + address(this), + evmxSlug + ); + + bytes32 slotValue = vm.load(address(forwarder), bytes32(uint256(50))); + assertEq(uint32(uint256(slotValue)), evmxSlug); + + slotValue = vm.load(address(forwarder), bytes32(uint256(53))); + assertEq(address(uint160(uint256(slotValue))), address(0)); + } + + function testAsyncPromiseSlot() public { + address asyncPromise = addressResolver.deployAsyncPromiseContract(address(this)); + + bytes32 slotValue = vm.load(address(asyncPromise), bytes32(uint256(51))); + assertEq(address(uint160(uint256(slotValue))), address(this)); + + slotValue = vm.load(address(asyncPromise), bytes32(uint256(103))); + assertEq(address(uint160(uint256(slotValue))), address(addressResolver)); + } + + function testDeliveryHelperSlot() public { + bytes32 slotValue = vm.load(address(deliveryHelper), bytes32(uint256(50))); + assertEq(uint256(uint256(slotValue)), 0); + + slotValue = vm.load(address(deliveryHelper), bytes32(uint256(109))); + assertEq(address(uint160(uint256(slotValue))), address(addressResolver)); + } +}