From cdc226bd9f73ea2615dac9c3b72f157950eade1f Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 25 Dec 2024 18:46:50 +0530 Subject: [PATCH 1/9] fix: review todos and read parallel batch --- .../AuctionContracts/FirstBidAuction.sol | 4 ++ .../app-gateway/AuctionManager.sol | 12 +++++- .../app-gateway/BatchAsync.sol | 10 ++++- .../app-gateway/DeliveryHelper.sol | 39 +++++++++++++------ .../app-gateway/QueueAsync.sol | 1 + contracts/socket/Socket.sol | 1 + 6 files changed, 53 insertions(+), 14 deletions(-) diff --git a/contracts/apps/payload-delivery/app-gateway/AuctionContracts/FirstBidAuction.sol b/contracts/apps/payload-delivery/app-gateway/AuctionContracts/FirstBidAuction.sol index 820b3a6..f29cd3b 100644 --- a/contracts/apps/payload-delivery/app-gateway/AuctionContracts/FirstBidAuction.sol +++ b/contracts/apps/payload-delivery/app-gateway/AuctionContracts/FirstBidAuction.sol @@ -2,6 +2,10 @@ pragma solidity ^0.8.0; import {Bid, FeesData} from "../../../../common/Structs.sol"; import {IAuctionContract} from "../../../../interfaces/IAuctionContract.sol"; + +// todo: is manager auction system flexible? + + /// @title AuctionHouse /// @notice Contract for managing auctions and placing bids contract FirstBidAuction is IAuctionContract { diff --git a/contracts/apps/payload-delivery/app-gateway/AuctionManager.sol b/contracts/apps/payload-delivery/app-gateway/AuctionManager.sol index 3b7656f..3be9b44 100644 --- a/contracts/apps/payload-delivery/app-gateway/AuctionManager.sol +++ b/contracts/apps/payload-delivery/app-gateway/AuctionManager.sol @@ -37,8 +37,11 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) { auctionStarted[asyncId_] = true; emit AuctionStarted(asyncId_); + + // todo: fix auction contract address uint256 auctionEndDelaySeconds = IAuctionContract(address(this)) .auctionEndDelaySeconds(); + watcherPrecompile().setTimeout( abi.encodeWithSelector(this.endAuction.selector, asyncId_), auctionEndDelaySeconds @@ -57,8 +60,9 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) { ) external { require(!auctionClosed[asyncId_], "Auction closed"); + // todo: check chain slug for multiple offchain vm address transmitter = signatureVerifier__.recoverSigner( - keccak256(abi.encode(address(this), asyncId_, fee)), + keccak256(abi.encode(address(this), asyncId_, fee, extraData)), transmitterSignature ); @@ -67,9 +71,14 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) { transmitter: transmitter, extraData: extraData }); + + // todo: fix auction contract address (address auctionContract, FeesData memory feesData) = AuctionHouse() .getAuctionContractAndFeesData(asyncId_); + require(fee <= feesData.maxFees, "Bid exceeds max fees"); + + // todo: revisit if should revert or not require( IAuctionContract(auctionContract).isNewBidBetter( winningBids[asyncId_], @@ -77,6 +86,7 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) { ), "Bid is not better" ); + 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 5884128..eb4ea58 100644 --- a/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol +++ b/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol @@ -129,11 +129,15 @@ abstract contract BatchAsync is QueueAsync { // Rest of the existing deliverPayload logic for each payload if (payloadDetails_[i].callType == CallType.DEPLOY) { + // considering contract factory as plug of delivery helper payloadDetails_[i].target = getPlugAddress( address(this), payloadDetails_[i].chainSlug ); + + // todo: encode for deployContract } else if (payloadDetails_[i].callType == CallType.WRITE) { + // todo: if need to remove this forwarderAppGateway = IAddressResolver(addressResolver) .contractsToGateways(msg.sender); @@ -149,14 +153,17 @@ abstract contract BatchAsync is QueueAsync { this.callback.selector, abi.encode(asyncId) ); + + // todo: rename payloadDetailsArrays to payloadDetails_ payloadDetailsArrays[asyncId].push(payloadDetails_[i]); } + // todo: merge all async id vars in one struct totalPayloadsRemaining[asyncId] = payloadDetails_.length - readEndIndex; payloadBatches[asyncId] = PayloadBatch({ appGateway: forwarderAppGateway, feesData: feesData_, - currentPayloadIndex: 0, + currentPayloadIndex: readEndIndex, auctionEndDelayMS: auctionEndDelayMS_, isBatchCancelled: false }); @@ -248,7 +255,6 @@ abstract contract BatchAsync is QueueAsync { return asyncId; } - /// @notice Withdraws funds to a specified receiver /// @param chainSlug_ The chain identifier /// @param token_ The address of the token diff --git a/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol index 3c37036..02ffa79 100644 --- a/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol @@ -12,7 +12,7 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { /// @notice Starts the batch processing /// @param asyncId_ The ID of the batch - function startBatchProcessing(bytes32 asyncId_) external onlyFeesManager { + function startBatchProcessing(bytes32 asyncId_) external onlyAuctionManager { PayloadBatch storage payloadBatch = payloadBatches[asyncId_]; if (payloadBatch.isBatchCancelled) return; @@ -64,29 +64,46 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { totalPayloadsRemaining[asyncId_]--; PayloadDetails[] storage payloads = payloadDetailsArrays[asyncId_]; - PayloadDetails storage payloadDetails = payloads[currentPayloadIndex]; bytes32 payloadId; bytes32 root; - // todo: if multiple query, process all at once if (payloadDetails.callType == CallType.READ) { - payloadId = watcherPrecompile().query( - payloadDetails.chainSlug, - payloadDetails.target, - payloadDetails.next, - payloadDetails.payload - ); - payloadIdToBatchHash[payloadId] = asyncId_; + // Find consecutive READ calls + uint256 readEndIndex = currentPayloadIndex; + while (readEndIndex + 1 < payloads.length && + payloads[readEndIndex + 1].callType == CallType.READ) { + readEndIndex++; + } + + // Create a batched read promise + address batchPromise = IAddressResolver(addressResolver) + .deployAsyncPromiseContract(address(this)); + isValidPromise[batchPromise] = true; + + // Process all reads in the batch + for (uint256 i = currentPayloadIndex; i <= readEndIndex; i++) { + payloadId = watcherPrecompile().query( + payloads[i].chainSlug, + payloads[i].target, + [batchPromise, address(0)], // Use same promise for all reads in batch + payloads[i].payload + ); + payloadIdToBatchHash[payloadId] = asyncId_; + } + + // Skip the batched payloads + payloadBatch.currentPayloadIndex = readEndIndex; } else { FinalizeParams memory finalizeParams = FinalizeParams({ payloadDetails: payloadDetails, - transmitter: winningBids[asyncId_].transmitter + transmitter: IAuctionManager(auctionManager).winningBids(asyncId_).transmitter }); (payloadId, root) = watcherPrecompile().finalize(finalizeParams); payloadIdToBatchHash[payloadId] = asyncId_; + payloadBatch.currentPayloadIndex = currentPayloadIndex; } emit PayloadAsyncRequested(asyncId_, payloadId, root, payloadDetails); diff --git a/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol b/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol index 67fc07e..c2e394c 100644 --- a/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol @@ -154,6 +154,7 @@ abstract contract QueueAsync is AddressResolverUtil { (uint32 chainSlug, bytes32 contractId, address appDeployer) = abi .decode(data_, (uint32, bytes32, address)); + // todo: use getOrDeploy here address forwarderContractAddress = addressResolver .deployForwarderContract( appDeployer, diff --git a/contracts/socket/Socket.sol b/contracts/socket/Socket.sol index 5da1256..68f860b 100644 --- a/contracts/socket/Socket.sol +++ b/contracts/socket/Socket.sol @@ -82,6 +82,7 @@ contract Socket is SocketBase { // extract plug address from msgID address switchboard = _decodeSwitchboard(payloadId_); uint32 localSlug = _decodeChainSlug(payloadId_); + // todo: check if switchboard is same as config in plug if (localSlug != chainSlug) revert InvalidSlug(); From 95edc877e2d4948cd9c0320033c72b2febfd06c4 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 27 Dec 2024 01:08:01 +0530 Subject: [PATCH 2/9] fix: todo --- .../AuctionContracts/FirstBidAuction.sol | 2 - .../app-gateway/BatchAsync.sol | 73 +++++-------------- .../app-gateway/QueueAsync.sol | 13 ++-- contracts/common/Structs.sol | 2 + contracts/socket/Socket.sol | 4 +- 5 files changed, 30 insertions(+), 64 deletions(-) diff --git a/contracts/apps/payload-delivery/app-gateway/AuctionContracts/FirstBidAuction.sol b/contracts/apps/payload-delivery/app-gateway/AuctionContracts/FirstBidAuction.sol index f29cd3b..5b9b555 100644 --- a/contracts/apps/payload-delivery/app-gateway/AuctionContracts/FirstBidAuction.sol +++ b/contracts/apps/payload-delivery/app-gateway/AuctionContracts/FirstBidAuction.sol @@ -3,8 +3,6 @@ pragma solidity ^0.8.0; import {Bid, FeesData} from "../../../../common/Structs.sol"; import {IAuctionContract} from "../../../../interfaces/IAuctionContract.sol"; -// todo: is manager auction system flexible? - /// @title AuctionHouse /// @notice Contract for managing auctions and placing bids diff --git a/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol b/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol index 11f80ee..ad4850a 100644 --- a/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol +++ b/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol @@ -49,11 +49,6 @@ abstract contract BatchAsync is QueueAsync { 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_); @@ -75,7 +70,7 @@ abstract contract BatchAsync is QueueAsync { function deliverPayload( PayloadDetails[] memory payloadDetails_, FeesData memory feesData_, - uint256 auctionEndDelayMS_ + uint256 auctionEndDelay_ ) internal returns (bytes32) { address forwarderAppGateway = msg.sender; bytes32 asyncId = getCurrentAsyncId(); @@ -122,6 +117,10 @@ abstract contract BatchAsync is QueueAsync { ); } + if (readEndIndex == payloadDetails_.length) { + return asyncId; + } + // Process remaining payloads normally for (uint256 i = readEndIndex; i < payloadDetails_.length; i++) { if (payloadDetails_[i].payload.length > 24.5 * 1024) @@ -134,8 +133,6 @@ abstract contract BatchAsync is QueueAsync { address(this), payloadDetails_[i].chainSlug ); - - // todo: encode for deployContract } else if (payloadDetails_[i].callType == CallType.WRITE) { // todo: if need to remove this forwarderAppGateway = IAddressResolver(addressResolver) @@ -153,29 +150,31 @@ abstract contract BatchAsync is QueueAsync { this.callback.selector, abi.encode(asyncId) ); - - // todo: rename payloadDetailsArrays to payloadDetails_ - payloadDetailsArrays[asyncId].push(payloadDetails_[i]); + + payloadBatchDetails[asyncId].push(payloadDetails_[i]); } - // todo: merge all async id vars in one struct - totalPayloadsRemaining[asyncId] = payloadDetails_.length - readEndIndex; payloadBatches[asyncId] = PayloadBatch({ appGateway: forwarderAppGateway, feesData: feesData_, currentPayloadIndex: readEndIndex, - auctionEndDelayMS: auctionEndDelayMS_, - isBatchCancelled: false + auctionEndDelaySeconds: auctionEndDelay_, + winningBid: Bid({ + fee: 0, + transmitter: address(0), + extraData: new bytes(0) + }), + isBatchCancelled: false, + totalPayloadsRemaining: payloadDetails_.length - readEndIndex }); IAuctionManager(auctionManager).startAuction(asyncId); - emit PayloadSubmitted( asyncId, forwarderAppGateway, payloadDetails_, feesData_, - auctionEndDelayMS_ + auctionEndDelay_ ); return asyncId; } @@ -214,45 +213,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 diff --git a/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol b/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol index b046b8b..a3bef77 100644 --- a/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol @@ -19,16 +19,15 @@ abstract contract QueueAsync is AddressResolverUtil { 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; + + // todo: merge all async id vars in one struct // asyncId => PayloadDetails[] - mapping(bytes32 => PayloadDetails[]) public payloadDetailsArrays; + mapping(bytes32 => PayloadDetails[]) public payloadBatchDetails; error InvalidPromise(); @@ -132,7 +131,11 @@ abstract contract QueueAsync is AddressResolverUtil { 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 diff --git a/contracts/common/Structs.sol b/contracts/common/Structs.sol index 71bac17..b955c12 100644 --- a/contracts/common/Structs.sol +++ b/contracts/common/Structs.sol @@ -48,6 +48,8 @@ struct PayloadBatch { FeesData feesData; uint256 currentPayloadIndex; uint256 auctionEndDelaySeconds; + uint256 totalPayloadsRemaining; + Bid winningBid; bool isBatchCancelled; } diff --git a/contracts/socket/Socket.sol b/contracts/socket/Socket.sol index 44aad5d..681b728 100644 --- a/contracts/socket/Socket.sol +++ b/contracts/socket/Socket.sol @@ -40,6 +40,7 @@ contract Socket is SocketBase { */ error LowGasLimit(); error InvalidSlug(); + error InvalidSwitchboard(); //////////////////////////////////////////////////////////// ////////////////////// State Vars ////////////////////////// @@ -82,8 +83,9 @@ contract Socket is SocketBase { // extract plug address from msgID address switchboard = _decodeSwitchboard(payloadId_); uint32 localSlug = _decodeChainSlug(payloadId_); - // todo: check if switchboard is same as config in plug + (, address configSwitchboard) = _plugConfigs[target_]; + if (switchboard != configSwitchboard) revert InvalidSwitchboard(); if (localSlug != chainSlug) revert InvalidSlug(); address transmitter = signatureVerifier__.recoverSigner( From 853ec3306231bae7bad358624eb1448376c589d0 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Fri, 27 Dec 2024 01:08:14 +0530 Subject: [PATCH 3/9] feat: parallel payload processing --- .../app-gateway/DeliveryHelper.sol | 116 +++++++++++++----- 1 file changed, 84 insertions(+), 32 deletions(-) diff --git a/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol index 9a4251f..ca48170 100644 --- a/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol @@ -53,7 +53,7 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { payloadDetails_ ); - PayloadDetails storage payloadDetails = payloadDetailsArrays[ + PayloadDetails storage payloadDetails = payloadBatchDetails[ asyncId ][payloadBatch.currentPayloadIndex]; @@ -69,52 +69,104 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { /// @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_]; - PayloadDetails storage payloadDetails = payloads[currentPayloadIndex]; - - bytes32 payloadId; - bytes32 root; - - if (payloadDetails.callType == CallType.READ) { - // Find consecutive READ calls - uint256 readEndIndex = currentPayloadIndex; - while (readEndIndex + 1 < payloads.length && - payloads[readEndIndex + 1].callType == CallType.READ) { + uint256 currentIndex = payloadBatch.currentPayloadIndex; + PayloadDetails[] storage payloads = payloadBatchDetails[asyncId_]; + + // Find consecutive reads + if (payloads[currentIndex].callType == CallType.READ) { + uint256 readEndIndex = currentIndex; + while ( + readEndIndex + 1 < payloads.length && + payloads[readEndIndex + 1].callType == CallType.READ + ) { readEndIndex++; } - // Create a batched read promise + // Process all reads together address batchPromise = IAddressResolver(addressResolver) .deployAsyncPromiseContract(address(this)); isValidPromise[batchPromise] = true; - // Process all reads in the batch - for (uint256 i = currentPayloadIndex; i <= readEndIndex; i++) { - payloadId = watcherPrecompile().query( + for (uint256 i = currentIndex; i <= readEndIndex; i++) { + bytes32 payloadId = watcherPrecompile().query( payloads[i].chainSlug, payloads[i].target, - [batchPromise, address(0)], // Use same promise for all reads in batch + [batchPromise, address(0)], payloads[i].payload ); payloadIdToBatchHash[payloadId] = asyncId_; + emit PayloadAsyncRequested( + asyncId_, + payloadId, + bytes32(0), + payloads[i] + ); } - // Skip the batched payloads - payloadBatch.currentPayloadIndex = readEndIndex; - } else { - FinalizeParams memory finalizeParams = FinalizeParams({ - payloadDetails: payloadDetails, - transmitter: IAuctionManager(auctionManager).winningBids(asyncId_).transmitter - }); - - (payloadId, root) = watcherPrecompile().finalize(finalizeParams); - payloadIdToBatchHash[payloadId] = asyncId_; - payloadBatch.currentPayloadIndex = currentPayloadIndex; + totalPayloadsRemaining[asyncId_] -= (readEndIndex - + currentIndex + + 1); + payloadBatch.currentPayloadIndex = readEndIndex + 1; + return; } - emit PayloadAsyncRequested(asyncId_, payloadId, root, payloadDetails); + // Find consecutive parallel non-read calls + if (!payloads[currentIndex].isSequential) { + uint256 parallelEndIndex = currentIndex; + while ( + parallelEndIndex + 1 < payloads.length && + !payloads[parallelEndIndex + 1].isSequential + ) { + parallelEndIndex++; + } + + // Process all parallel calls together + address parallelPromise = IAddressResolver(addressResolver) + .deployAsyncPromiseContract(address(this)); + isValidPromise[parallelPromise] = true; + + for (uint256 i = currentIndex; i <= parallelEndIndex; i++) { + FinalizeParams memory finalizeParams = FinalizeParams({ + payloadDetails: payloads[i], + transmitter: winningBids[asyncId_].transmitter + }); + + (bytes32 payloadId, bytes32 root) = watcherPrecompile() + .finalize(finalizeParams); + payloadIdToBatchHash[payloadId] = asyncId_; + emit PayloadAsyncRequested( + asyncId_, + payloadId, + root, + payloads[i] + ); + } + + totalPayloadsRemaining[asyncId_] -= (parallelEndIndex - + currentIndex + + 1); + payloadBatch.currentPayloadIndex = parallelEndIndex + 1; + return; + } + + // Process single sequential call + FinalizeParams memory finalizeParams = FinalizeParams({ + payloadDetails: payloads[currentIndex], + transmitter: winningBids[asyncId_].transmitter + }); + + (bytes32 payloadId, bytes32 root) = watcherPrecompile().finalize( + finalizeParams + ); + payloadIdToBatchHash[payloadId] = asyncId_; + emit PayloadAsyncRequested( + asyncId_, + payloadId, + root, + payloads[currentIndex] + ); + + totalPayloadsRemaining[asyncId_]--; + payloadBatch.currentPayloadIndex++; } } From 82e427a139b24ec77d9d190616471b907d258a8c Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 31 Dec 2024 02:03:23 +0530 Subject: [PATCH 4/9] fix: merge auction manager and contracts --- .../AuctionContracts/FirstBidAuction.sol | 21 ---------------- .../app-gateway/AuctionManager.sol | 24 ++++--------------- contracts/interfaces/IAuctionContract.sol | 11 --------- contracts/interfaces/IAuctionHouse.sol | 6 ++--- contracts/interfaces/IAuctionManager.sol | 4 ++-- 5 files changed, 9 insertions(+), 57 deletions(-) delete mode 100644 contracts/apps/payload-delivery/app-gateway/AuctionContracts/FirstBidAuction.sol delete mode 100644 contracts/interfaces/IAuctionContract.sol 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 5b9b555..0000000 --- a/contracts/apps/payload-delivery/app-gateway/AuctionContracts/FirstBidAuction.sol +++ /dev/null @@ -1,21 +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 3be9b44..91e8592 100644 --- a/contracts/apps/payload-delivery/app-gateway/AuctionManager.sol +++ b/contracts/apps/payload-delivery/app-gateway/AuctionManager.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} from "../../../common/Structs.sol"; -import {IAuctionContract} from "../../../interfaces/IAuctionContract.sol"; import {IAuctionHouse} from "../../../interfaces/IAuctionHouse.sol"; /// @title AuctionHouse @@ -17,6 +16,8 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) { 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 @@ -31,17 +32,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_); - // todo: fix auction contract address - uint256 auctionEndDelaySeconds = IAuctionContract(address(this)) - .auctionEndDelaySeconds(); - watcherPrecompile().setTimeout( abi.encodeWithSelector(this.endAuction.selector, asyncId_), auctionEndDelaySeconds @@ -55,6 +52,7 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) { function bid( bytes32 asyncId_, uint256 fee, + FeesData memory feesData, bytes memory transmitterSignature, bytes memory extraData ) external { @@ -72,20 +70,8 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) { extraData: extraData }); - // todo: fix auction contract address - (address auctionContract, FeesData memory feesData) = AuctionHouse() - .getAuctionContractAndFeesData(asyncId_); - require(fee <= feesData.maxFees, "Bid exceeds max fees"); - - // todo: revisit if should revert or not - 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/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..178199b 100644 --- a/contracts/interfaces/IAuctionHouse.sol +++ b/contracts/interfaces/IAuctionHouse.sol @@ -24,7 +24,7 @@ interface IAuctionHouse { function queue( uint32 chainSlug_, address target_, - bytes32 asyncPromiseOrId_, + address asyncPromise_, CallType callType_, bytes memory payload_ ) external; @@ -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; From a2680489b865b3294ec76fa6331b7919fee4c3e2 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 31 Dec 2024 02:05:14 +0530 Subject: [PATCH 5/9] fix: todos --- contracts/AddressResolver.sol | 46 +++-------------- contracts/Forwarder.sol | 2 +- .../app-gateway/BatchAsync.sol | 16 +++--- .../app-gateway/FeesManager.sol | 4 +- .../app-gateway/QueueAsync.sol | 51 ++----------------- contracts/base/AppDeployerBase.sol | 41 ++++++++++----- contracts/base/AppGatewayBase.sol | 12 ++--- contracts/common/Structs.sol | 2 +- 8 files changed, 56 insertions(+), 118 deletions(-) diff --git a/contracts/AddressResolver.sol b/contracts/AddressResolver.sol index 741290e..2f1d8f8 100644 --- a/contracts/AddressResolver.sol +++ b/contracts/AddressResolver.sol @@ -111,51 +111,17 @@ contract AddressResolver is Ownable, IAddressResolver { revert(0, 0) } } + + // todo: what to do? + // _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/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/payload-delivery/app-gateway/BatchAsync.sol b/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol index ad4850a..17b618a 100644 --- a/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol +++ b/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol @@ -18,7 +18,6 @@ abstract contract BatchAsync is QueueAsync { error AllPayloadsExecuted(); error NotFromForwarder(); error CallFailed(bytes32 payloadId); - error DelayLimitReached(); error PayloadTooLarge(); event PayloadSubmitted( bytes32 indexed asyncId, @@ -42,16 +41,13 @@ abstract contract BatchAsync is QueueAsync { /// @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(); // 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 @@ -70,7 +66,7 @@ abstract contract BatchAsync is QueueAsync { function deliverPayload( PayloadDetails[] memory payloadDetails_, FeesData memory feesData_, - uint256 auctionEndDelay_ + address auctionManager_ ) internal returns (bytes32) { address forwarderAppGateway = msg.sender; bytes32 asyncId = getCurrentAsyncId(); @@ -158,7 +154,7 @@ abstract contract BatchAsync is QueueAsync { appGateway: forwarderAppGateway, feesData: feesData_, currentPayloadIndex: readEndIndex, - auctionEndDelaySeconds: auctionEndDelay_, + auctionManager: auctionManager_, winningBid: Bid({ fee: 0, transmitter: address(0), @@ -168,13 +164,13 @@ abstract contract BatchAsync is QueueAsync { totalPayloadsRemaining: payloadDetails_.length - readEndIndex }); - IAuctionManager(auctionManager).startAuction(asyncId); + IAuctionManager(auctionManager_).startAuction(asyncId); emit PayloadSubmitted( asyncId, forwarderAppGateway, payloadDetails_, feesData_, - auctionEndDelay_ + auctionManager_ ); return asyncId; } diff --git a/contracts/apps/payload-delivery/app-gateway/FeesManager.sol b/contracts/apps/payload-delivery/app-gateway/FeesManager.sol index 0aa1715..c6f6df2 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( diff --git a/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol b/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol index a3bef77..57081f0 100644 --- a/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol @@ -25,7 +25,6 @@ abstract contract QueueAsync is AddressResolverUtil { // asyncId => PayloadBatch mapping(bytes32 => PayloadBatch) public payloadBatches; - // todo: merge all async id vars in one struct // asyncId => PayloadDetails[] mapping(bytes32 => PayloadDetails[]) public payloadBatchDetails; @@ -60,14 +59,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 { @@ -75,7 +74,7 @@ abstract contract QueueAsync is AddressResolverUtil { callParamsArray.push( CallParams({ callType: callType_, - asyncPromiseOrId: asyncPromiseOrId_, + asyncPromise: asyncPromise_, chainSlug: chainSlug_, target: target_, payload: payload_, @@ -109,28 +108,14 @@ 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.encodeWithSelector( IContractFactoryPlug.deployContract.selector, params.payload, @@ -151,30 +136,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)); - - // todo: use getOrDeploy here - 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..a1fc4fb 100644 --- a/contracts/base/AppDeployerBase.sol +++ b/contracts/base/AppDeployerBase.sol @@ -19,27 +19,44 @@ 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 = IAddressResolver(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( + abi.decode(returnData_, (address)), + chainSlug + ); + + forwarderAddresses[contractId][chainSlug] = forwarderContractAddress; } /// @notice Gets the on-chain address diff --git a/contracts/base/AppGatewayBase.sol b/contracts/base/AppGatewayBase.sol index 1f727c8..c5befa2 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 diff --git a/contracts/common/Structs.sol b/contracts/common/Structs.sol index 8b4a7ad..5077a56 100644 --- a/contracts/common/Structs.sol +++ b/contracts/common/Structs.sol @@ -30,7 +30,7 @@ struct DeployParams { struct CallParams { CallType callType; - bytes32 asyncPromiseOrId; + address asyncPromise; uint32 chainSlug; address target; uint256 gasLimit; From 9ff8adffaea6f21ecd496dbbca4fb23d6ad951d5 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 31 Dec 2024 03:07:52 +0530 Subject: [PATCH 6/9] fix: batch and on complete call --- .../app-gateway/BatchAsync.sol | 47 +-- .../app-gateway/DeliveryHelper.sol | 287 ++++++++++-------- 2 files changed, 186 insertions(+), 148 deletions(-) diff --git a/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol b/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol index 17b618a..1e8d6c9 100644 --- a/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol +++ b/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol @@ -56,7 +56,21 @@ abstract contract BatchAsync is QueueAsync { function callback( bytes memory asyncId_, bytes memory payloadDetails_ - ) external virtual onlyPromises {} + ) external virtual onlyPromises { + bytes32 asyncId = abi.decode(asyncId_, (bytes32)); + PayloadBatch storage payloadBatch = payloadBatches[asyncId]; + if (payloadBatch.isBatchCancelled) return; + + if (totalPayloadsRemaining[asyncId] > 0) { + _finalizeNextPayload(asyncId); + } else { + // Notify app gateway about batch completion + IAppGateway(payloadBatch.appGateway).onBatchComplete( + asyncId, + payloadBatchDetails[asyncId] + ); + } + } /// @notice Delivers a payload batch /// @param payloadDetails_ The payload details @@ -72,7 +86,7 @@ abstract contract BatchAsync is QueueAsync { bytes32 asyncId = getCurrentAsyncId(); asyncCounter++; - // Check for consecutive reads at the start + // Handle initial read operations first uint256 readEndIndex = 0; while ( readEndIndex < payloadDetails_.length && @@ -81,23 +95,20 @@ abstract contract BatchAsync is QueueAsync { readEndIndex++; } - // Process consecutive reads together if there are 1 or more - if (readEndIndex >= 1) { - // Create a batched read promise + // Process initial reads if any exist + if (readEndIndex > 0) { address batchPromise = IAddressResolver(addressResolver) .deployAsyncPromiseContract(address(this)); isValidPromise[batchPromise] = true; - // Process all reads in the batch for (uint256 i = 0; i < readEndIndex; i++) { bytes32 payloadId = watcherPrecompile().query( payloadDetails_[i].chainSlug, payloadDetails_[i].target, - [batchPromise, address(0)], // Use same promise for all reads in batch + [batchPromise, address(0)], payloadDetails_[i].payload ); payloadIdToBatchHash[payloadId] = asyncId; - emit PayloadAsyncRequested( asyncId, payloadId, @@ -106,50 +117,39 @@ abstract contract BatchAsync is QueueAsync { ); } - // Setup callback for the entire batch of reads IPromise(batchPromise).then( this.callback.selector, abi.encode(asyncId) ); } + // If only reads, return early if (readEndIndex == payloadDetails_.length) { return asyncId; } - // Process remaining payloads normally + // 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) { - // considering contract factory as plug of delivery helper payloadDetails_[i].target = getPlugAddress( address(this), payloadDetails_[i].chainSlug ); } else if (payloadDetails_[i].callType == CallType.WRITE) { - // todo: if need to remove this 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) - ); - payloadBatchDetails[asyncId].push(payloadDetails_[i]); } + // Initialize batch payloadBatches[asyncId] = PayloadBatch({ appGateway: forwarderAppGateway, feesData: feesData_, @@ -164,6 +164,7 @@ abstract contract BatchAsync is QueueAsync { totalPayloadsRemaining: payloadDetails_.length - readEndIndex }); + // Start auction IAuctionManager(auctionManager_).startAuction(asyncId); emit PayloadSubmitted( asyncId, diff --git a/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol index ca48170..81e4d6e 100644 --- a/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol @@ -7,166 +7,203 @@ 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 function startBatchProcessing( bytes32 asyncId_ ) external onlyAuctionManager { - PayloadBatch storage payloadBatch = payloadBatches[asyncId_]; - if (payloadBatch.isBatchCancelled) return; - - _finalizeNextPayload(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_); + } + + function _process(bytes32 asyncId, bytes memory payloadDetails_) internal { PayloadBatch storage payloadBatch = payloadBatches[asyncId]; if (payloadBatch.isBatchCancelled) return; - uint256 payloadsRemaining = totalPayloadsRemaining[asyncId]; - - if (payloadsRemaining > 0) { - payloadBatch.currentPayloadIndex++; + if (totalPayloadsRemaining[asyncId] > 0) { _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, + winningBids[asyncId] ); - PayloadDetails storage payloadDetails = payloadBatchDetails[ - asyncId - ][payloadBatch.currentPayloadIndex]; + payloadIdToBatchHash[payloadId] = asyncId; + emit PayloadAsyncRequested(asyncId, payloadId, root, payloadDetails_); - if (payloadDetails.callType == CallType.DEPLOY) { - IAppGateway(payloadBatch.appGateway).allContractsDeployed( - payloadDetails.chainSlug - ); - } - } + PayloadDetails storage lastPayload = payloadBatchDetails[asyncId][ + payloadBatch.currentPayloadIndex + ]; + + IAppGateway(payloadBatch.appGateway).onBatchComplete( + asyncId, + payloadBatchDetails[asyncId] + ); } - /// @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 currentIndex = payloadBatch.currentPayloadIndex; PayloadDetails[] storage payloads = payloadBatchDetails[asyncId_]; - // Find consecutive reads + // Early return if batch is empty or completed + if (currentIndex >= payloads.length) return; + + // 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_) + ); + + // Handle batch processing based on type if (payloads[currentIndex].callType == CallType.READ) { - uint256 readEndIndex = currentIndex; - while ( - readEndIndex + 1 < payloads.length && - payloads[readEndIndex + 1].callType == CallType.READ - ) { - readEndIndex++; - } - - // Process all reads together - address batchPromise = IAddressResolver(addressResolver) - .deployAsyncPromiseContract(address(this)); - isValidPromise[batchPromise] = true; - - for (uint256 i = currentIndex; i <= readEndIndex; i++) { - bytes32 payloadId = watcherPrecompile().query( - payloads[i].chainSlug, - payloads[i].target, - [batchPromise, address(0)], - payloads[i].payload - ); - payloadIdToBatchHash[payloadId] = asyncId_; - emit PayloadAsyncRequested( - asyncId_, - payloadId, - bytes32(0), - payloads[i] - ); - } - - totalPayloadsRemaining[asyncId_] -= (readEndIndex - - currentIndex + - 1); - payloadBatch.currentPayloadIndex = readEndIndex + 1; - return; + _processBatchedReads( + asyncId_, + payloadBatch, + payloads, + currentIndex, + batchPromise + ); + } else if (!payloads[currentIndex].isSequential) { + _processParallelCalls( + asyncId_, + payloadBatch, + payloads, + currentIndex, + batchPromise + ); + } else { + _processSequentialCall( + asyncId_, + payloadBatch, + payloads, + currentIndex, + batchPromise + ); + } + } + + function _executeWatcherCall( + bytes32 asyncId_, + PayloadDetails storage payload, + address batchPromise, + bool isRead + ) internal returns (bytes32 payloadId, bytes32 root) { + if (isRead) { + payloadId = watcherPrecompile().query( + payload.chainSlug, + payload.target, + [batchPromise, address(0)], + payload.payload + ); + root = bytes32(0); + } else { + FinalizeParams memory finalizeParams = FinalizeParams({ + payloadDetails: payload, + transmitter: winningBids[asyncId_].transmitter + }); + (payloadId, root) = watcherPrecompile().finalize(finalizeParams); } - // Find consecutive parallel non-read calls - if (!payloads[currentIndex].isSequential) { - uint256 parallelEndIndex = currentIndex; - while ( - parallelEndIndex + 1 < payloads.length && - !payloads[parallelEndIndex + 1].isSequential - ) { - parallelEndIndex++; - } - - // Process all parallel calls together - address parallelPromise = IAddressResolver(addressResolver) - .deployAsyncPromiseContract(address(this)); - isValidPromise[parallelPromise] = true; - - for (uint256 i = currentIndex; i <= parallelEndIndex; i++) { - FinalizeParams memory finalizeParams = FinalizeParams({ - payloadDetails: payloads[i], - transmitter: winningBids[asyncId_].transmitter - }); - - (bytes32 payloadId, bytes32 root) = watcherPrecompile() - .finalize(finalizeParams); - payloadIdToBatchHash[payloadId] = asyncId_; - emit PayloadAsyncRequested( - asyncId_, - payloadId, - root, - payloads[i] - ); - } - - totalPayloadsRemaining[asyncId_] -= (parallelEndIndex - - currentIndex + - 1); - payloadBatch.currentPayloadIndex = parallelEndIndex + 1; - return; + 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++; } - // Process single sequential call - FinalizeParams memory finalizeParams = FinalizeParams({ - payloadDetails: payloads[currentIndex], - transmitter: winningBids[asyncId_].transmitter - }); + for (uint256 i = startIndex; i <= endIndex; i++) { + _executeWatcherCall(asyncId_, payloads[i], batchPromise, true); + } - (bytes32 payloadId, bytes32 root) = watcherPrecompile().finalize( - finalizeParams + _updateBatchState( + payloadBatch, + endIndex - startIndex + 1, + endIndex + 1 ); - payloadIdToBatchHash[payloadId] = asyncId_; - emit PayloadAsyncRequested( + } + + 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++; + } + + for (uint256 i = startIndex; i <= endIndex; i++) { + _executeWatcherCall(asyncId_, payloads[i], batchPromise, false); + } + + _updateBatchState( + payloadBatch, + endIndex - startIndex + 1, + endIndex + 1 + ); + } + + function _processSequentialCall( + bytes32 asyncId_, + PayloadBatch storage payloadBatch, + PayloadDetails[] storage payloads, + uint256 currentIndex, + address batchPromise + ) internal { + _executeWatcherCall( asyncId_, - payloadId, - root, - payloads[currentIndex] + payloads[currentIndex], + batchPromise, + false ); + _updateBatchState(payloadBatch, 1, currentIndex + 1); + } - totalPayloadsRemaining[asyncId_]--; - payloadBatch.currentPayloadIndex++; + function _updateBatchState( + PayloadBatch storage batch, + uint256 completedCount, + uint256 nextIndex + ) internal { + totalPayloadsRemaining[asyncId_] -= completedCount; + batch.currentPayloadIndex = nextIndex; } } From 8c5d7ea0efc8deaa13056171c7e754edf6b75398 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Tue, 31 Dec 2024 13:06:03 +0530 Subject: [PATCH 7/9] fix: build errors --- .../counter/app-gateway/CounterComposer.sol | 8 +-- .../app-gateway/BatchAsync.sol | 27 +++------- .../app-gateway/DeliveryHelper.sol | 51 ++++++++++++++----- .../app-gateway/FeesManager.sol | 6 ++- .../app-gateway/QueueAsync.sol | 11 +--- contracts/base/AppDeployerBase.sol | 19 ++++--- contracts/base/AppGatewayBase.sol | 8 +-- contracts/common/Structs.sol | 7 ++- contracts/interfaces/IAddressResolver.sol | 6 +-- contracts/interfaces/IAppDeployer.sol | 10 ---- contracts/interfaces/IAppGateway.sol | 6 ++- contracts/interfaces/IAuctionHouse.sol | 2 +- contracts/interfaces/IContractFactoryPlug.sol | 24 +++++++++ contracts/socket/Socket.sol | 7 +-- contracts/socket/SocketBatcher.sol | 1 + 15 files changed, 110 insertions(+), 83 deletions(-) create mode 100644 contracts/interfaces/IContractFactoryPlug.sol 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/app-gateway/BatchAsync.sol b/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol index 1e8d6c9..d75ed46 100644 --- a/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol +++ b/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol @@ -24,7 +24,7 @@ abstract contract BatchAsync is QueueAsync { address indexed appGateway, PayloadDetails[] payloads, FeesData feesData, - uint256 auctionEndDelay + address auctionManager ); event PayloadAsyncRequested( @@ -37,7 +37,7 @@ 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_, @@ -56,26 +56,12 @@ abstract contract BatchAsync is QueueAsync { function callback( bytes memory asyncId_, bytes memory payloadDetails_ - ) external virtual onlyPromises { - bytes32 asyncId = abi.decode(asyncId_, (bytes32)); - PayloadBatch storage payloadBatch = payloadBatches[asyncId]; - if (payloadBatch.isBatchCancelled) return; - - if (totalPayloadsRemaining[asyncId] > 0) { - _finalizeNextPayload(asyncId); - } else { - // Notify app gateway about batch completion - IAppGateway(payloadBatch.appGateway).onBatchComplete( - asyncId, - payloadBatchDetails[asyncId] - ); - } - } + ) 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_, @@ -102,10 +88,11 @@ abstract contract BatchAsync is QueueAsync { isValidPromise[batchPromise] = true; for (uint256 i = 0; i < readEndIndex; i++) { + payloadDetails_[i].next[1] = batchPromise; bytes32 payloadId = watcherPrecompile().query( payloadDetails_[i].chainSlug, payloadDetails_[i].target, - [batchPromise, address(0)], + payloadDetails_[i].next, payloadDetails_[i].payload ); payloadIdToBatchHash[payloadId] = asyncId; @@ -234,6 +221,6 @@ abstract contract BatchAsync is QueueAsync { amount_, receiver_ ); - deliverPayload(payloadDetailsArray, feesData_, 0); + deliverPayload(payloadDetailsArray, feesData_, address(0)); } } diff --git a/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol b/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol index 81e4d6e..9e170ba 100644 --- a/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol @@ -8,6 +8,15 @@ import {DISTRIBUTE_FEE, DEPLOY} from "../../../common/Constants.sol"; import "./BatchAsync.sol"; contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { + constructor( + address _addressResolver, + address _auctionManager, + address _feesManager + ) AddressResolverUtil(_addressResolver) { + auctionManager = _auctionManager; + feesManager = _feesManager; + } + function startBatchProcessing( bytes32 asyncId_ ) external onlyAuctionManager { @@ -26,7 +35,7 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { PayloadBatch storage payloadBatch = payloadBatches[asyncId]; if (payloadBatch.isBatchCancelled) return; - if (totalPayloadsRemaining[asyncId] > 0) { + if (payloadBatch.totalPayloadsRemaining > 0) { _finalizeNextPayload(asyncId); } else { _finishBatch(asyncId, payloadBatch, payloadDetails_); @@ -42,19 +51,20 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { .distributeFees( payloadBatch.appGateway, payloadBatch.feesData, - winningBids[asyncId] + payloadBatch.winningBid ); payloadIdToBatchHash[payloadId] = asyncId; - emit PayloadAsyncRequested(asyncId, payloadId, root, payloadDetails_); - - PayloadDetails storage lastPayload = payloadBatchDetails[asyncId][ - payloadBatch.currentPayloadIndex - ]; + emit PayloadAsyncRequested( + asyncId, + payloadId, + root, + abi.decode(payloadDetails_, (PayloadDetails)) + ); IAppGateway(payloadBatch.appGateway).onBatchComplete( asyncId, - payloadBatchDetails[asyncId] + payloadBatch ); } @@ -106,21 +116,23 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { 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( payload.chainSlug, payload.target, - [batchPromise, address(0)], + payload.next, payload.payload ); root = bytes32(0); } else { FinalizeParams memory finalizeParams = FinalizeParams({ payloadDetails: payload, - transmitter: winningBids[asyncId_].transmitter + transmitter: payloadBatch.winningBid.transmitter }); (payloadId, root) = watcherPrecompile().finalize(finalizeParams); } @@ -146,7 +158,13 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { } for (uint256 i = startIndex; i <= endIndex; i++) { - _executeWatcherCall(asyncId_, payloads[i], batchPromise, true); + _executeWatcherCall( + asyncId_, + payloads[i], + payloadBatch, + batchPromise, + true + ); } _updateBatchState( @@ -172,7 +190,13 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { } for (uint256 i = startIndex; i <= endIndex; i++) { - _executeWatcherCall(asyncId_, payloads[i], batchPromise, false); + _executeWatcherCall( + asyncId_, + payloads[i], + payloadBatch, + batchPromise, + false + ); } _updateBatchState( @@ -192,6 +216,7 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { _executeWatcherCall( asyncId_, payloads[currentIndex], + payloadBatch, batchPromise, false ); @@ -203,7 +228,7 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { uint256 completedCount, uint256 nextIndex ) internal { - totalPayloadsRemaining[asyncId_] -= completedCount; + batch.totalPayloadsRemaining -= completedCount; batch.currentPayloadIndex = nextIndex; } } diff --git a/contracts/apps/payload-delivery/app-gateway/FeesManager.sol b/contracts/apps/payload-delivery/app-gateway/FeesManager.sol index c6f6df2..a998441 100644 --- a/contracts/apps/payload-delivery/app-gateway/FeesManager.sol +++ b/contracts/apps/payload-delivery/app-gateway/FeesManager.sol @@ -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 57081f0..b5f777b 100644 --- a/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol @@ -8,6 +8,7 @@ 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 { @@ -24,7 +25,6 @@ abstract contract QueueAsync is AddressResolverUtil { mapping(bytes32 => bytes32) public payloadIdToBatchHash; // asyncId => PayloadBatch mapping(bytes32 => PayloadBatch) public payloadBatches; - // asyncId => PayloadDetails[] mapping(bytes32 => PayloadDetails[]) public payloadBatchDetails; @@ -42,15 +42,6 @@ abstract contract QueueAsync is AddressResolverUtil { _; } - constructor( - address _addressResolver, - address _auctionManager, - address _feesManager - ) AddressResolverUtil(_addressResolver) { - auctionManager = _auctionManager; - feesManager = _feesManager; - } - /// @notice Clears the call parameters array function clearQueue() public { delete callParamsArray; diff --git a/contracts/base/AppDeployerBase.sol b/contracts/base/AppDeployerBase.sol index a1fc4fb..0fb09af 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,8 +20,9 @@ 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 = IAddressResolver(addressResolver) - .deployAsyncPromiseContract(address(this)); + address asyncPromise = addressResolver.deployAsyncPromiseContract( + address(this) + ); isValidPromise[asyncPromise] = true; IPromise(asyncPromise).then( @@ -76,13 +78,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 c5befa2..64fb731 100644 --- a/contracts/base/AppGatewayBase.sol +++ b/contracts/base/AppGatewayBase.sol @@ -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 5077a56..e03b57e 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 { @@ -35,6 +36,7 @@ struct CallParams { address target; uint256 gasLimit; bytes payload; + bool isSequential; } struct Bid { @@ -47,10 +49,10 @@ struct PayloadBatch { address appGateway; FeesData feesData; uint256 currentPayloadIndex; - uint256 auctionEndDelaySeconds; - uint256 totalPayloadsRemaining; + address auctionManager; Bid winningBid; bool isBatchCancelled; + uint256 totalPayloadsRemaining; } struct FinalizeParams { @@ -112,6 +114,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..6f6f6e2 100644 --- a/contracts/interfaces/IAddressResolver.sol +++ b/contracts/interfaces/IAddressResolver.sol @@ -66,13 +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( - address appDeployer_, + function getOrDeployForwarderContract( address chainContractAddress_, uint32 chainSlug_ ) external returns (address); 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/IAuctionHouse.sol b/contracts/interfaces/IAuctionHouse.sol index 178199b..0ed583f 100644 --- a/contracts/interfaces/IAuctionHouse.sol +++ b/contracts/interfaces/IAuctionHouse.sol @@ -31,7 +31,7 @@ interface IAuctionHouse { function batch( FeesData memory feesData_, - uint256 auctionEndDelaySeconds_ + address auctionManager_ ) external returns (bytes32); function withdrawTo( 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/socket/Socket.sol b/contracts/socket/Socket.sol index 0045735..ea916c9 100644 --- a/contracts/socket/Socket.sol +++ b/contracts/socket/Socket.sol @@ -40,7 +40,6 @@ contract Socket is SocketBase { */ error LowGasLimit(); error InvalidSlug(); - error InvalidSwitchboard(); //////////////////////////////////////////////////////////// ////////////////////// State Vars ////////////////////////// @@ -111,8 +110,10 @@ contract Socket is SocketBase { address switchboard = _decodeSwitchboard(payloadId_); uint32 localSlug = _decodeChainSlug(payloadId_); - (, address configSwitchboard) = _plugConfigs[target_]; - if (switchboard != configSwitchboard) revert InvalidSwitchboard(); + 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 From fa185bb206375d6a11baee0ca56adafc34a03512 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 1 Jan 2025 15:12:23 +0530 Subject: [PATCH 8/9] fix: check if prev resolved --- contracts/AsyncPromise.sol | 10 ++--- .../app-gateway/BatchAsync.sol | 10 ++++- .../app-gateway/DeliveryHelper.sol | 45 ++++++++++++++++--- contracts/common/Structs.sol | 1 + contracts/interfaces/IPromise.sol | 2 + 5 files changed, 54 insertions(+), 14 deletions(-) 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/apps/payload-delivery/app-gateway/BatchAsync.sol b/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol index d75ed46..5fcb6ef 100644 --- a/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol +++ b/contracts/apps/payload-delivery/app-gateway/BatchAsync.sol @@ -81,14 +81,18 @@ abstract contract BatchAsync is QueueAsync { readEndIndex++; } + 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; 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, @@ -148,7 +152,8 @@ abstract contract BatchAsync is QueueAsync { extraData: new bytes(0) }), isBatchCancelled: false, - totalPayloadsRemaining: payloadDetails_.length - readEndIndex + totalPayloadsRemaining: payloadDetails_.length - readEndIndex, + lastBatchPromises: lastBatchPromises }); // Start auction @@ -211,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); @@ -221,6 +227,6 @@ abstract contract BatchAsync is QueueAsync { amount_, receiver_ ); - deliverPayload(payloadDetailsArray, feesData_, address(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 9e170ba..33e5703 100644 --- a/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol +++ b/contracts/apps/payload-delivery/app-gateway/DeliveryHelper.sol @@ -10,16 +10,14 @@ import "./BatchAsync.sol"; contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { constructor( address _addressResolver, - address _auctionManager, address _feesManager ) AddressResolverUtil(_addressResolver) { - auctionManager = _auctionManager; feesManager = _feesManager; } function startBatchProcessing( bytes32 asyncId_ - ) external onlyAuctionManager { + ) external onlyAuctionManager(asyncId_) { _process(asyncId_, ""); } @@ -28,14 +26,37 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { 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; + // 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; + } + + // Proceed with next payload only if all promises are resolved _finalizeNextPayload(asyncId); } else { _finishBatch(asyncId, payloadBatch, payloadDetails_); @@ -73,9 +94,6 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { uint256 currentIndex = payloadBatch.currentPayloadIndex; PayloadDetails[] storage payloads = payloadBatchDetails[asyncId_]; - // Early return if batch is empty or completed - if (currentIndex >= payloads.length) return; - // Deploy single promise for the next batch of operations address batchPromise = IAddressResolver(addressResolver) .deployAsyncPromiseContract(address(this)); @@ -157,7 +175,9 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { 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], @@ -169,6 +189,7 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { _updateBatchState( payloadBatch, + promises, endIndex - startIndex + 1, endIndex + 1 ); @@ -189,7 +210,11 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { 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], @@ -201,6 +226,7 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { _updateBatchState( payloadBatch, + promises, endIndex - startIndex + 1, endIndex + 1 ); @@ -213,6 +239,9 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { uint256 currentIndex, address batchPromise ) internal { + address[] memory promises = new address[](1); + promises[0] = payloads[currentIndex].next[0]; + _executeWatcherCall( asyncId_, payloads[currentIndex], @@ -220,15 +249,17 @@ contract DeliveryHelper is BatchAsync, Ownable(msg.sender) { batchPromise, false ); - _updateBatchState(payloadBatch, 1, currentIndex + 1); + _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/common/Structs.sol b/contracts/common/Structs.sol index e03b57e..21003c3 100644 --- a/contracts/common/Structs.sol +++ b/contracts/common/Structs.sol @@ -53,6 +53,7 @@ struct PayloadBatch { Bid winningBid; bool isBatchCancelled; uint256 totalPayloadsRemaining; + address[] lastBatchPromises; } struct FinalizeParams { 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); } From 5a75aed9a67c35f6f176e09c14095559898c8258 Mon Sep 17 00:00:00 2001 From: Ameesha Agrawal Date: Wed, 1 Jan 2025 15:13:34 +0530 Subject: [PATCH 9/9] fix: build and todos --- contracts/AddressResolver.sol | 4 ++-- contracts/apps/payload-delivery/ContractFactoryPlug.sol | 5 +++++ .../apps/payload-delivery/app-gateway/AuctionManager.sol | 8 ++++++-- .../apps/payload-delivery/app-gateway/QueueAsync.sol | 8 +++----- contracts/base/AppDeployerBase.sol | 1 + contracts/interfaces/IAddressResolver.sol | 1 + contracts/socket/Socket.sol | 1 - contracts/socket/switchboard/FastSwitchboard.sol | 4 ---- 8 files changed, 18 insertions(+), 14 deletions(-) diff --git a/contracts/AddressResolver.sol b/contracts/AddressResolver.sol index 2f1d8f8..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) { @@ -112,8 +113,7 @@ contract AddressResolver is Ownable, IAddressResolver { } } - // todo: what to do? - // _setConfig(appDeployer_, newForwarder); + _setConfig(appDeployer_, newForwarder); emit ForwarderDeployed(newForwarder, salt); return newForwarder; } 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/AuctionManager.sol b/contracts/apps/payload-delivery/app-gateway/AuctionManager.sol index 91e8592..77f57db 100644 --- a/contracts/apps/payload-delivery/app-gateway/AuctionManager.sol +++ b/contracts/apps/payload-delivery/app-gateway/AuctionManager.sol @@ -11,6 +11,7 @@ import {IAuctionHouse} from "../../../interfaces/IAuctionHouse.sol"; /// @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; @@ -22,9 +23,11 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) { /// @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_; } @@ -58,9 +61,10 @@ contract AuctionManager is AddressResolverUtil, Ownable(msg.sender) { ) external { require(!auctionClosed[asyncId_], "Auction closed"); - // todo: check chain slug for multiple offchain vm address transmitter = signatureVerifier__.recoverSigner( - keccak256(abi.encode(address(this), asyncId_, fee, extraData)), + keccak256( + abi.encode(address(this), vmChainSlug, asyncId_, fee, extraData) + ), transmitterSignature ); diff --git a/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol b/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol index b5f777b..c9646d8 100644 --- a/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol +++ b/contracts/apps/payload-delivery/app-gateway/QueueAsync.sol @@ -14,8 +14,6 @@ import {IContractFactoryPlug} from "../../../interfaces/IContractFactoryPlug.sol abstract contract QueueAsync is AddressResolverUtil { uint256 public saltCounter; uint256 public asyncCounter; - - address public auctionManager; address public feesManager; CallParams[] public callParamsArray; @@ -27,7 +25,6 @@ abstract contract QueueAsync is AddressResolverUtil { mapping(bytes32 => PayloadBatch) public payloadBatches; // asyncId => PayloadDetails[] mapping(bytes32 => PayloadDetails[]) public payloadBatchDetails; - error InvalidPromise(); modifier onlyPromises() { @@ -37,8 +34,9 @@ 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(); _; } diff --git a/contracts/base/AppDeployerBase.sol b/contracts/base/AppDeployerBase.sol index 0fb09af..0954527 100644 --- a/contracts/base/AppDeployerBase.sol +++ b/contracts/base/AppDeployerBase.sol @@ -54,6 +54,7 @@ abstract contract AppDeployerBase is AppGatewayBase, IAppDeployer { address forwarderContractAddress = addressResolver .getOrDeployForwarderContract( + address(this), abi.decode(returnData_, (address)), chainSlug ); diff --git a/contracts/interfaces/IAddressResolver.sol b/contracts/interfaces/IAddressResolver.sol index 6f6f6e2..05f9ba4 100644 --- a/contracts/interfaces/IAddressResolver.sol +++ b/contracts/interfaces/IAddressResolver.sol @@ -71,6 +71,7 @@ interface IAddressResolver { /// @param chainSlug_ The identifier of the destination chain /// @return The address of the newly deployed forwarder contract function getOrDeployForwarderContract( + address appDeployer_, address chainContractAddress_, uint32 chainSlug_ ) external returns (address); diff --git a/contracts/socket/Socket.sol b/contracts/socket/Socket.sol index ea916c9..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( 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_