diff --git a/contracts/AddressResolver.sol b/contracts/AddressResolver.sol index 741290e..4f00355 100644 --- a/contracts/AddressResolver.sol +++ b/contracts/AddressResolver.sol @@ -73,6 +73,7 @@ contract AddressResolver is Ownable, IAddressResolver { /// @param chainSlug_ The chain slug /// @return The address of the deployed Forwarder contract function getOrDeployForwarderContract( + address appDeployer_, address chainContractAddress_, uint32 chainSlug_ ) public returns (address) { @@ -111,51 +112,16 @@ contract AddressResolver is Ownable, IAddressResolver { revert(0, 0) } } + + _setConfig(appDeployer_, newForwarder); emit ForwarderDeployed(newForwarder, salt); return newForwarder; } - /// @notice Deploys a Forwarder contract - /// @param appDeployer_ The address of the app deployer - /// @param chainContractAddress_ The address of the chain contract - /// @param chainSlug_ The chain slug - /// @return The address of the deployed Forwarder contract - function deployForwarderContract( - address appDeployer_, - address chainContractAddress_, - uint32 chainSlug_ - ) public returns (address) { - bytes memory constructorArgs = abi.encode( - chainSlug_, - chainContractAddress_, - address(this) - ); - - bytes memory combinedBytecode = abi.encodePacked( - forwarderBytecode, - constructorArgs - ); - - bytes32 salt = keccak256(constructorArgs); - address newForwarder; - - assembly { - newForwarder := create2( - callvalue(), - add(combinedBytecode, 0x20), - mload(combinedBytecode), - salt - ) - if iszero(extcodesize(newForwarder)) { - revert(0, 0) - } - } - emit ForwarderDeployed(newForwarder, salt); - + function _setConfig(address appDeployer_, address newForwarder_) internal { address gateway = contractsToGateways[appDeployer_]; - gatewaysToContracts[gateway] = newForwarder; - contractsToGateways[newForwarder] = gateway; - return newForwarder; + gatewaysToContracts[gateway] = newForwarder_; + contractsToGateways[newForwarder_] = gateway; } /// @notice Deploys an AsyncPromise contract diff --git a/contracts/AsyncPromise.sol b/contracts/AsyncPromise.sol index 46f6fc9..d9b3d0e 100644 --- a/contracts/AsyncPromise.sol +++ b/contracts/AsyncPromise.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.13; import {AddressResolverUtil} from "./utils/AddressResolverUtil.sol"; - +import {IPromise} from "./interfaces/IPromise.sol"; /// @notice The state of the async promise enum AsyncPromiseState { WAITING_FOR_SET_CALLBACK_SELECTOR, @@ -13,7 +13,7 @@ enum AsyncPromiseState { /// @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 AddressResolverUtil { +contract AsyncPromise is AddressResolverUtil, IPromise { /// @notice The callback data to be used when the promise is resolved. bytes public callbackData; @@ -28,7 +28,7 @@ contract AsyncPromise is AddressResolverUtil { address public immutable forwarder; /// @notice Indicates whether the promise has been resolved. - bool public resolved = false; + bool public override resolved = false; /// @notice Error thrown when attempting to resolve an already resolved promise. error PromiseAlreadyResolved(); @@ -55,7 +55,7 @@ contract AsyncPromise is AddressResolverUtil { /// @dev Only callable by the watcher precompile. function markResolved( bytes memory returnData - ) external onlyWatcherPrecompile { + ) external override onlyWatcherPrecompile { if (resolved) revert PromiseAlreadyResolved(); resolved = true; state = AsyncPromiseState.RESOLVED; @@ -79,7 +79,7 @@ contract AsyncPromise is AddressResolverUtil { function then( bytes4 selector, bytes memory data - ) external returns (address promise_) { + ) external override returns (address promise_) { require( msg.sender == forwarder || msg.sender == localInvoker, "Only the forwarder or local invoker can set this promise's callback" diff --git a/contracts/Forwarder.sol b/contracts/Forwarder.sol index 8e6237b..e034898 100644 --- a/contracts/Forwarder.sol +++ b/contracts/Forwarder.sol @@ -84,7 +84,7 @@ contract Forwarder is IForwarder { IAuctionHouse(auctionHouse).queue( chainSlug, onChainAddress, - bytes32(uint256(uint160(latestAsyncPromise))), + latestAsyncPromise, isReadCall ? CallType.READ : CallType.WRITE, msg.data ); diff --git a/contracts/apps/counter/app-gateway/CounterComposer.sol b/contracts/apps/counter/app-gateway/CounterComposer.sol index 020ce6e..123f0f1 100644 --- a/contracts/apps/counter/app-gateway/CounterComposer.sol +++ b/contracts/apps/counter/app-gateway/CounterComposer.sol @@ -2,8 +2,8 @@ pragma solidity >=0.7.0 <0.9.0; import "../../../base/AppGatewayBase.sol"; -import "../Counter.sol"; import "../../../utils/Ownable.sol"; +import "../Counter.sol"; contract CounterComposer is AppGatewayBase, Ownable { constructor( @@ -15,12 +15,6 @@ contract CounterComposer is AppGatewayBase, Ownable { _setFeesData(feesData_); } - // function incrementCounters(address[] calldata _instance, uint256 _counter) public queueAndExecute { - // for (uint256 i = 0; i < _instance.length; i++) { - // Counter(_instance[i]).setCounter(_counter); - // } - // } - function incrementCounter( address _instance, uint256 _counter diff --git a/contracts/apps/payload-delivery/ContractFactoryPlug.sol b/contracts/apps/payload-delivery/ContractFactoryPlug.sol index 9123f77..118c2d2 100644 --- a/contracts/apps/payload-delivery/ContractFactoryPlug.sol +++ b/contracts/apps/payload-delivery/ContractFactoryPlug.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.13; import {PlugBase} from "../../base/PlugBase.sol"; import {Ownable} from "../../utils/Ownable.sol"; + /// @title ContractFactory /// @notice Abstract contract for deploying contracts contract ContractFactoryPlug is PlugBase, Ownable { @@ -17,6 +18,10 @@ contract ContractFactoryPlug is PlugBase, Ownable { bytes memory creationCode, bytes32 salt ) public returns (address) { + if (msg.sender != address(socket__)) { + revert("Only socket can deploy contracts"); + } + address addr; assembly { addr := create2( diff --git a/contracts/apps/payload-delivery/app-gateway/AuctionContracts/FirstBidAuction.sol b/contracts/apps/payload-delivery/app-gateway/AuctionContracts/FirstBidAuction.sol deleted file mode 100644 index 820b3a6..0000000 --- a/contracts/apps/payload-delivery/app-gateway/AuctionContracts/FirstBidAuction.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; -import {Bid, FeesData} from "../../../../common/Structs.sol"; -import {IAuctionContract} from "../../../../interfaces/IAuctionContract.sol"; -/// @title AuctionHouse -/// @notice Contract for managing auctions and placing bids -contract FirstBidAuction is IAuctionContract { - uint256 public constant auctionEndDelaySeconds = 0; - - constructor() {} - - function isNewBidBetter( - Bid memory oldWinningBid, - Bid memory newBid - ) external pure returns (bool) { - if (oldWinningBid.transmitter == address(0)) return true; - return newBid.fee < oldWinningBid.fee; - } -} diff --git a/contracts/apps/payload-delivery/app-gateway/AuctionManager.sol b/contracts/apps/payload-delivery/app-gateway/AuctionManager.sol index 3b7656f..77f57db 100644 --- a/contracts/apps/payload-delivery/app-gateway/AuctionManager.sol +++ b/contracts/apps/payload-delivery/app-gateway/AuctionManager.sol @@ -5,25 +5,29 @@ import {Ownable} from "../../../utils/Ownable.sol"; import {SignatureVerifier} from "../../../socket/utils/SignatureVerifier.sol"; import {AddressResolverUtil} from "../../../utils/AddressResolverUtil.sol"; import {Bid, FeesData} from "../../../common/Structs.sol"; -import {IAuctionContract} from "../../../interfaces/IAuctionContract.sol"; import {IAuctionHouse} from "../../../interfaces/IAuctionHouse.sol"; /// @title AuctionHouse /// @notice Contract for managing auctions and placing bids contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) { SignatureVerifier public immutable signatureVerifier__; + uint32 public immutable vmChainSlug; mapping(bytes32 => Bid) public winningBids; // asyncId => auction status mapping(bytes32 => bool) public auctionClosed; mapping(bytes32 => bool) public auctionStarted; + uint256 public constant auctionEndDelaySeconds = 0; + /// @notice Constructor for AuctionHouse /// @param addressResolver_ The address of the address resolver /// @param signatureVerifier_ The address of the signature verifier constructor( + uint32 vmChainSlug_, address addressResolver_, SignatureVerifier signatureVerifier_ ) AddressResolverUtil(addressResolver_) { + vmChainSlug = vmChainSlug_; signatureVerifier__ = signatureVerifier_; } @@ -31,14 +35,13 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) { event AuctionEnded(bytes32 asyncId_, Bid winningBid); event BidPlaced(bytes32 asyncId_, Bid bid); - function startAuction(bytes32 asyncId_) external { + function startAuction(bytes32 asyncId_) external onlyPayloadDelivery { require(!auctionClosed[asyncId_], "Auction closed"); require(!auctionStarted[asyncId_], "Auction already started"); auctionStarted[asyncId_] = true; emit AuctionStarted(asyncId_); - uint256 auctionEndDelaySeconds = IAuctionContract(address(this)) - .auctionEndDelaySeconds(); + watcherPrecompile().setTimeout( abi.encodeWithSelector(this.endAuction.selector, asyncId_), auctionEndDelaySeconds @@ -52,13 +55,16 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) { function bid( bytes32 asyncId_, uint256 fee, + FeesData memory feesData, bytes memory transmitterSignature, bytes memory extraData ) external { require(!auctionClosed[asyncId_], "Auction closed"); address transmitter = signatureVerifier__.recoverSigner( - keccak256(abi.encode(address(this), asyncId_, fee)), + keccak256( + abi.encode(address(this), vmChainSlug, asyncId_, fee, extraData) + ), transmitterSignature ); @@ -67,16 +73,10 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) { transmitter: transmitter, extraData: extraData }); - (address auctionContract, FeesData memory feesData) = AuctionHouse() - .getAuctionContractAndFeesData(asyncId_); + require(fee <= feesData.maxFees, "Bid exceeds max fees"); - require( - IAuctionContract(auctionContract).isNewBidBetter( - winningBids[asyncId_], - newBid - ), - "Bid is not better" - ); + if (fee < winningBids[asyncId_].fee) return; + winningBids[asyncId_] = newBid; emit BidPlaced(asyncId_, newBid); } diff --git a/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol b/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol index 197f1ad..5fcb6ef 100644 --- a/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol +++ b/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol @@ -18,14 +18,13 @@ abstract contract BatchAsync is QueueAsync { error AllPayloadsExecuted(); error NotFromForwarder(); error CallFailed(bytes32 payloadId); - error DelayLimitReached(); error PayloadTooLarge(); event PayloadSubmitted( bytes32 indexed asyncId, address indexed appGateway, PayloadDetails[] payloads, FeesData feesData, - uint256 auctionEndDelay + address auctionManager ); event PayloadAsyncRequested( @@ -38,25 +37,17 @@ abstract contract BatchAsync is QueueAsync { /// @notice Initiates a batch of payloads /// @param feesData_ The fees data - /// @param auctionEndDelayMS_ The auction end delay in milliseconds + /// @param auctionManager_ The auction manager address /// @return asyncId The ID of the batch function batch( FeesData memory feesData_, - uint256 auctionEndDelayMS_ + address auctionManager_ ) external returns (bytes32) { - if (auctionEndDelayMS_ > 10 * 60 * 1000) revert DelayLimitReached(); - PayloadDetails[] memory payloadDetailsArray = createPayloadDetailsArray(); - // Check if batch contains only READ calls - if (_isReadOnlyBatch(payloadDetailsArray)) { - return _processReadOnlyBatch(payloadDetailsArray); - } - // Default flow for other cases (including mixed read/write) - return - deliverPayload(payloadDetailsArray, feesData_, auctionEndDelayMS_); + return deliverPayload(payloadDetailsArray, feesData_, auctionManager_); } /// @notice Callback function for handling promises @@ -65,23 +56,23 @@ abstract contract BatchAsync is QueueAsync { function callback( bytes memory asyncId_, bytes memory payloadDetails_ - ) external virtual onlyPromises {} + ) external virtual {} /// @notice Delivers a payload batch /// @param payloadDetails_ The payload details /// @param feesData_ The fees data - /// @param auctionEndDelayMS_ The auction end delay in milliseconds + /// @param auctionManager_ The auction manager address /// @return asyncId The ID of the batch function deliverPayload( PayloadDetails[] memory payloadDetails_, FeesData memory feesData_, - uint256 auctionEndDelayMS_ + address auctionManager_ ) internal returns (bytes32) { address forwarderAppGateway = msg.sender; bytes32 asyncId = getCurrentAsyncId(); asyncCounter++; - // Check for consecutive reads at the start + // Handle initial read operations first uint256 readEndIndex = 0; while ( readEndIndex < payloadDetails_.length && @@ -90,23 +81,25 @@ abstract contract BatchAsync is QueueAsync { readEndIndex++; } - // Process consecutive reads together if there are 1 or more - if (readEndIndex >= 1) { - // Create a batched read promise + address[] memory lastBatchPromises; + // Process initial reads if any exist + if (readEndIndex > 0) { + lastBatchPromises = new address[](readEndIndex); address batchPromise = IAddressResolver(addressResolver) .deployAsyncPromiseContract(address(this)); isValidPromise[batchPromise] = true; - // Process all reads in the batch for (uint256 i = 0; i < readEndIndex; i++) { + payloadDetails_[i].next[1] = batchPromise; + lastBatchPromises[i] = payloadDetails_[i].next[0]; + bytes32 payloadId = watcherPrecompile().query( payloadDetails_[i].chainSlug, payloadDetails_[i].target, - [batchPromise, address(0)], // Use same promise for all reads in batch + payloadDetails_[i].next, payloadDetails_[i].payload ); payloadIdToBatchHash[payloadId] = asyncId; - emit PayloadAsyncRequested( asyncId, payloadId, @@ -115,19 +108,23 @@ abstract contract BatchAsync is QueueAsync { ); } - // Setup callback for the entire batch of reads IPromise(batchPromise).then( this.callback.selector, abi.encode(asyncId) ); } - // Process remaining payloads normally + // If only reads, return early + if (readEndIndex == payloadDetails_.length) { + return asyncId; + } + + // Process and store remaining payloads for (uint256 i = readEndIndex; i < payloadDetails_.length; i++) { if (payloadDetails_[i].payload.length > 24.5 * 1024) revert PayloadTooLarge(); - // Rest of the existing deliverPayload logic for each payload + // Handle forwarder logic if (payloadDetails_[i].callType == CallType.DEPLOY) { payloadDetails_[i].target = getPlugAddress( address(this), @@ -136,39 +133,37 @@ abstract contract BatchAsync is QueueAsync { } else if (payloadDetails_[i].callType == CallType.WRITE) { forwarderAppGateway = IAddressResolver(addressResolver) .contractsToGateways(msg.sender); - if (forwarderAppGateway == address(0)) forwarderAppGateway = msg.sender; } - payloadDetails_[i].next[1] = IAddressResolver(addressResolver) - .deployAsyncPromiseContract(address(this)); - - isValidPromise[payloadDetails_[i].next[1]] = true; - IPromise(payloadDetails_[i].next[1]).then( - this.callback.selector, - abi.encode(asyncId) - ); - payloadDetailsArrays[asyncId].push(payloadDetails_[i]); + payloadBatchDetails[asyncId].push(payloadDetails_[i]); } - totalPayloadsRemaining[asyncId] = payloadDetails_.length - readEndIndex; + // Initialize batch payloadBatches[asyncId] = PayloadBatch({ appGateway: forwarderAppGateway, feesData: feesData_, - currentPayloadIndex: 0, - auctionEndDelayMS: auctionEndDelayMS_, - isBatchCancelled: false + currentPayloadIndex: readEndIndex, + auctionManager: auctionManager_, + winningBid: Bid({ + fee: 0, + transmitter: address(0), + extraData: new bytes(0) + }), + isBatchCancelled: false, + totalPayloadsRemaining: payloadDetails_.length - readEndIndex, + lastBatchPromises: lastBatchPromises }); - IAuctionManager(auctionManager).startAuction(asyncId); - + // Start auction + IAuctionManager(auctionManager_).startAuction(asyncId); emit PayloadSubmitted( asyncId, forwarderAppGateway, payloadDetails_, feesData_, - auctionEndDelayMS_ + auctionManager_ ); return asyncId; } @@ -207,45 +202,7 @@ abstract contract BatchAsync is QueueAsync { bytes32 asyncId_, uint256 index_ ) external view returns (PayloadDetails memory) { - return payloadDetailsArrays[asyncId_][index_]; - } - - function _isReadOnlyBatch( - PayloadDetails[] memory payloadDetails_ - ) internal pure returns (bool) { - for (uint256 i = 0; i < payloadDetails_.length; i++) { - if (payloadDetails_[i].callType != CallType.READ) { - return false; - } - } - return true; - } - - function _processReadOnlyBatch( - PayloadDetails[] memory payloadDetails_ - ) internal returns (bytes32) { - bytes32 asyncId = getCurrentAsyncId(); - asyncCounter++; - - // Process all reads in a loop without waiting for callbacks - for (uint256 i = 0; i < payloadDetails_.length; i++) { - bytes32 payloadId = watcherPrecompile().query( - payloadDetails_[i].chainSlug, - payloadDetails_[i].target, - payloadDetails_[i].next, - payloadDetails_[i].payload - ); - payloadIdToBatchHash[payloadId] = asyncId; - - emit PayloadAsyncRequested( - asyncId, - payloadId, - bytes32(0), // No root for read-only queries - payloadDetails_[i] - ); - } - - return asyncId; + return payloadBatchDetails[asyncId_][index_]; } /// @notice Withdraws funds to a specified receiver @@ -259,6 +216,7 @@ abstract contract BatchAsync is QueueAsync { address token_, uint256 amount_, address receiver_, + address auctionManager_, FeesData memory feesData_ ) external { PayloadDetails[] memory payloadDetailsArray = new PayloadDetails[](1); @@ -269,6 +227,6 @@ abstract contract BatchAsync is QueueAsync { amount_, receiver_ ); - deliverPayload(payloadDetailsArray, feesData_, 0); + deliverPayload(payloadDetailsArray, feesData_, auctionManager_); } } diff --git a/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol index e7b94f4..33e5703 100644 --- a/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol @@ -7,97 +7,259 @@ import {Bid, PayloadBatch, FeesData, PayloadDetails, FinalizeParams} from "../.. import {DISTRIBUTE_FEE, DEPLOY} from "../../../common/Constants.sol"; import "./BatchAsync.sol"; -// msg.sender map and call next function flow contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { - /// @notice Starts the batch processing - /// @param asyncId_ The ID of the batch + constructor( + address _addressResolver, + address _feesManager + ) AddressResolverUtil(_addressResolver) { + feesManager = _feesManager; + } + function startBatchProcessing( bytes32 asyncId_ - ) external onlyAuctionManager { - PayloadBatch storage payloadBatch = payloadBatches[asyncId_]; - if (payloadBatch.isBatchCancelled) return; - - _finalizeNextPayload(asyncId_); + ) external onlyAuctionManager(asyncId_) { + _process(asyncId_, ""); } - /// @notice Callback function for handling promises - /// @param asyncId_ The ID of the batch - /// @param payloadDetails_ The payload details function callback( bytes memory asyncId_, bytes memory payloadDetails_ ) external override onlyPromises { bytes32 asyncId = abi.decode(asyncId_, (bytes32)); + + _process(asyncId, payloadDetails_); + } + + error PromisesNotResolved(); + + function _process(bytes32 asyncId, bytes memory payloadDetails_) internal { PayloadBatch storage payloadBatch = payloadBatches[asyncId]; if (payloadBatch.isBatchCancelled) return; - uint256 payloadsRemaining = totalPayloadsRemaining[asyncId]; + // Check if there are remaining payloads to process + if (payloadBatch.totalPayloadsRemaining > 0) { + // Check if there are promises from last batch that need to be resolved + if (payloadBatch.lastBatchPromises.length > 0) { + // Check if all promises are resolved + for ( + uint256 i = 0; + i < payloadBatch.lastBatchPromises.length; + i++ + ) { + if ( + !IPromise(payloadBatch.lastBatchPromises[i]).resolved() + ) { + revert PromisesNotResolved(); + } + } + // Clear promises array after all are resolved + delete payloadBatch.lastBatchPromises; + } - if (payloadsRemaining > 0) { - payloadBatch.currentPayloadIndex++; + // Proceed with next payload only if all promises are resolved _finalizeNextPayload(asyncId); } else { - // todo: change it to call to fees manager - (bytes32 payloadId, bytes32 root) = IFeesManager(feesManager) - .distributeFees( - asyncId, - payloadBatch.appGateway, - payloadBatch.feesData, - winningBids[asyncId] - ); - payloadIdToBatchHash[payloadId] = asyncId; - emit PayloadAsyncRequested( - asyncId, - payloadId, - root, - payloadDetails_ + _finishBatch(asyncId, payloadBatch, payloadDetails_); + } + } + + function _finishBatch( + bytes32 asyncId, + PayloadBatch storage payloadBatch, + bytes memory payloadDetails_ + ) internal { + (bytes32 payloadId, bytes32 root) = IFeesManager(feesManager) + .distributeFees( + payloadBatch.appGateway, + payloadBatch.feesData, + payloadBatch.winningBid ); - PayloadDetails storage payloadDetails = payloadDetailsArrays[ - asyncId - ][payloadBatch.currentPayloadIndex]; + payloadIdToBatchHash[payloadId] = asyncId; + emit PayloadAsyncRequested( + asyncId, + payloadId, + root, + abi.decode(payloadDetails_, (PayloadDetails)) + ); - if (payloadDetails.callType == CallType.DEPLOY) { - IAppGateway(payloadBatch.appGateway).allContractsDeployed( - payloadDetails.chainSlug - ); - } - } + IAppGateway(payloadBatch.appGateway).onBatchComplete( + asyncId, + payloadBatch + ); } - /// @notice Finalizes the next payload in the batch - /// @param asyncId_ The ID of the batch function _finalizeNextPayload(bytes32 asyncId_) internal { PayloadBatch storage payloadBatch = payloadBatches[asyncId_]; - uint256 currentPayloadIndex = payloadBatch.currentPayloadIndex; - totalPayloadsRemaining[asyncId_]--; - - PayloadDetails[] storage payloads = payloadDetailsArrays[asyncId_]; + uint256 currentIndex = payloadBatch.currentPayloadIndex; + PayloadDetails[] storage payloads = payloadBatchDetails[asyncId_]; - PayloadDetails storage payloadDetails = payloads[currentPayloadIndex]; + // Deploy single promise for the next batch of operations + address batchPromise = IAddressResolver(addressResolver) + .deployAsyncPromiseContract(address(this)); + isValidPromise[batchPromise] = true; + IPromise(batchPromise).then( + this.callback.selector, + abi.encode(asyncId_) + ); - bytes32 payloadId; - bytes32 root; + // Handle batch processing based on type + if (payloads[currentIndex].callType == CallType.READ) { + _processBatchedReads( + asyncId_, + payloadBatch, + payloads, + currentIndex, + batchPromise + ); + } else if (!payloads[currentIndex].isSequential) { + _processParallelCalls( + asyncId_, + payloadBatch, + payloads, + currentIndex, + batchPromise + ); + } else { + _processSequentialCall( + asyncId_, + payloadBatch, + payloads, + currentIndex, + batchPromise + ); + } + } - // todo: if multiple query, process all at once - if (payloadDetails.callType == CallType.READ) { + function _executeWatcherCall( + bytes32 asyncId_, + PayloadDetails storage payload, + PayloadBatch storage payloadBatch, + address batchPromise, + bool isRead + ) internal returns (bytes32 payloadId, bytes32 root) { + if (isRead) { + payload.next[1] = batchPromise; payloadId = watcherPrecompile().query( - payloadDetails.chainSlug, - payloadDetails.target, - payloadDetails.next, - payloadDetails.payload + payload.chainSlug, + payload.target, + payload.next, + payload.payload ); - payloadIdToBatchHash[payloadId] = asyncId_; + root = bytes32(0); } else { FinalizeParams memory finalizeParams = FinalizeParams({ - payloadDetails: payloadDetails, - transmitter: winningBids[asyncId_].transmitter + payloadDetails: payload, + transmitter: payloadBatch.winningBid.transmitter }); - (payloadId, root) = watcherPrecompile().finalize(finalizeParams); - payloadIdToBatchHash[payloadId] = asyncId_; } - emit PayloadAsyncRequested(asyncId_, payloadId, root, payloadDetails); + payload.next[1] = batchPromise; + payloadIdToBatchHash[payloadId] = asyncId_; + emit PayloadAsyncRequested(asyncId_, payloadId, root, payload); + } + + function _processBatchedReads( + bytes32 asyncId_, + PayloadBatch storage payloadBatch, + PayloadDetails[] storage payloads, + uint256 startIndex, + address batchPromise + ) internal { + uint256 endIndex = startIndex; + while ( + endIndex + 1 < payloads.length && + payloads[endIndex + 1].callType == CallType.READ + ) { + endIndex++; + } + + address[] memory promises = new address[](endIndex - startIndex + 1); + for (uint256 i = startIndex; i <= endIndex; i++) { + promises[i - startIndex] = payloads[i].next[0]; + _executeWatcherCall( + asyncId_, + payloads[i], + payloadBatch, + batchPromise, + true + ); + } + + _updateBatchState( + payloadBatch, + promises, + endIndex - startIndex + 1, + endIndex + 1 + ); + } + + function _processParallelCalls( + bytes32 asyncId_, + PayloadBatch storage payloadBatch, + PayloadDetails[] storage payloads, + uint256 startIndex, + address batchPromise + ) internal { + uint256 endIndex = startIndex; + while ( + endIndex + 1 < payloads.length && + !payloads[endIndex + 1].isSequential + ) { + endIndex++; + } + + // Store promises for last batch + address[] memory promises = new address[](endIndex - startIndex + 1); + for (uint256 i = startIndex; i <= endIndex; i++) { + promises[i - startIndex] = payloads[i].next[0]; + + _executeWatcherCall( + asyncId_, + payloads[i], + payloadBatch, + batchPromise, + false + ); + } + + _updateBatchState( + payloadBatch, + promises, + endIndex - startIndex + 1, + endIndex + 1 + ); + } + + function _processSequentialCall( + bytes32 asyncId_, + PayloadBatch storage payloadBatch, + PayloadDetails[] storage payloads, + uint256 currentIndex, + address batchPromise + ) internal { + address[] memory promises = new address[](1); + promises[0] = payloads[currentIndex].next[0]; + + _executeWatcherCall( + asyncId_, + payloads[currentIndex], + payloadBatch, + batchPromise, + false + ); + _updateBatchState(payloadBatch, promises, 1, currentIndex + 1); + } + + function _updateBatchState( + PayloadBatch storage batch, + address[] memory promises, + uint256 completedCount, + uint256 nextIndex + ) internal { + batch.totalPayloadsRemaining -= completedCount; + batch.currentPayloadIndex = nextIndex; + batch.lastBatchPromises = promises; } } diff --git a/contracts/apps/payload-delivery/app-gateway/FeesManager.sol b/contracts/apps/payload-delivery/app-gateway/FeesManager.sol index 0aa1715..a998441 100644 --- a/contracts/apps/payload-delivery/app-gateway/FeesManager.sol +++ b/contracts/apps/payload-delivery/app-gateway/FeesManager.sol @@ -5,7 +5,6 @@ import {Ownable} from "../../../utils/Ownable.sol"; import {SignatureVerifier} from "../../../socket/utils/SignatureVerifier.sol"; import {AddressResolverUtil} from "../../../utils/AddressResolverUtil.sol"; import {Bid, FeesData, PayloadDetails, CallType, FinalizeParams} from "../../../common/Structs.sol"; -import {IAuctionContract} from "../../../interfaces/IAuctionContract.sol"; import {IAuctionHouse} from "../../../interfaces/IAuctionHouse.sol"; import {FORWARD_CALL, DISTRIBUTE_FEE, DEPLOY, WITHDRAW} from "../../../common/Constants.sol"; import {IFeesPlug} from "../../../interfaces/IFeesPlug.sol"; @@ -21,11 +20,12 @@ contract FeesManager is AddressResolverUtil, Ownable(msg.sender) { constructor( address addressResolver_ ) AddressResolverUtil(addressResolver_) {} + function distributeFees( address appGateway_, FeesData memory feesData_, Bid memory winningBid_ - ) external returns (bytes32 payloadId, bytes32 root) { + ) external onlyPayloadDelivery returns (bytes32 payloadId, bytes32 root) { bytes32 feesId = _encodeFeesId(feesCounter++); // Create payload for pool contract bytes memory payload = abi.encodeCall( @@ -45,7 +45,8 @@ contract FeesManager is AddressResolverUtil, Ownable(msg.sender) { payload: payload, callType: CallType.WRITE, executionGasLimit: feeCollectionGasLimit[feesData_.feePoolChain], - next: new address[](0) + next: new address[](0), + isSequential: true }); FinalizeParams memory finalizeParams = FinalizeParams({ @@ -81,7 +82,8 @@ contract FeesManager is AddressResolverUtil, Ownable(msg.sender) { payload: payload, callType: CallType.WITHDRAW, executionGasLimit: feeCollectionGasLimit[chainSlug_], - next: new address[](0) + next: new address[](0), + isSequential: true }); } diff --git a/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol b/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol index 475899b..c9646d8 100644 --- a/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol @@ -8,28 +8,23 @@ 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"; /// @notice Abstract contract for managing asynchronous payloads abstract contract QueueAsync is AddressResolverUtil { uint256 public saltCounter; uint256 public asyncCounter; - - address public auctionManager; address public feesManager; CallParams[] public callParamsArray; mapping(address => bool) public isValidPromise; - mapping(bytes32 => Bid) public winningBids; // payloadId => asyncId mapping(bytes32 => bytes32) public payloadIdToBatchHash; // asyncId => PayloadBatch mapping(bytes32 => PayloadBatch) public payloadBatches; - // asyncId => totalPayloadsRemaining - mapping(bytes32 => uint256) public totalPayloadsRemaining; // asyncId => PayloadDetails[] - mapping(bytes32 => PayloadDetails[]) public payloadDetailsArrays; - + mapping(bytes32 => PayloadDetails[]) public payloadBatchDetails; error InvalidPromise(); modifier onlyPromises() { @@ -39,20 +34,12 @@ abstract contract QueueAsync is AddressResolverUtil { _; } - modifier onlyAuctionManager() { - if (msg.sender != auctionManager) revert NotAuctionManager(); + modifier onlyAuctionManager(bytes32 asyncId_) { + if (msg.sender != payloadBatches[asyncId_].auctionManager) + revert NotAuctionManager(); _; } - constructor( - address _addressResolver, - address _auctionManager, - address _feesManager - ) AddressResolverUtil(_addressResolver) { - auctionManager = _auctionManager; - feesManager = _feesManager; - } - /// @notice Clears the call parameters array function clearQueue() public { delete callParamsArray; @@ -61,14 +48,14 @@ abstract contract QueueAsync is AddressResolverUtil { /// @notice Queues a new payload /// @param chainSlug_ The chain identifier /// @param target_ The target address - /// @param asyncPromiseOrId_ The async promise or ID + /// @param asyncPromise_ The async promise or ID /// @param callType_ The call type /// @param payload_ The payload function queue( bool isSequential_, uint32 chainSlug_, address target_, - bytes32 asyncPromiseOrId_, + address asyncPromise_, CallType callType_, bytes memory payload_ ) external { @@ -76,7 +63,7 @@ abstract contract QueueAsync is AddressResolverUtil { callParamsArray.push( CallParams({ callType: callType_, - asyncPromiseOrId: asyncPromiseOrId_, + asyncPromise: asyncPromise_, chainSlug: chainSlug_, target: target_, payload: payload_, @@ -110,29 +97,19 @@ abstract contract QueueAsync is AddressResolverUtil { CallParams memory params ) internal returns (PayloadDetails memory) { address[] memory next = new address[](2); - next[0] = address(uint160(uint256(params.asyncPromiseOrId))); + next[0] = params.asyncPromise; bytes memory payload = params.payload; if (params.callType == CallType.DEPLOY) { - address asyncPromise = IAddressResolver(addressResolver) - .deployAsyncPromiseContract(address(this)); - - isValidPromise[asyncPromise] = true; - next[0] = asyncPromise; - - IPromise(asyncPromise).then( - this.setAddress.selector, - abi.encode( - params.chainSlug, - params.asyncPromiseOrId, - msg.sender - ) - ); - bytes32 salt = keccak256( abi.encode(msg.sender, params.chainSlug, saltCounter++) ); - payload = abi.encode(params.payload, salt); + + payload = abi.encodeWithSelector( + IContractFactoryPlug.deployContract.selector, + params.payload, + salt + ); } return @@ -148,29 +125,4 @@ abstract contract QueueAsync is AddressResolverUtil { isSequential: params.isSequential }); } - - // todo: change it to call to gateway instead - /// @notice Sets the address for a deployed contract - /// @param data_ The data - /// @param returnData_ The return data - function setAddress( - bytes memory data_, - bytes memory returnData_ - ) external onlyPromises { - (uint32 chainSlug, bytes32 contractId, address appDeployer) = abi - .decode(data_, (uint32, bytes32, address)); - - address forwarderContractAddress = addressResolver - .deployForwarderContract( - appDeployer, - abi.decode(returnData_, (address)), - chainSlug - ); - - IAppDeployer(appDeployer).setForwarderContract( - chainSlug, - forwarderContractAddress, - contractId - ); - } } diff --git a/contracts/base/AppDeployerBase.sol b/contracts/base/AppDeployerBase.sol index 40b3948..0954527 100644 --- a/contracts/base/AppDeployerBase.sol +++ b/contracts/base/AppDeployerBase.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.13; -import {DeployParams, FeesData, CallType} from "../common/Structs.sol"; +import {DeployParams, FeesData, CallType, PayloadBatch} from "../common/Structs.sol"; import {AppGatewayBase} from "./AppGatewayBase.sol"; import {IForwarder} from "../interfaces/IForwarder.sol"; +import {IPromise} from "../interfaces/IPromise.sol"; import {IAppDeployer} from "../interfaces/IAppDeployer.sol"; import {IAuctionHouse} from "../interfaces/IAuctionHouse.sol"; @@ -19,27 +20,46 @@ abstract contract AppDeployerBase is AppGatewayBase, IAppDeployer { /// @param contractId_ The contract ID /// @param chainSlug_ The chain slug function _deploy(bytes32 contractId_, uint32 chainSlug_) internal { + address asyncPromise = addressResolver.deployAsyncPromiseContract( + address(this) + ); + + isValidPromise[asyncPromise] = true; + IPromise(asyncPromise).then( + this.setAddress.selector, + abi.encode(chainSlug_, contractId_) + ); + IAuctionHouse(auctionHouse()).queue( chainSlug_, address(0), // hacked for contract addr, need to revisit - contractId_, + asyncPromise, CallType.DEPLOY, creationCodeWithArgs[contractId_] ); } - /// @notice Sets the forwarder contract - /// @param chainSlug The chain slug - /// @param forwarderContractAddr The forwarder contract address - /// @param contractId The contract ID - /// @dev callback in payload delivery promise after contract deployment - function setForwarderContract( - uint32 chainSlug, - address forwarderContractAddr, - bytes32 contractId - ) external onlyPayloadDelivery { - forwarderAddresses[contractId][chainSlug] = forwarderContractAddr; + /// @notice Sets the address for a deployed contract + /// @param data_ The data + /// @param returnData_ The return data + function setAddress( + bytes memory data_, + bytes memory returnData_ + ) external onlyPromises { + (uint32 chainSlug, bytes32 contractId) = abi.decode( + data_, + (uint32, bytes32) + ); + + address forwarderContractAddress = addressResolver + .getOrDeployForwarderContract( + address(this), + abi.decode(returnData_, (address)), + chainSlug + ); + + forwarderAddresses[contractId][chainSlug] = forwarderContractAddress; } /// @notice Gets the on-chain address @@ -59,13 +79,16 @@ abstract contract AppDeployerBase is AppGatewayBase, IAppDeployer { } /// @notice Callback in pd promise to be called after all contracts are deployed - /// @param chainSlug The chain slug + /// @param asyncId The async ID + /// @param payloadBatch The payload batch /// @dev only payload delivery can call this /// @dev callback in pd promise to be called after all contracts are deployed - function allContractsDeployed( - uint32 chainSlug + function onBatchComplete( + bytes32 asyncId, + PayloadBatch memory payloadBatch ) external override onlyPayloadDelivery { - initialize(chainSlug); + // todo + // initialize(payloadBatch.chainSlug); } /// @notice Gets the socket address diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index 1f727c8..64fb731 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -16,7 +16,7 @@ abstract contract AppGatewayBase is FeesPlugin { bool public override isReadCall; - uint256 auctionDelayInMs; + address public auctionManager; mapping(address => bool) public isValidPromise; error InvalidPromise(); @@ -28,7 +28,7 @@ abstract contract AppGatewayBase is auctionHouse().clearQueue(); addressResolver.clearPromises(); _; - auctionHouse().batch(feesData, auctionDelayInMs); + auctionHouse().batch(feesData, auctionManager); _markValidPromises(); } @@ -56,10 +56,10 @@ abstract contract AppGatewayBase is return keccak256(abi.encode(contractName_)); } - /// @notice Sets the auction delay in milliseconds - /// @param auctionDelayInMs_ The auction delay in milliseconds - function _setAuctionDelayInMs(uint256 auctionDelayInMs_) internal { - auctionDelayInMs = auctionDelayInMs_; + /// @notice Sets the auction manager + /// @param auctionManager_ The auction manager + function _setAuctionManager(address auctionManager_) internal { + auctionManager = auctionManager_; } /// @notice Sets the read call flag @@ -113,9 +113,11 @@ abstract contract AppGatewayBase is } /// @notice Callback in pd promise to be called after all contracts are deployed - /// @param chainSlug_ The chain slug - function allContractsDeployed( - uint32 chainSlug_ + /// @param asyncId_ The async ID + /// @param payloadBatch_ The payload batch + function onBatchComplete( + bytes32 asyncId_, + PayloadBatch memory payloadBatch_ ) external virtual onlyPayloadDelivery {} function callFromInbox( diff --git a/contracts/common/Structs.sol b/contracts/common/Structs.sol index 2669dd4..21003c3 100644 --- a/contracts/common/Structs.sol +++ b/contracts/common/Structs.sol @@ -21,6 +21,7 @@ struct PayloadDetails { CallType callType; uint256 executionGasLimit; address[] next; + bool isSequential; } struct DeployParams { @@ -30,11 +31,12 @@ struct DeployParams { struct CallParams { CallType callType; - bytes32 asyncPromiseOrId; + address asyncPromise; uint32 chainSlug; address target; uint256 gasLimit; bytes payload; + bool isSequential; } struct Bid { @@ -47,8 +49,11 @@ struct PayloadBatch { address appGateway; FeesData feesData; uint256 currentPayloadIndex; - uint256 auctionEndDelaySeconds; + address auctionManager; + Bid winningBid; bool isBatchCancelled; + uint256 totalPayloadsRemaining; + address[] lastBatchPromises; } struct FinalizeParams { @@ -110,6 +115,7 @@ struct ExecutePayloadParams { uint256 executionGasLimit; bytes transmitterSignature; bytes payload; + address target; } struct TimeoutRequest { diff --git a/contracts/interfaces/IAddressResolver.sol b/contracts/interfaces/IAddressResolver.sol index d5714fe..05f9ba4 100644 --- a/contracts/interfaces/IAddressResolver.sol +++ b/contracts/interfaces/IAddressResolver.sol @@ -66,12 +66,11 @@ interface IAddressResolver { /// @dev Only callable by contract owner function clearPromises() external; - /// @notice Deploys a new forwarder contract - /// @param appDeployer_ The app deployer contract address + /// @notice Deploys a new forwarder contract if not already deployed /// @param chainContractAddress_ The contract address on the destination chain /// @param chainSlug_ The identifier of the destination chain /// @return The address of the newly deployed forwarder contract - function deployForwarderContract( + function getOrDeployForwarderContract( address appDeployer_, address chainContractAddress_, uint32 chainSlug_ diff --git a/contracts/interfaces/IAppDeployer.sol b/contracts/interfaces/IAppDeployer.sol index cc116a6..5a4d15c 100644 --- a/contracts/interfaces/IAppDeployer.sol +++ b/contracts/interfaces/IAppDeployer.sol @@ -3,16 +3,6 @@ pragma solidity ^0.8.13; /// @title IAppDeployer /// @notice Interface for the app deployer interface IAppDeployer { - /// @notice Sets the forwarder contract - /// @param chainSlug The chain slug - /// @param forwarderContractAddr The forwarder contract address - /// @param contractId The contract ID - function setForwarderContract( - uint32 chainSlug, - address forwarderContractAddr, - bytes32 contractId - ) external; - /// @notice initialize the contracts on chain function initialize(uint32 chainSlug) external; diff --git a/contracts/interfaces/IAppGateway.sol b/contracts/interfaces/IAppGateway.sol index 5d1da8d..eb1ebcb 100644 --- a/contracts/interfaces/IAppGateway.sol +++ b/contracts/interfaces/IAppGateway.sol @@ -1,10 +1,14 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.13; +import {PayloadBatch} from "../common/Structs.sol"; interface IAppGateway { function isReadCall() external view returns (bool); - function allContractsDeployed(uint32 chainSlug) external; + function onBatchComplete( + bytes32 asyncId, + PayloadBatch memory payloadBatch + ) external; function callFromInbox( uint32 chainSlug, diff --git a/contracts/interfaces/IAuctionContract.sol b/contracts/interfaces/IAuctionContract.sol deleted file mode 100644 index 434e1de..0000000 --- a/contracts/interfaces/IAuctionContract.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.3; -import {Bid} from "../common/Structs.sol"; - -interface IAuctionContract { - function auctionEndDelaySeconds() external view returns (uint256); - function isNewBidBetter( - Bid memory oldWinningBid, - Bid memory newBid - ) external view returns (bool); -} diff --git a/contracts/interfaces/IAuctionHouse.sol b/contracts/interfaces/IAuctionHouse.sol index 1a14143..0ed583f 100644 --- a/contracts/interfaces/IAuctionHouse.sol +++ b/contracts/interfaces/IAuctionHouse.sol @@ -24,14 +24,14 @@ interface IAuctionHouse { function queue( uint32 chainSlug_, address target_, - bytes32 asyncPromiseOrId_, + address asyncPromise_, CallType callType_, bytes memory payload_ ) external; function batch( FeesData memory feesData_, - uint256 auctionEndDelaySeconds_ + address auctionManager_ ) external returns (bytes32); function withdrawTo( @@ -44,9 +44,7 @@ interface IAuctionHouse { function cancelTransaction(bytes32 asyncId_) external; - function getAuctionContractAndFeesData( - bytes32 asyncId_ - ) external view returns (address, FeesData memory); function startBatchProcessing(bytes32 asyncId_) external; + function getCurrentAsyncId() external view returns (bytes32); } diff --git a/contracts/interfaces/IAuctionManager.sol b/contracts/interfaces/IAuctionManager.sol index 0edda88..d33c902 100644 --- a/contracts/interfaces/IAuctionManager.sol +++ b/contracts/interfaces/IAuctionManager.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.3; -import {Bid} from "../common/Structs.sol"; - +import {Bid, FeesData} from "../common/Structs.sol"; interface IAuctionManager { function startAuction(bytes32 asyncId_) external; function bid( bytes32 asyncId_, uint256 fee, + FeesData memory feesData, bytes memory transmitterSignature, bytes memory extraData ) external; diff --git a/contracts/interfaces/IContractFactoryPlug.sol b/contracts/interfaces/IContractFactoryPlug.sol new file mode 100644 index 0000000..73abdfa --- /dev/null +++ b/contracts/interfaces/IContractFactoryPlug.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +/// @title IContractFactory +/// @notice Interface for contract factory functionality +interface IContractFactoryPlug { + /// @notice Deploys a contract using CREATE2 + /// @param creationCode The contract creation code + /// @param salt The salt value for CREATE2 + /// @return address The deployed contract address + function deployContract( + bytes memory creationCode, + bytes32 salt + ) external returns (address); + + /// @notice Gets the deterministic address for a contract deployment + /// @param creationCode The contract creation code + /// @param salt The salt value + /// @return address The predicted contract address + function getAddress( + bytes memory creationCode, + uint256 salt + ) external view returns (address); +} diff --git a/contracts/interfaces/IPromise.sol b/contracts/interfaces/IPromise.sol index 7d887ed..3fd1440 100644 --- a/contracts/interfaces/IPromise.sol +++ b/contracts/interfaces/IPromise.sol @@ -8,4 +8,6 @@ interface IPromise { ) external returns (address promise_); function markResolved(bytes memory returnData) external; + + function resolved() external view returns (bool); } diff --git a/contracts/socket/Socket.sol b/contracts/socket/Socket.sol index c02dfc2..c7f9c2e 100644 --- a/contracts/socket/Socket.sol +++ b/contracts/socket/Socket.sol @@ -49,7 +49,6 @@ contract Socket is SocketBase { /** * @dev keeps track of whether a payload has been executed or not using payload id */ - // todo: add nottried, reverting and success mapping(bytes32 => bool) public payloadExecuted; constructor( @@ -110,6 +109,10 @@ contract Socket is SocketBase { address switchboard = _decodeSwitchboard(payloadId_); uint32 localSlug = _decodeChainSlug(payloadId_); + PlugConfig memory plugConfig = _plugConfigs[target_]; + if (switchboard != address(plugConfig.switchboard__)) + revert InvalidSwitchboard(); + if (localSlug != chainSlug) revert InvalidSlug(); address transmitter = signatureVerifier__.recoverSigner( diff --git a/contracts/socket/SocketBatcher.sol b/contracts/socket/SocketBatcher.sol index ce1cbb8..47b6037 100644 --- a/contracts/socket/SocketBatcher.sol +++ b/contracts/socket/SocketBatcher.sol @@ -36,6 +36,7 @@ contract SocketBatcher is Ownable { socket__.execute( params.payloadId, params.appGateway, + params.target, params.executionGasLimit, params.transmitterSignature, params.payload diff --git a/contracts/socket/switchboard/FastSwitchboard.sol b/contracts/socket/switchboard/FastSwitchboard.sol index b7f4ed2..f56c9f6 100644 --- a/contracts/socket/switchboard/FastSwitchboard.sol +++ b/contracts/socket/switchboard/FastSwitchboard.sol @@ -45,10 +45,6 @@ contract FastSwitchboard is SwitchboardBase { bytes32 root_, bytes calldata signature_ ) external { - // removed root verification for now - - // todo: can include orderHash, transmitter, bidAmount in digest and verify these - // here instead of including in root address watcher = signatureVerifier__.recoverSigner( keccak256(abi.encode(address(this), root_)), signature_