diff --git a/suave/sol/libraries/Suave.sol b/suave/sol/libraries/Suave.sol index 3522bbc94..cc5d0808e 100644 --- a/suave/sol/libraries/Suave.sol +++ b/suave/sol/libraries/Suave.sol @@ -4,11 +4,11 @@ pragma solidity ^0.8.8; library Suave { error PeekerReverted(address, bytes); - type BidId is bytes16; + type DataId is bytes16; - struct Bid { - BidId id; - BidId salt; + struct DataRecord { + DataId id; + DataId salt; uint64 decryptionCondition; address[] allowedPeekers; address[] allowedStores; @@ -50,11 +50,11 @@ library Suave { address public constant EXTRACT_HINT = 0x0000000000000000000000000000000042100037; - address public constant FETCH_BIDS = 0x0000000000000000000000000000000042030001; + address public constant FETCH_DATA = 0x0000000000000000000000000000000042030001; address public constant FILL_MEV_SHARE_BUNDLE = 0x0000000000000000000000000000000043200001; - address public constant NEW_BID = 0x0000000000000000000000000000000042030000; + address public constant NEW_DATA = 0x0000000000000000000000000000000042030000; address public constant SIGN_ETH_TRANSACTION = 0x0000000000000000000000000000000040100001; @@ -78,12 +78,12 @@ library Suave { } } - function buildEthBlock(BuildBlockArgs memory blockArgs, BidId bidId, string memory namespace) + function buildEthBlock(BuildBlockArgs memory blockArgs, DataId dataId, string memory namespace) internal view returns (bytes memory, bytes memory) { - (bool success, bytes memory data) = BUILD_ETH_BLOCK.staticcall(abi.encode(blockArgs, bidId, namespace)); + (bool success, bytes memory data) = BUILD_ETH_BLOCK.staticcall(abi.encode(blockArgs, dataId, namespace)); if (!success) { revert PeekerReverted(BUILD_ETH_BLOCK, data); } @@ -100,8 +100,8 @@ library Suave { return data; } - function confidentialRetrieve(BidId bidId, string memory key) internal view returns (bytes memory) { - (bool success, bytes memory data) = CONFIDENTIAL_RETRIEVE.staticcall(abi.encode(bidId, key)); + function confidentialRetrieve(DataId dataId, string memory key) internal view returns (bytes memory) { + (bool success, bytes memory data) = CONFIDENTIAL_RETRIEVE.staticcall(abi.encode(dataId, key)); if (!success) { revert PeekerReverted(CONFIDENTIAL_RETRIEVE, data); } @@ -109,8 +109,8 @@ library Suave { return data; } - function confidentialStore(BidId bidId, string memory key, bytes memory data1) internal view { - (bool success, bytes memory data) = CONFIDENTIAL_STORE.staticcall(abi.encode(bidId, key, data1)); + function confidentialStore(DataId dataId, string memory key, bytes memory data1) internal view { + (bool success, bytes memory data) = CONFIDENTIAL_STORE.staticcall(abi.encode(dataId, key, data1)); if (!success) { revert PeekerReverted(CONFIDENTIAL_STORE, data); } @@ -135,18 +135,18 @@ library Suave { return data; } - function fetchBids(uint64 cond, string memory namespace) internal view returns (Bid[] memory) { - (bool success, bytes memory data) = FETCH_BIDS.staticcall(abi.encode(cond, namespace)); + function fetchData(uint64 cond, string memory namespace) internal view returns (DataRecord[] memory) { + (bool success, bytes memory data) = FETCH_DATA.staticcall(abi.encode(cond, namespace)); if (!success) { - revert PeekerReverted(FETCH_BIDS, data); + revert PeekerReverted(FETCH_DATA, data); } - return abi.decode(data, (Bid[])); + return abi.decode(data, (DataRecord[])); } - function fillMevShareBundle(BidId bidId) internal view returns (bytes memory) { + function fillMevShareBundle(DataId dataId) internal view returns (bytes memory) { require(isConfidential()); - (bool success, bytes memory data) = FILL_MEV_SHARE_BUNDLE.staticcall(abi.encode(bidId)); + (bool success, bytes memory data) = FILL_MEV_SHARE_BUNDLE.staticcall(abi.encode(dataId)); if (!success) { revert PeekerReverted(FILL_MEV_SHARE_BUNDLE, data); } @@ -154,19 +154,19 @@ library Suave { return data; } - function newBid( + function newDataRecord( uint64 decryptionCondition, address[] memory allowedPeekers, address[] memory allowedStores, - string memory bidType - ) internal view returns (Bid memory) { + string memory dataType + ) internal view returns (DataRecord memory) { (bool success, bytes memory data) = - NEW_BID.staticcall(abi.encode(decryptionCondition, allowedPeekers, allowedStores, bidType)); + NEW_DATA.staticcall(abi.encode(decryptionCondition, allowedPeekers, allowedStores, dataType)); if (!success) { - revert PeekerReverted(NEW_BID, data); + revert PeekerReverted(NEW_DATA, data); } - return abi.decode(data, (Bid)); + return abi.decode(data, (DataRecord)); } function signEthTransaction(bytes memory txn, string memory chainId, string memory signingKey) diff --git a/suave/sol/standard_peekers/bids.sol b/suave/sol/standard_peekers/bids.sol index 4c866a5a7..dcfb00ac9 100644 --- a/suave/sol/standard_peekers/bids.sol +++ b/suave/sol/standard_peekers/bids.sol @@ -2,358 +2,325 @@ pragma solidity ^0.8.8; import "../libraries/Suave.sol"; -contract AnyBidContract { - event BidEvent(Suave.BidId bidId, uint64 decryptionCondition, address[] allowedPeekers); +contract AnyBundleContract { + + event DataRecordEvent( + Suave.DataId dataId, + uint64 decryptionCondition, + address[] allowedPeekers + ); + + function fetchConfidentialBundleData() public returns (bytes memory) { + require(Suave.isConfidential()); + + bytes memory confidentialInputs = Suave.confidentialInputs(); + return abi.decode(confidentialInputs, (bytes)); + } + + // Bids to this contract should not be trusted! + function emitDataRecord(Suave.DataRecord calldata dataRecord) public { + emit DataRecordEvent(dataRecord.id, dataRecord.decryptionCondition, dataRecord.allowedPeekers); + } +} - function fetchBidConfidentialBundleData() public returns (bytes memory) { - require(Suave.isConfidential()); +contract BundleContract is AnyBundleContract { - bytes memory confidentialInputs = Suave.confidentialInputs(); - return abi.decode(confidentialInputs, (bytes)); - } + function newBundle(uint64 decryptionCondition, address[] memory dataAllowedPeekers, address[] memory dataAllowedStores) external payable returns (bytes memory) { + require(Suave.isConfidential()); - // Bids to this contract should not be trusted! - function emitBid(Suave.Bid calldata bid) public { - emit BidEvent(bid.id, bid.decryptionCondition, bid.allowedPeekers); - } -} + bytes memory bundleData = this.fetchConfidentialBundleData(); + + uint64 egp = Suave.simulateBundle(bundleData); -contract BundleBidContract is AnyBidContract { - function newBid(uint64 decryptionCondition, address[] memory bidAllowedPeekers, address[] memory bidAllowedStores) - external - payable - returns (bytes memory) - { - require(Suave.isConfidential()); + Suave.DataRecord memory dataRecord = Suave.newDataRecord(decryptionCondition, dataAllowedPeekers, dataAllowedStores, "default:v0:ethBundles"); - bytes memory bundleData = this.fetchBidConfidentialBundleData(); + Suave.confidentialStore(dataRecord.id, "default:v0:ethBundles", bundleData); + Suave.confidentialStore(dataRecord.id, "default:v0:ethBundleSimResults", abi.encode(egp)); - uint64 egp = Suave.simulateBundle(bundleData); + return emitAndReturn(dataRecord, bundleData); + } - Suave.Bid memory bid = - Suave.newBid(decryptionCondition, bidAllowedPeekers, bidAllowedStores, "default:v0:ethBundles"); + function emitAndReturn(Suave.DataRecord memory dataRecord, bytes memory) internal virtual returns (bytes memory) { + emit DataRecordEvent(dataRecord.id, dataRecord.decryptionCondition, dataRecord.allowedPeekers); + return bytes.concat(this.emitDataRecord.selector, abi.encode(dataRecord)); + } +} - Suave.confidentialStore(bid.id, "default:v0:ethBundles", bundleData); - Suave.confidentialStore(bid.id, "default:v0:ethBundleSimResults", abi.encode(egp)); +contract EthBundleSenderContract is BundleContract { + string[] public builderUrls; - return emitAndReturn(bid, bundleData); - } + constructor(string[] memory builderUrls_) { + builderUrls = builderUrls_; + } - function emitAndReturn(Suave.Bid memory bid, bytes memory) internal virtual returns (bytes memory) { - emit BidEvent(bid.id, bid.decryptionCondition, bid.allowedPeekers); - return bytes.concat(this.emitBid.selector, abi.encode(bid)); - } -} + function emitAndReturn(Suave.DataRecord memory dataRecord, bytes memory bundleData) internal virtual override returns (bytes memory) { + for (uint i = 0; i < builderUrls.length; i++) { + Suave.submitBundleJsonRPC(builderUrls[i], "eth_sendBundle", bundleData); + } -contract EthBundleSenderContract is BundleBidContract { - string[] public builderUrls; - - constructor(string[] memory builderUrls_) { - builderUrls = builderUrls_; - } - - function emitAndReturn(Suave.Bid memory bid, bytes memory bundleData) - internal - virtual - override - returns (bytes memory) - { - for (uint256 i = 0; i < builderUrls.length; i++) { - Suave.submitBundleJsonRPC(builderUrls[i], "eth_sendBundle", bundleData); - } - - return BundleBidContract.emitAndReturn(bid, bundleData); - } + return BundleContract.emitAndReturn(dataRecord, bundleData); + } } -contract MevShareBidContract is AnyBidContract { - event HintEvent(Suave.BidId bidId, bytes hint); - - event MatchEvent(Suave.BidId matchBidId, bytes matchHint); - - function newBid(uint64 decryptionCondition, address[] memory bidAllowedPeekers, address[] memory bidAllowedStores) - external - payable - returns (bytes memory) - { - // 0. check confidential execution - require(Suave.isConfidential()); - - // 1. fetch bundle data - bytes memory bundleData = this.fetchBidConfidentialBundleData(); - - // 2. sim bundle - uint64 egp = Suave.simulateBundle(bundleData); - - // 3. extract hint - bytes memory hint = Suave.extractHint(bundleData); - - // // 4. store bundle and sim results - Suave.Bid memory bid = - Suave.newBid(decryptionCondition, bidAllowedPeekers, bidAllowedStores, "mevshare:v0:unmatchedBundles"); - Suave.confidentialStore(bid.id, "mevshare:v0:ethBundles", bundleData); - Suave.confidentialStore(bid.id, "mevshare:v0:ethBundleSimResults", abi.encode(egp)); - emit BidEvent(bid.id, bid.decryptionCondition, bid.allowedPeekers); - emit HintEvent(bid.id, hint); - - // // 5. return "callback" to emit hint onchain - return bytes.concat(this.emitBidAndHint.selector, abi.encode(bid, hint)); - } - - function emitBidAndHint(Suave.Bid calldata bid, bytes memory hint) public { - emit BidEvent(bid.id, bid.decryptionCondition, bid.allowedPeekers); - emit HintEvent(bid.id, hint); - } - - function newMatch( - uint64 decryptionCondition, - address[] memory bidAllowedPeekers, - address[] memory bidAllowedStores, - Suave.BidId shareBidId - ) external payable returns (bytes memory) { - // WARNING : this function will copy the original mev share bid - // into a new key with potentially different permsissions - - require(Suave.isConfidential()); - // 1. fetch confidential data - bytes memory matchBundleData = this.fetchBidConfidentialBundleData(); - - // 2. sim match alone for validity - uint64 egp = Suave.simulateBundle(matchBundleData); - - // 3. extract hint - bytes memory matchHint = Suave.extractHint(matchBundleData); - - Suave.Bid memory bid = - Suave.newBid(decryptionCondition, bidAllowedPeekers, bidAllowedStores, "mevshare:v0:matchBids"); - Suave.confidentialStore(bid.id, "mevshare:v0:ethBundles", matchBundleData); - Suave.confidentialStore(bid.id, "mevshare:v0:ethBundleSimResults", abi.encode(0)); - - //4. merge bids - Suave.BidId[] memory bids = new Suave.BidId[](2); - bids[0] = shareBidId; - bids[1] = bid.id; - Suave.confidentialStore(bid.id, "mevshare:v0:mergedBids", abi.encode(bids)); - - return emitMatchBidAndHint(bid, matchHint); - } - - function emitMatchBidAndHint(Suave.Bid memory bid, bytes memory matchHint) - internal - virtual - returns (bytes memory) - { - emit BidEvent(bid.id, bid.decryptionCondition, bid.allowedPeekers); - emit MatchEvent(bid.id, matchHint); - - return bytes.concat(this.emitBid.selector, abi.encode(bid)); - } +contract MevShareBidContract is AnyBundleContract { + + event HintEvent( + Suave.DataId dataId, + bytes hint + ); + + event MatchEvent( + Suave.DataId matchDataId, + bytes matchHint + ); + + function newTransaction(uint64 decryptionCondition, address[] memory dataAllowedPeekers, address[] memory dataAllowedStores) external payable returns (bytes memory) { + // 0. check confidential execution + require(Suave.isConfidential()); + + // 1. fetch bundle data + bytes memory bundleData = this.fetchConfidentialBundleData(); + + // 2. sim bundle + uint64 egp = Suave.simulateBundle(bundleData); + + // 3. extract hint + bytes memory hint = Suave.extractHint(bundleData); + + // // 4. store bundle and sim results + Suave.DataRecord memory dataRecord = Suave.newDataRecord(decryptionCondition, dataAllowedPeekers, dataAllowedStores, "mevshare:v0:unmatchedBundles"); + Suave.confidentialStore(dataRecord.id, "mevshare:v0:ethBundles", bundleData); + Suave.confidentialStore(dataRecord.id, "mevshare:v0:ethBundleSimResults", abi.encode(egp)); + emit DataRecordEvent(dataRecord.id, dataRecord.decryptionCondition, dataRecord.allowedPeekers); + emit HintEvent(dataRecord.id, hint); + + // // 5. return "callback" to emit hint onchain + return bytes.concat(this.emitDataRecordAndHint.selector, abi.encode(dataRecord, hint)); + } + + function emitDataRecordAndHint(Suave.DataRecord calldata dataRecord, bytes memory hint) public { + emit DataRecordEvent(dataRecord.id, dataRecord.decryptionCondition, dataRecord.allowedPeekers); + emit HintEvent(dataRecord.id, hint); + } + + function newMatch(uint64 decryptionCondition, address[] memory dataAllowedPeekers, address[] memory dataAllowedStores, Suave.DataId sharedataId) external payable returns (bytes memory) { + // WARNING : this function will copy the original mev share bid + // into a new key with potentially different permsissions + + require(Suave.isConfidential()); + // 1. fetch confidential data + bytes memory matchBundleData = this.fetchConfidentialBundleData(); + + // 2. sim match alone for validity + uint64 egp = Suave.simulateBundle(matchBundleData); + + // 3. extract hint + bytes memory matchHint = Suave.extractHint(matchBundleData); + + Suave.DataRecord memory dataRecord = Suave.newDataRecord(decryptionCondition, dataAllowedPeekers, dataAllowedStores, "mevshare:v0:matchBids"); + Suave.confidentialStore(dataRecord.id, "mevshare:v0:ethBundles", matchBundleData); + Suave.confidentialStore(dataRecord.id, "mevshare:v0:ethBundleSimResults", abi.encode(0)); + + //4. merge bids + Suave.DataId[] memory dataRecords = new Suave.DataId[](2); + dataRecords[0] = sharedataId; + dataRecords[1] = dataRecord.id; + Suave.confidentialStore(dataRecord.id, "mevshare:v0:mergedDataRecords", abi.encode(dataRecords)); + + return emitMatchDataRecordAndHint(dataRecord, matchHint); + } + + function emitMatchDataRecordAndHint(Suave.DataRecord memory dataRecord, bytes memory matchHint) internal virtual returns (bytes memory) { + emit DataRecordEvent(dataRecord.id, dataRecord.decryptionCondition, dataRecord.allowedPeekers); + emit MatchEvent(dataRecord.id, matchHint); + + return bytes.concat(this.emitDataRecord.selector, abi.encode(dataRecord)); + } } contract MevShareBundleSenderContract is MevShareBidContract { - string[] public builderUrls; - - constructor(string[] memory builderUrls_) { - builderUrls = builderUrls_; - } - - function emitMatchBidAndHint(Suave.Bid memory bid, bytes memory matchHint) - internal - virtual - override - returns (bytes memory) - { - bytes memory bundleData = Suave.fillMevShareBundle(bid.id); - for (uint256 i = 0; i < builderUrls.length; i++) { - Suave.submitBundleJsonRPC(builderUrls[i], "mev_sendBundle", bundleData); - } - - return MevShareBidContract.emitMatchBidAndHint(bid, matchHint); - } + string[] public builderUrls; + + constructor(string[] memory builderUrls_) { + builderUrls = builderUrls_; + } + + function emitMatchDataRecordAndHint(Suave.DataRecord memory dataRecord, bytes memory matchHint) internal virtual override returns (bytes memory) { + bytes memory bundleData = Suave.fillMevShareBundle(dataRecord.id); + for (uint i = 0; i < builderUrls.length; i++) { + Suave.submitBundleJsonRPC(builderUrls[i], "mev_sendBundle", bundleData); + } + + return MevShareBidContract.emitMatchDataRecordAndHint(dataRecord, matchHint); + } } /* Not tested or implemented on the precompile side */ struct EgpBidPair { - uint64 egp; // in wei, beware overflow - Suave.BidId bidId; + uint64 egp; // in wei, beware overflow + Suave.DataId dataId; } -contract EthBlockBidContract is AnyBidContract { - event BuilderBoostBidEvent(Suave.BidId bidId, bytes builderBid); - - function idsEqual(Suave.BidId _l, Suave.BidId _r) public pure returns (bool) { - bytes memory l = abi.encodePacked(_l); - bytes memory r = abi.encodePacked(_r); - for (uint256 i = 0; i < l.length; i++) { - if (bytes(l)[i] != r[i]) { - return false; - } - } - - return true; - } - - function buildMevShare(Suave.BuildBlockArgs memory blockArgs, uint64 blockHeight) public returns (bytes memory) { - require(Suave.isConfidential()); - - Suave.Bid[] memory allShareMatchBids = Suave.fetchBids(blockHeight, "mevshare:v0:matchBids"); - Suave.Bid[] memory allShareUserBids = Suave.fetchBids(blockHeight, "mevshare:v0:unmatchedBundles"); - - if (allShareUserBids.length == 0) { - revert Suave.PeekerReverted(address(this), "no bids"); - } - - Suave.Bid[] memory allBids = new Suave.Bid[](allShareUserBids.length); - for (uint256 i = 0; i < allShareUserBids.length; i++) { - // TODO: sort matches by egp first! - Suave.Bid memory bidToInsert = allShareUserBids[i]; // will be updated with the best match if any - for (uint256 j = 0; j < allShareMatchBids.length; j++) { - // TODO: should be done once at the start and sorted - Suave.BidId[] memory mergedBidIds = abi.decode( - Suave.confidentialRetrieve(allShareMatchBids[j].id, "mevshare:v0:mergedBids"), (Suave.BidId[]) - ); - if (idsEqual(mergedBidIds[0], allShareUserBids[i].id)) { - bidToInsert = allShareMatchBids[j]; - break; - } - } - allBids[i] = bidToInsert; - } - - EgpBidPair[] memory bidsByEGP = new EgpBidPair[](allBids.length); - for (uint256 i = 0; i < allBids.length; i++) { - bytes memory simResults = Suave.confidentialRetrieve(allBids[i].id, "mevshare:v0:ethBundleSimResults"); - uint64 egp = abi.decode(simResults, (uint64)); - bidsByEGP[i] = EgpBidPair(egp, allBids[i].id); - } - - // Bubble sort, cause why not - uint256 n = bidsByEGP.length; - for (uint256 i = 0; i < n - 1; i++) { - for (uint256 j = i + 1; j < n; j++) { - if (bidsByEGP[i].egp < bidsByEGP[j].egp) { - EgpBidPair memory temp = bidsByEGP[i]; - bidsByEGP[i] = bidsByEGP[j]; - bidsByEGP[j] = temp; - } - } - } - - Suave.BidId[] memory allBidIds = new Suave.BidId[](allBids.length); - for (uint256 i = 0; i < bidsByEGP.length; i++) { - allBidIds[i] = bidsByEGP[i].bidId; - } - - return buildAndEmit(blockArgs, blockHeight, allBidIds, "mevshare:v0"); - } - - function buildFromPool(Suave.BuildBlockArgs memory blockArgs, uint64 blockHeight) public returns (bytes memory) { - require(Suave.isConfidential()); - - Suave.Bid[] memory allBids = Suave.fetchBids(blockHeight, "default:v0:ethBundles"); - if (allBids.length == 0) { - revert Suave.PeekerReverted(address(this), "no bids"); - } - - EgpBidPair[] memory bidsByEGP = new EgpBidPair[](allBids.length); - for (uint256 i = 0; i < allBids.length; i++) { - bytes memory simResults = Suave.confidentialRetrieve(allBids[i].id, "default:v0:ethBundleSimResults"); - uint64 egp = abi.decode(simResults, (uint64)); - bidsByEGP[i] = EgpBidPair(egp, allBids[i].id); - } - - // Bubble sort, cause why not - uint256 n = bidsByEGP.length; - for (uint256 i = 0; i < n - 1; i++) { - for (uint256 j = i + 1; j < n; j++) { - if (bidsByEGP[i].egp < bidsByEGP[j].egp) { - EgpBidPair memory temp = bidsByEGP[i]; - bidsByEGP[i] = bidsByEGP[j]; - bidsByEGP[j] = temp; - } - } - } - - Suave.BidId[] memory allBidIds = new Suave.BidId[](allBids.length); - for (uint256 i = 0; i < bidsByEGP.length; i++) { - allBidIds[i] = bidsByEGP[i].bidId; - } - - return buildAndEmit(blockArgs, blockHeight, allBidIds, ""); - } - - function buildAndEmit( - Suave.BuildBlockArgs memory blockArgs, - uint64 blockHeight, - Suave.BidId[] memory bids, - string memory namespace - ) public virtual returns (bytes memory) { - require(Suave.isConfidential()); - - (Suave.Bid memory blockBid, bytes memory builderBid) = this.doBuild(blockArgs, blockHeight, bids, namespace); - - emit BuilderBoostBidEvent(blockBid.id, builderBid); - emit BidEvent(blockBid.id, blockBid.decryptionCondition, blockBid.allowedPeekers); - return bytes.concat(this.emitBuilderBidAndBid.selector, abi.encode(blockBid, builderBid)); - } - - function doBuild( - Suave.BuildBlockArgs memory blockArgs, - uint64 blockHeight, - Suave.BidId[] memory bids, - string memory namespace - ) public view returns (Suave.Bid memory, bytes memory) { - address[] memory allowedPeekers = new address[](2); - allowedPeekers[0] = address(this); - allowedPeekers[1] = Suave.BUILD_ETH_BLOCK; - - Suave.Bid memory blockBid = Suave.newBid(blockHeight, allowedPeekers, allowedPeekers, "default:v0:mergedBids"); - Suave.confidentialStore(blockBid.id, "default:v0:mergedBids", abi.encode(bids)); - - (bytes memory builderBid, bytes memory payload) = Suave.buildEthBlock(blockArgs, blockBid.id, namespace); - Suave.confidentialStore(blockBid.id, "default:v0:builderPayload", payload); // only through this.unlock - - return (blockBid, builderBid); - } - - function emitBuilderBidAndBid(Suave.Bid memory bid, bytes memory builderBid) - public - returns (Suave.Bid memory, bytes memory) - { - emit BuilderBoostBidEvent(bid.id, builderBid); - emit BidEvent(bid.id, bid.decryptionCondition, bid.allowedPeekers); - return (bid, builderBid); - } - - function unlock(Suave.BidId bidId, bytes memory signedBlindedHeader) public view returns (bytes memory) { - require(Suave.isConfidential()); - - // TODO: verify the header is correct - // TODO: incorporate protocol name - bytes memory payload = Suave.confidentialRetrieve(bidId, "default:v0:builderPayload"); - return payload; - } +contract EthBlockBidContract is AnyBundleContract { + + event BuilderBoostBidEvent( + Suave.DataId dataId, + bytes builderBid + ); + + function idsEqual(Suave.DataId _l, Suave.DataId _r) public pure returns (bool) { + bytes memory l = abi.encodePacked(_l); + bytes memory r = abi.encodePacked(_r); + for (uint i = 0; i < l.length; i++) { + if (bytes(l)[i] != r[i]) { + return false; + } + } + + return true; + } + + function buildMevShare(Suave.BuildBlockArgs memory blockArgs, uint64 blockHeight) public returns (bytes memory) { + require(Suave.isConfidential()); + + Suave.DataRecord[] memory allShareMatchDataRecords = Suave.fetchData(blockHeight, "mevshare:v0:matchBids"); + Suave.DataRecord[] memory allShareUserDataRecords = Suave.fetchData(blockHeight, "mevshare:v0:unmatchedBundles"); + + if (allShareUserDataRecords.length == 0) { + revert Suave.PeekerReverted(address(this), "no bids"); + } + + Suave.DataRecord[] memory allBids = new Suave.DataRecord[](allShareUserDataRecords.length); + for (uint i = 0; i < allShareUserDataRecords.length; i++) { + // TODO: sort matches by egp first! + Suave.DataRecord memory dataRecordToInsert = allShareUserDataRecords[i]; // will be updated with the best match if any + for (uint j = 0; j < allShareMatchDataRecords.length; j++) { + // TODO: should be done once at the start and sorted + Suave.DataId[] memory mergeddataIds = abi.decode(Suave.confidentialRetrieve(allShareMatchDataRecords[j].id, "mevshare:v0:mergedBids"), (Suave.DataId[])); + if (idsEqual(mergeddataIds[0], allShareUserDataRecords[i].id)) { + dataRecordToInsert = allShareMatchDataRecords[j]; + break; + } + } + allBids[i] = dataRecordToInsert; + } + + EgpBidPair[] memory bidsByEGP = new EgpBidPair[](allBids.length); + for (uint i = 0; i < allBids.length; i++) { + bytes memory simResults = Suave.confidentialRetrieve(allBids[i].id, "mevshare:v0:ethBundleSimResults"); + uint64 egp = abi.decode(simResults, (uint64)); + bidsByEGP[i] = EgpBidPair(egp, allBids[i].id); + } + + // Bubble sort, cause why not + uint n = bidsByEGP.length; + for (uint i = 0; i < n - 1; i++) { + for (uint j = i + 1; j < n; j++) { + if (bidsByEGP[i].egp < bidsByEGP[j].egp) { + EgpBidPair memory temp = bidsByEGP[i]; + bidsByEGP[i] = bidsByEGP[j]; + bidsByEGP[j] = temp; + } + } + } + + Suave.DataId[] memory alldataIds = new Suave.DataId[](allBids.length); + for (uint i = 0; i < bidsByEGP.length; i++) { + alldataIds[i] = bidsByEGP[i].dataId; + } + + return buildAndEmit(blockArgs, blockHeight, alldataIds, "mevshare:v0"); + } + + function buildFromPool(Suave.BuildBlockArgs memory blockArgs, uint64 blockHeight) public returns (bytes memory) { + require(Suave.isConfidential()); + + Suave.DataRecord[] memory allBids = Suave.fetchData(blockHeight, "default:v0:ethBundles"); + if (allBids.length == 0) { + revert Suave.PeekerReverted(address(this), "no bids"); + } + + EgpBidPair[] memory bidsByEGP = new EgpBidPair[](allBids.length); + for (uint i = 0; i < allBids.length; i++) { + bytes memory simResults = Suave.confidentialRetrieve(allBids[i].id, "default:v0:ethBundleSimResults"); + uint64 egp = abi.decode(simResults, (uint64)); + bidsByEGP[i] = EgpBidPair(egp, allBids[i].id); + } + + // Bubble sort, cause why not + uint n = bidsByEGP.length; + for (uint i = 0; i < n - 1; i++) { + for (uint j = i + 1; j < n; j++) { + if (bidsByEGP[i].egp < bidsByEGP[j].egp) { + EgpBidPair memory temp = bidsByEGP[i]; + bidsByEGP[i] = bidsByEGP[j]; + bidsByEGP[j] = temp; + } + } + } + + Suave.DataId[] memory alldataIds = new Suave.DataId[](allBids.length); + for (uint i = 0; i < bidsByEGP.length; i++) { + alldataIds[i] = bidsByEGP[i].dataId; + } + + return buildAndEmit(blockArgs, blockHeight, alldataIds, ""); + } + + function buildAndEmit(Suave.BuildBlockArgs memory blockArgs, uint64 blockHeight, Suave.DataId[] memory bids, string memory namespace) public virtual returns (bytes memory) { + require(Suave.isConfidential()); + + (Suave.DataRecord memory blockBid, bytes memory builderBid) = this.doBuild(blockArgs, blockHeight, bids, namespace); + + emit BuilderBoostBidEvent(blockBid.id, builderBid); + emit DataRecordEvent(blockBid.id, blockBid.decryptionCondition, blockBid.allowedPeekers); + return bytes.concat(this.emitBuilderBidAndBid.selector, abi.encode(blockBid, builderBid)); + } + + function doBuild(Suave.BuildBlockArgs memory blockArgs, uint64 blockHeight, Suave.DataId[] memory bids, string memory namespace) public view returns (Suave.DataRecord memory, bytes memory) { + address[] memory allowedPeekers = new address[](2); + allowedPeekers[0] = address(this); + allowedPeekers[1] = Suave.BUILD_ETH_BLOCK; + + Suave.DataRecord memory blockBid = Suave.newDataRecord(blockHeight, allowedPeekers, allowedPeekers, "default:v0:mergedBids"); + Suave.confidentialStore(blockBid.id, "default:v0:mergedBids", abi.encode(bids)); + + (bytes memory builderBid, bytes memory payload) = Suave.buildEthBlock(blockArgs, blockBid.id, namespace); + Suave.confidentialStore(blockBid.id, "default:v0:builderPayload", payload); // only through this.unlock + + return (blockBid, builderBid); + } + + function emitBuilderBidAndBid(Suave.DataRecord memory dataRecord, bytes memory builderBid) public returns (Suave.DataRecord memory, bytes memory) { + emit BuilderBoostBidEvent(dataRecord.id, builderBid); + emit DataRecordEvent(dataRecord.id, dataRecord.decryptionCondition, dataRecord.allowedPeekers); + return (dataRecord, builderBid); + } + + function unlock(Suave.DataId dataId, bytes memory signedBlindedHeader) public view returns (bytes memory) { + require(Suave.isConfidential()); + + // TODO: verify the header is correct + // TODO: incorporate protocol name + bytes memory payload = Suave.confidentialRetrieve(dataId, "default:v0:builderPayload"); + return payload; + } } contract EthBlockBidSenderContract is EthBlockBidContract { - string boostRelayUrl; - - constructor(string memory boostRelayUrl_) { - boostRelayUrl = boostRelayUrl_; - } - - function buildAndEmit( - Suave.BuildBlockArgs memory blockArgs, - uint64 blockHeight, - Suave.BidId[] memory bids, - string memory namespace - ) public virtual override returns (bytes memory) { - require(Suave.isConfidential()); - - (Suave.Bid memory blockBid, bytes memory builderBid) = this.doBuild(blockArgs, blockHeight, bids, namespace); - Suave.submitEthBlockBidToRelay(boostRelayUrl, builderBid); - - emit BidEvent(blockBid.id, blockBid.decryptionCondition, blockBid.allowedPeekers); - return bytes.concat(this.emitBid.selector, abi.encode(blockBid)); - } + string boostRelayUrl; + + constructor(string memory boostRelayUrl_) { + boostRelayUrl = boostRelayUrl_; + } + + function buildAndEmit(Suave.BuildBlockArgs memory blockArgs, uint64 blockHeight, Suave.DataId[] memory dataRecords, string memory namespace) public virtual override returns (bytes memory) { + require(Suave.isConfidential()); + + (Suave.DataRecord memory blockDataRecord, bytes memory builderBid) = this.doBuild(blockArgs, blockHeight, dataRecords, namespace); + Suave.submitEthBlockBidToRelay(boostRelayUrl, builderBid); + + emit DataRecordEvent(blockDataRecord.id, blockDataRecord.decryptionCondition, blockDataRecord.allowedPeekers); + return bytes.concat(this.emitDataRecord.selector, abi.encode(blockDataRecord)); + } }