From c8ba20988d1e68f1ce35736c74c7c3f7c50bc1c9 Mon Sep 17 00:00:00 2001 From: Sunur Efe Vural Date: Thu, 14 Dec 2023 15:10:29 +0300 Subject: [PATCH 1/3] forge install: leb128-nooffset v0.0.2 --- .gitmodules | 3 +++ lib/leb128-nooffset | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/leb128-nooffset diff --git a/.gitmodules b/.gitmodules index da5e829..e088721 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "lib/solady"] path = lib/solady url = https://github.com/Vectorized/solady +[submodule "lib/leb128-nooffset"] + path = lib/leb128-nooffset + url = https://github.com/Shungy/leb128-nooffset diff --git a/lib/leb128-nooffset b/lib/leb128-nooffset new file mode 160000 index 0000000..30bfb0e --- /dev/null +++ b/lib/leb128-nooffset @@ -0,0 +1 @@ +Subproject commit 30bfb0ee63e0ffaaaf6cd67126b433359ccdb9a8 From ddb94b9e91187c9197694617124f0a0001b28d8e Mon Sep 17 00:00:00 2001 From: Sunur Efe Vural Date: Thu, 14 Dec 2023 15:20:57 +0300 Subject: [PATCH 2/3] use leb128 library --- foundry.toml | 3 +- src/EncodedCalls.sol | 4 +- src/Zone.sol | 1 - src/libraries/LEB128.sol | 22 -------- test/EncodedCalls.t.sol | 16 +++--- test/LEB128.t.sol | 107 ------------------------------------ test/Zone.t.sol | 12 ---- test/utils/LEB128Encode.sol | 29 ---------- 8 files changed, 12 insertions(+), 182 deletions(-) delete mode 100644 src/libraries/LEB128.sol delete mode 100644 test/LEB128.t.sol delete mode 100644 test/utils/LEB128Encode.sol diff --git a/foundry.toml b/foundry.toml index 9dc29f3..63f4b38 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,7 +3,8 @@ remappings = [ "@openzeppelin/=lib/openzeppelin-contracts/contracts/", "permit2/=lib/permit2/", - "solady/=lib/solady/src/" + "solady/=lib/solady/src/", + "leb128=lib/leb128-nooffset/src/" ] solc_version = "0.8.23" evm_version = "paris" diff --git a/src/EncodedCalls.sol b/src/EncodedCalls.sol index a3892f6..51848c6 100644 --- a/src/EncodedCalls.sol +++ b/src/EncodedCalls.sol @@ -5,7 +5,7 @@ pragma solidity 0.8.23; import {IEncodedCalls} from "./interfaces/IEncodedCalls.sol"; // Libraries -import {LEB128} from "./libraries/LEB128.sol"; +import {LEB128Lib} from "leb128/LEB128Lib.sol"; abstract contract EncodedCalls is IEncodedCalls { address[] public decoders; @@ -23,7 +23,7 @@ abstract contract EncodedCalls is IEncodedCalls { // ensure such byte exists. // Get the decoded decoder ID, and the new calldata ptr. - (uint256 decoderId, uint256 ptr) = LEB128.rawDecodeUint({ptr: 1}); + (uint256 decoderId, uint256 ptr) = LEB128Lib.rawDecodeUint({ptr: 1}); // Get the decoder. address decoder = decoders[decoderId]; diff --git a/src/Zone.sol b/src/Zone.sol index f30530e..208e182 100644 --- a/src/Zone.sol +++ b/src/Zone.sol @@ -29,7 +29,6 @@ contract Zone is IAuthZone, AccessControlDefaultAdminRules, Pausable { } function setFee(FeeInfo calldata newFee) external onlyRole(DEFAULT_ADMIN_ROLE) { - if (newFee.bps > 500) revert FeeTooHigh(); _fee.bps = newFee.bps; _fee.recipient = newFee.recipient; emit FeeUpdated(newFee); diff --git a/src/libraries/LEB128.sol b/src/libraries/LEB128.sol deleted file mode 100644 index b1ecabd..0000000 --- a/src/libraries/LEB128.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -library LEB128 { - /// @dev Decodes an unsigned LEB128 encoded value, starting from calldata `ptr`. - /// See https://en.wikipedia.org/wiki/LEB128#Decode_unsigned_integer. - /// Note: Anything overflowing 256 bits is truncated silently without a revert. - /// Note: Superfluous zero padding can be used to inflate the length of the encoded data. - function rawDecodeUint(uint256 ptr) internal pure returns (uint256 result, uint256 newPtr) { - /// @solidity memory-safe-assembly - assembly { - for { let shift := 0 } 1 { shift := add(shift, 7) } { - let nextByte := byte(0, calldataload(ptr)) - result := or(result, shl(shift, and(nextByte, 0x7f))) - ptr := add(ptr, 1) - if shr(7, nextByte) { continue } - break - } - newPtr := ptr - } - } -} diff --git a/test/EncodedCalls.t.sol b/test/EncodedCalls.t.sol index b52f80f..e0b4e5f 100644 --- a/test/EncodedCalls.t.sol +++ b/test/EncodedCalls.t.sol @@ -6,8 +6,7 @@ import "./utils/FloodPlainTestShared.sol"; import {EncodedCalls} from "src/EncodedCalls.sol"; import {IEncodedCalls} from "src/interfaces/IEncodedCalls.sol"; -import {LEB128} from "src/libraries/LEB128.sol"; -import {LEB128Encode} from "./utils/LEB128Encode.sol"; +import {LEB128Lib} from "leb128/LEB128Lib.sol"; contract EncodedCallsContract is EncodedCalls { uint256 public val; @@ -24,13 +23,13 @@ contract MockDecoder { assembly { ptr := data.offset } - (uint256 a, uint256 newPtr) = LEB128.rawDecodeUint(ptr); + (uint256 a, uint256 newPtr) = LEB128Lib.rawDecodeUint(ptr); require(data.length >= newPtr - ptr, "DECODING_ERROR"); return abi.encodeWithSelector(EncodedCallsContract.changeVal.selector, a); } } -contract EncodedCallsTest is LEB128Encode, FloodPlainTestShared { +contract EncodedCallsTest is FloodPlainTestShared { EncodedCallsContract encodedCalls; MockDecoder decoder; @@ -66,7 +65,7 @@ contract EncodedCallsTest is LEB128Encode, FloodPlainTestShared { encodedCalls.addDecoder(address(0x69)); } uint256 decoderId = encodedCalls.addDecoder(address(decoder)); - bytes memory callData = abi.encodePacked(hex"00", _encode(decoderId), _encode(a)); + bytes memory callData = abi.encodePacked(hex"00", LEB128Lib.encode(decoderId), LEB128Lib.encode(a)); (bool success, bytes memory data) = address(encodedCalls).call(callData); assertTrue(success); @@ -77,7 +76,8 @@ contract EncodedCallsTest is LEB128Encode, FloodPlainTestShared { function test_invalidDecoderId() public { encodedCalls.addDecoder(address(decoder)); - bytes memory callData = abi.encodePacked(hex"00", _encode(69), _encode(69)); + uint256 val = 69; + bytes memory callData = abi.encodePacked(hex"00", LEB128Lib.encode(val), LEB128Lib.encode(val)); (bool success, bytes memory data) = address(encodedCalls).call(callData); assertFalse(success); assertEq(data, abi.encodeWithSignature("Panic(uint256)", 0x32)); @@ -92,7 +92,7 @@ contract EncodedCallsTest is LEB128Encode, FloodPlainTestShared { function test_decoderError() public { uint256 decoderId = encodedCalls.addDecoder(address(decoder)); - bytes memory callData = abi.encodePacked(hex"00", _encode(decoderId)); + bytes memory callData = abi.encodePacked(hex"00", LEB128Lib.encode(decoderId)); (bool success, bytes memory data) = address(encodedCalls).call(callData); assertFalse(success); assertEq(data, abi.encodeWithSignature("Error(string)", "DECODING_ERROR")); @@ -100,7 +100,7 @@ contract EncodedCallsTest is LEB128Encode, FloodPlainTestShared { function test_methodError() public { uint256 decoderId = encodedCalls.addDecoder(address(decoder)); - bytes memory callData = abi.encodePacked(hex"00", _encode(decoderId)); + bytes memory callData = abi.encodePacked(hex"00", LEB128Lib.encode(decoderId)); (bool success, bytes memory data) = address(encodedCalls).call(callData); assertFalse(success); assertEq(data, abi.encodeWithSignature("Error(string)", "DECODING_ERROR")); diff --git a/test/LEB128.t.sol b/test/LEB128.t.sol deleted file mode 100644 index c08a7f3..0000000 --- a/test/LEB128.t.sol +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import {Test} from "forge-std/Test.sol"; -import {LEB128} from "src/libraries/LEB128.sol"; - -// Helpers -import {LibBit} from "solady/utils/LibBit.sol"; -import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol"; -import {LEB128Encode} from "./utils/LEB128Encode.sol"; - -contract LEB128Contract { - fallback(bytes calldata) external returns (bytes memory) { - (uint256 result, uint256 ptr) = LEB128.rawDecodeUint(0); - return abi.encode(result, ptr); - } -} - -contract LEB128Test is LEB128Encode, Test { - LEB128Contract public leb128; - - function setUp() public { - leb128 = new LEB128Contract(); - } - - function _encodedUintLength(uint256 x) internal pure returns (uint256) { - return x == 0 ? 1 : FixedPointMathLib.divUp(LibBit.fls(x) + 1, 7); - } - - function testUnsignedEncode() public { - assertEq(_encode(uint256(0)), hex"00"); - assertEq(_encode(uint256(1)), hex"01"); - assertEq(_encode(uint256(69)), hex"45"); - assertEq(_encode(uint256(420)), hex"a403"); - assertEq(_encode(uint256(666)), hex"9a05"); - assertEq(_encode(uint256(1 ether)), hex"808090bbbad6adf00d"); - assertEq( - _encode(type(uint256).max - 1), - hex"feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0f" - ); - assertEq( - _encode(type(uint256).max), - hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0f" - ); - } - - function testUnsignedEncodeLength(uint256 x) public { - vm.assume(x != 0); - assertEq(_encode(x).length, _encodedUintLength(x)); - } - - function testEncodeDecode(uint256 x) public { - bytes memory uencoded = _encode(x); - - (bool success, bytes memory result) = address(leb128).call(abi.encodePacked(uencoded)); - require(success); - (uint256 decoded, uint256 length) = abi.decode(result, (uint256, uint256)); - assertEq(decoded, x); - assertEq(length, uencoded.length); - } - - function testNoRevertOnOutOfBoundsRawDecoding(uint256 x) public { - bytes memory uencoded = _encode(x); - - uint256 uencodedPtr; - /// @solidity memory-safe-assembly - assembly { - uencodedPtr := add(uencoded, 0x20) - } - - uencoded[uencoded.length - 1] ^= 0x80; - - (bool success,) = address(leb128).call(abi.encodePacked(uencoded)); - require(success); - } - - function testBenchCompress() public { - // Test that encoding these values takes 72 bytes. - uint256[] memory a = new uint256[](21); - a[0] = 0x0000000000000000000000000000000000000000000000000000000000000020; - a[1] = 0x000000000000000000000000ca1694433e499862ee242f2f403cb1e73ae91cfb; - a[2] = 0x0000000000000000000000000000000000000000000000000000000000000001; - a[3] = 0x0000000000000000000000000000000000000000000000000000000000000000; - a[4] = 0x0000000000000000000000001f5d295778796a8b9f29600a585ab73d452acb1c; - a[5] = 0x0000000000000000000000000000000000000000000000000000000000000001; - a[6] = 0x0000000000000000000000000000000000000000000000000000000000000000; - a[7] = 0x00000000000000000000000000000000000000000000000000000000ffffffff; - a[8] = 0x0000000000000000000000000000000000000000000000000000000000000200; - a[9] = 0x0000000000000000000000000000000000000000000000000000000000000000; - a[10] = 0x0000000000000000000000000000000000000000000000000000000000000000; - a[11] = 0x0000000000000000000000000000000000000000000000000000000000000000; - a[12] = 0x0000000000000000000000000000000000000000000000000000000000000000; - a[13] = 0x0000000000000000000000000000000000000000000000000000000000000220; - a[14] = 0x0000000000000000000000000000000000000000000000000000000000000000; - a[15] = 0x0000000000000000000000000000000000000000000000000000000000000260; - a[16] = 0x0000000000000000000000000000000000000000000000000000000000000000; - a[17] = 0x0000000000000000000000000000000000000000000000000000000000000000; - a[18] = 0x0000000000000000000000000000000000000000000000000000000000000014; - a[19] = 0x0000000000000000000000000000000000000000000000000000000000000000; - a[20] = 0x0000000000000000000000000000000000000000000000000000000000000000; - bytes memory encodedData; - for (uint256 i; i < a.length; ++i) { - encodedData = abi.encodePacked(encodedData, _encode(a[i])); - } - assertEq(encodedData.length, 72); - } -} diff --git a/test/Zone.t.sol b/test/Zone.t.sol index 8c95fda..409fcc2 100644 --- a/test/Zone.t.sol +++ b/test/Zone.t.sol @@ -111,18 +111,6 @@ contract ZoneTest is FloodPlainTestShared { assertEq(abi.encode(fee), abi.encode(mainZone.fee(order, address(0xabcd)))); } - function test_RevertSetFeeAboveLimite(uint256 bps, address recipient) public { - bps = bound(bps, 501, type(uint64).max); // assume fee is greater than 5% - - IZone.FeeInfo memory fee; - fee.recipient = recipient; - fee.bps = uint64(bps); - - vm.prank(account0.addr); - vm.expectRevert(bytes4(keccak256("FeeTooHigh()"))); - mainZone.setFee(fee); - } - function test_RevertSetFeeUnprivilegedCalled(uint256 bps, address recipient) public { bps = bound(bps, 0, 500); // assume fee is 5% or less diff --git a/test/utils/LEB128Encode.sol b/test/utils/LEB128Encode.sol deleted file mode 100644 index 1c094cd..0000000 --- a/test/utils/LEB128Encode.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -abstract contract LEB128Encode { - function _encode(uint256 x) internal pure returns (bytes memory result) { - if (x == 0) return result = new bytes(1); - /// @solidity memory-safe-assembly - assembly { - result := mload(0x40) - let offset := add(result, 32) - let i := offset - for {} 1 {} { - let nextByte := and(x, 0x7f) - x := shr(7, x) - if x { - nextByte := or(nextByte, 0x80) - mstore8(i, nextByte) - i := add(i, 1) - continue - } - mstore8(i, nextByte) - i := add(i, 1) - break - } - mstore(result, sub(i, offset)) - mstore(0x40, i) - } - } -} From 51b53bc9c886e260c62c7251665a97c87a5e269b Mon Sep 17 00:00:00 2001 From: Sunur Efe Vural Date: Thu, 14 Dec 2023 15:31:52 +0300 Subject: [PATCH 3/3] remove native eth trading --- package.json | 2 +- src/FloodPlain.sol | 14 ++----- test/FloodPlain.t.sol | 93 ------------------------------------------- 3 files changed, 4 insertions(+), 105 deletions(-) diff --git a/package.json b/package.json index 58246f7..e03437f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flood-contracts", - "version": "1.0.0", + "version": "1.1.0", "description": "Flood contracts are the smart contracts that power Flood, the optimal routing protocol.", "homepage": "https://flood.bid", "bugs": "https://github.com/flood-protocol/flood-contracts/issues", diff --git a/src/FloodPlain.sol b/src/FloodPlain.sol index ad36a54..390218b 100644 --- a/src/FloodPlain.sol +++ b/src/FloodPlain.sol @@ -11,7 +11,6 @@ import {ReentrancyGuard} from "@openzeppelin/utils/ReentrancyGuard.sol"; import {OrderHash} from "./libraries/OrderHash.sol"; import {Hooks, SELECTOR_EXTENSION} from "./libraries/Hooks.sol"; import {Duplicates} from "./libraries/Duplicates.sol"; -import {Address} from "@openzeppelin/utils/Address.sol"; import {SafeERC20} from "@openzeppelin/token/ERC20/utils/SafeERC20.sol"; // Interfaces @@ -22,7 +21,6 @@ import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol" contract FloodPlain is IFloodPlain, EncodedCalls, OnChainOrders, ReentrancyGuard { using SafeERC20 for IERC20; - using Address for address payable; using OrderHash for Order; using Duplicates for Item[]; using Hooks for Hook[]; @@ -69,10 +67,8 @@ contract FloodPlain is IFloodPlain, EncodedCalls, OnChainOrders, ReentrancyGuard _permitTransferOffer(order, package.signature, orderHash, msg.sender); // Transfer consideration item from msg.sender to offerer. - address token = order.consideration.token; uint256 amount = order.consideration.amount; - if (token == address(0)) payable(order.recipient).sendValue(amount); - else IERC20(token).safeTransferFrom(msg.sender, order.recipient, amount); + IERC20(order.consideration.token).safeTransferFrom(msg.sender, order.recipient, amount); // Execute post hooks. order.postHooks.execute(); @@ -108,9 +104,7 @@ contract FloodPlain is IFloodPlain, EncodedCalls, OnChainOrders, ReentrancyGuard if (amount < order.consideration.amount) revert InsufficientAmountReceived(); // Transfer declared consideration amount from the fulfiller to the offerer. - address token = order.consideration.token; - if (token == address(0)) payable(order.recipient).sendValue(amount); - else IERC20(token).safeTransferFrom(fulfiller, order.recipient, amount); + IERC20(order.consideration.token).safeTransferFrom(fulfiller, order.recipient, amount); // Execute post hooks. order.postHooks.execute(); @@ -154,9 +148,7 @@ contract FloodPlain is IFloodPlain, EncodedCalls, OnChainOrders, ReentrancyGuard if (amount < order.consideration.amount) revert InsufficientAmountReceived(); // Transfer declared consideration amount from the fulfiller to the offerer. - address token = order.consideration.token; - if (token == address(0)) payable(order.recipient).sendValue(amount); - else IERC20(token).safeTransferFrom(fulfiller, order.recipient, amount); + IERC20(order.consideration.token).safeTransferFrom(fulfiller, order.recipient, amount); // Execute post hooks. order.postHooks.execute(); diff --git a/test/FloodPlain.t.sol b/test/FloodPlain.t.sol index dc9f048..6b6d238 100644 --- a/test/FloodPlain.t.sol +++ b/test/FloodPlain.t.sol @@ -55,30 +55,6 @@ contract FloodPlainTest is FloodPlainTestShared { assertEq(token1.balanceOf(address(account3.addr)), 500); } - function test_fulfillEthOrder() public { - IFloodPlain.SignedOrder memory signedOrder = setup_mostBasicOrder(); - - signedOrder.order.consideration.token = address(0); - deal(address(fulfiller), 500); - - signedOrder.signature = getSignature(signedOrder.order, account0); - - // Prechecks. - assertEq(token0.balanceOf(address(account0.addr)), 500); - assertEq(address(account0.addr).balance, 0); - assertEq(token0.balanceOf(address(fulfiller)), 0); - assertEq(address(fulfiller).balance, 500); - - // Fill the order. - book.fulfillOrder(signedOrder, address(fulfiller), ""); - - // Assertions. - assertEq(token0.balanceOf(address(account0.addr)), 0); - assertEq(address(account0.addr).balance, 500); - assertEq(token0.balanceOf(address(fulfiller)), 500); - assertEq(address(fulfiller).balance, 0); - } - function test_fulfillMultiItemOrder() public { IFloodPlain.SignedOrder memory signedOrder = setup_multiItemOrder(); @@ -122,19 +98,6 @@ contract FloodPlainTest is FloodPlainTestShared { book.fulfillOrder(signedOrder, address(maliciousFulfiller), ""); } - function test_RevertWhenInsufficientEthConsiderationReceived() public { - IFloodPlain.SignedOrder memory signedOrder = setup_mostBasicOrder(); - - signedOrder.order.consideration.token = address(0); - deal(address(maliciousFulfiller), 500); - - signedOrder.signature = getSignature(signedOrder.order, account0); - - // Filling order fails. - vm.expectRevert(abi.encodeWithSignature("AddressInsufficientBalance(address)", address(book))); - book.fulfillOrder(signedOrder, address(maliciousFulfiller), ""); - } - function test_RevertWhenInsufficientConsiderationIndicated() public { IFloodPlain.SignedOrder memory signedOrder = setup_mostBasicOrder(); @@ -398,25 +361,6 @@ contract FloodPlainTest is FloodPlainTestShared { assertEq(token1.balanceOf(address(this)), 0); } - function test_fulfillDirectEthOrder() public { - IFloodPlain.SignedOrder memory signedOrder = setup_mostBasicOrder(); - - signedOrder.order.consideration.token = address(0); - - signedOrder.signature = getSignature(signedOrder.order, account0); - - uint256 balanceBefore = address(this).balance; - - // Fill the order. - book.fulfillOrder{value: 500}(signedOrder); - - // Assertions. - assertEq(token0.balanceOf(address(account0.addr)), 0); - assertEq(address(account0.addr).balance, 500); - assertEq(token0.balanceOf(address(this)), 500); - assertEq(address(this).balance, balanceBefore - 500); - } - function test_fulfillMultiItemDirectOrder() public { IFloodPlain.SignedOrder memory signedOrder = setup_multiItemOrder(); @@ -459,18 +403,6 @@ contract FloodPlainTest is FloodPlainTestShared { book.fulfillOrder(signedOrder); } - function test_RevertDirectOrderWhenInsufficientEthConsiderationReceived() public { - IFloodPlain.SignedOrder memory signedOrder = setup_mostBasicOrder(); - - signedOrder.order.consideration.token = address(0); - - signedOrder.signature = getSignature(signedOrder.order, account0); - - // Filling order fails. - vm.expectRevert(abi.encodeWithSignature("AddressInsufficientBalance(address)", address(book))); - book.fulfillOrder{value: 499}(signedOrder); - } - function test_revertDirectOrderWhenOfferTokenRepeated(uint256 indexA, uint256 indexB) public { indexA = bound(indexA, 0, 2); indexB = bound(indexB, 0, 2); @@ -545,31 +477,6 @@ contract FloodPlainTest is FloodPlainTestShared { assertEq(token1.balanceOf(address(batchFulfiller)), 0); } - function test_fulfillEthBatchOrder() public { - IFloodPlain.SignedOrder[] memory signedOrders = new IFloodPlain.SignedOrder[](2); - signedOrders[0] = setup_mostBasicOrder(); - signedOrders[1] = setup_mostBasicOrder(); - - signedOrders[1].order.nonce = 1; - signedOrders[1].order.consideration.token = address(0); - signedOrders[1].signature = getSignature(signedOrders[1].order, account0); - - // Send tokens to fulfiller to fill the order. - deal(address(token1), address(batchFulfiller), 500); - deal(address(batchFulfiller), 500); - - // Fill the order. - book.fulfillOrders(signedOrders, address(batchFulfiller), ""); - - // Assertions. - assertEq(token0.balanceOf(address(account0.addr)), 0); - assertEq(token1.balanceOf(address(account0.addr)), 500); - assertEq(address(account0.addr).balance, 500); - assertEq(token0.balanceOf(address(batchFulfiller)), 1000); - assertEq(token1.balanceOf(address(batchFulfiller)), 0); - assertEq(address(batchFulfiller).balance, 0); - } - function test_RevertBatchFillWhenDeadlineExpire() public { IFloodPlain.SignedOrder[] memory signedOrders = new IFloodPlain.SignedOrder[](2); signedOrders[0] = setup_mostBasicOrder();