From 7cecd04199ea1acc84d05be59fc1ed31d3568e1f Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Tue, 9 Apr 2024 16:16:17 +0200 Subject: [PATCH 01/18] Update testing library --- .../evm-contracts/test-lib/cheatcodes.sol | 5 + .../evm-contracts/test-lib/console.sol | 1459 ++++++++++------- .../evm-contracts/test-lib/ctest.sol | 577 ++++++- 3 files changed, 1372 insertions(+), 669 deletions(-) diff --git a/packages/contracts/evm-contracts/test-lib/cheatcodes.sol b/packages/contracts/evm-contracts/test-lib/cheatcodes.sol index 830247b82..6ab18c355 100644 --- a/packages/contracts/evm-contracts/test-lib/cheatcodes.sol +++ b/packages/contracts/evm-contracts/test-lib/cheatcodes.sol @@ -52,6 +52,8 @@ interface CheatCodes { function expectRevert(bytes4) external; + function expectRevert() external; + // Record all storage reads and writes function record() external; @@ -81,4 +83,7 @@ interface CheatCodes { // Labels an address in call traces function label(address, string calldata) external; + + // If the condition is false, discard this run's fuzz inputs and generate new ones. + function assume(bool condition) external pure; } diff --git a/packages/contracts/evm-contracts/test-lib/console.sol b/packages/contracts/evm-contracts/test-lib/console.sol index a7a6dfdbf..05f562833 100644 --- a/packages/contracts/evm-contracts/test-lib/console.sol +++ b/packages/contracts/evm-contracts/test-lib/console.sol @@ -1,861 +1,1010 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.4.22 <0.9.0; -// source: https://github.com/gakonst/foundry/blob/bf845eb2144bc99352b7d9f77c3f6cdfe801f826/evm-adapters/testdata/console.sol -// date: 2022-03-03 - +/// @dev The original console.sol uses `int` and `uint` for computing function selectors, but it should +/// use `int256` and `uint256`. This modified version fixes that. This version is recommended +/// over `console.sol` if you don't need compatibility with Hardhat as the logs will show up in +/// forge stack traces. If you do need compatibility with Hardhat, you must use `console.sol`. +/// Reference: https://github.com/NomicFoundation/hardhat/issues/2178 library console { address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67); - function _sendLogPayload(bytes memory payload) private view { + function _castLogPayloadViewToPure( + function(bytes memory) internal view fnIn + ) internal pure returns (function(bytes memory) internal pure fnOut) { + assembly { + fnOut := fnIn + } + } + + function _sendLogPayload(bytes memory payload) internal pure { + _castLogPayloadViewToPure(_sendLogPayloadView)(payload); + } + + function _sendLogPayloadView(bytes memory payload) private view { uint256 payloadLength = payload.length; address consoleAddress = CONSOLE_ADDRESS; + /// @solidity memory-safe-assembly assembly { let payloadStart := add(payload, 32) let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0) } } - function log() internal view { + function log() internal pure { _sendLogPayload(abi.encodeWithSignature("log()")); } - function logInt(int256 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(int)", p0)); + function logInt(int256 p0) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(int256)", p0)); } - function logUint(uint256 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); + function logUint(uint256 p0) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); } - function logString(string memory p0) internal view { + function logString(string memory p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } - function logBool(bool p0) internal view { + function logBool(bool p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } - function logAddress(address p0) internal view { + function logAddress(address p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } - function logBytes(bytes memory p0) internal view { + function logBytes(bytes memory p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes)", p0)); } - function logBytes1(bytes1 p0) internal view { + function logBytes1(bytes1 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0)); } - function logBytes2(bytes2 p0) internal view { + function logBytes2(bytes2 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0)); } - function logBytes3(bytes3 p0) internal view { + function logBytes3(bytes3 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0)); } - function logBytes4(bytes4 p0) internal view { + function logBytes4(bytes4 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0)); } - function logBytes5(bytes5 p0) internal view { + function logBytes5(bytes5 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0)); } - function logBytes6(bytes6 p0) internal view { + function logBytes6(bytes6 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0)); } - function logBytes7(bytes7 p0) internal view { + function logBytes7(bytes7 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0)); } - function logBytes8(bytes8 p0) internal view { + function logBytes8(bytes8 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0)); } - function logBytes9(bytes9 p0) internal view { + function logBytes9(bytes9 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0)); } - function logBytes10(bytes10 p0) internal view { + function logBytes10(bytes10 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0)); } - function logBytes11(bytes11 p0) internal view { + function logBytes11(bytes11 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0)); } - function logBytes12(bytes12 p0) internal view { + function logBytes12(bytes12 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0)); } - function logBytes13(bytes13 p0) internal view { + function logBytes13(bytes13 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0)); } - function logBytes14(bytes14 p0) internal view { + function logBytes14(bytes14 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0)); } - function logBytes15(bytes15 p0) internal view { + function logBytes15(bytes15 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0)); } - function logBytes16(bytes16 p0) internal view { + function logBytes16(bytes16 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0)); } - function logBytes17(bytes17 p0) internal view { + function logBytes17(bytes17 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0)); } - function logBytes18(bytes18 p0) internal view { + function logBytes18(bytes18 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0)); } - function logBytes19(bytes19 p0) internal view { + function logBytes19(bytes19 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0)); } - function logBytes20(bytes20 p0) internal view { + function logBytes20(bytes20 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0)); } - function logBytes21(bytes21 p0) internal view { + function logBytes21(bytes21 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0)); } - function logBytes22(bytes22 p0) internal view { + function logBytes22(bytes22 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0)); } - function logBytes23(bytes23 p0) internal view { + function logBytes23(bytes23 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0)); } - function logBytes24(bytes24 p0) internal view { + function logBytes24(bytes24 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0)); } - function logBytes25(bytes25 p0) internal view { + function logBytes25(bytes25 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0)); } - function logBytes26(bytes26 p0) internal view { + function logBytes26(bytes26 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0)); } - function logBytes27(bytes27 p0) internal view { + function logBytes27(bytes27 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0)); } - function logBytes28(bytes28 p0) internal view { + function logBytes28(bytes28 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0)); } - function logBytes29(bytes29 p0) internal view { + function logBytes29(bytes29 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0)); } - function logBytes30(bytes30 p0) internal view { + function logBytes30(bytes30 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0)); } - function logBytes31(bytes31 p0) internal view { + function logBytes31(bytes31 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0)); } - function logBytes32(bytes32 p0) internal view { + function logBytes32(bytes32 p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0)); } - function log(uint256 p0) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint)", p0)); + function log(uint256 p0) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256)", p0)); } - function log(string memory p0) internal view { + function log(int256 p0) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(int256)", p0)); + } + + function log(string memory p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string)", p0)); } - function log(bool p0) internal view { + function log(bool p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool)", p0)); } - function log(address p0) internal view { + function log(address p0) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address)", p0)); } - function log(uint256 p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1)); + function log(uint256 p0, uint256 p1) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256)", p0, p1)); + } + + function log(uint256 p0, string memory p1) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string)", p0, p1)); } - function log(uint256 p0, string memory p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1)); + function log(uint256 p0, bool p1) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool)", p0, p1)); } - function log(uint256 p0, bool p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1)); + function log(uint256 p0, address p1) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address)", p0, p1)); } - function log(uint256 p0, address p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1)); + function log(string memory p0, uint256 p1) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1)); } - function log(string memory p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1)); + function log(string memory p0, int256 p1) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,int256)", p0, p1)); } - function log(string memory p0, string memory p1) internal view { + function log(string memory p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1)); } - function log(string memory p0, bool p1) internal view { + function log(string memory p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1)); } - function log(string memory p0, address p1) internal view { + function log(string memory p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1)); } - function log(bool p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1)); + function log(bool p0, uint256 p1) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256)", p0, p1)); } - function log(bool p0, string memory p1) internal view { + function log(bool p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1)); } - function log(bool p0, bool p1) internal view { + function log(bool p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1)); } - function log(bool p0, address p1) internal view { + function log(bool p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1)); } - function log(address p0, uint256 p1) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1)); + function log(address p0, uint256 p1) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256)", p0, p1)); } - function log(address p0, string memory p1) internal view { + function log(address p0, string memory p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1)); } - function log(address p0, bool p1) internal view { + function log(address p0, bool p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1)); } - function log(address p0, address p1) internal view { + function log(address p0, address p1) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1)); } - function log(uint256 p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2)); + function log(uint256 p0, uint256 p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,uint256)", p0, p1, p2)); } - function log(uint256 p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2)); + function log(uint256 p0, uint256 p1, string memory p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,string)", p0, p1, p2)); } - function log(uint256 p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2)); + function log(uint256 p0, uint256 p1, bool p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool)", p0, p1, p2)); } - function log(uint256 p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2)); + function log(uint256 p0, uint256 p1, address p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,address)", p0, p1, p2)); } - function log(uint256 p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2)); + function log(uint256 p0, string memory p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,uint256)", p0, p1, p2)); } - function log(uint256 p0, string memory p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2)); + function log(uint256 p0, string memory p1, string memory p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string)", p0, p1, p2)); } - function log(uint256 p0, string memory p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2)); + function log(uint256 p0, string memory p1, bool p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool)", p0, p1, p2)); } - function log(uint256 p0, string memory p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2)); + function log(uint256 p0, string memory p1, address p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,address)", p0, p1, p2)); } - function log(uint256 p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2)); + function log(uint256 p0, bool p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256)", p0, p1, p2)); } - function log(uint256 p0, bool p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2)); + function log(uint256 p0, bool p1, string memory p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string)", p0, p1, p2)); } - function log(uint256 p0, bool p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2)); + function log(uint256 p0, bool p1, bool p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool)", p0, p1, p2)); } - function log(uint256 p0, bool p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2)); + function log(uint256 p0, bool p1, address p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address)", p0, p1, p2)); } - function log(uint256 p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2)); + function log(uint256 p0, address p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,uint256)", p0, p1, p2)); } - function log(uint256 p0, address p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2)); + function log(uint256 p0, address p1, string memory p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,string)", p0, p1, p2)); } - function log(uint256 p0, address p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2)); + function log(uint256 p0, address p1, bool p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool)", p0, p1, p2)); } - function log(uint256 p0, address p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2)); + function log(uint256 p0, address p1, address p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,address)", p0, p1, p2)); } - function log(string memory p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2)); + function log(string memory p0, uint256 p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,uint256)", p0, p1, p2)); } - function log(string memory p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2)); + function log(string memory p0, uint256 p1, string memory p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string)", p0, p1, p2)); } - function log(string memory p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2)); + function log(string memory p0, uint256 p1, bool p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool)", p0, p1, p2)); } - function log(string memory p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2)); + function log(string memory p0, uint256 p1, address p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,address)", p0, p1, p2)); } - function log(string memory p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2)); + function log(string memory p0, string memory p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256)", p0, p1, p2)); } - function log(string memory p0, string memory p1, string memory p2) internal view { + function log(string memory p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2)); } - function log(string memory p0, string memory p1, bool p2) internal view { + function log(string memory p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2)); } - function log(string memory p0, string memory p1, address p2) internal view { + function log(string memory p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2)); } - function log(string memory p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2)); + function log(string memory p0, bool p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256)", p0, p1, p2)); } - function log(string memory p0, bool p1, string memory p2) internal view { + function log(string memory p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2)); } - function log(string memory p0, bool p1, bool p2) internal view { + function log(string memory p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2)); } - function log(string memory p0, bool p1, address p2) internal view { + function log(string memory p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2)); } - function log(string memory p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2)); + function log(string memory p0, address p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,address,uint256)", p0, p1, p2)); } - function log(string memory p0, address p1, string memory p2) internal view { + function log(string memory p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2)); } - function log(string memory p0, address p1, bool p2) internal view { + function log(string memory p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2)); } - function log(string memory p0, address p1, address p2) internal view { + function log(string memory p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2)); } - function log(bool p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2)); + function log(bool p0, uint256 p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256)", p0, p1, p2)); } - function log(bool p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2)); + function log(bool p0, uint256 p1, string memory p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string)", p0, p1, p2)); } - function log(bool p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2)); + function log(bool p0, uint256 p1, bool p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool)", p0, p1, p2)); } - function log(bool p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2)); + function log(bool p0, uint256 p1, address p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address)", p0, p1, p2)); } - function log(bool p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2)); + function log(bool p0, string memory p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256)", p0, p1, p2)); } - function log(bool p0, string memory p1, string memory p2) internal view { + function log(bool p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2)); } - function log(bool p0, string memory p1, bool p2) internal view { + function log(bool p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2)); } - function log(bool p0, string memory p1, address p2) internal view { + function log(bool p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2)); } - function log(bool p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2)); + function log(bool p0, bool p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256)", p0, p1, p2)); } - function log(bool p0, bool p1, string memory p2) internal view { + function log(bool p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2)); } - function log(bool p0, bool p1, bool p2) internal view { + function log(bool p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2)); } - function log(bool p0, bool p1, address p2) internal view { + function log(bool p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2)); } - function log(bool p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2)); + function log(bool p0, address p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256)", p0, p1, p2)); } - function log(bool p0, address p1, string memory p2) internal view { + function log(bool p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2)); } - function log(bool p0, address p1, bool p2) internal view { + function log(bool p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2)); } - function log(bool p0, address p1, address p2) internal view { + function log(bool p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2)); } - function log(address p0, uint256 p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2)); + function log(address p0, uint256 p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,uint256)", p0, p1, p2)); } - function log(address p0, uint256 p1, string memory p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2)); + function log(address p0, uint256 p1, string memory p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,string)", p0, p1, p2)); } - function log(address p0, uint256 p1, bool p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2)); + function log(address p0, uint256 p1, bool p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool)", p0, p1, p2)); } - function log(address p0, uint256 p1, address p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2)); + function log(address p0, uint256 p1, address p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,address)", p0, p1, p2)); } - function log(address p0, string memory p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2)); + function log(address p0, string memory p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(address,string,uint256)", p0, p1, p2)); } - function log(address p0, string memory p1, string memory p2) internal view { + function log(address p0, string memory p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2)); } - function log(address p0, string memory p1, bool p2) internal view { + function log(address p0, string memory p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2)); } - function log(address p0, string memory p1, address p2) internal view { + function log(address p0, string memory p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2)); } - function log(address p0, bool p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2)); + function log(address p0, bool p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256)", p0, p1, p2)); } - function log(address p0, bool p1, string memory p2) internal view { + function log(address p0, bool p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2)); } - function log(address p0, bool p1, bool p2) internal view { + function log(address p0, bool p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2)); } - function log(address p0, bool p1, address p2) internal view { + function log(address p0, bool p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2)); } - function log(address p0, address p1, uint256 p2) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2)); + function log(address p0, address p1, uint256 p2) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(address,address,uint256)", p0, p1, p2)); } - function log(address p0, address p1, string memory p2) internal view { + function log(address p0, address p1, string memory p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2)); } - function log(address p0, address p1, bool p2) internal view { + function log(address p0, address p1, bool p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2)); } - function log(address p0, address p1, address p2) internal view { + function log(address p0, address p1, address p2) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2)); } - function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,uint256,string)", p0, p1, p2, p3) + ); } - function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, uint256 p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,uint256,bool)", p0, p1, p2, p3) + ); } - function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, uint256 p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,uint256,address)", p0, p1, p2, p3) + ); } - function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,string,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, string memory p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,string,string)", p0, p1, p2, p3) + ); } - function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, string memory p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,string,bool)", p0, p1, p2, p3) + ); } - function log(uint256 p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, string memory p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,string,address)", p0, p1, p2, p3) + ); } - function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, bool p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,bool,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, bool p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,bool,string)", p0, p1, p2, p3) + ); } - function log(uint256 p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, bool p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,uint256,bool,bool)", p0, p1, p2, p3)); } - function log(uint256 p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, bool p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,bool,address)", p0, p1, p2, p3) + ); } - function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, address p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,address,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, address p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,address,string)", p0, p1, p2, p3) + ); } - function log(uint256 p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, address p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,address,bool)", p0, p1, p2, p3) + ); } - function log(uint256 p0, uint256 p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3)); + function log(uint256 p0, uint256 p1, address p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,uint256,address,address)", p0, p1, p2, p3) + ); } - function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,string,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,string,uint256,string)", p0, p1, p2, p3) + ); } - function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, uint256 p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,string,uint256,bool)", p0, p1, p2, p3) + ); } - function log(uint256 p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, uint256 p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,string,uint256,address)", p0, p1, p2, p3) + ); } - function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,string,string,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, string memory p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,string,string,string)", p0, p1, p2, p3) + ); } - function log(uint256 p0, string memory p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, string memory p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,string,bool)", p0, p1, p2, p3)); } - function log(uint256 p0, string memory p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, string memory p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,string,string,address)", p0, p1, p2, p3) + ); } - function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, bool p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,string,bool,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, string memory p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, bool p2, string memory p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,string)", p0, p1, p2, p3)); } - function log(uint256 p0, string memory p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, bool p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,string,bool,bool)", p0, p1, p2, p3)); } - function log(uint256 p0, string memory p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, bool p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,string,bool,address)", p0, p1, p2, p3) + ); } - function log(uint256 p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, address p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,string,address,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, string memory p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, address p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,string,address,string)", p0, p1, p2, p3) + ); } - function log(uint256 p0, string memory p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3)); + function log(uint256 p0, string memory p1, address p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,string,address,bool)", p0, p1, p2, p3) + ); } - function log(uint256 p0, string memory p1, address p2, address p3) internal view { + function log(uint256 p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3) + abi.encodeWithSignature("log(uint256,string,address,address)", p0, p1, p2, p3) ); } - function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,bool,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,bool,uint256,string)", p0, p1, p2, p3) + ); } - function log(uint256 p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, uint256 p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,uint256,bool)", p0, p1, p2, p3)); } - function log(uint256 p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, uint256 p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,bool,uint256,address)", p0, p1, p2, p3) + ); } - function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,bool,string,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, bool p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, string memory p2, string memory p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,string)", p0, p1, p2, p3)); } - function log(uint256 p0, bool p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, string memory p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,string,bool)", p0, p1, p2, p3)); } - function log(uint256 p0, bool p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, string memory p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,bool,string,address)", p0, p1, p2, p3) + ); } - function log(uint256 p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, bool p2, uint256 p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,uint256)", p0, p1, p2, p3)); } - function log(uint256 p0, bool p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, bool p2, string memory p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,string)", p0, p1, p2, p3)); } - function log(uint256 p0, bool p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, bool p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,bool)", p0, p1, p2, p3)); } - function log(uint256 p0, bool p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, bool p2, address p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,bool,address)", p0, p1, p2, p3)); } - function log(uint256 p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, address p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,bool,address,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, bool p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, address p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,bool,address,string)", p0, p1, p2, p3) + ); } - function log(uint256 p0, bool p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, address p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,bool,address,bool)", p0, p1, p2, p3)); } - function log(uint256 p0, bool p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3)); + function log(uint256 p0, bool p1, address p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,bool,address,address)", p0, p1, p2, p3) + ); } - function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3)); + function log(uint256 p0, address p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,address,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3)); + function log(uint256 p0, address p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,address,uint256,string)", p0, p1, p2, p3) + ); } - function log(uint256 p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3)); + function log(uint256 p0, address p1, uint256 p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,address,uint256,bool)", p0, p1, p2, p3) + ); } - function log(uint256 p0, address p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3)); + function log(uint256 p0, address p1, uint256 p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,address,uint256,address)", p0, p1, p2, p3) + ); } - function log(uint256 p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3)); + function log(uint256 p0, address p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,address,string,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, address p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3)); + function log(uint256 p0, address p1, string memory p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,address,string,string)", p0, p1, p2, p3) + ); } - function log(uint256 p0, address p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3)); + function log(uint256 p0, address p1, string memory p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,address,string,bool)", p0, p1, p2, p3) + ); } - function log(uint256 p0, address p1, string memory p2, address p3) internal view { + function log(uint256 p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3) + abi.encodeWithSignature("log(uint256,address,string,address)", p0, p1, p2, p3) ); } - function log(uint256 p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3)); + function log(uint256 p0, address p1, bool p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,address,bool,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, address p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3)); + function log(uint256 p0, address p1, bool p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,address,bool,string)", p0, p1, p2, p3) + ); } - function log(uint256 p0, address p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3)); + function log(uint256 p0, address p1, bool p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(uint256,address,bool,bool)", p0, p1, p2, p3)); } - function log(uint256 p0, address p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3)); + function log(uint256 p0, address p1, bool p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,address,bool,address)", p0, p1, p2, p3) + ); } - function log(uint256 p0, address p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3)); + function log(uint256 p0, address p1, address p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,address,address,uint256)", p0, p1, p2, p3) + ); } - function log(uint256 p0, address p1, address p2, string memory p3) internal view { + function log(uint256 p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3) + abi.encodeWithSignature("log(uint256,address,address,string)", p0, p1, p2, p3) ); } - function log(uint256 p0, address p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3)); + function log(uint256 p0, address p1, address p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(uint256,address,address,bool)", p0, p1, p2, p3) + ); } - function log(uint256 p0, address p1, address p2, address p3) internal view { + function log(uint256 p0, address p1, address p2, address p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3) + abi.encodeWithSignature("log(uint256,address,address,address)", p0, p1, p2, p3) ); } - function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,uint256,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,uint256,uint256,string)", p0, p1, p2, p3) + ); } - function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, uint256 p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,uint256,uint256,bool)", p0, p1, p2, p3) + ); } - function log(string memory p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, uint256 p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,uint256,uint256,address)", p0, p1, p2, p3) + ); } - function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,uint256,string,uint256)", p0, p1, p2, p3) + ); } - function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, string memory p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,uint256,string,string)", p0, p1, p2, p3) + ); } - function log(string memory p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, string memory p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,string,bool)", p0, p1, p2, p3)); } - function log(string memory p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, string memory p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,uint256,string,address)", p0, p1, p2, p3) + ); } - function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, bool p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,uint256,bool,uint256)", p0, p1, p2, p3) + ); } - function log(string memory p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, bool p2, string memory p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,string)", p0, p1, p2, p3)); } - function log(string memory p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, bool p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,uint256,bool,bool)", p0, p1, p2, p3)); } - function log(string memory p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, bool p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,uint256,bool,address)", p0, p1, p2, p3) + ); } - function log(string memory p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, address p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,uint256,address,uint256)", p0, p1, p2, p3) + ); } - function log(string memory p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, address p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,uint256,address,string)", p0, p1, p2, p3) + ); } - function log(string memory p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3)); + function log(string memory p0, uint256 p1, address p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,uint256,address,bool)", p0, p1, p2, p3) + ); } - function log(string memory p0, uint256 p1, address p2, address p3) internal view { + function log(string memory p0, uint256 p1, address p2, address p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3) + abi.encodeWithSignature("log(string,uint256,address,address)", p0, p1, p2, p3) ); } - function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3)); + function log(string memory p0, string memory p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,string,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3)); + function log(string memory p0, string memory p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,string,uint256,string)", p0, p1, p2, p3) + ); } - function log(string memory p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3)); + function log(string memory p0, string memory p1, uint256 p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,string,uint256,bool)", p0, p1, p2, p3)); } - function log(string memory p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3)); + function log(string memory p0, string memory p1, uint256 p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,string,uint256,address)", p0, p1, p2, p3) + ); } - function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3)); + function log(string memory p0, string memory p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,string,string,uint256)", p0, p1, p2, p3) + ); } function log( @@ -863,771 +1012,871 @@ library console { string memory p1, string memory p2, string memory p3 - ) internal view { + ) internal pure { _sendLogPayload( abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3) ); } - function log(string memory p0, string memory p1, string memory p2, bool p3) internal view { + function log(string memory p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3)); } - function log(string memory p0, string memory p1, string memory p2, address p3) internal view { + function log(string memory p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3) ); } - function log(string memory p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3)); + function log(string memory p0, string memory p1, bool p2, uint256 p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint256)", p0, p1, p2, p3)); } - function log(string memory p0, string memory p1, bool p2, string memory p3) internal view { + function log(string memory p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3)); } - function log(string memory p0, string memory p1, bool p2, bool p3) internal view { + function log(string memory p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3)); } - function log(string memory p0, string memory p1, bool p2, address p3) internal view { + function log(string memory p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3)); } - function log(string memory p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3)); + function log(string memory p0, string memory p1, address p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,string,address,uint256)", p0, p1, p2, p3) + ); } - function log(string memory p0, string memory p1, address p2, string memory p3) internal view { + function log(string memory p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3) ); } - function log(string memory p0, string memory p1, address p2, bool p3) internal view { + function log(string memory p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3)); } - function log(string memory p0, string memory p1, address p2, address p3) internal view { + function log(string memory p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3) ); } - function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3)); + function log(string memory p0, bool p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,bool,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(string memory p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3)); + function log(string memory p0, bool p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,string)", p0, p1, p2, p3)); } - function log(string memory p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3)); + function log(string memory p0, bool p1, uint256 p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint256,bool)", p0, p1, p2, p3)); } - function log(string memory p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3)); + function log(string memory p0, bool p1, uint256 p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,bool,uint256,address)", p0, p1, p2, p3) + ); } - function log(string memory p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3)); + function log(string memory p0, bool p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint256)", p0, p1, p2, p3)); } - function log(string memory p0, bool p1, string memory p2, string memory p3) internal view { + function log(string memory p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3)); } - function log(string memory p0, bool p1, string memory p2, bool p3) internal view { + function log(string memory p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3)); } - function log(string memory p0, bool p1, string memory p2, address p3) internal view { + function log(string memory p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3)); } - function log(string memory p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3)); + function log(string memory p0, bool p1, bool p2, uint256 p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint256)", p0, p1, p2, p3)); } - function log(string memory p0, bool p1, bool p2, string memory p3) internal view { + function log(string memory p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3)); } - function log(string memory p0, bool p1, bool p2, bool p3) internal view { + function log(string memory p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3)); } - function log(string memory p0, bool p1, bool p2, address p3) internal view { + function log(string memory p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3)); } - function log(string memory p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3)); + function log(string memory p0, bool p1, address p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,bool,address,uint256)", p0, p1, p2, p3) + ); } - function log(string memory p0, bool p1, address p2, string memory p3) internal view { + function log(string memory p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3)); } - function log(string memory p0, bool p1, address p2, bool p3) internal view { + function log(string memory p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3)); } - function log(string memory p0, bool p1, address p2, address p3) internal view { + function log(string memory p0, bool p1, address p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3) ); } - function log(string memory p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3)); + function log(string memory p0, address p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,address,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(string memory p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3)); + function log(string memory p0, address p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,address,uint256,string)", p0, p1, p2, p3) + ); } - function log(string memory p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3)); + function log(string memory p0, address p1, uint256 p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,address,uint256,bool)", p0, p1, p2, p3) + ); } - function log(string memory p0, address p1, uint256 p2, address p3) internal view { + function log(string memory p0, address p1, uint256 p2, address p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3) + abi.encodeWithSignature("log(string,address,uint256,address)", p0, p1, p2, p3) ); } - function log(string memory p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3)); + function log(string memory p0, address p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,address,string,uint256)", p0, p1, p2, p3) + ); } - function log(string memory p0, address p1, string memory p2, string memory p3) internal view { + function log(string memory p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3) ); } - function log(string memory p0, address p1, string memory p2, bool p3) internal view { + function log(string memory p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3)); } - function log(string memory p0, address p1, string memory p2, address p3) internal view { + function log(string memory p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3) ); } - function log(string memory p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3)); + function log(string memory p0, address p1, bool p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(string,address,bool,uint256)", p0, p1, p2, p3) + ); } - function log(string memory p0, address p1, bool p2, string memory p3) internal view { + function log(string memory p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3)); } - function log(string memory p0, address p1, bool p2, bool p3) internal view { + function log(string memory p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3)); } - function log(string memory p0, address p1, bool p2, address p3) internal view { + function log(string memory p0, address p1, bool p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3) ); } - function log(string memory p0, address p1, address p2, uint256 p3) internal view { + function log(string memory p0, address p1, address p2, uint256 p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3) + abi.encodeWithSignature("log(string,address,address,uint256)", p0, p1, p2, p3) ); } - function log(string memory p0, address p1, address p2, string memory p3) internal view { + function log(string memory p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3) ); } - function log(string memory p0, address p1, address p2, bool p3) internal view { + function log(string memory p0, address p1, address p2, bool p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3) ); } - function log(string memory p0, address p1, address p2, address p3) internal view { + function log(string memory p0, address p1, address p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3) ); } - function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,uint256,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,uint256,uint256,string)", p0, p1, p2, p3) + ); } - function log(bool p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, uint256 p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,uint256,bool)", p0, p1, p2, p3)); } - function log(bool p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, uint256 p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,uint256,uint256,address)", p0, p1, p2, p3) + ); } - function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,uint256,string,uint256)", p0, p1, p2, p3) + ); } - function log(bool p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, string memory p2, string memory p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,string)", p0, p1, p2, p3)); } - function log(bool p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, string memory p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,string,bool)", p0, p1, p2, p3)); } - function log(bool p0, uint256 p1, string memory p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, string memory p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,uint256,string,address)", p0, p1, p2, p3) + ); } - function log(bool p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, bool p2, uint256 p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,uint256)", p0, p1, p2, p3)); } - function log(bool p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, bool p2, string memory p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,string)", p0, p1, p2, p3)); } - function log(bool p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, bool p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,bool)", p0, p1, p2, p3)); } - function log(bool p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, bool p2, address p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,bool,address)", p0, p1, p2, p3)); } - function log(bool p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, address p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,uint256,address,uint256)", p0, p1, p2, p3) + ); } - function log(bool p0, uint256 p1, address p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, address p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,uint256,address,string)", p0, p1, p2, p3) + ); } - function log(bool p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, address p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,uint256,address,bool)", p0, p1, p2, p3)); } - function log(bool p0, uint256 p1, address p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3)); + function log(bool p0, uint256 p1, address p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,uint256,address,address)", p0, p1, p2, p3) + ); } - function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3)); + function log(bool p0, string memory p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,string,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(bool p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3)); + function log(bool p0, string memory p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,string)", p0, p1, p2, p3)); } - function log(bool p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3)); + function log(bool p0, string memory p1, uint256 p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint256,bool)", p0, p1, p2, p3)); } - function log(bool p0, string memory p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3)); + function log(bool p0, string memory p1, uint256 p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,string,uint256,address)", p0, p1, p2, p3) + ); } - function log(bool p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3)); + function log(bool p0, string memory p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint256)", p0, p1, p2, p3)); } - function log(bool p0, string memory p1, string memory p2, string memory p3) internal view { + function log(bool p0, string memory p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3)); } - function log(bool p0, string memory p1, string memory p2, bool p3) internal view { + function log(bool p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3)); } - function log(bool p0, string memory p1, string memory p2, address p3) internal view { + function log(bool p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3)); } - function log(bool p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3)); + function log(bool p0, string memory p1, bool p2, uint256 p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint256)", p0, p1, p2, p3)); } - function log(bool p0, string memory p1, bool p2, string memory p3) internal view { + function log(bool p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3)); } - function log(bool p0, string memory p1, bool p2, bool p3) internal view { + function log(bool p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3)); } - function log(bool p0, string memory p1, bool p2, address p3) internal view { + function log(bool p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3)); } - function log(bool p0, string memory p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3)); + function log(bool p0, string memory p1, address p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,string,address,uint256)", p0, p1, p2, p3) + ); } - function log(bool p0, string memory p1, address p2, string memory p3) internal view { + function log(bool p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3)); } - function log(bool p0, string memory p1, address p2, bool p3) internal view { + function log(bool p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3)); } - function log(bool p0, string memory p1, address p2, address p3) internal view { + function log(bool p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3) ); } - function log(bool p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3)); + function log(bool p0, bool p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,uint256)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3)); + function log(bool p0, bool p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,string)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3)); + function log(bool p0, bool p1, uint256 p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,bool)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3)); + function log(bool p0, bool p1, uint256 p2, address p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint256,address)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3)); + function log(bool p0, bool p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint256)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, string memory p2, string memory p3) internal view { + function log(bool p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, string memory p2, bool p3) internal view { + function log(bool p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, string memory p2, address p3) internal view { + function log(bool p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3)); + function log(bool p0, bool p1, bool p2, uint256 p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint256)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, bool p2, string memory p3) internal view { + function log(bool p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, bool p2, bool p3) internal view { + function log(bool p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, bool p2, address p3) internal view { + function log(bool p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3)); + function log(bool p0, bool p1, address p2, uint256 p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint256)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, address p2, string memory p3) internal view { + function log(bool p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, address p2, bool p3) internal view { + function log(bool p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3)); } - function log(bool p0, bool p1, address p2, address p3) internal view { + function log(bool p0, bool p1, address p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3)); } - function log(bool p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3)); + function log(bool p0, address p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,address,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(bool p0, address p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3)); + function log(bool p0, address p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,address,uint256,string)", p0, p1, p2, p3) + ); } - function log(bool p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3)); + function log(bool p0, address p1, uint256 p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint256,bool)", p0, p1, p2, p3)); } - function log(bool p0, address p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3)); + function log(bool p0, address p1, uint256 p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,address,uint256,address)", p0, p1, p2, p3) + ); } - function log(bool p0, address p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3)); + function log(bool p0, address p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,address,string,uint256)", p0, p1, p2, p3) + ); } - function log(bool p0, address p1, string memory p2, string memory p3) internal view { + function log(bool p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3)); } - function log(bool p0, address p1, string memory p2, bool p3) internal view { + function log(bool p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3)); } - function log(bool p0, address p1, string memory p2, address p3) internal view { + function log(bool p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3) ); } - function log(bool p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3)); + function log(bool p0, address p1, bool p2, uint256 p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint256)", p0, p1, p2, p3)); } - function log(bool p0, address p1, bool p2, string memory p3) internal view { + function log(bool p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3)); } - function log(bool p0, address p1, bool p2, bool p3) internal view { + function log(bool p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3)); } - function log(bool p0, address p1, bool p2, address p3) internal view { + function log(bool p0, address p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3)); } - function log(bool p0, address p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3)); + function log(bool p0, address p1, address p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(bool,address,address,uint256)", p0, p1, p2, p3) + ); } - function log(bool p0, address p1, address p2, string memory p3) internal view { + function log(bool p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3) ); } - function log(bool p0, address p1, address p2, bool p3) internal view { + function log(bool p0, address p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3)); } - function log(bool p0, address p1, address p2, address p3) internal view { + function log(bool p0, address p1, address p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3) ); } - function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3)); + function log(address p0, uint256 p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,uint256,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(address p0, uint256 p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3)); + function log(address p0, uint256 p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,uint256,uint256,string)", p0, p1, p2, p3) + ); } - function log(address p0, uint256 p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3)); + function log(address p0, uint256 p1, uint256 p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,uint256,uint256,bool)", p0, p1, p2, p3) + ); } - function log(address p0, uint256 p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3)); + function log(address p0, uint256 p1, uint256 p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,uint256,uint256,address)", p0, p1, p2, p3) + ); } - function log(address p0, uint256 p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3)); + function log(address p0, uint256 p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,uint256,string,uint256)", p0, p1, p2, p3) + ); } - function log(address p0, uint256 p1, string memory p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3)); + function log(address p0, uint256 p1, string memory p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,uint256,string,string)", p0, p1, p2, p3) + ); } - function log(address p0, uint256 p1, string memory p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3)); + function log(address p0, uint256 p1, string memory p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,uint256,string,bool)", p0, p1, p2, p3) + ); } - function log(address p0, uint256 p1, string memory p2, address p3) internal view { + function log(address p0, uint256 p1, string memory p2, address p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3) + abi.encodeWithSignature("log(address,uint256,string,address)", p0, p1, p2, p3) ); } - function log(address p0, uint256 p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3)); + function log(address p0, uint256 p1, bool p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,uint256,bool,uint256)", p0, p1, p2, p3) + ); } - function log(address p0, uint256 p1, bool p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3)); + function log(address p0, uint256 p1, bool p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,uint256,bool,string)", p0, p1, p2, p3) + ); } - function log(address p0, uint256 p1, bool p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3)); + function log(address p0, uint256 p1, bool p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(address,uint256,bool,bool)", p0, p1, p2, p3)); } - function log(address p0, uint256 p1, bool p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3)); + function log(address p0, uint256 p1, bool p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,uint256,bool,address)", p0, p1, p2, p3) + ); } - function log(address p0, uint256 p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3)); + function log(address p0, uint256 p1, address p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,uint256,address,uint256)", p0, p1, p2, p3) + ); } - function log(address p0, uint256 p1, address p2, string memory p3) internal view { + function log(address p0, uint256 p1, address p2, string memory p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3) + abi.encodeWithSignature("log(address,uint256,address,string)", p0, p1, p2, p3) ); } - function log(address p0, uint256 p1, address p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3)); + function log(address p0, uint256 p1, address p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,uint256,address,bool)", p0, p1, p2, p3) + ); } - function log(address p0, uint256 p1, address p2, address p3) internal view { + function log(address p0, uint256 p1, address p2, address p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3) + abi.encodeWithSignature("log(address,uint256,address,address)", p0, p1, p2, p3) ); } - function log(address p0, string memory p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3)); + function log(address p0, string memory p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,string,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(address p0, string memory p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3)); + function log(address p0, string memory p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,string,uint256,string)", p0, p1, p2, p3) + ); } - function log(address p0, string memory p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3)); + function log(address p0, string memory p1, uint256 p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,string,uint256,bool)", p0, p1, p2, p3) + ); } - function log(address p0, string memory p1, uint256 p2, address p3) internal view { + function log(address p0, string memory p1, uint256 p2, address p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3) + abi.encodeWithSignature("log(address,string,uint256,address)", p0, p1, p2, p3) ); } - function log(address p0, string memory p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3)); + function log(address p0, string memory p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,string,string,uint256)", p0, p1, p2, p3) + ); } - function log(address p0, string memory p1, string memory p2, string memory p3) internal view { + function log(address p0, string memory p1, string memory p2, string memory p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3) ); } - function log(address p0, string memory p1, string memory p2, bool p3) internal view { + function log(address p0, string memory p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3)); } - function log(address p0, string memory p1, string memory p2, address p3) internal view { + function log(address p0, string memory p1, string memory p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3) ); } - function log(address p0, string memory p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3)); + function log(address p0, string memory p1, bool p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,string,bool,uint256)", p0, p1, p2, p3) + ); } - function log(address p0, string memory p1, bool p2, string memory p3) internal view { + function log(address p0, string memory p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3)); } - function log(address p0, string memory p1, bool p2, bool p3) internal view { + function log(address p0, string memory p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3)); } - function log(address p0, string memory p1, bool p2, address p3) internal view { + function log(address p0, string memory p1, bool p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3) ); } - function log(address p0, string memory p1, address p2, uint256 p3) internal view { + function log(address p0, string memory p1, address p2, uint256 p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3) + abi.encodeWithSignature("log(address,string,address,uint256)", p0, p1, p2, p3) ); } - function log(address p0, string memory p1, address p2, string memory p3) internal view { + function log(address p0, string memory p1, address p2, string memory p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3) ); } - function log(address p0, string memory p1, address p2, bool p3) internal view { + function log(address p0, string memory p1, address p2, bool p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3) ); } - function log(address p0, string memory p1, address p2, address p3) internal view { + function log(address p0, string memory p1, address p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3) ); } - function log(address p0, bool p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3)); + function log(address p0, bool p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,bool,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(address p0, bool p1, uint256 p2, string memory p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3)); + function log(address p0, bool p1, uint256 p2, string memory p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,bool,uint256,string)", p0, p1, p2, p3) + ); } - function log(address p0, bool p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3)); + function log(address p0, bool p1, uint256 p2, bool p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint256,bool)", p0, p1, p2, p3)); } - function log(address p0, bool p1, uint256 p2, address p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3)); + function log(address p0, bool p1, uint256 p2, address p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,bool,uint256,address)", p0, p1, p2, p3) + ); } - function log(address p0, bool p1, string memory p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3)); + function log(address p0, bool p1, string memory p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,bool,string,uint256)", p0, p1, p2, p3) + ); } - function log(address p0, bool p1, string memory p2, string memory p3) internal view { + function log(address p0, bool p1, string memory p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3)); } - function log(address p0, bool p1, string memory p2, bool p3) internal view { + function log(address p0, bool p1, string memory p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3)); } - function log(address p0, bool p1, string memory p2, address p3) internal view { + function log(address p0, bool p1, string memory p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3) ); } - function log(address p0, bool p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3)); + function log(address p0, bool p1, bool p2, uint256 p3) internal pure { + _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint256)", p0, p1, p2, p3)); } - function log(address p0, bool p1, bool p2, string memory p3) internal view { + function log(address p0, bool p1, bool p2, string memory p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3)); } - function log(address p0, bool p1, bool p2, bool p3) internal view { + function log(address p0, bool p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3)); } - function log(address p0, bool p1, bool p2, address p3) internal view { + function log(address p0, bool p1, bool p2, address p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3)); } - function log(address p0, bool p1, address p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3)); + function log(address p0, bool p1, address p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,bool,address,uint256)", p0, p1, p2, p3) + ); } - function log(address p0, bool p1, address p2, string memory p3) internal view { + function log(address p0, bool p1, address p2, string memory p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3) ); } - function log(address p0, bool p1, address p2, bool p3) internal view { + function log(address p0, bool p1, address p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3)); } - function log(address p0, bool p1, address p2, address p3) internal view { + function log(address p0, bool p1, address p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3) ); } - function log(address p0, address p1, uint256 p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3)); + function log(address p0, address p1, uint256 p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,address,uint256,uint256)", p0, p1, p2, p3) + ); } - function log(address p0, address p1, uint256 p2, string memory p3) internal view { + function log(address p0, address p1, uint256 p2, string memory p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3) + abi.encodeWithSignature("log(address,address,uint256,string)", p0, p1, p2, p3) ); } - function log(address p0, address p1, uint256 p2, bool p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3)); + function log(address p0, address p1, uint256 p2, bool p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,address,uint256,bool)", p0, p1, p2, p3) + ); } - function log(address p0, address p1, uint256 p2, address p3) internal view { + function log(address p0, address p1, uint256 p2, address p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3) + abi.encodeWithSignature("log(address,address,uint256,address)", p0, p1, p2, p3) ); } - function log(address p0, address p1, string memory p2, uint256 p3) internal view { + function log(address p0, address p1, string memory p2, uint256 p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3) + abi.encodeWithSignature("log(address,address,string,uint256)", p0, p1, p2, p3) ); } - function log(address p0, address p1, string memory p2, string memory p3) internal view { + function log(address p0, address p1, string memory p2, string memory p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3) ); } - function log(address p0, address p1, string memory p2, bool p3) internal view { + function log(address p0, address p1, string memory p2, bool p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3) ); } - function log(address p0, address p1, string memory p2, address p3) internal view { + function log(address p0, address p1, string memory p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3) ); } - function log(address p0, address p1, bool p2, uint256 p3) internal view { - _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3)); + function log(address p0, address p1, bool p2, uint256 p3) internal pure { + _sendLogPayload( + abi.encodeWithSignature("log(address,address,bool,uint256)", p0, p1, p2, p3) + ); } - function log(address p0, address p1, bool p2, string memory p3) internal view { + function log(address p0, address p1, bool p2, string memory p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3) ); } - function log(address p0, address p1, bool p2, bool p3) internal view { + function log(address p0, address p1, bool p2, bool p3) internal pure { _sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3)); } - function log(address p0, address p1, bool p2, address p3) internal view { + function log(address p0, address p1, bool p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3) ); } - function log(address p0, address p1, address p2, uint256 p3) internal view { + function log(address p0, address p1, address p2, uint256 p3) internal pure { _sendLogPayload( - abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3) + abi.encodeWithSignature("log(address,address,address,uint256)", p0, p1, p2, p3) ); } - function log(address p0, address p1, address p2, string memory p3) internal view { + function log(address p0, address p1, address p2, string memory p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3) ); } - function log(address p0, address p1, address p2, bool p3) internal view { + function log(address p0, address p1, address p2, bool p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3) ); } - function log(address p0, address p1, address p2, address p3) internal view { + function log(address p0, address p1, address p2, address p3) internal pure { _sendLogPayload( abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3) ); diff --git a/packages/contracts/evm-contracts/test-lib/ctest.sol b/packages/contracts/evm-contracts/test-lib/ctest.sol index 15339d958..a1775c35c 100644 --- a/packages/contracts/evm-contracts/test-lib/ctest.sol +++ b/packages/contracts/evm-contracts/test-lib/ctest.sol @@ -7,127 +7,576 @@ contract CTest { event log_address(address); event log_bytes32(bytes32); - event log_int(int256); - event log_uint(uint256); + event log_int(int); + event log_uint(uint); event log_bytes(bytes); event log_string(string); event log_named_address(string key, address val); event log_named_bytes32(string key, bytes32 val); - event log_named_decimal_int(string key, int256 val, uint256 decimals); - event log_named_decimal_uint(string key, uint256 val, uint256 decimals); - event log_named_int(string key, int256 val); - event log_named_uint(string key, uint256 val); + event log_named_decimal_int(string key, int val, uint decimals); + event log_named_decimal_uint(string key, uint val, uint decimals); + event log_named_int(string key, int val); + event log_named_uint(string key, uint val); event log_named_bytes(string key, bytes val); event log_named_string(string key, string val); - bool public failed; + bool public IS_TEST = true; + bool private _failed; + address constant HEVM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code"))))); - function fail() internal { - failed = true; + modifier mayRevert() { + _; + } + modifier testopts(string memory) { + _; + } + + function failed() public returns (bool) { + if (_failed) { + return _failed; + } else { + bool globalFailed = false; + if (hasHEVMContext()) { + (, bytes memory retdata) = HEVM_ADDRESS.call( + abi.encodePacked( + bytes4(keccak256("load(address,bytes32)")), + abi.encode(HEVM_ADDRESS, bytes32("failed")) + ) + ); + globalFailed = abi.decode(retdata, (bool)); + } + return globalFailed; + } + } + + function fail() internal virtual { + if (hasHEVMContext()) { + (bool status, ) = HEVM_ADDRESS.call( + abi.encodePacked( + bytes4(keccak256("store(address,bytes32,bytes32)")), + abi.encode(HEVM_ADDRESS, bytes32("failed"), bytes32(uint256(0x01))) + ) + ); + status; // Silence compiler warnings + } + _failed = true; + } + + function hasHEVMContext() internal view returns (bool) { + uint256 hevmCodeSize = 0; + assembly { + hevmCodeSize := extcodesize(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D) + } + return hevmCodeSize > 0; } - // modifier logs_gas() { - // uint startGas = gasleft(); - // _; - // uint endGas = gasleft(); - // emit log_named_uint("gas", startGas - endGas); - // } + modifier logs_gas() { + uint startGas = gasleft(); + _; + uint endGas = gasleft(); + emit log_named_uint("gas", startGas - endGas); + } - function assertTrue(bool condition) internal returns (bool) { + function assertTrue(bool condition) internal { if (!condition) { emit log("Error: Assertion Failed"); fail(); } - return condition; } - function assertTrue(bool condition, string memory err) internal returns (bool) { - if (bytes(err).length == 0) return assertTrue(condition); + function assertTrue(bool condition, string memory err) internal { if (!condition) { - emit log_named_string("Error:", err); - fail(); + emit log_named_string("Error", err); + assertTrue(condition); } - return condition; } function assertEq(address a, address b) internal { - assertEq(a, b, ""); + if (a != b) { + emit log("Error: a == b not satisfied [address]"); + emit log_named_address(" Left", a); + emit log_named_address(" Right", b); + fail(); + } } - function assertEq(address a, address b, string memory err) internal { - if (!assertTrue(a == b, err)) { - emit log("Reason: a == b not satisfied [address]"); - emit log_named_address(" Expected", b); - emit log_named_address(" Actual", a); + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); } } function assertEq(bytes32 a, bytes32 b) internal { - assertEq(a, b, ""); + if (a != b) { + emit log("Error: a == b not satisfied [bytes32]"); + emit log_named_bytes32(" Left", a); + emit log_named_bytes32(" Right", b); + fail(); + } } - function assertEq(bytes32 a, bytes32 b, string memory err) internal { - if (!assertTrue(a == b, err)) { - emit log("Reason: a == b not satisfied [bytes32]"); - emit log_named_bytes32(" Expected", b); - emit log_named_bytes32(" Actual", a); + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); } } + function assertEq32(bytes32 a, bytes32 b) internal { + assertEq(a, b); + } + function assertEq32(bytes32 a, bytes32 b, string memory err) internal { + assertEq(a, b, err); + } - function assertEq(int256 a, int256 b) internal { - assertEq(a, b, ""); + function assertEq(int a, int b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [int]"); + emit log_named_int(" Left", a); + emit log_named_int(" Right", b); + fail(); + } + } + function assertEq(int a, int b, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + function assertEq(uint a, uint b) internal { + if (a != b) { + emit log("Error: a == b not satisfied [uint]"); + emit log_named_uint(" Left", a); + emit log_named_uint(" Right", b); + fail(); + } + } + function assertEq(uint a, uint b, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } + function assertEqDecimal(int a, int b, uint decimals) internal { + if (a != b) { + emit log("Error: a == b not satisfied [decimal int]"); + emit log_named_decimal_int(" Left", a, decimals); + emit log_named_decimal_int(" Right", b, decimals); + fail(); + } + } + function assertEqDecimal(int a, int b, uint decimals, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEqDecimal(a, b, decimals); + } + } + function assertEqDecimal(uint a, uint b, uint decimals) internal { + if (a != b) { + emit log("Error: a == b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Left", a, decimals); + emit log_named_decimal_uint(" Right", b, decimals); + fail(); + } + } + function assertEqDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a != b) { + emit log_named_string("Error", err); + assertEqDecimal(a, b, decimals); + } } - function assertEq(int256 a, int256 b, string memory err) internal { - if (!assertTrue(a == b, err)) { - emit log("Reason: a == b not satisfied [int]"); - emit log_named_int(" Expected", b); - emit log_named_int(" Actual", a); + function assertNotEq(address a, address b) internal { + if (a == b) { + emit log("Error: a != b not satisfied [address]"); + emit log_named_address(" Left", a); + emit log_named_address(" Right", b); + fail(); + } + } + function assertNotEq(address a, address b, string memory err) internal { + if (a == b) { + emit log_named_string("Error", err); + assertNotEq(a, b); } } - function assertEq(uint256 a, uint256 b) internal { - assertEq(a, b, ""); + function assertNotEq(bytes32 a, bytes32 b) internal { + if (a == b) { + emit log("Error: a != b not satisfied [bytes32]"); + emit log_named_bytes32(" Left", a); + emit log_named_bytes32(" Right", b); + fail(); + } + } + function assertNotEq(bytes32 a, bytes32 b, string memory err) internal { + if (a == b) { + emit log_named_string("Error", err); + assertNotEq(a, b); + } + } + function assertNotEq32(bytes32 a, bytes32 b) internal { + assertNotEq(a, b); + } + function assertNotEq32(bytes32 a, bytes32 b, string memory err) internal { + assertNotEq(a, b, err); } - function assertEq(uint256 a, uint256 b, string memory err) internal { - if (!assertTrue(a == b, err)) { - emit log("Reason: a == b not satisfied [uint]"); - emit log_named_uint(" Expected", b); - emit log_named_uint(" Actual", a); + function assertNotEq(int a, int b) internal { + if (a == b) { + emit log("Error: a != b not satisfied [int]"); + emit log_named_int(" Left", a); + emit log_named_int(" Right", b); + fail(); + } + } + function assertNotEq(int a, int b, string memory err) internal { + if (a == b) { + emit log_named_string("Error", err); + assertNotEq(a, b); + } + } + function assertNotEq(uint a, uint b) internal { + if (a == b) { + emit log("Error: a != b not satisfied [uint]"); + emit log_named_uint(" Left", a); + emit log_named_uint(" Right", b); + fail(); + } + } + function assertNotEq(uint a, uint b, string memory err) internal { + if (a == b) { + emit log_named_string("Error", err); + assertNotEq(a, b); + } + } + function assertNotEqDecimal(int a, int b, uint decimals) internal { + if (a == b) { + emit log("Error: a != b not satisfied [decimal int]"); + emit log_named_decimal_int(" Left", a, decimals); + emit log_named_decimal_int(" Right", b, decimals); + fail(); + } + } + function assertNotEqDecimal(int a, int b, uint decimals, string memory err) internal { + if (a == b) { + emit log_named_string("Error", err); + assertNotEqDecimal(a, b, decimals); + } + } + function assertNotEqDecimal(uint a, uint b, uint decimals) internal { + if (a == b) { + emit log("Error: a != b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Left", a, decimals); + emit log_named_decimal_uint(" Right", b, decimals); + fail(); + } + } + function assertNotEqDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a == b) { + emit log_named_string("Error", err); + assertNotEqDecimal(a, b, decimals); } } - function assertEq(string memory a, string memory b) internal { - assertEq(a, b, ""); + function assertGt(uint a, uint b) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertGt(uint a, uint b, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGt(a, b); + } + } + function assertGt(int a, int b) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertGt(int a, int b, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGt(a, b); + } + } + function assertGtDecimal(int a, int b, uint decimals) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertGtDecimal(int a, int b, uint decimals, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGtDecimal(a, b, decimals); + } + } + function assertGtDecimal(uint a, uint b, uint decimals) internal { + if (a <= b) { + emit log("Error: a > b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertGtDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a <= b) { + emit log_named_string("Error", err); + assertGtDecimal(a, b, decimals); + } + } + + function assertGe(uint a, uint b) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertGe(uint a, uint b, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGe(a, b); + } + } + function assertGe(int a, int b) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertGe(int a, int b, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGe(a, b); + } + } + function assertGeDecimal(int a, int b, uint decimals) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertGeDecimal(int a, int b, uint decimals, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + function assertGeDecimal(uint a, uint b, uint decimals) internal { + if (a < b) { + emit log("Error: a >= b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertGeDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a < b) { + emit log_named_string("Error", err); + assertGeDecimal(a, b, decimals); + } + } + + function assertLt(uint a, uint b) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertLt(uint a, uint b, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLt(a, b); + } + } + function assertLt(int a, int b) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertLt(int a, int b, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLt(a, b); + } + } + function assertLtDecimal(int a, int b, uint decimals) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertLtDecimal(int a, int b, uint decimals, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLtDecimal(a, b, decimals); + } + } + function assertLtDecimal(uint a, uint b, uint decimals) internal { + if (a >= b) { + emit log("Error: a < b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertLtDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a >= b) { + emit log_named_string("Error", err); + assertLtDecimal(a, b, decimals); + } + } + + function assertLe(uint a, uint b) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [uint]"); + emit log_named_uint(" Value a", a); + emit log_named_uint(" Value b", b); + fail(); + } + } + function assertLe(uint a, uint b, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLe(a, b); + } + } + function assertLe(int a, int b) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [int]"); + emit log_named_int(" Value a", a); + emit log_named_int(" Value b", b); + fail(); + } + } + function assertLe(int a, int b, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLe(a, b); + } + } + function assertLeDecimal(int a, int b, uint decimals) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [decimal int]"); + emit log_named_decimal_int(" Value a", a, decimals); + emit log_named_decimal_int(" Value b", b, decimals); + fail(); + } + } + function assertLeDecimal(int a, int b, uint decimals, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLeDecimal(a, b, decimals); + } + } + function assertLeDecimal(uint a, uint b, uint decimals) internal { + if (a > b) { + emit log("Error: a <= b not satisfied [decimal uint]"); + emit log_named_decimal_uint(" Value a", a, decimals); + emit log_named_decimal_uint(" Value b", b, decimals); + fail(); + } + } + function assertLeDecimal(uint a, uint b, uint decimals, string memory err) internal { + if (a > b) { + emit log_named_string("Error", err); + assertLeDecimal(a, b, decimals); + } } + function assertEq(string memory a, string memory b) internal { + if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { + emit log("Error: a == b not satisfied [string]"); + emit log_named_string(" Left", a); + emit log_named_string(" Right", b); + fail(); + } + } function assertEq(string memory a, string memory b, string memory err) internal { - if (!assertTrue(keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b)), err)) { - emit log("Reason: a == b not satisfied [string]"); - emit log_named_string(" Expected", b); - emit log_named_string(" Actual", a); + if (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))) { + emit log_named_string("Error", err); + assertEq(a, b); } } - function checkEq0(bytes memory a, bytes memory b) internal pure returns (bool) { - if (a.length != b.length) return false; - for (uint256 i = 0; i < a.length; i++) if (a[i] != b[i]) return false; - return true; + function assertNotEq(string memory a, string memory b) internal { + if (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b))) { + emit log("Error: a != b not satisfied [string]"); + emit log_named_string(" Left", a); + emit log_named_string(" Right", b); + fail(); + } + } + function assertNotEq(string memory a, string memory b, string memory err) internal { + if (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b))) { + emit log_named_string("Error", err); + assertNotEq(a, b); + } } + function checkEq0(bytes memory a, bytes memory b) internal pure returns (bool ok) { + ok = true; + if (a.length == b.length) { + for (uint i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + ok = false; + } + } + } else { + ok = false; + } + } function assertEq0(bytes memory a, bytes memory b) internal { - assertEq0(a, b, ""); + if (!checkEq0(a, b)) { + emit log("Error: a == b not satisfied [bytes]"); + emit log_named_bytes(" Left", a); + emit log_named_bytes(" Right", b); + fail(); + } } - function assertEq0(bytes memory a, bytes memory b, string memory err) internal { - if (!assertTrue(checkEq0(a, b), err)) { - emit log("Reason: a == b not satisfied [bytes]"); - emit log_named_bytes(" Expected", b); - emit log_named_bytes(" Actual", a); + if (!checkEq0(a, b)) { + emit log_named_string("Error", err); + assertEq0(a, b); + } + } + + function assertNotEq0(bytes memory a, bytes memory b) internal { + if (checkEq0(a, b)) { + emit log("Error: a != b not satisfied [bytes]"); + emit log_named_bytes(" Left", a); + emit log_named_bytes(" Right", b); + fail(); + } + } + function assertNotEq0(bytes memory a, bytes memory b, string memory err) internal { + if (checkEq0(a, b)) { + emit log_named_string("Error", err); + assertNotEq0(a, b); } } } From 1420c348456d486f7709c27da8c009ca4463fc0f Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Tue, 9 Apr 2024 16:16:17 +0200 Subject: [PATCH 02/18] Orderbook DEX implementation --- .../contracts/orderbook/IOrderbookDex.sol | 34 ++++ .../contracts/orderbook/OrderbookDex.sol | 102 +++++++++++ .../evm-contracts/test/OrderbookDex.t.sol | 162 ++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol create mode 100644 packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol create mode 100644 packages/contracts/evm-contracts/test/OrderbookDex.t.sol diff --git a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol new file mode 100644 index 000000000..7fcaca795 --- /dev/null +++ b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +/// @notice Facilitates base-chain trading of an asset that is living on a different app-chain. +interface IOrderbookDex is IERC165 { + struct Order { + uint256 assetAmount; + uint256 price; + address payable seller; + bool active; + } + + /// @notice Returns the current index of orders (index that a new sell order will be mapped to). + function getOrdersIndex() external view returns (uint256); + + /// @notice Returns the Order struct information about order of specified `orderId`. + function getOrder(uint256 orderId) external view returns (Order memory); + + /// @notice Creates a sell order for the specified `assetAmount` at specified `price`. + /// @dev The order is saved in a mapping from incremental ID to Order struct. + function createSellOrder(uint256 assetAmount, uint256 price) external; + + /// @notice Fills an array of orders specified by `orderIds`. + /// @dev Reverts if msg.value is less than the sum of orders' prices. + /// If msg.value is more than the sum of orders' prices, it should refund the difference back to msg.sender. + function fillSellOrders(uint256[] memory orderIds) external payable; + + /// @notice Cancels the sell order specified by `orderId`, making it unfillable. + /// @dev Reverts if the msg.sender is not the order's seller. + function cancelSellOrder(uint256 orderId) external; +} diff --git a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol new file mode 100644 index 000000000..857c2647d --- /dev/null +++ b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import {IOrderbookDex} from "./IOrderbookDex.sol"; + +/// @notice Facilitates trading an asset that is living on a different app-chain. +contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { + using Address for address payable; + + mapping(uint256 => Order) public orders; + uint256 public ordersIndex; + + event OrderCreated( + uint256 indexed orderId, + address indexed seller, + uint256 assetAmount, + uint256 price + ); + event OrderFilled( + uint256 indexed orderId, + address indexed seller, + address indexed buyer, + uint256 assetAmount, + uint256 price + ); + event OrderCancelled(uint256 indexed orderId); + + error OrderIsInactive(uint256 orderId); + error Unauthorized(); + + /// @notice Returns the current index of orders (index that a new sell order will be mapped to). + function getOrdersIndex() public view returns (uint256) { + return ordersIndex; + } + + /// @notice Returns the Order struct information about order of specified `orderId`. + function getOrder(uint256 orderId) public view returns (Order memory) { + return orders[orderId]; + } + + /// @notice Creates a sell order for the specified `assetAmount` at specified `price`. + /// @dev The order is saved in a mapping from incremental ID to Order struct. + function createSellOrder(uint256 assetAmount, uint256 price) public { + Order memory newOrder = Order({ + seller: payable(msg.sender), + assetAmount: assetAmount, + price: price, + active: true + }); + orders[ordersIndex] = newOrder; + emit OrderCreated(ordersIndex, msg.sender, assetAmount, price); + ordersIndex++; + } + + /// @notice Fills an array of orders specified by `orderIds`. + /// @dev Reverts if msg.value is less than the sum of orders' prices. + /// If msg.value is more than the sum of orders' prices, it should refund the difference back to msg.sender. + function fillSellOrders(uint256[] memory orderIds) public payable nonReentrant { + uint256 length = orderIds.length; + uint256 totalPaid; + for (uint256 i = 0; i < length; ) { + uint256 orderId = orderIds[i]; + Order memory order = orders[orderId]; + if (!order.active) { + revert OrderIsInactive(orderId); + } + order.seller.sendValue(order.price); + totalPaid += order.price; + orders[orderId].active = false; + emit OrderFilled(orderId, order.seller, msg.sender, order.assetAmount, order.price); + unchecked { + i++; + } + } + if (msg.value > totalPaid) { + payable(msg.sender).sendValue(msg.value - totalPaid); + } + } + + /// @notice Cancels the sell order specified by `orderId`, making it unfillable. + /// @dev Reverts if the msg.sender is not the order's seller. + function cancelSellOrder(uint256 orderId) public { + if (msg.sender != orders[orderId].seller) { + revert Unauthorized(); + } + orders[orderId].active = false; + emit OrderCancelled(orderId); + } + + /// @dev Returns true if this contract implements the interface defined by `interfaceId`. See EIP165. + function supportsInterface( + bytes4 interfaceId + ) public view override(ERC165, IERC165) returns (bool) { + return + interfaceId == type(IOrderbookDex).interfaceId || super.supportsInterface(interfaceId); + } +} diff --git a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol new file mode 100644 index 000000000..4854399bb --- /dev/null +++ b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +import {CheatCodes} from "../test-lib/cheatcodes.sol"; +import {CTest} from "../test-lib/ctest.sol"; +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {IOrderbookDex} from "../contracts/orderbook/IOrderbookDex.sol"; +import {OrderbookDex} from "../contracts/orderbook/OrderbookDex.sol"; + +contract OrderbookDexTest is CTest { + using Address for address payable; + + CheatCodes vm = CheatCodes(HEVM_ADDRESS); + OrderbookDex public dex; + address alice = vm.addr(uint256(keccak256(abi.encodePacked("alice")))); + address boris = vm.addr(uint256(keccak256(abi.encodePacked("boris")))); + + function tryToSendEthToDex() public { + payable(address(dex)).sendValue(1); + } + + function setUp() public { + dex = new OrderbookDex(); + vm.deal(alice, 100 ether); + vm.deal(boris, 100 ether); + } + + function test_SupportsInterface() public { + assertTrue(dex.supportsInterface(type(IERC165).interfaceId)); + assertTrue(dex.supportsInterface(type(IOrderbookDex).interfaceId)); + } + + function testFuzz_Fills(uint256 price) public { + uint256 ordersCount = 5; + vm.assume(price < type(uint256).max / ordersCount); + uint256 firstOrderId = dex.getOrdersIndex(); + for (uint256 i = 0; i < ordersCount; i++) { + uint256 orderId = dex.getOrdersIndex(); + address seller = vm.addr(uint256(keccak256(abi.encodePacked(i)))); + uint256 assetAmount = uint256(keccak256(abi.encodePacked(price))); + vm.prank(seller); + + vm.expectEmit(true, true, true, true); + emit OrderbookDex.OrderCreated(orderId, seller, assetAmount, price); + dex.createSellOrder(assetAmount, price); + + uint256 newOrderId = dex.getOrdersIndex(); + OrderbookDex.Order memory order = dex.getOrder(orderId); + assertEq(newOrderId, orderId + 1); + assertTrue(order.active); + assertEq(order.assetAmount, assetAmount); + assertEq(order.price, price); + assertEq(order.seller, seller); + } + + { + uint256 currentOrderId = dex.getOrdersIndex(); + uint256[] memory orderIds = new uint256[](ordersCount); + for (uint256 i = 0; i < ordersCount; i++) { + orderIds[i] = firstOrderId + i; + } + + address buyer = vm.addr( + uint256(keccak256(abi.encodePacked(uint256(type(uint256).max)))) + ); + vm.deal(buyer, type(uint256).max); + uint256[] memory sellersBalancesBefore = new uint256[](ordersCount); + for (uint256 i = 0; i < ordersCount; i++) { + OrderbookDex.Order memory order = dex.getOrder(firstOrderId + i); + sellersBalancesBefore[i] = order.seller.balance; + vm.expectEmit(true, true, true, true); + emit OrderbookDex.OrderFilled( + firstOrderId + i, + order.seller, + buyer, + order.assetAmount, + order.price + ); + } + vm.prank(buyer); + dex.fillSellOrders{value: price * ordersCount}(orderIds); + + assertEq(dex.getOrdersIndex(), currentOrderId); + for (uint256 i = 0; i < ordersCount; i++) { + OrderbookDex.Order memory order = dex.getOrder(firstOrderId + i); + assertTrue(!order.active); + assertEq(order.seller.balance, sellersBalancesBefore[i] + order.price); + } + } + } + + function test_ExcessValueIsRefunded() public { + uint256 price = 100; + uint256 orderId = dex.getOrdersIndex(); + dex.createSellOrder(10, price); + + vm.prank(alice); + uint256 aliceBalanceBefore = alice.balance; + uint256[] memory orderIds = new uint256[](1); + orderIds[0] = orderId; + dex.fillSellOrders{value: price * 5}(orderIds); + assertEq(alice.balance, aliceBalanceBefore - price); + } + + function test_CannotCancelOrderIfUnauthorized() public { + uint256 orderId = dex.getOrdersIndex(); + dex.createSellOrder(100, 200); + + vm.prank(alice); + vm.expectRevert(OrderbookDex.Unauthorized.selector); + dex.cancelSellOrder(orderId); + } + + function test_CannotFillOrderIfCancelled() public { + uint256 orderId = dex.getOrdersIndex(); + dex.createSellOrder(100, 200); + dex.cancelSellOrder(orderId); + + vm.prank(alice); + uint256[] memory orderIds = new uint256[](1); + orderIds[0] = orderId; + vm.expectRevert(abi.encodeWithSelector(OrderbookDex.OrderIsInactive.selector, orderId)); + dex.fillSellOrders(orderIds); + } + + function test_CannotFillOrderIfAlreadyFilled() public { + uint256 orderId = dex.getOrdersIndex(); + uint256 price = 1000; + dex.createSellOrder(100, price); + + vm.prank(alice); + uint256[] memory orderIds = new uint256[](1); + orderIds[0] = orderId; + dex.fillSellOrders{value: price}(orderIds); + + vm.prank(boris); + vm.expectRevert(abi.encodeWithSelector(OrderbookDex.OrderIsInactive.selector, orderId)); + dex.fillSellOrders{value: price}(orderIds); + } + + function test_CannotFillOrderIfInsufficientValue() public { + uint256 price = 100; + uint256 orderId = dex.getOrdersIndex(); + dex.createSellOrder(10, price); + + vm.prank(alice); + uint256[] memory orderIds = new uint256[](1); + orderIds[0] = orderId; + vm.expectRevert( + abi.encodeWithSelector(Address.AddressInsufficientBalance.selector, address(dex)) + ); + dex.fillSellOrders{value: price - 1}(orderIds); + } + + function test_CannotSendEtherToDex() public { + vm.expectRevert(Address.FailedInnerCall.selector); + this.tryToSendEthToDex(); + } + + receive() external payable {} +} From 3f91009b3b9115d069cb7f08b59918fcc5618eb2 Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Tue, 9 Apr 2024 16:16:17 +0200 Subject: [PATCH 03/18] Move events to interface and add more comments --- .../contracts/orderbook/IOrderbookDex.sol | 28 +++++++++++++++++-- .../contracts/orderbook/OrderbookDex.sol | 15 ---------- .../evm-contracts/test/OrderbookDex.t.sol | 4 +-- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol index 7fcaca795..738aed340 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol @@ -5,6 +5,7 @@ pragma solidity ^0.8.20; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; /// @notice Facilitates base-chain trading of an asset that is living on a different app-chain. +/// @dev The contract should never hold any ETH itself. interface IOrderbookDex is IERC165 { struct Order { uint256 assetAmount; @@ -13,6 +14,21 @@ interface IOrderbookDex is IERC165 { bool active; } + event OrderCreated( + uint256 indexed orderId, + address indexed seller, + uint256 assetAmount, + uint256 price + ); + event OrderFilled( + uint256 indexed orderId, + address indexed seller, + address indexed buyer, + uint256 assetAmount, + uint256 price + ); + event OrderCancelled(uint256 indexed orderId); + /// @notice Returns the current index of orders (index that a new sell order will be mapped to). function getOrdersIndex() external view returns (uint256); @@ -21,14 +37,20 @@ interface IOrderbookDex is IERC165 { /// @notice Creates a sell order for the specified `assetAmount` at specified `price`. /// @dev The order is saved in a mapping from incremental ID to Order struct. + /// MUST emit `OrderCreated` event. function createSellOrder(uint256 assetAmount, uint256 price) external; - /// @notice Fills an array of orders specified by `orderIds`. - /// @dev Reverts if msg.value is less than the sum of orders' prices. - /// If msg.value is more than the sum of orders' prices, it should refund the difference back to msg.sender. + /// @notice Fills an array of orders specified by `orderIds`, transferring portion of msg.value + /// to the orders' sellers according to the price. + /// @dev MUST revert if `active` parameter is `false` for any of the orders. + /// MUST change the `active` parameter for the specified order to `false`. + /// MUST emit `OrderFilled` event for each order. + /// If msg.value is more than the sum of orders' prices, it SHOULD refund the difference back to msg.sender. function fillSellOrders(uint256[] memory orderIds) external payable; /// @notice Cancels the sell order specified by `orderId`, making it unfillable. /// @dev Reverts if the msg.sender is not the order's seller. + /// MUST change the `active` parameter for the specified order to `false`. + /// MUST emit `OrderCancelled` event. function cancelSellOrder(uint256 orderId) external; } diff --git a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol index 857c2647d..65885fd8c 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol @@ -15,21 +15,6 @@ contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { mapping(uint256 => Order) public orders; uint256 public ordersIndex; - event OrderCreated( - uint256 indexed orderId, - address indexed seller, - uint256 assetAmount, - uint256 price - ); - event OrderFilled( - uint256 indexed orderId, - address indexed seller, - address indexed buyer, - uint256 assetAmount, - uint256 price - ); - event OrderCancelled(uint256 indexed orderId); - error OrderIsInactive(uint256 orderId); error Unauthorized(); diff --git a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol index 4854399bb..5e6bd20a6 100644 --- a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol +++ b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol @@ -42,7 +42,7 @@ contract OrderbookDexTest is CTest { vm.prank(seller); vm.expectEmit(true, true, true, true); - emit OrderbookDex.OrderCreated(orderId, seller, assetAmount, price); + emit IOrderbookDex.OrderCreated(orderId, seller, assetAmount, price); dex.createSellOrder(assetAmount, price); uint256 newOrderId = dex.getOrdersIndex(); @@ -70,7 +70,7 @@ contract OrderbookDexTest is CTest { OrderbookDex.Order memory order = dex.getOrder(firstOrderId + i); sellersBalancesBefore[i] = order.seller.balance; vm.expectEmit(true, true, true, true); - emit OrderbookDex.OrderFilled( + emit IOrderbookDex.OrderFilled( firstOrderId + i, order.seller, buyer, From 4cfa7015eb655e77fa47b23a706fc55935e4aadc Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Tue, 9 Apr 2024 16:16:17 +0200 Subject: [PATCH 04/18] Latest design changes according to PRC-4 --- .../contracts/orderbook/IOrderbookDex.sol | 72 ++-- .../contracts/orderbook/OrderbookDex.sol | 168 +++++--- .../evm-contracts/test/OrderbookDex.t.sol | 361 ++++++++++++++---- 3 files changed, 461 insertions(+), 140 deletions(-) diff --git a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol index 738aed340..3519b81d7 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol @@ -9,48 +9,66 @@ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; interface IOrderbookDex is IERC165 { struct Order { uint256 assetAmount; - uint256 price; - address payable seller; - bool active; + uint256 pricePerAsset; + bool cancelled; } event OrderCreated( - uint256 indexed orderId, address indexed seller, + uint256 indexed orderId, uint256 assetAmount, - uint256 price + uint256 pricePerAsset ); event OrderFilled( - uint256 indexed orderId, address indexed seller, + uint256 indexed orderId, address indexed buyer, uint256 assetAmount, - uint256 price + uint256 pricePerAsset ); - event OrderCancelled(uint256 indexed orderId); + event OrderCancelled(address indexed seller, uint256 indexed orderId); - /// @notice Returns the current index of orders (index that a new sell order will be mapped to). - function getOrdersIndex() external view returns (uint256); + /// @notice Returns the seller's current `orderId` (index that their new sell order will be mapped to). + function getSellerOrderId(address seller) external view returns (uint256); - /// @notice Returns the Order struct information about order of specified `orderId`. - function getOrder(uint256 orderId) external view returns (Order memory); + /// @notice Returns the Order struct information about an order identified by the combination ``. + function getOrder(address seller, uint256 orderId) external view returns (Order memory); - /// @notice Creates a sell order for the specified `assetAmount` at specified `price`. - /// @dev The order is saved in a mapping from incremental ID to Order struct. + /// @notice Creates a sell order with incremental seller-specific `orderId` for the specified `assetAmount` at specified `pricePerAsset`. + /// @dev The order information is saved in a nested mapping `seller address -> orderId -> Order`. /// MUST emit `OrderCreated` event. - function createSellOrder(uint256 assetAmount, uint256 price) external; - - /// @notice Fills an array of orders specified by `orderIds`, transferring portion of msg.value - /// to the orders' sellers according to the price. - /// @dev MUST revert if `active` parameter is `false` for any of the orders. - /// MUST change the `active` parameter for the specified order to `false`. - /// MUST emit `OrderFilled` event for each order. - /// If msg.value is more than the sum of orders' prices, it SHOULD refund the difference back to msg.sender. - function fillSellOrders(uint256[] memory orderIds) external payable; - - /// @notice Cancels the sell order specified by `orderId`, making it unfillable. - /// @dev Reverts if the msg.sender is not the order's seller. - /// MUST change the `active` parameter for the specified order to `false`. + function createSellOrder(uint256 assetAmount, uint256 pricePerAsset) external; + + /// @notice Consecutively fills an array of orders identified by the combination ``, + /// by providing an exact amount of ETH and requesting a specific minimum amount of asset to receive. + /// @dev Transfers portions of msg.value to the orders' sellers according to the price. + /// The sum of asset amounts of filled orders MUST be at least `minimumAsset`. + /// If msg.value is more than the sum of orders' prices, it MUST refund the excess back to msg.sender. + /// An order whose `cancelled` parameter has value `true` MUST NOT be filled. + /// MUST change the `assetAmount` parameter for the specified order according to how much of it was filled. + /// MUST emit `OrderFilled` event for each order accordingly. + function fillOrdersExactEth( + uint256 minimumAsset, + address payable[] memory sellers, + uint256[] memory orderIds + ) external payable; + + /// @notice Consecutively fills an array of orders identified by the combination ``, + /// by providing a possibly surplus amount of ETH and requesting an exact amount of asset to receive. + /// @dev Transfers portions of msg.value to the orders' sellers according to the price. + /// The sum of asset amounts of filled orders MUST be exactly `assetAmount`. Excess ETH MUST be returned back to `msg.sender`. + /// An order whose `cancelled` parameter has value `true` MUST NOT be filled. + /// MUST change the `assetAmount` parameter for the specified order according to how much of it was filled. + /// MUST emit `OrderFilled` event for each order accordingly. + /// If msg.value is more than the sum of orders' prices, it MUST refund the difference back to msg.sender. + function fillOrdersExactAsset( + uint256 assetAmount, + address payable[] memory sellers, + uint256[] memory orderIds + ) external payable; + + /// @notice Cancels the sell order identified by combination ``, making it unfillable. + /// @dev MUST change the `cancelled` parameter for the specified order to `true`. /// MUST emit `OrderCancelled` event. function cancelSellOrder(uint256 orderId) external; } diff --git a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol index 65885fd8c..8032d2fd1 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol @@ -12,69 +12,151 @@ import {IOrderbookDex} from "./IOrderbookDex.sol"; contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { using Address for address payable; - mapping(uint256 => Order) public orders; - uint256 public ordersIndex; + mapping(address => mapping(uint256 => Order)) public orders; + mapping(address => uint256) public sellersOrderId; - error OrderIsInactive(uint256 orderId); - error Unauthorized(); + error OrderDoesNotExist(uint256 orderId); + error InsufficientEndAmount(uint256 expectedAmount, uint256 actualAmount); + error InvalidInput(uint256 input); + error InvalidInputArity(); - /// @notice Returns the current index of orders (index that a new sell order will be mapped to). - function getOrdersIndex() public view returns (uint256) { - return ordersIndex; + /// @notice Returns the seller's current `orderId` (index that their new sell order will be mapped to). + function getSellerOrderId(address seller) external view returns (uint256) { + return sellersOrderId[seller]; } - /// @notice Returns the Order struct information about order of specified `orderId`. - function getOrder(uint256 orderId) public view returns (Order memory) { - return orders[orderId]; + /// @notice Returns the Order struct information about an order identified by the combination ``. + function getOrder(address seller, uint256 orderId) external view returns (Order memory) { + return orders[seller][orderId]; } - /// @notice Creates a sell order for the specified `assetAmount` at specified `price`. - /// @dev The order is saved in a mapping from incremental ID to Order struct. - function createSellOrder(uint256 assetAmount, uint256 price) public { + /// @notice Creates a sell order with incremental seller-specific `orderId` for the specified `assetAmount` at specified `pricePerAsset`. + /// @dev The order information is saved in a nested mapping `seller address -> orderId -> Order`. + /// MUST emit `OrderCreated` event. + function createSellOrder(uint256 assetAmount, uint256 pricePerAsset) public { + if (assetAmount == 0 || pricePerAsset == 0) { + revert InvalidInput(0); + } Order memory newOrder = Order({ - seller: payable(msg.sender), assetAmount: assetAmount, - price: price, - active: true + pricePerAsset: pricePerAsset, + cancelled: false }); - orders[ordersIndex] = newOrder; - emit OrderCreated(ordersIndex, msg.sender, assetAmount, price); - ordersIndex++; + uint256 orderId = sellersOrderId[msg.sender]; + orders[msg.sender][orderId] = newOrder; + emit OrderCreated(msg.sender, orderId, assetAmount, pricePerAsset); + sellersOrderId[msg.sender]++; + } + + /// @notice Consecutively fills an array of orders identified by the combination ``, + /// by providing an exact amount of ETH and requesting a specific minimum amount of asset to receive. + /// @dev Transfers portions of msg.value to the orders' sellers according to the price. + /// The sum of asset amounts of filled orders MUST be at least `minimumAsset`. + /// If msg.value is more than the sum of orders' prices, it MUST refund the excess back to msg.sender. + /// An order whose `cancelled` parameter has value `true` MUST NOT be filled. + /// MUST change the `assetAmount` parameter for the specified order according to how much of it was filled. + /// MUST emit `OrderFilled` event for each order accordingly. + function fillOrdersExactEth( + uint256 minimumAsset, + address payable[] memory sellers, + uint256[] memory orderIds + ) public payable nonReentrant { + if (sellers.length != orderIds.length) { + revert InvalidInputArity(); + } + uint256 length = sellers.length; + uint256 remainingEth = msg.value; + uint256 totalAssetReceived; + for (uint256 i = 0; i < length; i++) { + address payable seller = sellers[i]; + uint256 orderId = orderIds[i]; + Order storage order = orders[seller][orderId]; + if (order.cancelled || order.assetAmount == 0 || order.pricePerAsset == 0) { + continue; + } + uint256 assetsToBuy = remainingEth / order.pricePerAsset; + if (assetsToBuy == 0) { + continue; + } + if (assetsToBuy > order.assetAmount) { + assetsToBuy = order.assetAmount; + } + seller.sendValue(assetsToBuy * order.pricePerAsset); + order.assetAmount -= assetsToBuy; + remainingEth -= assetsToBuy * order.pricePerAsset; + totalAssetReceived += assetsToBuy; + emit OrderFilled(seller, orderId, msg.sender, assetsToBuy, order.pricePerAsset); + if (remainingEth == 0) { + break; + } + } + if (totalAssetReceived < minimumAsset) { + revert InsufficientEndAmount(minimumAsset, totalAssetReceived); + } + if (remainingEth > 0) { + payable(msg.sender).sendValue(remainingEth); + } } - /// @notice Fills an array of orders specified by `orderIds`. - /// @dev Reverts if msg.value is less than the sum of orders' prices. - /// If msg.value is more than the sum of orders' prices, it should refund the difference back to msg.sender. - function fillSellOrders(uint256[] memory orderIds) public payable nonReentrant { - uint256 length = orderIds.length; - uint256 totalPaid; - for (uint256 i = 0; i < length; ) { + /// @notice Consecutively fills an array of orders identified by the combination ``, + /// by providing a possibly surplus amount of ETH and requesting an exact amount of asset to receive. + /// @dev Transfers portions of msg.value to the orders' sellers according to the price. + /// The sum of asset amounts of filled orders MUST be exactly `assetAmount`. Excess ETH MUST be returned back to `msg.sender`. + /// An order whose `cancelled` parameter has value `true` MUST NOT be filled. + /// MUST change the `assetAmount` parameter for the specified order according to how much of it was filled. + /// MUST emit `OrderFilled` event for each order accordingly. + /// If msg.value is more than the sum of orders' prices, it MUST refund the difference back to msg.sender. + function fillOrdersExactAsset( + uint256 assetAmount, + address payable[] memory sellers, + uint256[] memory orderIds + ) public payable nonReentrant { + if (sellers.length != orderIds.length) { + revert InvalidInputArity(); + } + uint256 length = sellers.length; + uint256 remainingAsset = assetAmount; + uint256 remainingEth = msg.value; + for (uint256 i = 0; i < length; i++) { + address payable seller = sellers[i]; uint256 orderId = orderIds[i]; - Order memory order = orders[orderId]; - if (!order.active) { - revert OrderIsInactive(orderId); + Order storage order = orders[seller][orderId]; + if (order.cancelled || order.assetAmount == 0 || order.pricePerAsset == 0) { + continue; + } + uint256 assetsToBuy = order.assetAmount; + if (assetsToBuy > remainingAsset) { + assetsToBuy = remainingAsset; } - order.seller.sendValue(order.price); - totalPaid += order.price; - orders[orderId].active = false; - emit OrderFilled(orderId, order.seller, msg.sender, order.assetAmount, order.price); - unchecked { - i++; + if (assetsToBuy == 0) { + continue; } + seller.sendValue(assetsToBuy * order.pricePerAsset); + order.assetAmount -= assetsToBuy; + remainingEth -= assetsToBuy * order.pricePerAsset; + remainingAsset -= assetsToBuy; + emit OrderFilled(seller, orderId, msg.sender, assetsToBuy, order.pricePerAsset); + if (remainingAsset == 0) { + break; + } + } + if (remainingAsset > 0) { + revert InsufficientEndAmount(assetAmount, assetAmount - remainingAsset); } - if (msg.value > totalPaid) { - payable(msg.sender).sendValue(msg.value - totalPaid); + if (remainingEth > 0) { + payable(msg.sender).sendValue(remainingEth); } } - /// @notice Cancels the sell order specified by `orderId`, making it unfillable. - /// @dev Reverts if the msg.sender is not the order's seller. + /// @notice Cancels the sell order identified by combination ``, making it unfillable. + /// @dev MUST change the `cancelled` parameter for the specified order to `true`. + /// MUST emit `OrderCancelled` event. function cancelSellOrder(uint256 orderId) public { - if (msg.sender != orders[orderId].seller) { - revert Unauthorized(); + if (orders[msg.sender][orderId].assetAmount == 0) { + revert OrderDoesNotExist(orderId); } - orders[orderId].active = false; - emit OrderCancelled(orderId); + orders[msg.sender][orderId].cancelled = true; + emit OrderCancelled(msg.sender, orderId); } /// @dev Returns true if this contract implements the interface defined by `interfaceId`. See EIP165. diff --git a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol index 5e6bd20a6..3b5e98388 100644 --- a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol +++ b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol @@ -22,8 +22,8 @@ contract OrderbookDexTest is CTest { function setUp() public { dex = new OrderbookDex(); - vm.deal(alice, 100 ether); - vm.deal(boris, 100 ether); + vm.deal(alice, 1_000 ether); + vm.deal(boris, 1_000 ether); } function test_SupportsInterface() public { @@ -31,126 +31,347 @@ contract OrderbookDexTest is CTest { assertTrue(dex.supportsInterface(type(IOrderbookDex).interfaceId)); } - function testFuzz_Fills(uint256 price) public { + function test_CreateOrderSatisfiesRequirements() public { + uint256 orderId = dex.getSellerOrderId(address(this)); + uint256 assetAmount = 100; + uint256 pricePerAsset = 200; + + vm.expectEmit(true, true, true, true); + emit IOrderbookDex.OrderCreated(address(this), orderId, assetAmount, pricePerAsset); + dex.createSellOrder(assetAmount, pricePerAsset); + IOrderbookDex.Order memory order = dex.getOrder(address(this), orderId); + assertEq(order.assetAmount, assetAmount); + assertEq(order.pricePerAsset, pricePerAsset); + assertTrue(!order.cancelled); + } + + function test_CancelOrderSatisfiesRequirements() public { + uint256 orderId = dex.getSellerOrderId(address(this)); + dex.createSellOrder(100, 200); + + vm.expectEmit(true, true, true, true); + emit IOrderbookDex.OrderCancelled(address(this), orderId); + dex.cancelSellOrder(orderId); + IOrderbookDex.Order memory order = dex.getOrder(address(this), orderId); + assertTrue(order.cancelled); + } + + function testFuzz_FillOrdersExactEth(uint256 price) public { uint256 ordersCount = 5; + vm.assume(price / ordersCount > 0); vm.assume(price < type(uint256).max / ordersCount); - uint256 firstOrderId = dex.getOrdersIndex(); + uint256[] memory orderIds = new uint256[](ordersCount); + address payable[] memory sellers = new address payable[](ordersCount); + uint256 totalAssetAmount; + address buyer = address(this); for (uint256 i = 0; i < ordersCount; i++) { - uint256 orderId = dex.getOrdersIndex(); - address seller = vm.addr(uint256(keccak256(abi.encodePacked(i)))); - uint256 assetAmount = uint256(keccak256(abi.encodePacked(price))); + address payable seller = payable(vm.addr(uint256(keccak256(abi.encodePacked(i))))); + uint256 assetAmount = price / (ordersCount - i); + uint256 orderId = dex.getSellerOrderId(seller); + uint256 pricePerAsset = price / assetAmount; + orderIds[i] = orderId; + sellers[i] = seller; vm.prank(seller); - - vm.expectEmit(true, true, true, true); - emit IOrderbookDex.OrderCreated(orderId, seller, assetAmount, price); - dex.createSellOrder(assetAmount, price); - - uint256 newOrderId = dex.getOrdersIndex(); - OrderbookDex.Order memory order = dex.getOrder(orderId); - assertEq(newOrderId, orderId + 1); - assertTrue(order.active); - assertEq(order.assetAmount, assetAmount); - assertEq(order.price, price); - assertEq(order.seller, seller); + dex.createSellOrder(assetAmount, pricePerAsset); + totalAssetAmount += assetAmount; } { - uint256 currentOrderId = dex.getOrdersIndex(); - uint256[] memory orderIds = new uint256[](ordersCount); + vm.deal(buyer, type(uint256).max); + uint256 buyerBalanceBefore = buyer.balance; + uint256[] memory sellersBalancesBefore = new uint256[](ordersCount); + uint256[] memory expectedPayouts = new uint256[](ordersCount); + uint256 totalExpectedPayout; for (uint256 i = 0; i < ordersCount; i++) { - orderIds[i] = firstOrderId + i; + IOrderbookDex.Order memory order = dex.getOrder(sellers[i], orderIds[i]); + expectedPayouts[i] = order.pricePerAsset * order.assetAmount; + totalExpectedPayout += expectedPayouts[i]; + sellersBalancesBefore[i] = sellers[i].balance; + vm.expectEmit(true, true, true, true); + emit IOrderbookDex.OrderFilled( + sellers[i], + orderIds[i], + buyer, + order.assetAmount, + order.pricePerAsset + ); } - - address buyer = vm.addr( - uint256(keccak256(abi.encodePacked(uint256(type(uint256).max)))) + vm.prank(buyer); + dex.fillOrdersExactEth{value: totalExpectedPayout + 1000}( + totalAssetAmount, + sellers, + orderIds ); + + assertEq(buyer.balance, buyerBalanceBefore - totalExpectedPayout); + for (uint256 i = 0; i < ordersCount; i++) { + IOrderbookDex.Order memory order = dex.getOrder(sellers[i], orderIds[i]); + assertEq(sellers[i].balance, sellersBalancesBefore[i] + expectedPayouts[i]); + assertEq(order.assetAmount, 0); + } + } + } + + function testFuzz_FillOrdersExactAsset(uint256 price) public { + uint256 ordersCount = 5; + vm.assume(price / ordersCount > 0); + vm.assume(price < type(uint256).max / ordersCount); + uint256[] memory orderIds = new uint256[](ordersCount); + address payable[] memory sellers = new address payable[](ordersCount); + uint256 totalAssetAmount; + address buyer = address(this); + for (uint256 i = 0; i < ordersCount; i++) { + address payable seller = payable(vm.addr(uint256(keccak256(abi.encodePacked(i))))); + uint256 assetAmount = price / (ordersCount - i); + uint256 orderId = dex.getSellerOrderId(seller); + uint256 pricePerAsset = price / assetAmount; + orderIds[i] = orderId; + sellers[i] = seller; + vm.prank(seller); + dex.createSellOrder(assetAmount, pricePerAsset); + totalAssetAmount += assetAmount; + } + + { vm.deal(buyer, type(uint256).max); + uint256 buyerBalanceBefore = buyer.balance; uint256[] memory sellersBalancesBefore = new uint256[](ordersCount); + uint256[] memory expectedPayouts = new uint256[](ordersCount); + uint256 totalExpectedPayout; for (uint256 i = 0; i < ordersCount; i++) { - OrderbookDex.Order memory order = dex.getOrder(firstOrderId + i); - sellersBalancesBefore[i] = order.seller.balance; + IOrderbookDex.Order memory order = dex.getOrder(sellers[i], orderIds[i]); + expectedPayouts[i] = order.pricePerAsset * order.assetAmount; + totalExpectedPayout += expectedPayouts[i]; + sellersBalancesBefore[i] = sellers[i].balance; vm.expectEmit(true, true, true, true); emit IOrderbookDex.OrderFilled( - firstOrderId + i, - order.seller, + sellers[i], + orderIds[i], buyer, order.assetAmount, - order.price + order.pricePerAsset ); } vm.prank(buyer); - dex.fillSellOrders{value: price * ordersCount}(orderIds); + dex.fillOrdersExactAsset{value: totalExpectedPayout + 1000}( + totalAssetAmount, + sellers, + orderIds + ); - assertEq(dex.getOrdersIndex(), currentOrderId); + assertEq(buyer.balance, buyerBalanceBefore - totalExpectedPayout); for (uint256 i = 0; i < ordersCount; i++) { - OrderbookDex.Order memory order = dex.getOrder(firstOrderId + i); - assertTrue(!order.active); - assertEq(order.seller.balance, sellersBalancesBefore[i] + order.price); + IOrderbookDex.Order memory order = dex.getOrder(sellers[i], orderIds[i]); + assertEq(sellers[i].balance, sellersBalancesBefore[i] + expectedPayouts[i]); + assertEq(order.assetAmount, 0); } } } - function test_ExcessValueIsRefunded() public { - uint256 price = 100; - uint256 orderId = dex.getOrdersIndex(); - dex.createSellOrder(10, price); + function testFuzz_PartialFillExactEth( + uint256 assetAmount, + uint256 pricePerAsset, + uint256 assetAmountToBuy + ) public { + vm.assume(assetAmount > 0 && pricePerAsset > 0); + vm.assume(assetAmount < type(uint256).max / pricePerAsset); + vm.assume(pricePerAsset < type(uint256).max / assetAmount); + vm.assume(assetAmountToBuy < assetAmount); - vm.prank(alice); - uint256 aliceBalanceBefore = alice.balance; + address buyer = alice; + address payable seller = payable(address(this)); + vm.deal(buyer, assetAmount * pricePerAsset); + uint256 orderId = dex.getSellerOrderId(address(this)); + dex.createSellOrder(assetAmount, pricePerAsset); + + uint256 buyerBalanceBefore = buyer.balance; + uint256 sellerBalanceBefore = address(this).balance; + address payable[] memory sellers = new address payable[](1); + sellers[0] = seller; uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; - dex.fillSellOrders{value: price * 5}(orderIds); - assertEq(alice.balance, aliceBalanceBefore - price); + vm.prank(buyer); + dex.fillOrdersExactEth{value: assetAmountToBuy * pricePerAsset}( + assetAmountToBuy, + sellers, + orderIds + ); + + IOrderbookDex.Order memory order = dex.getOrder(address(this), orderId); + assertEq(buyer.balance, buyerBalanceBefore - (assetAmountToBuy * pricePerAsset)); + assertEq(address(this).balance, sellerBalanceBefore + (assetAmountToBuy * pricePerAsset)); + assertEq(order.assetAmount, assetAmount - assetAmountToBuy); } - function test_CannotCancelOrderIfUnauthorized() public { - uint256 orderId = dex.getOrdersIndex(); - dex.createSellOrder(100, 200); + function testFuzz_PartialFillExactAsset( + uint256 assetAmount, + uint256 pricePerAsset, + uint256 assetAmountToBuy + ) public { + vm.assume(assetAmount > 0 && pricePerAsset > 0); + vm.assume(assetAmount < type(uint256).max / pricePerAsset); + vm.assume(pricePerAsset < type(uint256).max / assetAmount); + vm.assume(assetAmountToBuy < assetAmount); - vm.prank(alice); - vm.expectRevert(OrderbookDex.Unauthorized.selector); - dex.cancelSellOrder(orderId); + address buyer = alice; + address payable seller = payable(address(this)); + vm.deal(buyer, assetAmount * pricePerAsset); + uint256 orderId = dex.getSellerOrderId(address(this)); + dex.createSellOrder(assetAmount, pricePerAsset); + + uint256 buyerBalanceBefore = buyer.balance; + uint256 sellerBalanceBefore = address(this).balance; + address payable[] memory sellers = new address payable[](1); + sellers[0] = seller; + uint256[] memory orderIds = new uint256[](1); + orderIds[0] = orderId; + vm.prank(buyer); + dex.fillOrdersExactAsset{value: buyerBalanceBefore}(assetAmountToBuy, sellers, orderIds); + + IOrderbookDex.Order memory order = dex.getOrder(address(this), orderId); + assertEq(buyer.balance, buyerBalanceBefore - (assetAmountToBuy * pricePerAsset)); + assertEq(address(this).balance, sellerBalanceBefore + (assetAmountToBuy * pricePerAsset)); + assertEq(order.assetAmount, assetAmount - assetAmountToBuy); } - function test_CannotFillOrderIfCancelled() public { - uint256 orderId = dex.getOrdersIndex(); - dex.createSellOrder(100, 200); - dex.cancelSellOrder(orderId); + function testFuzz_ExcessValueIsRefundedFillExactEth( + uint256 assetAmount, + uint256 pricePerAsset + ) public { + uint256 multiplier = 3; + vm.assume(assetAmount > 0 && pricePerAsset > 0); + vm.assume(assetAmount < type(uint256).max / pricePerAsset / multiplier); + vm.assume(pricePerAsset < type(uint256).max / assetAmount / multiplier); - vm.prank(alice); + vm.deal(alice, assetAmount * pricePerAsset * multiplier); + uint256 orderId = dex.getSellerOrderId(address(this)); + dex.createSellOrder(assetAmount, pricePerAsset); + + uint256 aliceBalanceBefore = alice.balance; + address payable[] memory sellers = new address payable[](1); + sellers[0] = payable(address(this)); uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; - vm.expectRevert(abi.encodeWithSelector(OrderbookDex.OrderIsInactive.selector, orderId)); - dex.fillSellOrders(orderIds); + vm.prank(alice); + dex.fillOrdersExactEth{value: alice.balance}(assetAmount, sellers, orderIds); + assertEq(alice.balance, aliceBalanceBefore - (assetAmount * pricePerAsset)); } - function test_CannotFillOrderIfAlreadyFilled() public { - uint256 orderId = dex.getOrdersIndex(); - uint256 price = 1000; - dex.createSellOrder(100, price); + function testFuzz_ExcessValueIsRefundedFillExactAsset( + uint256 assetAmount, + uint256 pricePerAsset + ) public { + uint256 multiplier = 3; + vm.assume(assetAmount > 0 && pricePerAsset > 0); + vm.assume(assetAmount < type(uint256).max / pricePerAsset / multiplier); + vm.assume(pricePerAsset < type(uint256).max / assetAmount / multiplier); + vm.deal(alice, assetAmount * pricePerAsset * multiplier); + uint256 orderId = dex.getSellerOrderId(address(this)); + dex.createSellOrder(assetAmount, pricePerAsset); + + uint256 aliceBalanceBefore = alice.balance; + address payable[] memory sellers = new address payable[](1); + sellers[0] = payable(address(this)); + uint256[] memory orderIds = new uint256[](1); + orderIds[0] = orderId; vm.prank(alice); + dex.fillOrdersExactAsset{value: alice.balance}(assetAmount, sellers, orderIds); + assertEq(alice.balance, aliceBalanceBefore - (assetAmount * pricePerAsset)); + } + + function test_WontFillOrderIfCancelled() public { + uint256 assetAmount = 100; + uint256 pricePerAsset = 200; + uint256 orderId = dex.getSellerOrderId(address(this)); + dex.createSellOrder(assetAmount, pricePerAsset); + dex.cancelSellOrder(orderId); + + uint256 aliceBalanceBefore = alice.balance; + address payable[] memory sellers = new address payable[](1); + sellers[0] = payable(address(this)); uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; - dex.fillSellOrders{value: price}(orderIds); + vm.prank(alice); - vm.prank(boris); - vm.expectRevert(abi.encodeWithSelector(OrderbookDex.OrderIsInactive.selector, orderId)); - dex.fillSellOrders{value: price}(orderIds); + dex.fillOrdersExactEth{value: assetAmount * pricePerAsset}(0, sellers, orderIds); + assertEq(alice.balance, aliceBalanceBefore); + dex.fillOrdersExactAsset{value: assetAmount * pricePerAsset}(0, sellers, orderIds); + assertEq(alice.balance, aliceBalanceBefore); } - function test_CannotFillOrderIfInsufficientValue() public { - uint256 price = 100; - uint256 orderId = dex.getOrdersIndex(); - dex.createSellOrder(10, price); + function test_CannotCreateOrderWithZeroAssetAmount() public { + vm.expectRevert(abi.encodeWithSelector(OrderbookDex.InvalidInput.selector, 0)); + dex.createSellOrder(0, 100); + } + + function test_CannotCancelOrderIfDoesNotExist() public { + uint256 orderId = dex.getSellerOrderId(address(this)); + vm.expectRevert(abi.encodeWithSelector(OrderbookDex.OrderDoesNotExist.selector, orderId)); + dex.cancelSellOrder(orderId); + } + + function testFuzz_CannotFillOrderWithInvalidInputArity( + address payable[] memory sellers, + uint256[] memory orderIds + ) public { + vm.assume(sellers.length != orderIds.length); + vm.expectRevert(OrderbookDex.InvalidInputArity.selector); + dex.fillOrdersExactEth(0, sellers, orderIds); + + vm.expectRevert(OrderbookDex.InvalidInputArity.selector); + dex.fillOrdersExactAsset(0, sellers, orderIds); + } + + function test_CannotFillOrderIfInsufficientEndAmountExactEth() public { + uint256 assetAmount = 10; + uint256 pricePerAsset = 100; + uint256 orderId = dex.getSellerOrderId(address(this)); + dex.createSellOrder(assetAmount, pricePerAsset); + + uint256[] memory orderIds = new uint256[](1); + orderIds[0] = orderId; + address payable[] memory sellers = new address payable[](1); + sellers[0] = payable(address(this)); vm.prank(alice); + vm.expectRevert( + abi.encodeWithSelector( + OrderbookDex.InsufficientEndAmount.selector, + assetAmount + 1, + assetAmount + ) + ); + dex.fillOrdersExactEth{value: assetAmount * pricePerAsset}( + assetAmount + 1, + sellers, + orderIds + ); + } + + function test_CannotFillOrderIfInsufficientEndAmountExactAsset() public { + uint256 assetAmount = 10; + uint256 pricePerAsset = 100; + uint256 orderId = dex.getSellerOrderId(address(this)); + dex.createSellOrder(assetAmount, pricePerAsset); + uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; + address payable[] memory sellers = new address payable[](1); + sellers[0] = payable(address(this)); + vm.prank(alice); vm.expectRevert( - abi.encodeWithSelector(Address.AddressInsufficientBalance.selector, address(dex)) + abi.encodeWithSelector( + OrderbookDex.InsufficientEndAmount.selector, + assetAmount + 1, + assetAmount + ) + ); + dex.fillOrdersExactAsset{value: assetAmount * pricePerAsset}( + assetAmount + 1, + sellers, + orderIds ); - dex.fillSellOrders{value: price - 1}(orderIds); } function test_CannotSendEtherToDex() public { From 98125300e7d03d193990ca209aaf28ce30a14e82 Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Tue, 9 Apr 2024 16:16:17 +0200 Subject: [PATCH 05/18] Make impl fns public, add getAsset fn --- .../contracts/orderbook/IOrderbookDex.sol | 3 +++ .../contracts/orderbook/OrderbookDex.sol | 22 +++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol index 3519b81d7..e725ca880 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol @@ -28,6 +28,9 @@ interface IOrderbookDex is IERC165 { ); event OrderCancelled(address indexed seller, uint256 indexed orderId); + /// @notice Returns the address of the asset that is being traded. + function getAsset() external view returns (address); + /// @notice Returns the seller's current `orderId` (index that their new sell order will be mapped to). function getSellerOrderId(address seller) external view returns (uint256); diff --git a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol index 8032d2fd1..b103d7eed 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol @@ -6,12 +6,15 @@ import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; + +import {IInverseProjected1155} from "../token/IInverseProjected1155.sol"; import {IOrderbookDex} from "./IOrderbookDex.sol"; /// @notice Facilitates trading an asset that is living on a different app-chain. contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { using Address for address payable; + IInverseProjected1155 asset; mapping(address => mapping(uint256 => Order)) public orders; mapping(address => uint256) public sellersOrderId; @@ -20,20 +23,25 @@ contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { error InvalidInput(uint256 input); error InvalidInputArity(); + /// @notice Returns the address of the asset that is being traded. + function getAsset() public view virtual returns (address) { + return address(asset); + } + /// @notice Returns the seller's current `orderId` (index that their new sell order will be mapped to). - function getSellerOrderId(address seller) external view returns (uint256) { + function getSellerOrderId(address seller) public view virtual returns (uint256) { return sellersOrderId[seller]; } /// @notice Returns the Order struct information about an order identified by the combination ``. - function getOrder(address seller, uint256 orderId) external view returns (Order memory) { + function getOrder(address seller, uint256 orderId) public view virtual returns (Order memory) { return orders[seller][orderId]; } /// @notice Creates a sell order with incremental seller-specific `orderId` for the specified `assetAmount` at specified `pricePerAsset`. /// @dev The order information is saved in a nested mapping `seller address -> orderId -> Order`. /// MUST emit `OrderCreated` event. - function createSellOrder(uint256 assetAmount, uint256 pricePerAsset) public { + function createSellOrder(uint256 assetAmount, uint256 pricePerAsset) public virtual { if (assetAmount == 0 || pricePerAsset == 0) { revert InvalidInput(0); } @@ -60,7 +68,7 @@ contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { uint256 minimumAsset, address payable[] memory sellers, uint256[] memory orderIds - ) public payable nonReentrant { + ) public payable virtual nonReentrant { if (sellers.length != orderIds.length) { revert InvalidInputArity(); } @@ -110,7 +118,7 @@ contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { uint256 assetAmount, address payable[] memory sellers, uint256[] memory orderIds - ) public payable nonReentrant { + ) public payable virtual nonReentrant { if (sellers.length != orderIds.length) { revert InvalidInputArity(); } @@ -151,7 +159,7 @@ contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { /// @notice Cancels the sell order identified by combination ``, making it unfillable. /// @dev MUST change the `cancelled` parameter for the specified order to `true`. /// MUST emit `OrderCancelled` event. - function cancelSellOrder(uint256 orderId) public { + function cancelSellOrder(uint256 orderId) public virtual { if (orders[msg.sender][orderId].assetAmount == 0) { revert OrderDoesNotExist(orderId); } @@ -162,7 +170,7 @@ contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { /// @dev Returns true if this contract implements the interface defined by `interfaceId`. See EIP165. function supportsInterface( bytes4 interfaceId - ) public view override(ERC165, IERC165) returns (bool) { + ) public view virtual override(ERC165, IERC165) returns (bool) { return interfaceId == type(IOrderbookDex).interfaceId || super.supportsInterface(interfaceId); } From 9ae00cf09d312cb6d831ad83bb0b3f8cada3d5d9 Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Tue, 9 Apr 2024 16:16:17 +0200 Subject: [PATCH 06/18] Remove public from variables having getters --- .../evm-contracts/contracts/orderbook/OrderbookDex.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol index b103d7eed..4f89d20d5 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol @@ -15,8 +15,8 @@ contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { using Address for address payable; IInverseProjected1155 asset; - mapping(address => mapping(uint256 => Order)) public orders; - mapping(address => uint256) public sellersOrderId; + mapping(address => mapping(uint256 => Order)) orders; + mapping(address => uint256) sellersOrderId; error OrderDoesNotExist(uint256 orderId); error InsufficientEndAmount(uint256 expectedAmount, uint256 actualAmount); From 404a371736a9d69c2a144d0ea9cf073b0f1931f7 Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Tue, 9 Apr 2024 16:16:17 +0200 Subject: [PATCH 07/18] Add the InverseProjected1155 to the dex --- .../contracts/orderbook/IOrderbookDex.sol | 7 +- .../contracts/orderbook/OrderbookDex.sol | 48 ++++++++-- .../evm-contracts/test/OrderbookDex.t.sol | 94 ++++++++++++++----- 3 files changed, 113 insertions(+), 36 deletions(-) diff --git a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol index e725ca880..fa5741483 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol @@ -2,12 +2,13 @@ pragma solidity ^0.8.20; -import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; /// @notice Facilitates base-chain trading of an asset that is living on a different app-chain. /// @dev The contract should never hold any ETH itself. -interface IOrderbookDex is IERC165 { +interface IOrderbookDex is IERC1155Receiver { struct Order { + uint256 assetId; uint256 assetAmount; uint256 pricePerAsset; bool cancelled; @@ -40,7 +41,7 @@ interface IOrderbookDex is IERC165 { /// @notice Creates a sell order with incremental seller-specific `orderId` for the specified `assetAmount` at specified `pricePerAsset`. /// @dev The order information is saved in a nested mapping `seller address -> orderId -> Order`. /// MUST emit `OrderCreated` event. - function createSellOrder(uint256 assetAmount, uint256 pricePerAsset) external; + function createSellOrder(uint256 assetId, uint256 assetAmount, uint256 pricePerAsset) external; /// @notice Consecutively fills an array of orders identified by the combination ``, /// by providing an exact amount of ETH and requesting a specific minimum amount of asset to receive. diff --git a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol index 4f89d20d5..f09c2102d 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.20; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; -import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol"; +import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; @@ -11,18 +11,22 @@ import {IInverseProjected1155} from "../token/IInverseProjected1155.sol"; import {IOrderbookDex} from "./IOrderbookDex.sol"; /// @notice Facilitates trading an asset that is living on a different app-chain. -contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { +contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { using Address for address payable; - IInverseProjected1155 asset; - mapping(address => mapping(uint256 => Order)) orders; - mapping(address => uint256) sellersOrderId; + IInverseProjected1155 internal asset; + mapping(address => mapping(uint256 => Order)) internal orders; + mapping(address => uint256) internal sellersOrderId; error OrderDoesNotExist(uint256 orderId); error InsufficientEndAmount(uint256 expectedAmount, uint256 actualAmount); error InvalidInput(uint256 input); error InvalidInputArity(); + constructor(IInverseProjected1155 _asset) { + asset = _asset; + } + /// @notice Returns the address of the asset that is being traded. function getAsset() public view virtual returns (address) { return address(asset); @@ -41,11 +45,17 @@ contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { /// @notice Creates a sell order with incremental seller-specific `orderId` for the specified `assetAmount` at specified `pricePerAsset`. /// @dev The order information is saved in a nested mapping `seller address -> orderId -> Order`. /// MUST emit `OrderCreated` event. - function createSellOrder(uint256 assetAmount, uint256 pricePerAsset) public virtual { + function createSellOrder( + uint256 assetId, + uint256 assetAmount, + uint256 pricePerAsset + ) public virtual { if (assetAmount == 0 || pricePerAsset == 0) { revert InvalidInput(0); } + asset.safeTransferFrom(msg.sender, address(this), assetId, assetAmount, bytes("")); Order memory newOrder = Order({ + assetId: assetId, assetAmount: assetAmount, pricePerAsset: pricePerAsset, cancelled: false @@ -93,6 +103,13 @@ contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { order.assetAmount -= assetsToBuy; remainingEth -= assetsToBuy * order.pricePerAsset; totalAssetReceived += assetsToBuy; + asset.safeTransferFrom( + address(this), + msg.sender, + order.assetId, + assetsToBuy, + bytes("") + ); emit OrderFilled(seller, orderId, msg.sender, assetsToBuy, order.pricePerAsset); if (remainingEth == 0) { break; @@ -143,6 +160,13 @@ contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { order.assetAmount -= assetsToBuy; remainingEth -= assetsToBuy * order.pricePerAsset; remainingAsset -= assetsToBuy; + asset.safeTransferFrom( + address(this), + msg.sender, + order.assetId, + assetsToBuy, + bytes("") + ); emit OrderFilled(seller, orderId, msg.sender, assetsToBuy, order.pricePerAsset); if (remainingAsset == 0) { break; @@ -160,17 +184,25 @@ contract OrderbookDex is IOrderbookDex, ERC165, ReentrancyGuard { /// @dev MUST change the `cancelled` parameter for the specified order to `true`. /// MUST emit `OrderCancelled` event. function cancelSellOrder(uint256 orderId) public virtual { - if (orders[msg.sender][orderId].assetAmount == 0) { + Order memory order = orders[msg.sender][orderId]; + if (order.assetAmount == 0) { revert OrderDoesNotExist(orderId); } orders[msg.sender][orderId].cancelled = true; + asset.safeTransferFrom( + address(this), + msg.sender, + order.assetId, + order.assetAmount, + bytes("") + ); emit OrderCancelled(msg.sender, orderId); } /// @dev Returns true if this contract implements the interface defined by `interfaceId`. See EIP165. function supportsInterface( bytes4 interfaceId - ) public view virtual override(ERC165, IERC165) returns (bool) { + ) public view virtual override(ERC1155Holder, IERC165) returns (bool) { return interfaceId == type(IOrderbookDex).interfaceId || super.supportsInterface(interfaceId); } diff --git a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol index 3b5e98388..62cd716b3 100644 --- a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol +++ b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol @@ -3,16 +3,22 @@ pragma solidity ^0.8.18; import {CheatCodes} from "../test-lib/cheatcodes.sol"; import {CTest} from "../test-lib/ctest.sol"; + import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +import {IInverseAppProjected1155} from "../contracts/token/IInverseAppProjected1155.sol"; +import {InverseAppProjected1155} from "../contracts/token/InverseAppProjected1155.sol"; import {IOrderbookDex} from "../contracts/orderbook/IOrderbookDex.sol"; import {OrderbookDex} from "../contracts/orderbook/OrderbookDex.sol"; -contract OrderbookDexTest is CTest { +contract OrderbookDexTest is CTest, ERC1155Holder { using Address for address payable; CheatCodes vm = CheatCodes(HEVM_ADDRESS); OrderbookDex public dex; + IInverseAppProjected1155 asset; address alice = vm.addr(uint256(keccak256(abi.encodePacked("alice")))); address boris = vm.addr(uint256(keccak256(abi.encodePacked("boris")))); @@ -21,7 +27,9 @@ contract OrderbookDexTest is CTest { } function setUp() public { - dex = new OrderbookDex(); + asset = new InverseAppProjected1155("Gold", "GOLD", address(this)); + dex = new OrderbookDex(asset); + asset.setApprovalForAll(address(dex), true); vm.deal(alice, 1_000 ether); vm.deal(boris, 1_000 ether); } @@ -36,24 +44,31 @@ contract OrderbookDexTest is CTest { uint256 assetAmount = 100; uint256 pricePerAsset = 200; + uint256 assetId = asset.mint(assetAmount, ""); vm.expectEmit(true, true, true, true); emit IOrderbookDex.OrderCreated(address(this), orderId, assetAmount, pricePerAsset); - dex.createSellOrder(assetAmount, pricePerAsset); + dex.createSellOrder(assetId, assetAmount, pricePerAsset); IOrderbookDex.Order memory order = dex.getOrder(address(this), orderId); + assertEq(order.assetId, assetId); assertEq(order.assetAmount, assetAmount); assertEq(order.pricePerAsset, pricePerAsset); assertTrue(!order.cancelled); + assertEq(asset.balanceOf(address(dex), assetId), assetAmount); } function test_CancelOrderSatisfiesRequirements() public { uint256 orderId = dex.getSellerOrderId(address(this)); - dex.createSellOrder(100, 200); + uint256 assetAmount = 100; + uint256 assetId = asset.mint(assetAmount, ""); + dex.createSellOrder(assetId, assetAmount, 200); vm.expectEmit(true, true, true, true); emit IOrderbookDex.OrderCancelled(address(this), orderId); dex.cancelSellOrder(orderId); IOrderbookDex.Order memory order = dex.getOrder(address(this), orderId); assertTrue(order.cancelled); + assertEq(asset.balanceOf(address(dex), assetId), 0); + assertEq(asset.balanceOf(address(this), assetId), assetAmount); } function testFuzz_FillOrdersExactEth(uint256 price) public { @@ -71,8 +86,10 @@ contract OrderbookDexTest is CTest { uint256 pricePerAsset = price / assetAmount; orderIds[i] = orderId; sellers[i] = seller; - vm.prank(seller); - dex.createSellOrder(assetAmount, pricePerAsset); + vm.startPrank(seller); + uint256 assetId = asset.mint(assetAmount, ""); + asset.setApprovalForAll(address(dex), true); + dex.createSellOrder(assetId, assetAmount, pricePerAsset); totalAssetAmount += assetAmount; } @@ -81,10 +98,12 @@ contract OrderbookDexTest is CTest { uint256 buyerBalanceBefore = buyer.balance; uint256[] memory sellersBalancesBefore = new uint256[](ordersCount); uint256[] memory expectedPayouts = new uint256[](ordersCount); + uint256[] memory ordersAssetAmounts = new uint256[](ordersCount); uint256 totalExpectedPayout; for (uint256 i = 0; i < ordersCount; i++) { IOrderbookDex.Order memory order = dex.getOrder(sellers[i], orderIds[i]); expectedPayouts[i] = order.pricePerAsset * order.assetAmount; + ordersAssetAmounts[i] = order.assetAmount; totalExpectedPayout += expectedPayouts[i]; sellersBalancesBefore[i] = sellers[i].balance; vm.expectEmit(true, true, true, true); @@ -96,7 +115,7 @@ contract OrderbookDexTest is CTest { order.pricePerAsset ); } - vm.prank(buyer); + vm.startPrank(buyer); dex.fillOrdersExactEth{value: totalExpectedPayout + 1000}( totalAssetAmount, sellers, @@ -108,6 +127,8 @@ contract OrderbookDexTest is CTest { IOrderbookDex.Order memory order = dex.getOrder(sellers[i], orderIds[i]); assertEq(sellers[i].balance, sellersBalancesBefore[i] + expectedPayouts[i]); assertEq(order.assetAmount, 0); + assertEq(asset.balanceOf(address(dex), order.assetId), 0); + assertEq(asset.balanceOf(buyer, order.assetId), ordersAssetAmounts[i]); } } } @@ -127,8 +148,10 @@ contract OrderbookDexTest is CTest { uint256 pricePerAsset = price / assetAmount; orderIds[i] = orderId; sellers[i] = seller; - vm.prank(seller); - dex.createSellOrder(assetAmount, pricePerAsset); + vm.startPrank(seller); + uint256 assetId = asset.mint(assetAmount, ""); + asset.setApprovalForAll(address(dex), true); + dex.createSellOrder(assetId, assetAmount, pricePerAsset); totalAssetAmount += assetAmount; } @@ -137,10 +160,12 @@ contract OrderbookDexTest is CTest { uint256 buyerBalanceBefore = buyer.balance; uint256[] memory sellersBalancesBefore = new uint256[](ordersCount); uint256[] memory expectedPayouts = new uint256[](ordersCount); + uint256[] memory ordersAssetAmounts = new uint256[](ordersCount); uint256 totalExpectedPayout; for (uint256 i = 0; i < ordersCount; i++) { IOrderbookDex.Order memory order = dex.getOrder(sellers[i], orderIds[i]); expectedPayouts[i] = order.pricePerAsset * order.assetAmount; + ordersAssetAmounts[i] = order.assetAmount; totalExpectedPayout += expectedPayouts[i]; sellersBalancesBefore[i] = sellers[i].balance; vm.expectEmit(true, true, true, true); @@ -152,7 +177,7 @@ contract OrderbookDexTest is CTest { order.pricePerAsset ); } - vm.prank(buyer); + vm.startPrank(buyer); dex.fillOrdersExactAsset{value: totalExpectedPayout + 1000}( totalAssetAmount, sellers, @@ -164,6 +189,8 @@ contract OrderbookDexTest is CTest { IOrderbookDex.Order memory order = dex.getOrder(sellers[i], orderIds[i]); assertEq(sellers[i].balance, sellersBalancesBefore[i] + expectedPayouts[i]); assertEq(order.assetAmount, 0); + assertEq(asset.balanceOf(address(dex), order.assetId), 0); + assertEq(asset.balanceOf(buyer, order.assetId), ordersAssetAmounts[i]); } } } @@ -182,7 +209,10 @@ contract OrderbookDexTest is CTest { address payable seller = payable(address(this)); vm.deal(buyer, assetAmount * pricePerAsset); uint256 orderId = dex.getSellerOrderId(address(this)); - dex.createSellOrder(assetAmount, pricePerAsset); + vm.startPrank(seller); + uint256 assetId = asset.mint(assetAmount, ""); + asset.setApprovalForAll(address(dex), true); + dex.createSellOrder(assetId, assetAmount, pricePerAsset); uint256 buyerBalanceBefore = buyer.balance; uint256 sellerBalanceBefore = address(this).balance; @@ -190,7 +220,7 @@ contract OrderbookDexTest is CTest { sellers[0] = seller; uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; - vm.prank(buyer); + vm.startPrank(buyer); dex.fillOrdersExactEth{value: assetAmountToBuy * pricePerAsset}( assetAmountToBuy, sellers, @@ -201,6 +231,8 @@ contract OrderbookDexTest is CTest { assertEq(buyer.balance, buyerBalanceBefore - (assetAmountToBuy * pricePerAsset)); assertEq(address(this).balance, sellerBalanceBefore + (assetAmountToBuy * pricePerAsset)); assertEq(order.assetAmount, assetAmount - assetAmountToBuy); + assertEq(asset.balanceOf(address(dex), assetId), assetAmount - assetAmountToBuy); + assertEq(asset.balanceOf(buyer, assetId), assetAmountToBuy); } function testFuzz_PartialFillExactAsset( @@ -217,7 +249,10 @@ contract OrderbookDexTest is CTest { address payable seller = payable(address(this)); vm.deal(buyer, assetAmount * pricePerAsset); uint256 orderId = dex.getSellerOrderId(address(this)); - dex.createSellOrder(assetAmount, pricePerAsset); + vm.startPrank(seller); + uint256 assetId = asset.mint(assetAmount, ""); + asset.setApprovalForAll(address(dex), true); + dex.createSellOrder(assetId, assetAmount, pricePerAsset); uint256 buyerBalanceBefore = buyer.balance; uint256 sellerBalanceBefore = address(this).balance; @@ -225,13 +260,15 @@ contract OrderbookDexTest is CTest { sellers[0] = seller; uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; - vm.prank(buyer); + vm.startPrank(buyer); dex.fillOrdersExactAsset{value: buyerBalanceBefore}(assetAmountToBuy, sellers, orderIds); IOrderbookDex.Order memory order = dex.getOrder(address(this), orderId); assertEq(buyer.balance, buyerBalanceBefore - (assetAmountToBuy * pricePerAsset)); assertEq(address(this).balance, sellerBalanceBefore + (assetAmountToBuy * pricePerAsset)); assertEq(order.assetAmount, assetAmount - assetAmountToBuy); + assertEq(asset.balanceOf(address(dex), assetId), assetAmount - assetAmountToBuy); + assertEq(asset.balanceOf(buyer, assetId), assetAmountToBuy); } function testFuzz_ExcessValueIsRefundedFillExactEth( @@ -245,14 +282,15 @@ contract OrderbookDexTest is CTest { vm.deal(alice, assetAmount * pricePerAsset * multiplier); uint256 orderId = dex.getSellerOrderId(address(this)); - dex.createSellOrder(assetAmount, pricePerAsset); + uint256 assetId = asset.mint(assetAmount, ""); + dex.createSellOrder(assetId, assetAmount, pricePerAsset); uint256 aliceBalanceBefore = alice.balance; address payable[] memory sellers = new address payable[](1); sellers[0] = payable(address(this)); uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; - vm.prank(alice); + vm.startPrank(alice); dex.fillOrdersExactEth{value: alice.balance}(assetAmount, sellers, orderIds); assertEq(alice.balance, aliceBalanceBefore - (assetAmount * pricePerAsset)); } @@ -268,14 +306,15 @@ contract OrderbookDexTest is CTest { vm.deal(alice, assetAmount * pricePerAsset * multiplier); uint256 orderId = dex.getSellerOrderId(address(this)); - dex.createSellOrder(assetAmount, pricePerAsset); + uint256 assetId = asset.mint(assetAmount, ""); + dex.createSellOrder(assetId, assetAmount, pricePerAsset); uint256 aliceBalanceBefore = alice.balance; address payable[] memory sellers = new address payable[](1); sellers[0] = payable(address(this)); uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; - vm.prank(alice); + vm.startPrank(alice); dex.fillOrdersExactAsset{value: alice.balance}(assetAmount, sellers, orderIds); assertEq(alice.balance, aliceBalanceBefore - (assetAmount * pricePerAsset)); } @@ -284,7 +323,8 @@ contract OrderbookDexTest is CTest { uint256 assetAmount = 100; uint256 pricePerAsset = 200; uint256 orderId = dex.getSellerOrderId(address(this)); - dex.createSellOrder(assetAmount, pricePerAsset); + uint256 assetId = asset.mint(assetAmount, ""); + dex.createSellOrder(assetId, assetAmount, pricePerAsset); dex.cancelSellOrder(orderId); uint256 aliceBalanceBefore = alice.balance; @@ -292,7 +332,7 @@ contract OrderbookDexTest is CTest { sellers[0] = payable(address(this)); uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; - vm.prank(alice); + vm.startPrank(alice); dex.fillOrdersExactEth{value: assetAmount * pricePerAsset}(0, sellers, orderIds); assertEq(alice.balance, aliceBalanceBefore); @@ -301,8 +341,10 @@ contract OrderbookDexTest is CTest { } function test_CannotCreateOrderWithZeroAssetAmount() public { + uint256 assetId = asset.mint(0, ""); + vm.expectRevert(abi.encodeWithSelector(OrderbookDex.InvalidInput.selector, 0)); - dex.createSellOrder(0, 100); + dex.createSellOrder(assetId, 0, 100); } function test_CannotCancelOrderIfDoesNotExist() public { @@ -328,13 +370,14 @@ contract OrderbookDexTest is CTest { uint256 assetAmount = 10; uint256 pricePerAsset = 100; uint256 orderId = dex.getSellerOrderId(address(this)); - dex.createSellOrder(assetAmount, pricePerAsset); + uint256 assetId = asset.mint(assetAmount, ""); + dex.createSellOrder(assetId, assetAmount, pricePerAsset); uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; address payable[] memory sellers = new address payable[](1); sellers[0] = payable(address(this)); - vm.prank(alice); + vm.startPrank(alice); vm.expectRevert( abi.encodeWithSelector( OrderbookDex.InsufficientEndAmount.selector, @@ -353,13 +396,14 @@ contract OrderbookDexTest is CTest { uint256 assetAmount = 10; uint256 pricePerAsset = 100; uint256 orderId = dex.getSellerOrderId(address(this)); - dex.createSellOrder(assetAmount, pricePerAsset); + uint256 assetId = asset.mint(assetAmount, ""); + dex.createSellOrder(assetId, assetAmount, pricePerAsset); uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; address payable[] memory sellers = new address payable[](1); sellers[0] = payable(address(this)); - vm.prank(alice); + vm.startPrank(alice); vm.expectRevert( abi.encodeWithSelector( OrderbookDex.InsufficientEndAmount.selector, From 8539df78d1f387f9cf5f312bb96f435b2cce02c0 Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Tue, 9 Apr 2024 16:16:17 +0200 Subject: [PATCH 08/18] Remove unnecessary `cancelled` attribute --- .../contracts/orderbook/IOrderbookDex.sol | 5 +---- .../contracts/orderbook/OrderbookDex.sol | 13 +++++-------- .../contracts/evm-contracts/test/OrderbookDex.t.sol | 3 +-- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol index fa5741483..c946fe6ca 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol @@ -11,7 +11,6 @@ interface IOrderbookDex is IERC1155Receiver { uint256 assetId; uint256 assetAmount; uint256 pricePerAsset; - bool cancelled; } event OrderCreated( @@ -48,7 +47,6 @@ interface IOrderbookDex is IERC1155Receiver { /// @dev Transfers portions of msg.value to the orders' sellers according to the price. /// The sum of asset amounts of filled orders MUST be at least `minimumAsset`. /// If msg.value is more than the sum of orders' prices, it MUST refund the excess back to msg.sender. - /// An order whose `cancelled` parameter has value `true` MUST NOT be filled. /// MUST change the `assetAmount` parameter for the specified order according to how much of it was filled. /// MUST emit `OrderFilled` event for each order accordingly. function fillOrdersExactEth( @@ -61,7 +59,6 @@ interface IOrderbookDex is IERC1155Receiver { /// by providing a possibly surplus amount of ETH and requesting an exact amount of asset to receive. /// @dev Transfers portions of msg.value to the orders' sellers according to the price. /// The sum of asset amounts of filled orders MUST be exactly `assetAmount`. Excess ETH MUST be returned back to `msg.sender`. - /// An order whose `cancelled` parameter has value `true` MUST NOT be filled. /// MUST change the `assetAmount` parameter for the specified order according to how much of it was filled. /// MUST emit `OrderFilled` event for each order accordingly. /// If msg.value is more than the sum of orders' prices, it MUST refund the difference back to msg.sender. @@ -72,7 +69,7 @@ interface IOrderbookDex is IERC1155Receiver { ) external payable; /// @notice Cancels the sell order identified by combination ``, making it unfillable. - /// @dev MUST change the `cancelled` parameter for the specified order to `true`. + /// @dev MUST change the `assetAmount` parameter for the specified order to `0`. /// MUST emit `OrderCancelled` event. function cancelSellOrder(uint256 orderId) external; } diff --git a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol index f09c2102d..810edb55d 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol @@ -57,8 +57,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { Order memory newOrder = Order({ assetId: assetId, assetAmount: assetAmount, - pricePerAsset: pricePerAsset, - cancelled: false + pricePerAsset: pricePerAsset }); uint256 orderId = sellersOrderId[msg.sender]; orders[msg.sender][orderId] = newOrder; @@ -71,7 +70,6 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { /// @dev Transfers portions of msg.value to the orders' sellers according to the price. /// The sum of asset amounts of filled orders MUST be at least `minimumAsset`. /// If msg.value is more than the sum of orders' prices, it MUST refund the excess back to msg.sender. - /// An order whose `cancelled` parameter has value `true` MUST NOT be filled. /// MUST change the `assetAmount` parameter for the specified order according to how much of it was filled. /// MUST emit `OrderFilled` event for each order accordingly. function fillOrdersExactEth( @@ -89,7 +87,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { address payable seller = sellers[i]; uint256 orderId = orderIds[i]; Order storage order = orders[seller][orderId]; - if (order.cancelled || order.assetAmount == 0 || order.pricePerAsset == 0) { + if (order.assetAmount == 0) { continue; } uint256 assetsToBuy = remainingEth / order.pricePerAsset; @@ -127,7 +125,6 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { /// by providing a possibly surplus amount of ETH and requesting an exact amount of asset to receive. /// @dev Transfers portions of msg.value to the orders' sellers according to the price. /// The sum of asset amounts of filled orders MUST be exactly `assetAmount`. Excess ETH MUST be returned back to `msg.sender`. - /// An order whose `cancelled` parameter has value `true` MUST NOT be filled. /// MUST change the `assetAmount` parameter for the specified order according to how much of it was filled. /// MUST emit `OrderFilled` event for each order accordingly. /// If msg.value is more than the sum of orders' prices, it MUST refund the difference back to msg.sender. @@ -146,7 +143,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { address payable seller = sellers[i]; uint256 orderId = orderIds[i]; Order storage order = orders[seller][orderId]; - if (order.cancelled || order.assetAmount == 0 || order.pricePerAsset == 0) { + if (order.assetAmount == 0) { continue; } uint256 assetsToBuy = order.assetAmount; @@ -181,14 +178,14 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { } /// @notice Cancels the sell order identified by combination ``, making it unfillable. - /// @dev MUST change the `cancelled` parameter for the specified order to `true`. + /// @dev MUST change the `assetAmount` parameter for the specified order to `0`. /// MUST emit `OrderCancelled` event. function cancelSellOrder(uint256 orderId) public virtual { Order memory order = orders[msg.sender][orderId]; if (order.assetAmount == 0) { revert OrderDoesNotExist(orderId); } - orders[msg.sender][orderId].cancelled = true; + orders[msg.sender][orderId].assetAmount = 0; asset.safeTransferFrom( address(this), msg.sender, diff --git a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol index 62cd716b3..14969dfb1 100644 --- a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol +++ b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol @@ -52,7 +52,6 @@ contract OrderbookDexTest is CTest, ERC1155Holder { assertEq(order.assetId, assetId); assertEq(order.assetAmount, assetAmount); assertEq(order.pricePerAsset, pricePerAsset); - assertTrue(!order.cancelled); assertEq(asset.balanceOf(address(dex), assetId), assetAmount); } @@ -66,7 +65,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { emit IOrderbookDex.OrderCancelled(address(this), orderId); dex.cancelSellOrder(orderId); IOrderbookDex.Order memory order = dex.getOrder(address(this), orderId); - assertTrue(order.cancelled); + assertEq(order.assetAmount, 0); assertEq(asset.balanceOf(address(dex), assetId), 0); assertEq(asset.balanceOf(address(this), assetId), assetAmount); } From 8e625b97e50eff43c1d96f0d686410988afc7c2d Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Tue, 9 Apr 2024 16:16:17 +0200 Subject: [PATCH 09/18] `cancelSellOrder` gas optimization --- .../contracts/orderbook/OrderbookDex.sol | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol index 810edb55d..b06bb93b6 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol @@ -181,18 +181,13 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { /// @dev MUST change the `assetAmount` parameter for the specified order to `0`. /// MUST emit `OrderCancelled` event. function cancelSellOrder(uint256 orderId) public virtual { - Order memory order = orders[msg.sender][orderId]; + Order storage order = orders[msg.sender][orderId]; + uint256 assetAmount = order.assetAmount; if (order.assetAmount == 0) { revert OrderDoesNotExist(orderId); } - orders[msg.sender][orderId].assetAmount = 0; - asset.safeTransferFrom( - address(this), - msg.sender, - order.assetId, - order.assetAmount, - bytes("") - ); + order.assetAmount = 0; + asset.safeTransferFrom(address(this), msg.sender, order.assetId, assetAmount, bytes("")); emit OrderCancelled(msg.sender, orderId); } From 6a362eba4b86777b885e21a4d10f4ecc535a334e Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Tue, 9 Apr 2024 16:16:17 +0200 Subject: [PATCH 10/18] Identify order by a singular id (seller+sellerNonce packed), add documentation --- .../contracts/orderbook/IOrderbookDex.sol | 63 +++++--- .../contracts/orderbook/OrderbookDex.sol | 86 ++++++----- .../evm-contracts/test/OrderbookDex.t.sol | 141 +++++++----------- 3 files changed, 142 insertions(+), 148 deletions(-) diff --git a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol index c946fe6ca..8e3652d9e 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol @@ -5,20 +5,34 @@ pragma solidity ^0.8.20; import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; /// @notice Facilitates base-chain trading of an asset that is living on a different app-chain. -/// @dev The contract should never hold any ETH itself. interface IOrderbookDex is IERC1155Receiver { struct Order { + /// @dev The asset's unique token identifier. uint256 assetId; + /// @dev The amount of the asset that is available to be sold. uint256 assetAmount; + /// @dev The price per one unit of asset. uint256 pricePerAsset; } + /// @param seller The seller's address. + /// @param orderId The order's unique identifier. + /// @param assetId The asset's unique token identifier. + /// @param assetAmount The amount of the asset that has been put for sale. + /// @param pricePerAsset The requested price per one unit of asset. event OrderCreated( address indexed seller, uint256 indexed orderId, + uint256 indexed assetId, uint256 assetAmount, uint256 pricePerAsset ); + + /// @param seller The seller's address. + /// @param orderId The order's unique identifier. + /// @param buyer The buyer's address. + /// @param assetAmount The amount of the asset that was traded. + /// @param pricePerAsset The price per one unit of asset that was paid. event OrderFilled( address indexed seller, uint256 indexed orderId, @@ -26,50 +40,51 @@ interface IOrderbookDex is IERC1155Receiver { uint256 assetAmount, uint256 pricePerAsset ); + + /// @param seller The seller's address. + /// @param orderId The order's unique identifier. event OrderCancelled(address indexed seller, uint256 indexed orderId); - /// @notice Returns the address of the asset that is being traded. + /// @notice Returns the address of the asset that is being traded in this DEX contract. function getAsset() external view returns (address); - /// @notice Returns the seller's current `orderId` (index that their new sell order will be mapped to). - function getSellerOrderId(address seller) external view returns (uint256); + /// @notice Returns the `orderId` of the next sell order of `seller`. + function getNextOrderId(address seller) external view returns (uint256); - /// @notice Returns the Order struct information about an order identified by the combination ``. - function getOrder(address seller, uint256 orderId) external view returns (Order memory); + /// @notice Returns the Order struct information about an order identified by the `orderId`. + function getOrder(uint256 orderId) external view returns (Order memory); - /// @notice Creates a sell order with incremental seller-specific `orderId` for the specified `assetAmount` at specified `pricePerAsset`. - /// @dev The order information is saved in a nested mapping `seller address -> orderId -> Order`. + /// @notice Creates a sell order for the `assetAmount` of `assetId` at `pricePerAsset`. + /// @dev The order information is saved in a mapping `orderId -> Order`. + /// orderId SHOULD be created by packing the seller's address (uint160) and their incremental `sellerOrderNonce` (uint96) into uint256. + /// MUST transfer the `assetAmount` of `assetId` from the seller to the contract. /// MUST emit `OrderCreated` event. function createSellOrder(uint256 assetId, uint256 assetAmount, uint256 pricePerAsset) external; - /// @notice Consecutively fills an array of orders identified by the combination ``, + /// @notice Consecutively fills an array of orders identified by the `orderId` of each order, /// by providing an exact amount of ETH and requesting a specific minimum amount of asset to receive. /// @dev Transfers portions of msg.value to the orders' sellers according to the price. /// The sum of asset amounts of filled orders MUST be at least `minimumAsset`. /// If msg.value is more than the sum of orders' prices, it MUST refund the excess back to msg.sender. - /// MUST change the `assetAmount` parameter for the specified order according to how much of it was filled. + /// MUST decrease the `assetAmount` parameter for the specified order according to how much of it was filled, + /// and transfer that amount of the order's `assetId` to the buyer. /// MUST emit `OrderFilled` event for each order accordingly. - function fillOrdersExactEth( - uint256 minimumAsset, - address payable[] memory sellers, - uint256[] memory orderIds - ) external payable; + function fillOrdersExactEth(uint256 minimumAsset, uint256[] memory orderIds) external payable; - /// @notice Consecutively fills an array of orders identified by the combination ``, + /// @notice Consecutively fills an array of orders identified by the `orderId` of each order, /// by providing a possibly surplus amount of ETH and requesting an exact amount of asset to receive. /// @dev Transfers portions of msg.value to the orders' sellers according to the price. /// The sum of asset amounts of filled orders MUST be exactly `assetAmount`. Excess ETH MUST be returned back to `msg.sender`. - /// MUST change the `assetAmount` parameter for the specified order according to how much of it was filled. + /// MUST decrease the `assetAmount` parameter for the specified order according to how much of it was filled, + /// and transfer that amount of the order's `assetId` to the buyer. /// MUST emit `OrderFilled` event for each order accordingly. /// If msg.value is more than the sum of orders' prices, it MUST refund the difference back to msg.sender. - function fillOrdersExactAsset( - uint256 assetAmount, - address payable[] memory sellers, - uint256[] memory orderIds - ) external payable; + function fillOrdersExactAsset(uint256 assetAmount, uint256[] memory orderIds) external payable; - /// @notice Cancels the sell order identified by combination ``, making it unfillable. - /// @dev MUST change the `assetAmount` parameter for the specified order to `0`. + /// @notice Cancels the sell order identified by the `orderId`, transferring the order's assets back to the seller. + /// @dev MUST revert if the seller decoded from the `orderId` is not `msg.sender`. + /// MUST change the `assetAmount` parameter for the specified order to `0`. /// MUST emit `OrderCancelled` event. + /// MUST transfer the `assetAmount` of `assetId` back to the seller. function cancelSellOrder(uint256 orderId) external; } diff --git a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol index b06bb93b6..c5663a171 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol @@ -10,40 +10,42 @@ import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol import {IInverseProjected1155} from "../token/IInverseProjected1155.sol"; import {IOrderbookDex} from "./IOrderbookDex.sol"; -/// @notice Facilitates trading an asset that is living on a different app-chain. +/// @notice Facilitates base-chain trading of an asset that is living on a different app-chain. contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { using Address for address payable; IInverseProjected1155 internal asset; - mapping(address => mapping(uint256 => Order)) internal orders; - mapping(address => uint256) internal sellersOrderId; + mapping(uint256 orderId => Order) internal orders; + mapping(address seller => uint96 nonce) internal sellersOrderNonce; error OrderDoesNotExist(uint256 orderId); error InsufficientEndAmount(uint256 expectedAmount, uint256 actualAmount); error InvalidInput(uint256 input); - error InvalidInputArity(); + error Unauthorized(); constructor(IInverseProjected1155 _asset) { asset = _asset; } - /// @notice Returns the address of the asset that is being traded. + /// @notice Returns the address of the asset that is being traded in this DEX contract. function getAsset() public view virtual returns (address) { return address(asset); } - /// @notice Returns the seller's current `orderId` (index that their new sell order will be mapped to). - function getSellerOrderId(address seller) public view virtual returns (uint256) { - return sellersOrderId[seller]; + /// @notice Returns the `orderId` of the next sell order of `seller`. + function getNextOrderId(address seller) public view virtual returns (uint256) { + return _getOrderId(seller, sellersOrderNonce[seller]); } - /// @notice Returns the Order struct information about an order identified by the combination ``. - function getOrder(address seller, uint256 orderId) public view virtual returns (Order memory) { - return orders[seller][orderId]; + /// @notice Returns the Order struct information about an order identified by the `orderId`. + function getOrder(uint256 orderId) public view virtual returns (Order memory) { + return orders[orderId]; } - /// @notice Creates a sell order with incremental seller-specific `orderId` for the specified `assetAmount` at specified `pricePerAsset`. - /// @dev The order information is saved in a nested mapping `seller address -> orderId -> Order`. + /// @notice Creates a sell order for the `assetAmount` of `assetId` at `pricePerAsset`. + /// @dev The order information is saved in a mapping `orderId -> Order`. + /// orderId SHOULD be created by packing the seller's address (uint160) and their incremental `sellerOrderNonce` (uint96) into uint256. + /// MUST transfer the `assetAmount` of `assetId` from the seller to the contract. /// MUST emit `OrderCreated` event. function createSellOrder( uint256 assetId, @@ -59,37 +61,34 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { assetAmount: assetAmount, pricePerAsset: pricePerAsset }); - uint256 orderId = sellersOrderId[msg.sender]; - orders[msg.sender][orderId] = newOrder; - emit OrderCreated(msg.sender, orderId, assetAmount, pricePerAsset); - sellersOrderId[msg.sender]++; + uint256 orderId = getNextOrderId(msg.sender); + orders[orderId] = newOrder; + emit OrderCreated(msg.sender, orderId, assetId, assetAmount, pricePerAsset); + sellersOrderNonce[msg.sender]++; } - /// @notice Consecutively fills an array of orders identified by the combination ``, + /// @notice Consecutively fills an array of orders identified by the `orderId` of each order, /// by providing an exact amount of ETH and requesting a specific minimum amount of asset to receive. /// @dev Transfers portions of msg.value to the orders' sellers according to the price. /// The sum of asset amounts of filled orders MUST be at least `minimumAsset`. /// If msg.value is more than the sum of orders' prices, it MUST refund the excess back to msg.sender. - /// MUST change the `assetAmount` parameter for the specified order according to how much of it was filled. + /// MUST decrease the `assetAmount` parameter for the specified order according to how much of it was filled, + /// and transfer that amount of the order's `assetId` to the buyer. /// MUST emit `OrderFilled` event for each order accordingly. function fillOrdersExactEth( uint256 minimumAsset, - address payable[] memory sellers, uint256[] memory orderIds ) public payable virtual nonReentrant { - if (sellers.length != orderIds.length) { - revert InvalidInputArity(); - } - uint256 length = sellers.length; + uint256 length = orderIds.length; uint256 remainingEth = msg.value; uint256 totalAssetReceived; for (uint256 i = 0; i < length; i++) { - address payable seller = sellers[i]; uint256 orderId = orderIds[i]; - Order storage order = orders[seller][orderId]; + Order storage order = orders[orderId]; if (order.assetAmount == 0) { continue; } + address payable seller = payable(address(uint160(orderId >> 96))); uint256 assetsToBuy = remainingEth / order.pricePerAsset; if (assetsToBuy == 0) { continue; @@ -97,7 +96,6 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { if (assetsToBuy > order.assetAmount) { assetsToBuy = order.assetAmount; } - seller.sendValue(assetsToBuy * order.pricePerAsset); order.assetAmount -= assetsToBuy; remainingEth -= assetsToBuy * order.pricePerAsset; totalAssetReceived += assetsToBuy; @@ -108,6 +106,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { assetsToBuy, bytes("") ); + seller.sendValue(assetsToBuy * order.pricePerAsset); emit OrderFilled(seller, orderId, msg.sender, assetsToBuy, order.pricePerAsset); if (remainingEth == 0) { break; @@ -121,31 +120,28 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { } } - /// @notice Consecutively fills an array of orders identified by the combination ``, + /// @notice Consecutively fills an array of orders identified by the `orderId` of each order, /// by providing a possibly surplus amount of ETH and requesting an exact amount of asset to receive. /// @dev Transfers portions of msg.value to the orders' sellers according to the price. /// The sum of asset amounts of filled orders MUST be exactly `assetAmount`. Excess ETH MUST be returned back to `msg.sender`. - /// MUST change the `assetAmount` parameter for the specified order according to how much of it was filled. + /// MUST decrease the `assetAmount` parameter for the specified order according to how much of it was filled, + /// and transfer that amount of the order's `assetId` to the buyer. /// MUST emit `OrderFilled` event for each order accordingly. /// If msg.value is more than the sum of orders' prices, it MUST refund the difference back to msg.sender. function fillOrdersExactAsset( uint256 assetAmount, - address payable[] memory sellers, uint256[] memory orderIds ) public payable virtual nonReentrant { - if (sellers.length != orderIds.length) { - revert InvalidInputArity(); - } - uint256 length = sellers.length; + uint256 length = orderIds.length; uint256 remainingAsset = assetAmount; uint256 remainingEth = msg.value; for (uint256 i = 0; i < length; i++) { - address payable seller = sellers[i]; uint256 orderId = orderIds[i]; - Order storage order = orders[seller][orderId]; + Order storage order = orders[orderId]; if (order.assetAmount == 0) { continue; } + address payable seller = payable(address(uint160(orderId >> 96))); uint256 assetsToBuy = order.assetAmount; if (assetsToBuy > remainingAsset) { assetsToBuy = remainingAsset; @@ -153,7 +149,6 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { if (assetsToBuy == 0) { continue; } - seller.sendValue(assetsToBuy * order.pricePerAsset); order.assetAmount -= assetsToBuy; remainingEth -= assetsToBuy * order.pricePerAsset; remainingAsset -= assetsToBuy; @@ -164,6 +159,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { assetsToBuy, bytes("") ); + seller.sendValue(assetsToBuy * order.pricePerAsset); emit OrderFilled(seller, orderId, msg.sender, assetsToBuy, order.pricePerAsset); if (remainingAsset == 0) { break; @@ -177,11 +173,17 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { } } - /// @notice Cancels the sell order identified by combination ``, making it unfillable. - /// @dev MUST change the `assetAmount` parameter for the specified order to `0`. + /// @notice Cancels the sell order identified by the `orderId`, transferring the order's assets back to the seller. + /// @dev MUST revert if the seller decoded from the `orderId` is not `msg.sender`. + /// MUST change the `assetAmount` parameter for the specified order to `0`. /// MUST emit `OrderCancelled` event. + /// MUST transfer the `assetAmount` of `assetId` back to the seller. function cancelSellOrder(uint256 orderId) public virtual { - Order storage order = orders[msg.sender][orderId]; + address seller = address(uint160(orderId >> 96)); + if (msg.sender != seller) { + revert Unauthorized(); + } + Order storage order = orders[orderId]; uint256 assetAmount = order.assetAmount; if (order.assetAmount == 0) { revert OrderDoesNotExist(orderId); @@ -198,4 +200,8 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { return interfaceId == type(IOrderbookDex).interfaceId || super.supportsInterface(interfaceId); } + + function _getOrderId(address seller, uint96 orderId) internal view virtual returns (uint256) { + return (uint256(uint160(seller)) << 96) ^ orderId; + } } diff --git a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol index 14969dfb1..4a01e653e 100644 --- a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol +++ b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol @@ -6,6 +6,7 @@ import {CTest} from "../test-lib/ctest.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; +import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {IInverseAppProjected1155} from "../contracts/token/IInverseAppProjected1155.sol"; @@ -35,20 +36,27 @@ contract OrderbookDexTest is CTest, ERC1155Holder { } function test_SupportsInterface() public { + assertTrue(dex.supportsInterface(type(IERC165).interfaceId)); assertTrue(dex.supportsInterface(type(IERC165).interfaceId)); assertTrue(dex.supportsInterface(type(IOrderbookDex).interfaceId)); } function test_CreateOrderSatisfiesRequirements() public { - uint256 orderId = dex.getSellerOrderId(address(this)); uint256 assetAmount = 100; uint256 pricePerAsset = 200; + uint256 orderId = dex.getNextOrderId(address(this)); uint256 assetId = asset.mint(assetAmount, ""); vm.expectEmit(true, true, true, true); - emit IOrderbookDex.OrderCreated(address(this), orderId, assetAmount, pricePerAsset); + emit IOrderbookDex.OrderCreated( + address(this), + orderId, + assetId, + assetAmount, + pricePerAsset + ); dex.createSellOrder(assetId, assetAmount, pricePerAsset); - IOrderbookDex.Order memory order = dex.getOrder(address(this), orderId); + IOrderbookDex.Order memory order = dex.getOrder(orderId); assertEq(order.assetId, assetId); assertEq(order.assetAmount, assetAmount); assertEq(order.pricePerAsset, pricePerAsset); @@ -56,15 +64,15 @@ contract OrderbookDexTest is CTest, ERC1155Holder { } function test_CancelOrderSatisfiesRequirements() public { - uint256 orderId = dex.getSellerOrderId(address(this)); uint256 assetAmount = 100; uint256 assetId = asset.mint(assetAmount, ""); + uint256 orderId = dex.getNextOrderId(address(this)); dex.createSellOrder(assetId, assetAmount, 200); vm.expectEmit(true, true, true, true); emit IOrderbookDex.OrderCancelled(address(this), orderId); dex.cancelSellOrder(orderId); - IOrderbookDex.Order memory order = dex.getOrder(address(this), orderId); + IOrderbookDex.Order memory order = dex.getOrder(orderId); assertEq(order.assetAmount, 0); assertEq(asset.balanceOf(address(dex), assetId), 0); assertEq(asset.balanceOf(address(this), assetId), assetAmount); @@ -81,9 +89,8 @@ contract OrderbookDexTest is CTest, ERC1155Holder { for (uint256 i = 0; i < ordersCount; i++) { address payable seller = payable(vm.addr(uint256(keccak256(abi.encodePacked(i))))); uint256 assetAmount = price / (ordersCount - i); - uint256 orderId = dex.getSellerOrderId(seller); uint256 pricePerAsset = price / assetAmount; - orderIds[i] = orderId; + orderIds[i] = dex.getNextOrderId(seller); sellers[i] = seller; vm.startPrank(seller); uint256 assetId = asset.mint(assetAmount, ""); @@ -100,7 +107,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { uint256[] memory ordersAssetAmounts = new uint256[](ordersCount); uint256 totalExpectedPayout; for (uint256 i = 0; i < ordersCount; i++) { - IOrderbookDex.Order memory order = dex.getOrder(sellers[i], orderIds[i]); + IOrderbookDex.Order memory order = dex.getOrder(orderIds[i]); expectedPayouts[i] = order.pricePerAsset * order.assetAmount; ordersAssetAmounts[i] = order.assetAmount; totalExpectedPayout += expectedPayouts[i]; @@ -115,15 +122,11 @@ contract OrderbookDexTest is CTest, ERC1155Holder { ); } vm.startPrank(buyer); - dex.fillOrdersExactEth{value: totalExpectedPayout + 1000}( - totalAssetAmount, - sellers, - orderIds - ); + dex.fillOrdersExactEth{value: totalExpectedPayout + 1000}(totalAssetAmount, orderIds); assertEq(buyer.balance, buyerBalanceBefore - totalExpectedPayout); for (uint256 i = 0; i < ordersCount; i++) { - IOrderbookDex.Order memory order = dex.getOrder(sellers[i], orderIds[i]); + IOrderbookDex.Order memory order = dex.getOrder(orderIds[i]); assertEq(sellers[i].balance, sellersBalancesBefore[i] + expectedPayouts[i]); assertEq(order.assetAmount, 0); assertEq(asset.balanceOf(address(dex), order.assetId), 0); @@ -143,9 +146,8 @@ contract OrderbookDexTest is CTest, ERC1155Holder { for (uint256 i = 0; i < ordersCount; i++) { address payable seller = payable(vm.addr(uint256(keccak256(abi.encodePacked(i))))); uint256 assetAmount = price / (ordersCount - i); - uint256 orderId = dex.getSellerOrderId(seller); uint256 pricePerAsset = price / assetAmount; - orderIds[i] = orderId; + orderIds[i] = dex.getNextOrderId(seller); sellers[i] = seller; vm.startPrank(seller); uint256 assetId = asset.mint(assetAmount, ""); @@ -162,7 +164,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { uint256[] memory ordersAssetAmounts = new uint256[](ordersCount); uint256 totalExpectedPayout; for (uint256 i = 0; i < ordersCount; i++) { - IOrderbookDex.Order memory order = dex.getOrder(sellers[i], orderIds[i]); + IOrderbookDex.Order memory order = dex.getOrder(orderIds[i]); expectedPayouts[i] = order.pricePerAsset * order.assetAmount; ordersAssetAmounts[i] = order.assetAmount; totalExpectedPayout += expectedPayouts[i]; @@ -177,15 +179,11 @@ contract OrderbookDexTest is CTest, ERC1155Holder { ); } vm.startPrank(buyer); - dex.fillOrdersExactAsset{value: totalExpectedPayout + 1000}( - totalAssetAmount, - sellers, - orderIds - ); + dex.fillOrdersExactAsset{value: totalExpectedPayout + 1000}(totalAssetAmount, orderIds); assertEq(buyer.balance, buyerBalanceBefore - totalExpectedPayout); for (uint256 i = 0; i < ordersCount; i++) { - IOrderbookDex.Order memory order = dex.getOrder(sellers[i], orderIds[i]); + IOrderbookDex.Order memory order = dex.getOrder(orderIds[i]); assertEq(sellers[i].balance, sellersBalancesBefore[i] + expectedPayouts[i]); assertEq(order.assetAmount, 0); assertEq(asset.balanceOf(address(dex), order.assetId), 0); @@ -207,28 +205,22 @@ contract OrderbookDexTest is CTest, ERC1155Holder { address buyer = alice; address payable seller = payable(address(this)); vm.deal(buyer, assetAmount * pricePerAsset); - uint256 orderId = dex.getSellerOrderId(address(this)); + uint256 orderId = dex.getNextOrderId(seller); vm.startPrank(seller); uint256 assetId = asset.mint(assetAmount, ""); asset.setApprovalForAll(address(dex), true); dex.createSellOrder(assetId, assetAmount, pricePerAsset); uint256 buyerBalanceBefore = buyer.balance; - uint256 sellerBalanceBefore = address(this).balance; - address payable[] memory sellers = new address payable[](1); - sellers[0] = seller; + uint256 sellerBalanceBefore = seller.balance; uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; vm.startPrank(buyer); - dex.fillOrdersExactEth{value: assetAmountToBuy * pricePerAsset}( - assetAmountToBuy, - sellers, - orderIds - ); + dex.fillOrdersExactEth{value: assetAmountToBuy * pricePerAsset}(assetAmountToBuy, orderIds); - IOrderbookDex.Order memory order = dex.getOrder(address(this), orderId); + IOrderbookDex.Order memory order = dex.getOrder(orderId); assertEq(buyer.balance, buyerBalanceBefore - (assetAmountToBuy * pricePerAsset)); - assertEq(address(this).balance, sellerBalanceBefore + (assetAmountToBuy * pricePerAsset)); + assertEq(seller.balance, sellerBalanceBefore + (assetAmountToBuy * pricePerAsset)); assertEq(order.assetAmount, assetAmount - assetAmountToBuy); assertEq(asset.balanceOf(address(dex), assetId), assetAmount - assetAmountToBuy); assertEq(asset.balanceOf(buyer, assetId), assetAmountToBuy); @@ -247,24 +239,22 @@ contract OrderbookDexTest is CTest, ERC1155Holder { address buyer = alice; address payable seller = payable(address(this)); vm.deal(buyer, assetAmount * pricePerAsset); - uint256 orderId = dex.getSellerOrderId(address(this)); + uint256 orderId = dex.getNextOrderId(seller); vm.startPrank(seller); uint256 assetId = asset.mint(assetAmount, ""); asset.setApprovalForAll(address(dex), true); dex.createSellOrder(assetId, assetAmount, pricePerAsset); uint256 buyerBalanceBefore = buyer.balance; - uint256 sellerBalanceBefore = address(this).balance; - address payable[] memory sellers = new address payable[](1); - sellers[0] = seller; + uint256 sellerBalanceBefore = seller.balance; uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; vm.startPrank(buyer); - dex.fillOrdersExactAsset{value: buyerBalanceBefore}(assetAmountToBuy, sellers, orderIds); + dex.fillOrdersExactAsset{value: buyerBalanceBefore}(assetAmountToBuy, orderIds); - IOrderbookDex.Order memory order = dex.getOrder(address(this), orderId); + IOrderbookDex.Order memory order = dex.getOrder(orderId); assertEq(buyer.balance, buyerBalanceBefore - (assetAmountToBuy * pricePerAsset)); - assertEq(address(this).balance, sellerBalanceBefore + (assetAmountToBuy * pricePerAsset)); + assertEq(seller.balance, sellerBalanceBefore + (assetAmountToBuy * pricePerAsset)); assertEq(order.assetAmount, assetAmount - assetAmountToBuy); assertEq(asset.balanceOf(address(dex), assetId), assetAmount - assetAmountToBuy); assertEq(asset.balanceOf(buyer, assetId), assetAmountToBuy); @@ -280,17 +270,15 @@ contract OrderbookDexTest is CTest, ERC1155Holder { vm.assume(pricePerAsset < type(uint256).max / assetAmount / multiplier); vm.deal(alice, assetAmount * pricePerAsset * multiplier); - uint256 orderId = dex.getSellerOrderId(address(this)); + uint256 orderId = dex.getNextOrderId(address(this)); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); uint256 aliceBalanceBefore = alice.balance; - address payable[] memory sellers = new address payable[](1); - sellers[0] = payable(address(this)); uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; vm.startPrank(alice); - dex.fillOrdersExactEth{value: alice.balance}(assetAmount, sellers, orderIds); + dex.fillOrdersExactEth{value: alice.balance}(assetAmount, orderIds); assertEq(alice.balance, aliceBalanceBefore - (assetAmount * pricePerAsset)); } @@ -304,38 +292,34 @@ contract OrderbookDexTest is CTest, ERC1155Holder { vm.assume(pricePerAsset < type(uint256).max / assetAmount / multiplier); vm.deal(alice, assetAmount * pricePerAsset * multiplier); - uint256 orderId = dex.getSellerOrderId(address(this)); + uint256 orderId = dex.getNextOrderId(address(this)); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); uint256 aliceBalanceBefore = alice.balance; - address payable[] memory sellers = new address payable[](1); - sellers[0] = payable(address(this)); uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; vm.startPrank(alice); - dex.fillOrdersExactAsset{value: alice.balance}(assetAmount, sellers, orderIds); + dex.fillOrdersExactAsset{value: alice.balance}(assetAmount, orderIds); assertEq(alice.balance, aliceBalanceBefore - (assetAmount * pricePerAsset)); } function test_WontFillOrderIfCancelled() public { uint256 assetAmount = 100; uint256 pricePerAsset = 200; - uint256 orderId = dex.getSellerOrderId(address(this)); + uint256 orderId = dex.getNextOrderId(address(this)); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); dex.cancelSellOrder(orderId); uint256 aliceBalanceBefore = alice.balance; - address payable[] memory sellers = new address payable[](1); - sellers[0] = payable(address(this)); uint256[] memory orderIds = new uint256[](1); orderIds[0] = orderId; vm.startPrank(alice); - dex.fillOrdersExactEth{value: assetAmount * pricePerAsset}(0, sellers, orderIds); + dex.fillOrdersExactEth{value: assetAmount * pricePerAsset}(0, orderIds); assertEq(alice.balance, aliceBalanceBefore); - dex.fillOrdersExactAsset{value: assetAmount * pricePerAsset}(0, sellers, orderIds); + dex.fillOrdersExactAsset{value: assetAmount * pricePerAsset}(0, orderIds); assertEq(alice.balance, aliceBalanceBefore); } @@ -346,36 +330,33 @@ contract OrderbookDexTest is CTest, ERC1155Holder { dex.createSellOrder(assetId, 0, 100); } - function test_CannotCancelOrderIfDoesNotExist() public { - uint256 orderId = dex.getSellerOrderId(address(this)); - vm.expectRevert(abi.encodeWithSelector(OrderbookDex.OrderDoesNotExist.selector, orderId)); + function test_CannotCancelOrderIfUnauthorized() public { + uint256 orderId = dex.getNextOrderId(address(this)); + uint256 assetId = asset.mint(100, ""); + dex.createSellOrder(assetId, 100, 200); + + vm.startPrank(alice); + vm.expectRevert(OrderbookDex.Unauthorized.selector); dex.cancelSellOrder(orderId); } - function testFuzz_CannotFillOrderWithInvalidInputArity( - address payable[] memory sellers, - uint256[] memory orderIds - ) public { - vm.assume(sellers.length != orderIds.length); - - vm.expectRevert(OrderbookDex.InvalidInputArity.selector); - dex.fillOrdersExactEth(0, sellers, orderIds); - - vm.expectRevert(OrderbookDex.InvalidInputArity.selector); - dex.fillOrdersExactAsset(0, sellers, orderIds); + function test_CannotCancelOrderIfDoesNotExist() public { + uint256 orderId = dex.getNextOrderId(address(this)); + vm.expectRevert(abi.encodeWithSelector(OrderbookDex.OrderDoesNotExist.selector, orderId)); + dex.cancelSellOrder(orderId); } function test_CannotFillOrderIfInsufficientEndAmountExactEth() public { uint256 assetAmount = 10; uint256 pricePerAsset = 100; - uint256 orderId = dex.getSellerOrderId(address(this)); + uint256 orderId = dex.getNextOrderId(address(this)); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); - uint256[] memory orderIds = new uint256[](1); - orderIds[0] = orderId; address payable[] memory sellers = new address payable[](1); sellers[0] = payable(address(this)); + uint256[] memory orderIds = new uint256[](1); + orderIds[0] = orderId; vm.startPrank(alice); vm.expectRevert( abi.encodeWithSelector( @@ -384,24 +365,20 @@ contract OrderbookDexTest is CTest, ERC1155Holder { assetAmount ) ); - dex.fillOrdersExactEth{value: assetAmount * pricePerAsset}( - assetAmount + 1, - sellers, - orderIds - ); + dex.fillOrdersExactEth{value: assetAmount * pricePerAsset}(assetAmount + 1, orderIds); } function test_CannotFillOrderIfInsufficientEndAmountExactAsset() public { uint256 assetAmount = 10; uint256 pricePerAsset = 100; - uint256 orderId = dex.getSellerOrderId(address(this)); + uint256 orderId = dex.getNextOrderId(address(this)); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); - uint256[] memory orderIds = new uint256[](1); - orderIds[0] = orderId; address payable[] memory sellers = new address payable[](1); sellers[0] = payable(address(this)); + uint256[] memory orderIds = new uint256[](1); + orderIds[0] = orderId; vm.startPrank(alice); vm.expectRevert( abi.encodeWithSelector( @@ -410,11 +387,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { assetAmount ) ); - dex.fillOrdersExactAsset{value: assetAmount * pricePerAsset}( - assetAmount + 1, - sellers, - orderIds - ); + dex.fillOrdersExactAsset{value: assetAmount * pricePerAsset}(assetAmount + 1, orderIds); } function test_CannotSendEtherToDex() public { From dbd4853f49762221d8d2cc0c5f3313324944fd3c Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Tue, 9 Apr 2024 16:16:17 +0200 Subject: [PATCH 11/18] Incremental unique identifier for orders --- .../contracts/orderbook/IOrderbookDex.sol | 27 +++++++---- .../contracts/orderbook/OrderbookDex.sol | 46 +++++++++---------- .../evm-contracts/test/OrderbookDex.t.sol | 30 +++++------- 3 files changed, 50 insertions(+), 53 deletions(-) diff --git a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol index 8e3652d9e..02dde32f4 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol @@ -5,6 +5,7 @@ pragma solidity ^0.8.20; import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; /// @notice Facilitates base-chain trading of an asset that is living on a different app-chain. +/// @dev Orders are identified by a unique incremental `orderId`. interface IOrderbookDex is IERC1155Receiver { struct Order { /// @dev The asset's unique token identifier. @@ -13,6 +14,8 @@ interface IOrderbookDex is IERC1155Receiver { uint256 assetAmount; /// @dev The price per one unit of asset. uint256 pricePerAsset; + /// @dev The seller's address. + address payable seller; } /// @param seller The seller's address. @@ -42,30 +45,34 @@ interface IOrderbookDex is IERC1155Receiver { ); /// @param seller The seller's address. - /// @param orderId The order's unique identifier. - event OrderCancelled(address indexed seller, uint256 indexed orderId); + /// @param id The order's unique identifier. + event OrderCancelled(address indexed seller, uint256 indexed id); /// @notice Returns the address of the asset that is being traded in this DEX contract. function getAsset() external view returns (address); - /// @notice Returns the `orderId` of the next sell order of `seller`. - function getNextOrderId(address seller) external view returns (uint256); + /// @notice Returns the `orderId` of the next sell order. + function getCurrentOrderId() external view returns (uint256); /// @notice Returns the Order struct information about an order identified by the `orderId`. function getOrder(uint256 orderId) external view returns (Order memory); /// @notice Creates a sell order for the `assetAmount` of `assetId` at `pricePerAsset`. - /// @dev The order information is saved in a mapping `orderId -> Order`. - /// orderId SHOULD be created by packing the seller's address (uint160) and their incremental `sellerOrderNonce` (uint96) into uint256. + /// @dev The order information is saved in a mapping `orderId -> Order`, with `orderId` being a unique incremental identifier. /// MUST transfer the `assetAmount` of `assetId` from the seller to the contract. /// MUST emit `OrderCreated` event. - function createSellOrder(uint256 assetId, uint256 assetAmount, uint256 pricePerAsset) external; + /// @return The unique identifier of the created order. + function createSellOrder( + uint256 assetId, + uint256 assetAmount, + uint256 pricePerAsset + ) external returns (uint256); /// @notice Consecutively fills an array of orders identified by the `orderId` of each order, /// by providing an exact amount of ETH and requesting a specific minimum amount of asset to receive. /// @dev Transfers portions of msg.value to the orders' sellers according to the price. /// The sum of asset amounts of filled orders MUST be at least `minimumAsset`. - /// If msg.value is more than the sum of orders' prices, it MUST refund the excess back to msg.sender. + /// If msg.value is more than the sum of orders' prices, it MUST refund the excess back to `msg.sender`. /// MUST decrease the `assetAmount` parameter for the specified order according to how much of it was filled, /// and transfer that amount of the order's `assetId` to the buyer. /// MUST emit `OrderFilled` event for each order accordingly. @@ -78,11 +85,11 @@ interface IOrderbookDex is IERC1155Receiver { /// MUST decrease the `assetAmount` parameter for the specified order according to how much of it was filled, /// and transfer that amount of the order's `assetId` to the buyer. /// MUST emit `OrderFilled` event for each order accordingly. - /// If msg.value is more than the sum of orders' prices, it MUST refund the difference back to msg.sender. + /// If msg.value is more than the sum of orders' prices, it MUST refund the difference back to `msg.sender`. function fillOrdersExactAsset(uint256 assetAmount, uint256[] memory orderIds) external payable; /// @notice Cancels the sell order identified by the `orderId`, transferring the order's assets back to the seller. - /// @dev MUST revert if the seller decoded from the `orderId` is not `msg.sender`. + /// @dev MUST revert if the order's seller is not `msg.sender`. /// MUST change the `assetAmount` parameter for the specified order to `0`. /// MUST emit `OrderCancelled` event. /// MUST transfer the `assetAmount` of `assetId` back to the seller. diff --git a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol index c5663a171..a1c54124d 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol @@ -16,7 +16,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { IInverseProjected1155 internal asset; mapping(uint256 orderId => Order) internal orders; - mapping(address seller => uint96 nonce) internal sellersOrderNonce; + uint256 internal currentOrderId; error OrderDoesNotExist(uint256 orderId); error InsufficientEndAmount(uint256 expectedAmount, uint256 actualAmount); @@ -32,9 +32,9 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { return address(asset); } - /// @notice Returns the `orderId` of the next sell order of `seller`. - function getNextOrderId(address seller) public view virtual returns (uint256) { - return _getOrderId(seller, sellersOrderNonce[seller]); + /// @notice Returns the `orderId` of the next sell order. + function getCurrentOrderId() public view virtual returns (uint256) { + return currentOrderId; } /// @notice Returns the Order struct information about an order identified by the `orderId`. @@ -43,15 +43,15 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { } /// @notice Creates a sell order for the `assetAmount` of `assetId` at `pricePerAsset`. - /// @dev The order information is saved in a mapping `orderId -> Order`. - /// orderId SHOULD be created by packing the seller's address (uint160) and their incremental `sellerOrderNonce` (uint96) into uint256. + /// @dev The order information is saved in a mapping `orderId -> Order`, with `orderId` being a unique incremental identifier. /// MUST transfer the `assetAmount` of `assetId` from the seller to the contract. /// MUST emit `OrderCreated` event. + /// @return The unique identifier of the created order. function createSellOrder( uint256 assetId, uint256 assetAmount, uint256 pricePerAsset - ) public virtual { + ) public virtual returns (uint256) { if (assetAmount == 0 || pricePerAsset == 0) { revert InvalidInput(0); } @@ -59,19 +59,21 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { Order memory newOrder = Order({ assetId: assetId, assetAmount: assetAmount, - pricePerAsset: pricePerAsset + pricePerAsset: pricePerAsset, + seller: payable(msg.sender) }); - uint256 orderId = getNextOrderId(msg.sender); + uint256 orderId = currentOrderId; orders[orderId] = newOrder; emit OrderCreated(msg.sender, orderId, assetId, assetAmount, pricePerAsset); - sellersOrderNonce[msg.sender]++; + currentOrderId++; + return orderId; } /// @notice Consecutively fills an array of orders identified by the `orderId` of each order, /// by providing an exact amount of ETH and requesting a specific minimum amount of asset to receive. /// @dev Transfers portions of msg.value to the orders' sellers according to the price. /// The sum of asset amounts of filled orders MUST be at least `minimumAsset`. - /// If msg.value is more than the sum of orders' prices, it MUST refund the excess back to msg.sender. + /// If msg.value is more than the sum of orders' prices, it MUST refund the excess back to `msg.sender`. /// MUST decrease the `assetAmount` parameter for the specified order according to how much of it was filled, /// and transfer that amount of the order's `assetId` to the buyer. /// MUST emit `OrderFilled` event for each order accordingly. @@ -88,7 +90,6 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { if (order.assetAmount == 0) { continue; } - address payable seller = payable(address(uint160(orderId >> 96))); uint256 assetsToBuy = remainingEth / order.pricePerAsset; if (assetsToBuy == 0) { continue; @@ -106,8 +107,8 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { assetsToBuy, bytes("") ); - seller.sendValue(assetsToBuy * order.pricePerAsset); - emit OrderFilled(seller, orderId, msg.sender, assetsToBuy, order.pricePerAsset); + order.seller.sendValue(assetsToBuy * order.pricePerAsset); + emit OrderFilled(order.seller, orderId, msg.sender, assetsToBuy, order.pricePerAsset); if (remainingEth == 0) { break; } @@ -127,7 +128,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { /// MUST decrease the `assetAmount` parameter for the specified order according to how much of it was filled, /// and transfer that amount of the order's `assetId` to the buyer. /// MUST emit `OrderFilled` event for each order accordingly. - /// If msg.value is more than the sum of orders' prices, it MUST refund the difference back to msg.sender. + /// If msg.value is more than the sum of orders' prices, it MUST refund the difference back to `msg.sender`. function fillOrdersExactAsset( uint256 assetAmount, uint256[] memory orderIds @@ -141,7 +142,6 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { if (order.assetAmount == 0) { continue; } - address payable seller = payable(address(uint160(orderId >> 96))); uint256 assetsToBuy = order.assetAmount; if (assetsToBuy > remainingAsset) { assetsToBuy = remainingAsset; @@ -159,8 +159,8 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { assetsToBuy, bytes("") ); - seller.sendValue(assetsToBuy * order.pricePerAsset); - emit OrderFilled(seller, orderId, msg.sender, assetsToBuy, order.pricePerAsset); + order.seller.sendValue(assetsToBuy * order.pricePerAsset); + emit OrderFilled(order.seller, orderId, msg.sender, assetsToBuy, order.pricePerAsset); if (remainingAsset == 0) { break; } @@ -174,20 +174,16 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { } /// @notice Cancels the sell order identified by the `orderId`, transferring the order's assets back to the seller. - /// @dev MUST revert if the seller decoded from the `orderId` is not `msg.sender`. + /// @dev MUST revert if the order's seller is not `msg.sender`. /// MUST change the `assetAmount` parameter for the specified order to `0`. /// MUST emit `OrderCancelled` event. /// MUST transfer the `assetAmount` of `assetId` back to the seller. function cancelSellOrder(uint256 orderId) public virtual { - address seller = address(uint160(orderId >> 96)); - if (msg.sender != seller) { + Order storage order = orders[orderId]; + if (msg.sender != order.seller) { revert Unauthorized(); } - Order storage order = orders[orderId]; uint256 assetAmount = order.assetAmount; - if (order.assetAmount == 0) { - revert OrderDoesNotExist(orderId); - } order.assetAmount = 0; asset.safeTransferFrom(address(this), msg.sender, order.assetId, assetAmount, bytes("")); emit OrderCancelled(msg.sender, orderId); diff --git a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol index 4a01e653e..6e71a5d0d 100644 --- a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol +++ b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol @@ -44,7 +44,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { function test_CreateOrderSatisfiesRequirements() public { uint256 assetAmount = 100; uint256 pricePerAsset = 200; - uint256 orderId = dex.getNextOrderId(address(this)); + uint256 orderId = dex.getCurrentOrderId(); uint256 assetId = asset.mint(assetAmount, ""); vm.expectEmit(true, true, true, true); @@ -66,7 +66,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { function test_CancelOrderSatisfiesRequirements() public { uint256 assetAmount = 100; uint256 assetId = asset.mint(assetAmount, ""); - uint256 orderId = dex.getNextOrderId(address(this)); + uint256 orderId = dex.getCurrentOrderId(); dex.createSellOrder(assetId, assetAmount, 200); vm.expectEmit(true, true, true, true); @@ -90,7 +90,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { address payable seller = payable(vm.addr(uint256(keccak256(abi.encodePacked(i))))); uint256 assetAmount = price / (ordersCount - i); uint256 pricePerAsset = price / assetAmount; - orderIds[i] = dex.getNextOrderId(seller); + orderIds[i] = dex.getCurrentOrderId(); sellers[i] = seller; vm.startPrank(seller); uint256 assetId = asset.mint(assetAmount, ""); @@ -147,7 +147,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { address payable seller = payable(vm.addr(uint256(keccak256(abi.encodePacked(i))))); uint256 assetAmount = price / (ordersCount - i); uint256 pricePerAsset = price / assetAmount; - orderIds[i] = dex.getNextOrderId(seller); + orderIds[i] = dex.getCurrentOrderId(); sellers[i] = seller; vm.startPrank(seller); uint256 assetId = asset.mint(assetAmount, ""); @@ -205,7 +205,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { address buyer = alice; address payable seller = payable(address(this)); vm.deal(buyer, assetAmount * pricePerAsset); - uint256 orderId = dex.getNextOrderId(seller); + uint256 orderId = dex.getCurrentOrderId(); vm.startPrank(seller); uint256 assetId = asset.mint(assetAmount, ""); asset.setApprovalForAll(address(dex), true); @@ -239,7 +239,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { address buyer = alice; address payable seller = payable(address(this)); vm.deal(buyer, assetAmount * pricePerAsset); - uint256 orderId = dex.getNextOrderId(seller); + uint256 orderId = dex.getCurrentOrderId(); vm.startPrank(seller); uint256 assetId = asset.mint(assetAmount, ""); asset.setApprovalForAll(address(dex), true); @@ -270,7 +270,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { vm.assume(pricePerAsset < type(uint256).max / assetAmount / multiplier); vm.deal(alice, assetAmount * pricePerAsset * multiplier); - uint256 orderId = dex.getNextOrderId(address(this)); + uint256 orderId = dex.getCurrentOrderId(); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); @@ -292,7 +292,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { vm.assume(pricePerAsset < type(uint256).max / assetAmount / multiplier); vm.deal(alice, assetAmount * pricePerAsset * multiplier); - uint256 orderId = dex.getNextOrderId(address(this)); + uint256 orderId = dex.getCurrentOrderId(); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); @@ -307,7 +307,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { function test_WontFillOrderIfCancelled() public { uint256 assetAmount = 100; uint256 pricePerAsset = 200; - uint256 orderId = dex.getNextOrderId(address(this)); + uint256 orderId = dex.getCurrentOrderId(); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); dex.cancelSellOrder(orderId); @@ -331,7 +331,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { } function test_CannotCancelOrderIfUnauthorized() public { - uint256 orderId = dex.getNextOrderId(address(this)); + uint256 orderId = dex.getCurrentOrderId(); uint256 assetId = asset.mint(100, ""); dex.createSellOrder(assetId, 100, 200); @@ -340,16 +340,10 @@ contract OrderbookDexTest is CTest, ERC1155Holder { dex.cancelSellOrder(orderId); } - function test_CannotCancelOrderIfDoesNotExist() public { - uint256 orderId = dex.getNextOrderId(address(this)); - vm.expectRevert(abi.encodeWithSelector(OrderbookDex.OrderDoesNotExist.selector, orderId)); - dex.cancelSellOrder(orderId); - } - function test_CannotFillOrderIfInsufficientEndAmountExactEth() public { uint256 assetAmount = 10; uint256 pricePerAsset = 100; - uint256 orderId = dex.getNextOrderId(address(this)); + uint256 orderId = dex.getCurrentOrderId(); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); @@ -371,7 +365,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { function test_CannotFillOrderIfInsufficientEndAmountExactAsset() public { uint256 assetAmount = 10; uint256 pricePerAsset = 100; - uint256 orderId = dex.getNextOrderId(address(this)); + uint256 orderId = dex.getCurrentOrderId(); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); From ffe5d03abb8e79ccc79819753f0593f62b43b952 Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Tue, 9 Apr 2024 16:16:17 +0200 Subject: [PATCH 12/18] Add batch create and cancel order --- .../contracts/orderbook/IOrderbookDex.sol | 13 +++ .../contracts/orderbook/OrderbookDex.sol | 33 +++++++ .../evm-contracts/test-lib/ctest.sol | 72 +++++++++++++++ .../evm-contracts/test/OrderbookDex.t.sol | 91 +++++++++++++++++++ 4 files changed, 209 insertions(+) diff --git a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol index 02dde32f4..6d1357aed 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol @@ -68,6 +68,15 @@ interface IOrderbookDex is IERC1155Receiver { uint256 pricePerAsset ) external returns (uint256); + /// @notice Creates a batch of sell orders for the `assetAmount` of `assetId` at `pricePerAsset`. + /// @dev This is a batched version of `createSellOrder` that simply iterates through the arrays to call said function. + /// @return The unique identifiers of the created orders. + function createBatchSellOrder( + uint256[] memory assetIds, + uint256[] memory assetAmounts, + uint256[] memory pricesPerAssets + ) external returns (uint256[] memory); + /// @notice Consecutively fills an array of orders identified by the `orderId` of each order, /// by providing an exact amount of ETH and requesting a specific minimum amount of asset to receive. /// @dev Transfers portions of msg.value to the orders' sellers according to the price. @@ -94,4 +103,8 @@ interface IOrderbookDex is IERC1155Receiver { /// MUST emit `OrderCancelled` event. /// MUST transfer the `assetAmount` of `assetId` back to the seller. function cancelSellOrder(uint256 orderId) external; + + /// @notice Cancels a batch of sell orders identified by the `orderIds`, transferring the orders' assets back to the seller. + /// @dev This is a batched version of `cancelSellOrder` that simply iterates through the array to call said function. + function cancelBatchSellOrder(uint256[] memory orderIds) external; } diff --git a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol index a1c54124d..55720ea38 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.20; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {Arrays} from "@openzeppelin/contracts/utils/Arrays.sol"; import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; @@ -13,6 +14,7 @@ import {IOrderbookDex} from "./IOrderbookDex.sol"; /// @notice Facilitates base-chain trading of an asset that is living on a different app-chain. contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { using Address for address payable; + using Arrays for uint256[]; IInverseProjected1155 internal asset; mapping(uint256 orderId => Order) internal orders; @@ -20,6 +22,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { error OrderDoesNotExist(uint256 orderId); error InsufficientEndAmount(uint256 expectedAmount, uint256 actualAmount); + error InvalidArrayLength(); error InvalidInput(uint256 input); error Unauthorized(); @@ -69,6 +72,28 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { return orderId; } + /// @notice Creates a batch of sell orders for the `assetAmount` of `assetId` at `pricePerAsset`. + /// @dev This is a batched version of `createSellOrder` that simply iterates through the arrays to call said function. + /// @return The unique identifiers of the created orders. + function createBatchSellOrder( + uint256[] memory assetIds, + uint256[] memory assetAmounts, + uint256[] memory pricesPerAssets + ) public virtual returns (uint256[] memory) { + if (assetIds.length != assetAmounts.length || assetIds.length != pricesPerAssets.length) { + revert InvalidArrayLength(); + } + uint256[] memory orderIds = new uint256[](assetIds.length); + for (uint256 i = 0; i < assetIds.length; ++i) { + orderIds[i] = createSellOrder( + assetIds.unsafeMemoryAccess(i), + assetAmounts.unsafeMemoryAccess(i), + pricesPerAssets.unsafeMemoryAccess(i) + ); + } + return orderIds; + } + /// @notice Consecutively fills an array of orders identified by the `orderId` of each order, /// by providing an exact amount of ETH and requesting a specific minimum amount of asset to receive. /// @dev Transfers portions of msg.value to the orders' sellers according to the price. @@ -189,6 +214,14 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { emit OrderCancelled(msg.sender, orderId); } + /// @notice Cancels a batch of sell orders identified by the `orderIds`, transferring the orders' assets back to the seller. + /// @dev This is a batched version of `cancelSellOrder` that simply iterates through the array to call said function. + function cancelBatchSellOrder(uint256[] memory orderIds) public virtual { + for (uint256 i = 0; i < orderIds.length; ++i) { + cancelSellOrder(orderIds.unsafeMemoryAccess(i)); + } + } + /// @dev Returns true if this contract implements the interface defined by `interfaceId`. See EIP165. function supportsInterface( bytes4 interfaceId diff --git a/packages/contracts/evm-contracts/test-lib/ctest.sol b/packages/contracts/evm-contracts/test-lib/ctest.sol index a1775c35c..08bb7ded3 100644 --- a/packages/contracts/evm-contracts/test-lib/ctest.sol +++ b/packages/contracts/evm-contracts/test-lib/ctest.sol @@ -26,6 +26,11 @@ contract CTest { address constant HEVM_ADDRESS = address(bytes20(uint160(uint256(keccak256("hevm cheat code"))))); + address private constant CONSOLE2_ADDRESS = 0x000000000000000000636F6e736F6c652e6c6f67; + uint256 private constant INT256_MIN_ABS = + 57896044618658097711785492504343953926634992332820282019728792003956564819968; + uint256 private constant UINT256_MAX = + 115792089237316195423570985008687907853269984665640564039457584007913129639935; modifier mayRevert() { _; @@ -579,4 +584,71 @@ contract CTest { assertNotEq0(a, b); } } + + function console2_log(string memory p0, uint256 p1) private view { + (bool status,) = address(CONSOLE2_ADDRESS).staticcall(abi.encodeWithSignature("log(string,uint256)", p0, p1)); + status; + } + + function console2_log(string memory p0, string memory p1) private view { + (bool status,) = address(CONSOLE2_ADDRESS).staticcall(abi.encodeWithSignature("log(string,string)", p0, p1)); + status; + } + + function _bound(uint256 x, uint256 min, uint256 max) internal pure virtual returns (uint256 result) { + require(min <= max, "StdUtils bound(uint256,uint256,uint256): Max is less than min."); + // If x is between min and max, return x directly. This is to ensure that dictionary values + // do not get shifted if the min is nonzero. More info: https://github.com/foundry-rs/forge-std/issues/188 + if (x >= min && x <= max) return x; + + uint256 size = max - min + 1; + + // If the value is 0, 1, 2, 3, wrap that to min, min+1, min+2, min+3. Similarly for the UINT256_MAX side. + // This helps ensure coverage of the min/max values. + if (x <= 3 && size > x) return min + x; + if (x >= UINT256_MAX - 3 && size > UINT256_MAX - x) return max - (UINT256_MAX - x); + + // Otherwise, wrap x into the range [min, max], i.e. the range is inclusive. + if (x > max) { + uint256 diff = x - max; + uint256 rem = diff % size; + if (rem == 0) return max; + result = min + rem - 1; + } else if (x < min) { + uint256 diff = min - x; + uint256 rem = diff % size; + if (rem == 0) return min; + result = max - rem + 1; + } + } + + function bound(uint256 x, uint256 min, uint256 max) internal view virtual returns (uint256 result) { + result = _bound(x, min, max); + console2_log("Bound Result", result); + } + + function _bound(int256 x, int256 min, int256 max) internal pure virtual returns (int256 result) { + require(min <= max, "StdUtils bound(int256,int256,int256): Max is less than min."); + + // Shifting all int256 values to uint256 to use _bound function. The range of two types are: + // int256 : -(2**255) ~ (2**255 - 1) + // uint256: 0 ~ (2**256 - 1) + // So, add 2**255, INT256_MIN_ABS to the integer values. + // + // If the given integer value is -2**255, we cannot use `-uint256(-x)` because of the overflow. + // So, use `~uint256(x) + 1` instead. + uint256 _x = x < 0 ? (INT256_MIN_ABS - ~uint256(x) - 1) : (uint256(x) + INT256_MIN_ABS); + uint256 _min = min < 0 ? (INT256_MIN_ABS - ~uint256(min) - 1) : (uint256(min) + INT256_MIN_ABS); + uint256 _max = max < 0 ? (INT256_MIN_ABS - ~uint256(max) - 1) : (uint256(max) + INT256_MIN_ABS); + + uint256 y = _bound(_x, _min, _max); + + // To move it back to int256 value, subtract INT256_MIN_ABS at here. + result = y < INT256_MIN_ABS ? int256(~(INT256_MIN_ABS - y) + 1) : int256(y - INT256_MIN_ABS); + } + + function bound(int256 x, int256 min, int256 max) internal view virtual returns (int256 result) { + result = _bound(x, min, max); + // console2_log("Bound result", vm.toString(result)); + } } diff --git a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol index 6e71a5d0d..36ce7dc31 100644 --- a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol +++ b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol @@ -63,6 +63,62 @@ contract OrderbookDexTest is CTest, ERC1155Holder { assertEq(asset.balanceOf(address(dex), assetId), assetAmount); } + function testFuzz_CreateBatchOrderSatisfiesRequirements(uint256 orderCount) public { + orderCount = bound(orderCount, 0, 100); + uint256[] memory assetIds = new uint256[](orderCount); + uint256[] memory assetAmounts = new uint256[](orderCount); + uint256[] memory pricesPerAssets = new uint256[](orderCount); + + for (uint256 i = 0; i < orderCount; ++i) { + assetAmounts[i] = i == 0 ? 1 : i; + pricesPerAssets[i] = i == 0 ? 1 : i; + assetIds[i] = asset.mint(assetAmounts[i], ""); + } + + uint256 orderId = dex.getCurrentOrderId(); + for (uint256 i = 0; i < orderCount; ++i) { + vm.expectEmit(true, true, true, true); + emit IOrderbookDex.OrderCreated( + address(this), + orderId + i, + assetIds[i], + assetAmounts[i], + pricesPerAssets[i] + ); + } + + uint256[] memory orderIds = dex.createBatchSellOrder( + assetIds, + assetAmounts, + pricesPerAssets + ); + + for (uint256 i = 0; i < orderCount; ++i) { + IOrderbookDex.Order memory order = dex.getOrder(orderIds[i]); + assertEq(order.assetId, assetIds[i]); + assertEq(order.assetAmount, assetAmounts[i]); + assertEq(order.pricePerAsset, pricesPerAssets[i]); + assertEq(asset.balanceOf(address(dex), assetIds[i]), assetAmounts[i]); + } + } + + function testFuzz_CreateBatchOrderInvalidArrayLength( + uint256 length1, + uint256 length2, + uint256 length3 + ) public { + length1 = bound(length1, 0, 1000); + length2 = bound(length2, 0, 1000); + length3 = bound(length3, 0, 1000); + vm.assume(length1 != length2 || length2 != length3); + uint256[] memory assetIds = new uint256[](length1); + uint256[] memory assetAmounts = new uint256[](length2); + uint256[] memory pricesPerAssets = new uint256[](length3); + + vm.expectRevert(OrderbookDex.InvalidArrayLength.selector); + dex.createBatchSellOrder(assetIds, assetAmounts, pricesPerAssets); + } + function test_CancelOrderSatisfiesRequirements() public { uint256 assetAmount = 100; uint256 assetId = asset.mint(assetAmount, ""); @@ -78,6 +134,41 @@ contract OrderbookDexTest is CTest, ERC1155Holder { assertEq(asset.balanceOf(address(this), assetId), assetAmount); } + function testFuzz_CancelBatchOrderSatisfiesRequirements(uint256 orderCount) public { + orderCount = bound(orderCount, 0, 100); + uint256[] memory assetIds = new uint256[](orderCount); + uint256[] memory assetAmounts = new uint256[](orderCount); + uint256[] memory pricesPerAssets = new uint256[](orderCount); + + for (uint256 i = 0; i < orderCount; ++i) { + assetAmounts[i] = i == 0 ? 1 : i; + pricesPerAssets[i] = i == 0 ? 1 : i; + assetIds[i] = asset.mint(assetAmounts[i], ""); + } + + uint256 orderId = dex.getCurrentOrderId(); + + uint256[] memory orderIds = dex.createBatchSellOrder( + assetIds, + assetAmounts, + pricesPerAssets + ); + + for (uint256 i = 0; i < orderCount; ++i) { + vm.expectEmit(true, true, true, true); + emit IOrderbookDex.OrderCancelled(address(this), orderId + i); + } + + dex.cancelBatchSellOrder(orderIds); + + for (uint256 i = 0; i < orderCount; ++i) { + IOrderbookDex.Order memory order = dex.getOrder(orderIds[i]); + assertEq(order.assetAmount, 0); + assertEq(asset.balanceOf(address(dex), assetIds[i]), 0); + assertEq(asset.balanceOf(address(this), assetIds[i]), assetAmounts[i]); + } + } + function testFuzz_FillOrdersExactEth(uint256 price) public { uint256 ordersCount = 5; vm.assume(price / ordersCount > 0); From 3eb5bde69f4ff4b59a001d3ff55d243c082bf898 Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Tue, 9 Apr 2024 16:16:17 +0200 Subject: [PATCH 13/18] Gas optimizations --- .../contracts/orderbook/OrderbookDex.sol | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol index 55720ea38..efb07c209 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol @@ -16,7 +16,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { using Address for address payable; using Arrays for uint256[]; - IInverseProjected1155 internal asset; + IInverseProjected1155 internal immutable asset; mapping(uint256 orderId => Order) internal orders; uint256 internal currentOrderId; @@ -68,7 +68,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { uint256 orderId = currentOrderId; orders[orderId] = newOrder; emit OrderCreated(msg.sender, orderId, assetId, assetAmount, pricePerAsset); - currentOrderId++; + ++currentOrderId; return orderId; } @@ -84,12 +84,15 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { revert InvalidArrayLength(); } uint256[] memory orderIds = new uint256[](assetIds.length); - for (uint256 i = 0; i < assetIds.length; ++i) { + for (uint256 i; i < assetIds.length; ) { orderIds[i] = createSellOrder( assetIds.unsafeMemoryAccess(i), assetAmounts.unsafeMemoryAccess(i), pricesPerAssets.unsafeMemoryAccess(i) ); + unchecked { + ++i; + } } return orderIds; } @@ -109,8 +112,8 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { uint256 length = orderIds.length; uint256 remainingEth = msg.value; uint256 totalAssetReceived; - for (uint256 i = 0; i < length; i++) { - uint256 orderId = orderIds[i]; + for (uint256 i; i < length; ++i) { + uint256 orderId = orderIds.unsafeMemoryAccess(i); Order storage order = orders[orderId]; if (order.assetAmount == 0) { continue; @@ -161,8 +164,8 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { uint256 length = orderIds.length; uint256 remainingAsset = assetAmount; uint256 remainingEth = msg.value; - for (uint256 i = 0; i < length; i++) { - uint256 orderId = orderIds[i]; + for (uint256 i; i < length; ++i) { + uint256 orderId = orderIds.unsafeMemoryAccess(i); Order storage order = orders[orderId]; if (order.assetAmount == 0) { continue; @@ -209,7 +212,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { revert Unauthorized(); } uint256 assetAmount = order.assetAmount; - order.assetAmount = 0; + delete order.assetAmount; asset.safeTransferFrom(address(this), msg.sender, order.assetId, assetAmount, bytes("")); emit OrderCancelled(msg.sender, orderId); } @@ -217,8 +220,11 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { /// @notice Cancels a batch of sell orders identified by the `orderIds`, transferring the orders' assets back to the seller. /// @dev This is a batched version of `cancelSellOrder` that simply iterates through the array to call said function. function cancelBatchSellOrder(uint256[] memory orderIds) public virtual { - for (uint256 i = 0; i < orderIds.length; ++i) { + for (uint256 i; i < orderIds.length; ) { cancelSellOrder(orderIds.unsafeMemoryAccess(i)); + unchecked { + ++i; + } } } From 8b9d014592d2d409ff6b9110743c7767d29f5a4b Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Tue, 9 Apr 2024 16:16:17 +0200 Subject: [PATCH 14/18] Refactor tests --- .../evm-contracts/test/OrderbookDex.t.sol | 199 ++++++++++-------- 1 file changed, 111 insertions(+), 88 deletions(-) diff --git a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol index 36ce7dc31..9e5420ca2 100644 --- a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol +++ b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol @@ -23,10 +23,6 @@ contract OrderbookDexTest is CTest, ERC1155Holder { address alice = vm.addr(uint256(keccak256(abi.encodePacked("alice")))); address boris = vm.addr(uint256(keccak256(abi.encodePacked("boris")))); - function tryToSendEthToDex() public { - payable(address(dex)).sendValue(1); - } - function setUp() public { asset = new InverseAppProjected1155("Gold", "GOLD", address(this)); dex = new OrderbookDex(asset); @@ -35,18 +31,15 @@ contract OrderbookDexTest is CTest, ERC1155Holder { vm.deal(boris, 1_000 ether); } - function test_SupportsInterface() public { - assertTrue(dex.supportsInterface(type(IERC165).interfaceId)); - assertTrue(dex.supportsInterface(type(IERC165).interfaceId)); - assertTrue(dex.supportsInterface(type(IOrderbookDex).interfaceId)); - } + function testFuzz_createSellOrder_satisfiesRequirements( + uint256 assetAmount, + uint256 pricePerAsset + ) public { + vm.assume(assetAmount > 0 && pricePerAsset > 0); - function test_CreateOrderSatisfiesRequirements() public { - uint256 assetAmount = 100; - uint256 pricePerAsset = 200; uint256 orderId = dex.getCurrentOrderId(); - uint256 assetId = asset.mint(assetAmount, ""); + vm.expectEmit(true, true, true, true); emit IOrderbookDex.OrderCreated( address(this), @@ -56,6 +49,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { pricePerAsset ); dex.createSellOrder(assetId, assetAmount, pricePerAsset); + IOrderbookDex.Order memory order = dex.getOrder(orderId); assertEq(order.assetId, assetId); assertEq(order.assetAmount, assetAmount); @@ -63,7 +57,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { assertEq(asset.balanceOf(address(dex), assetId), assetAmount); } - function testFuzz_CreateBatchOrderSatisfiesRequirements(uint256 orderCount) public { + function testFuzz_createBatchSellOrder_satisfiesRequirements(uint256 orderCount) public { orderCount = bound(orderCount, 0, 100); uint256[] memory assetIds = new uint256[](orderCount); uint256[] memory assetAmounts = new uint256[](orderCount); @@ -102,7 +96,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { } } - function testFuzz_CreateBatchOrderInvalidArrayLength( + function testFuzz_createBatchSellOrder_reverts_ifInvalidArrayLength( uint256 length1, uint256 length2, uint256 length3 @@ -119,57 +113,14 @@ contract OrderbookDexTest is CTest, ERC1155Holder { dex.createBatchSellOrder(assetIds, assetAmounts, pricesPerAssets); } - function test_CancelOrderSatisfiesRequirements() public { - uint256 assetAmount = 100; - uint256 assetId = asset.mint(assetAmount, ""); - uint256 orderId = dex.getCurrentOrderId(); - dex.createSellOrder(assetId, assetAmount, 200); - - vm.expectEmit(true, true, true, true); - emit IOrderbookDex.OrderCancelled(address(this), orderId); - dex.cancelSellOrder(orderId); - IOrderbookDex.Order memory order = dex.getOrder(orderId); - assertEq(order.assetAmount, 0); - assertEq(asset.balanceOf(address(dex), assetId), 0); - assertEq(asset.balanceOf(address(this), assetId), assetAmount); - } - - function testFuzz_CancelBatchOrderSatisfiesRequirements(uint256 orderCount) public { - orderCount = bound(orderCount, 0, 100); - uint256[] memory assetIds = new uint256[](orderCount); - uint256[] memory assetAmounts = new uint256[](orderCount); - uint256[] memory pricesPerAssets = new uint256[](orderCount); - - for (uint256 i = 0; i < orderCount; ++i) { - assetAmounts[i] = i == 0 ? 1 : i; - pricesPerAssets[i] = i == 0 ? 1 : i; - assetIds[i] = asset.mint(assetAmounts[i], ""); - } - - uint256 orderId = dex.getCurrentOrderId(); - - uint256[] memory orderIds = dex.createBatchSellOrder( - assetIds, - assetAmounts, - pricesPerAssets - ); - - for (uint256 i = 0; i < orderCount; ++i) { - vm.expectEmit(true, true, true, true); - emit IOrderbookDex.OrderCancelled(address(this), orderId + i); - } - - dex.cancelBatchSellOrder(orderIds); + function testFuzz_createSellOrder_reverts_ifAssetAmountIsZero(uint256 pricePerAsset) public { + uint256 assetId = asset.mint(0, ""); - for (uint256 i = 0; i < orderCount; ++i) { - IOrderbookDex.Order memory order = dex.getOrder(orderIds[i]); - assertEq(order.assetAmount, 0); - assertEq(asset.balanceOf(address(dex), assetIds[i]), 0); - assertEq(asset.balanceOf(address(this), assetIds[i]), assetAmounts[i]); - } + vm.expectRevert(abi.encodeWithSelector(OrderbookDex.InvalidInput.selector, 0)); + dex.createSellOrder(assetId, 0, pricePerAsset); } - function testFuzz_FillOrdersExactEth(uint256 price) public { + function testFuzz_fillOrdersExactEth_transfersCorrectly(uint256 price) public { uint256 ordersCount = 5; vm.assume(price / ordersCount > 0); vm.assume(price < type(uint256).max / ordersCount); @@ -226,7 +177,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { } } - function testFuzz_FillOrdersExactAsset(uint256 price) public { + function testFuzz_fillOrdersExactAsset_transfersCorrectly(uint256 price) public { uint256 ordersCount = 5; vm.assume(price / ordersCount > 0); vm.assume(price < type(uint256).max / ordersCount); @@ -283,7 +234,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { } } - function testFuzz_PartialFillExactEth( + function testFuzz_fillOrdersExactEth_transfersCorrectlyWithPartialFill( uint256 assetAmount, uint256 pricePerAsset, uint256 assetAmountToBuy @@ -317,7 +268,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { assertEq(asset.balanceOf(buyer, assetId), assetAmountToBuy); } - function testFuzz_PartialFillExactAsset( + function testFuzz_fillOrdersExactAsset_transfersCorrectlyWithPartialFill( uint256 assetAmount, uint256 pricePerAsset, uint256 assetAmountToBuy @@ -351,7 +302,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { assertEq(asset.balanceOf(buyer, assetId), assetAmountToBuy); } - function testFuzz_ExcessValueIsRefundedFillExactEth( + function testFuzz_fillOrdersExactEth_refundsExcessValue( uint256 assetAmount, uint256 pricePerAsset ) public { @@ -373,7 +324,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { assertEq(alice.balance, aliceBalanceBefore - (assetAmount * pricePerAsset)); } - function testFuzz_ExcessValueIsRefundedFillExactAsset( + function testFuzz_fillOrdersExactAsset_refundsExcessValue( uint256 assetAmount, uint256 pricePerAsset ) public { @@ -395,7 +346,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { assertEq(alice.balance, aliceBalanceBefore - (assetAmount * pricePerAsset)); } - function test_WontFillOrderIfCancelled() public { + function test_fillOrdersExactEth_wontFillOrderIfOrderWasCancelled() public { uint256 assetAmount = 100; uint256 pricePerAsset = 200; uint256 orderId = dex.getCurrentOrderId(); @@ -410,28 +361,26 @@ contract OrderbookDexTest is CTest, ERC1155Holder { dex.fillOrdersExactEth{value: assetAmount * pricePerAsset}(0, orderIds); assertEq(alice.balance, aliceBalanceBefore); - dex.fillOrdersExactAsset{value: assetAmount * pricePerAsset}(0, orderIds); - assertEq(alice.balance, aliceBalanceBefore); - } - - function test_CannotCreateOrderWithZeroAssetAmount() public { - uint256 assetId = asset.mint(0, ""); - - vm.expectRevert(abi.encodeWithSelector(OrderbookDex.InvalidInput.selector, 0)); - dex.createSellOrder(assetId, 0, 100); } - function test_CannotCancelOrderIfUnauthorized() public { + function test_fillOrdersExactAsset_wontFillOrderIfOrderWasCancelled() public { + uint256 assetAmount = 100; + uint256 pricePerAsset = 200; uint256 orderId = dex.getCurrentOrderId(); - uint256 assetId = asset.mint(100, ""); - dex.createSellOrder(assetId, 100, 200); + uint256 assetId = asset.mint(assetAmount, ""); + dex.createSellOrder(assetId, assetAmount, pricePerAsset); + dex.cancelSellOrder(orderId); + uint256 aliceBalanceBefore = alice.balance; + uint256[] memory orderIds = new uint256[](1); + orderIds[0] = orderId; vm.startPrank(alice); - vm.expectRevert(OrderbookDex.Unauthorized.selector); - dex.cancelSellOrder(orderId); + + dex.fillOrdersExactAsset{value: assetAmount * pricePerAsset}(0, orderIds); + assertEq(alice.balance, aliceBalanceBefore); } - function test_CannotFillOrderIfInsufficientEndAmountExactEth() public { + function test_fillOrdersExactEth_reverts_ifInsufficientEndAmount() public { uint256 assetAmount = 10; uint256 pricePerAsset = 100; uint256 orderId = dex.getCurrentOrderId(); @@ -453,7 +402,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { dex.fillOrdersExactEth{value: assetAmount * pricePerAsset}(assetAmount + 1, orderIds); } - function test_CannotFillOrderIfInsufficientEndAmountExactAsset() public { + function test_fillOrdersExactAsset_reverts_ifInsufficientEndAmount() public { uint256 assetAmount = 10; uint256 pricePerAsset = 100; uint256 orderId = dex.getCurrentOrderId(); @@ -475,9 +424,83 @@ contract OrderbookDexTest is CTest, ERC1155Holder { dex.fillOrdersExactAsset{value: assetAmount * pricePerAsset}(assetAmount + 1, orderIds); } - function test_CannotSendEtherToDex() public { - vm.expectRevert(Address.FailedInnerCall.selector); - this.tryToSendEthToDex(); + function test_supportsInterface_returnsTrueForImplementedInterfaces() public { + assertTrue(dex.supportsInterface(type(IERC165).interfaceId)); + assertTrue(dex.supportsInterface(type(IERC165).interfaceId)); + assertTrue(dex.supportsInterface(type(IOrderbookDex).interfaceId)); + } + + function testFuzz_cancelSellOrder_satisfiesRequirements(uint256 assetAmount) public { + vm.assume(assetAmount > 0); + uint256 assetId = asset.mint(assetAmount, ""); + uint256 orderId = dex.getCurrentOrderId(); + dex.createSellOrder(assetId, assetAmount, 200); + + vm.expectEmit(true, true, true, true); + emit IOrderbookDex.OrderCancelled(address(this), orderId); + dex.cancelSellOrder(orderId); + IOrderbookDex.Order memory order = dex.getOrder(orderId); + assertEq(order.assetAmount, 0); + assertEq(asset.balanceOf(address(dex), assetId), 0); + assertEq(asset.balanceOf(address(this), assetId), assetAmount); + } + + function test_cancelSellOrder_reverts_ifUnauthorized() public { + uint256 orderId = dex.getCurrentOrderId(); + uint256 assetId = asset.mint(100, ""); + dex.createSellOrder(assetId, 100, 200); + + vm.startPrank(alice); + vm.expectRevert(OrderbookDex.Unauthorized.selector); + dex.cancelSellOrder(orderId); + } + + function testFuzz_cancelBatchSellOrder_satisfiesRequirements(uint256 orderCount) public { + orderCount = bound(orderCount, 0, 100); + uint256[] memory assetIds = new uint256[](orderCount); + uint256[] memory assetAmounts = new uint256[](orderCount); + uint256[] memory pricesPerAssets = new uint256[](orderCount); + + for (uint256 i = 0; i < orderCount; ++i) { + assetAmounts[i] = i == 0 ? 1 : i; + pricesPerAssets[i] = i == 0 ? 1 : i; + assetIds[i] = asset.mint(assetAmounts[i], ""); + } + + uint256 orderId = dex.getCurrentOrderId(); + + uint256[] memory orderIds = dex.createBatchSellOrder( + assetIds, + assetAmounts, + pricesPerAssets + ); + + for (uint256 i = 0; i < orderCount; ++i) { + vm.expectEmit(true, true, true, true); + emit IOrderbookDex.OrderCancelled(address(this), orderId + i); + } + + dex.cancelBatchSellOrder(orderIds); + + for (uint256 i = 0; i < orderCount; ++i) { + IOrderbookDex.Order memory order = dex.getOrder(orderIds[i]); + assertEq(order.assetAmount, 0); + assertEq(asset.balanceOf(address(dex), assetIds[i]), 0); + assertEq(asset.balanceOf(address(this), assetIds[i]), assetAmounts[i]); + } + } + + function test_cancelBatchSellOrder_reverts_ifUnauthorized() public { + uint256 orderId = dex.getCurrentOrderId(); + uint256 assetId = asset.mint(100, ""); + dex.createSellOrder(assetId, 100, 200); + + uint256[] memory orderIds = new uint256[](1); + orderIds[0] = orderId; + + vm.startPrank(alice); + vm.expectRevert(OrderbookDex.Unauthorized.selector); + dex.cancelBatchSellOrder(orderIds); } receive() external payable {} From 9fbc06b0c6fc2f87c3d0b1a004ffaf103fb30b82 Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Wed, 10 Apr 2024 16:25:36 +0200 Subject: [PATCH 15/18] Invariant testing suite --- .../contracts/orderbook/OrderbookDex.sol | 4 +- packages/contracts/evm-contracts/foundry.toml | 4 +- .../evm-contracts/test-lib/StdInvariant.sol | 92 +++++ .../evm-contracts/test-lib/ctest.sol | 4 +- .../evm-contracts/test/OrderbookDex.t.sol | 4 +- .../test/OrderbookDexInvariant.t.sol | 390 ++++++++++++++++++ 6 files changed, 492 insertions(+), 6 deletions(-) create mode 100644 packages/contracts/evm-contracts/test-lib/StdInvariant.sol create mode 100644 packages/contracts/evm-contracts/test/OrderbookDexInvariant.t.sol diff --git a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol index efb07c209..d66a495b9 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol @@ -24,7 +24,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { error InsufficientEndAmount(uint256 expectedAmount, uint256 actualAmount); error InvalidArrayLength(); error InvalidInput(uint256 input); - error Unauthorized(); + error Unauthorized(address sender); constructor(IInverseProjected1155 _asset) { asset = _asset; @@ -209,7 +209,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { function cancelSellOrder(uint256 orderId) public virtual { Order storage order = orders[orderId]; if (msg.sender != order.seller) { - revert Unauthorized(); + revert Unauthorized(msg.sender); } uint256 assetAmount = order.assetAmount; delete order.assetAmount; diff --git a/packages/contracts/evm-contracts/foundry.toml b/packages/contracts/evm-contracts/foundry.toml index 75d5992f6..013100149 100644 --- a/packages/contracts/evm-contracts/foundry.toml +++ b/packages/contracts/evm-contracts/foundry.toml @@ -2,7 +2,7 @@ src = 'contracts' # the source directory test = 'test' # the test directory out = 'artifacts/forge' # the output directory (for artifacts) -libs = ['test-lib'] # a list of library directories +libs = ['test-lib'] # a list of library directories remappings = [ # a list of remappings '@openzeppelin/=../../../node_modules/@openzeppelin/', ] @@ -31,3 +31,5 @@ evm_version = 'berlin' # the evm version (by hardfork name) #block_coinbase = '0x0000000000000000000000000000000000000000' # the address of `block.coinbase` in tests #block_timestamp = 0 # the value of `block.timestamp` in tests #block_difficulty = 0 # the value of `block.difficulty` in tests +[invariant] +fail_on_revert = true \ No newline at end of file diff --git a/packages/contracts/evm-contracts/test-lib/StdInvariant.sol b/packages/contracts/evm-contracts/test-lib/StdInvariant.sol new file mode 100644 index 000000000..fd9d0a1db --- /dev/null +++ b/packages/contracts/evm-contracts/test-lib/StdInvariant.sol @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.2 <0.9.0; + +pragma experimental ABIEncoderV2; + +abstract contract StdInvariant { + struct FuzzSelector { + address addr; + bytes4[] selectors; + } + + address[] private _excludedContracts; + address[] private _excludedSenders; + address[] private _targetedContracts; + address[] private _targetedSenders; + + string[] private _excludedArtifacts; + string[] private _targetedArtifacts; + + FuzzSelector[] private _targetedArtifactSelectors; + FuzzSelector[] private _targetedSelectors; + + // Functions for users: + // These are intended to be called in tests. + + function excludeContract(address newExcludedContract_) internal { + _excludedContracts.push(newExcludedContract_); + } + + function excludeSender(address newExcludedSender_) internal { + _excludedSenders.push(newExcludedSender_); + } + + function excludeArtifact(string memory newExcludedArtifact_) internal { + _excludedArtifacts.push(newExcludedArtifact_); + } + + function targetArtifact(string memory newTargetedArtifact_) internal { + _targetedArtifacts.push(newTargetedArtifact_); + } + + function targetArtifactSelector(FuzzSelector memory newTargetedArtifactSelector_) internal { + _targetedArtifactSelectors.push(newTargetedArtifactSelector_); + } + + function targetContract(address newTargetedContract_) internal { + _targetedContracts.push(newTargetedContract_); + } + + function targetSelector(FuzzSelector memory newTargetedSelector_) internal { + _targetedSelectors.push(newTargetedSelector_); + } + + function targetSender(address newTargetedSender_) internal { + _targetedSenders.push(newTargetedSender_); + } + + // Functions for forge: + // These are called by forge to run invariant tests and don't need to be called in tests. + + function excludeArtifacts() public view returns (string[] memory excludedArtifacts_) { + excludedArtifacts_ = _excludedArtifacts; + } + + function excludeContracts() public view returns (address[] memory excludedContracts_) { + excludedContracts_ = _excludedContracts; + } + + function excludeSenders() public view returns (address[] memory excludedSenders_) { + excludedSenders_ = _excludedSenders; + } + + function targetArtifacts() public view returns (string[] memory targetedArtifacts_) { + targetedArtifacts_ = _targetedArtifacts; + } + + function targetArtifactSelectors() public view returns (FuzzSelector[] memory targetedArtifactSelectors_) { + targetedArtifactSelectors_ = _targetedArtifactSelectors; + } + + function targetContracts() public view returns (address[] memory targetedContracts_) { + targetedContracts_ = _targetedContracts; + } + + function targetSelectors() public view returns (FuzzSelector[] memory targetedSelectors_) { + targetedSelectors_ = _targetedSelectors; + } + + function targetSenders() public view returns (address[] memory targetedSenders_) { + targetedSenders_ = _targetedSenders; + } +} diff --git a/packages/contracts/evm-contracts/test-lib/ctest.sol b/packages/contracts/evm-contracts/test-lib/ctest.sol index 08bb7ded3..dd052a1f1 100644 --- a/packages/contracts/evm-contracts/test-lib/ctest.sol +++ b/packages/contracts/evm-contracts/test-lib/ctest.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.5.0; -contract CTest { +import {StdInvariant} from "./StdInvariant.sol"; + +contract CTest is StdInvariant { event log(string); event logs(bytes); diff --git a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol index 9e5420ca2..05403a105 100644 --- a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol +++ b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol @@ -451,7 +451,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { dex.createSellOrder(assetId, 100, 200); vm.startPrank(alice); - vm.expectRevert(OrderbookDex.Unauthorized.selector); + vm.expectRevert(abi.encodeWithSelector(OrderbookDex.Unauthorized.selector, alice)); dex.cancelSellOrder(orderId); } @@ -499,7 +499,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { orderIds[0] = orderId; vm.startPrank(alice); - vm.expectRevert(OrderbookDex.Unauthorized.selector); + vm.expectRevert(abi.encodeWithSelector(OrderbookDex.Unauthorized.selector, alice)); dex.cancelBatchSellOrder(orderIds); } diff --git a/packages/contracts/evm-contracts/test/OrderbookDexInvariant.t.sol b/packages/contracts/evm-contracts/test/OrderbookDexInvariant.t.sol new file mode 100644 index 000000000..1d1dc08c5 --- /dev/null +++ b/packages/contracts/evm-contracts/test/OrderbookDexInvariant.t.sol @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.18; + +import {CheatCodes} from "../test-lib/cheatcodes.sol"; +import {CTest} from "../test-lib/ctest.sol"; + +import {Address} from "@openzeppelin/contracts/utils/Address.sol"; +import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; +import {IERC1155Receiver} from "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol"; +import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +import {IInverseAppProjected1155} from "../contracts/token/IInverseAppProjected1155.sol"; +import {InverseAppProjected1155} from "../contracts/token/InverseAppProjected1155.sol"; +import {IOrderbookDex} from "../contracts/orderbook/IOrderbookDex.sol"; +import {OrderbookDex} from "../contracts/orderbook/OrderbookDex.sol"; + +contract AssetHandler is CTest { + CheatCodes vm = CheatCodes(HEVM_ADDRESS); + IOrderbookDex public dex; + IInverseAppProjected1155 asset; + + constructor(IInverseAppProjected1155 _asset, IOrderbookDex _dex) { + asset = _asset; + dex = _dex; + } + + function safeTransferFrom( + address from, + address to, + uint256 id, + uint256 value, + bytes calldata data + ) public { + vm.assume(asset.balanceOf(from, id) >= value); + address sender = msg.sender; + vm.prank(from); + asset.setApprovalForAll(sender, true); + vm.prank(sender); + asset.safeTransferFrom(from, to, id, value, data); + } + + function safeBatchTransferFrom( + address from, + address to, + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata data + ) public { + vm.assume(ids.length == values.length); + for (uint256 i; i < ids.length; ++i) { + vm.assume(asset.balanceOf(from, ids[i]) >= values[i]); + } + address sender = msg.sender; + vm.prank(from); + asset.setApprovalForAll(sender, true); + vm.prank(sender); + asset.safeBatchTransferFrom(from, to, ids, values, data); + } +} + +contract OrderbookDexHandler is CTest, ERC1155Holder { + CheatCodes vm = CheatCodes(HEVM_ADDRESS); + IOrderbookDex public dex; + IInverseAppProjected1155 asset; + + // Reasonable limits resulting in acceptable testing time + uint256 constant MAX_ASSET_AMOUNT = 100 ether; + uint256 constant MAX_PRICE = 1 ether; + uint256 constant MAX_BATCH_CREATE_SELL_ORDER = 50; + uint256 constant MAX_BATCH_CANCEL_SELL_ORDER = 10; + uint256 constant MAX_ORDER_FILL_LENGTH = 5; + + // Helper variables + mapping(uint256 tokenId => uint256) buyerTokenBalanceBefore; + mapping(uint256 tokenId => uint256) expectedTokenBalanceGain; + mapping(uint256 orderId => bool) orderIdUsed; + uint256[] newOrderIds; + uint256 public previousOrderId; + + address internal currentActor; + + modifier useActor(uint256 actorIndexSeed) { + _useActor(actorIndexSeed); + _; + vm.stopPrank(); + } + + function _useActor(uint256 actorIndexSeed) internal { + uint256 actorIndex = bound(actorIndexSeed, 1, 100); + currentActor = address(uint160(uint256(keccak256(abi.encodePacked(actorIndex))))); + vm.startPrank(currentActor); + } + + constructor(IInverseAppProjected1155 _asset, IOrderbookDex _dex) { + asset = _asset; + dex = _dex; + } + + function createSellOrder( + uint256 assetId, + uint256 assetAmount, + uint256 price, + uint256 actorIndexSeed + ) public useActor(actorIndexSeed) returns (uint256) { + // Bound amount and price to reasonable limits, mint the asset and set approval for the dex + assetAmount = bound(assetAmount, 1, MAX_ASSET_AMOUNT); + price = bound(price, 1, MAX_PRICE); + assetId = asset.mint(assetAmount, ""); + asset.setApprovalForAll(address(dex), true); + + // Take note of the previous order id + previousOrderId = dex.getCurrentOrderId(); + + // Execute the sell order creation + return dex.createSellOrder(assetId, assetAmount, price); + } + + function createBatchSellOrder( + uint256[] memory assetAmounts, + uint256[] memory pricesPerAssets, + uint256 actorIndexSeed + ) public useActor(actorIndexSeed) returns (uint256[] memory) { + // Use the smaller of the input arrays length and bound it to a reasonable limit + uint256 smallestLength = assetAmounts.length; + if (pricesPerAssets.length < smallestLength) { + smallestLength = pricesPerAssets.length; + } + smallestLength = bound(smallestLength, 0, MAX_BATCH_CREATE_SELL_ORDER); + + // Bound amounts and prices to reasonable limits, mint the assets and set approval for the dex + uint256[] memory newAssetIds = new uint256[](smallestLength); + uint256[] memory newAssetAmounts = new uint256[](smallestLength); + uint256[] memory newPricesPerAssets = new uint256[](smallestLength); + for (uint256 i; i < smallestLength; ++i) { + newAssetAmounts[i] = bound(assetAmounts[i], 1, MAX_ASSET_AMOUNT); + newPricesPerAssets[i] = bound(pricesPerAssets[i], 1, MAX_PRICE); + newAssetIds[i] = asset.mint(newAssetAmounts[i], ""); + } + asset.setApprovalForAll(address(dex), true); + + // Execute the batch sell order creation + return dex.createBatchSellOrder(newAssetIds, newAssetAmounts, newPricesPerAssets); + } + + function fillOrdersExactEth( + uint256 minimumAsset, + uint256[] memory orderIds, + uint256 actorIndexSeed + ) public payable useActor(actorIndexSeed) { + // Bound the input length to a reasonable limit + uint256 inputLength = orderIds.length; + inputLength = bound(inputLength, 0, MAX_ORDER_FILL_LENGTH); + + // Calculate the sum of the asset amounts and prices of the orders + uint256 sumAssetAmount; + uint256 sumPrice; + for (uint256 i; i < inputLength; ++i) { + IOrderbookDex.Order memory order = dex.getOrder(orderIds[i]); + + if (!orderIdUsed[orderIds[i]]) { + sumAssetAmount += order.assetAmount; + sumPrice += order.pricePerAsset * order.assetAmount; + orderIdUsed[orderIds[i]] = true; + newOrderIds.push(orderIds[i]); + } + } + inputLength = newOrderIds.length; + + // Cap the asset amount to the sum of the asset amounts of the orders + if (minimumAsset > sumAssetAmount) { + minimumAsset = sumAssetAmount; + } + + // Set current actor's balance to expected total price to avoid revert + uint256 value = sumPrice; + vm.deal(currentActor, value); + + // Take note of buyer's tokens balances and orders' asset amounts before filling orders + // for the assertions later + uint256[] memory orderAssetAmountBefore = new uint256[](inputLength); + for (uint256 i; i < inputLength; ++i) { + IOrderbookDex.Order memory order = dex.getOrder(newOrderIds[i]); + buyerTokenBalanceBefore[order.assetId] = asset.balanceOf(currentActor, order.assetId); + orderAssetAmountBefore[i] = order.assetAmount; + } + + // Execute the fills + dex.fillOrdersExactEth{value: value}(minimumAsset, newOrderIds); + + // Calculate the expected token balance gain for each token + for (uint256 i; i < inputLength; ++i) { + IOrderbookDex.Order memory order = dex.getOrder(newOrderIds[i]); + expectedTokenBalanceGain[order.assetId] += + orderAssetAmountBefore[i] - + order.assetAmount; + } + + // Assert that the buyer's token balances have increased by the expected amount (that was credited from the orders' asset amounts) + for (uint256 i; i < inputLength; ++i) { + IOrderbookDex.Order memory order = dex.getOrder(newOrderIds[i]); + assertEq( + asset.balanceOf(currentActor, order.assetId), + buyerTokenBalanceBefore[order.assetId] + expectedTokenBalanceGain[order.assetId] + ); + } + + // Clean-up + for (uint256 i; i < inputLength; ++i) { + IOrderbookDex.Order memory order = dex.getOrder(newOrderIds[i]); + delete expectedTokenBalanceGain[order.assetId]; + delete orderIdUsed[newOrderIds[i]]; + } + delete newOrderIds; + } + + function fillOrdersExactAsset( + uint256 assetAmount, + uint256[] memory orderIds, + uint256 actorIndexSeed + ) public payable useActor(actorIndexSeed) { + // Bound the input length to a reasonable limit + uint256 inputLength = orderIds.length; + inputLength = bound(inputLength, 0, MAX_ORDER_FILL_LENGTH); + + // Calculate the sum of the asset amounts and prices of the orders + uint256 sumAssetAmount; + uint256 sumPrice; + for (uint256 i; i < inputLength; ++i) { + IOrderbookDex.Order memory order = dex.getOrder(orderIds[i]); + + if (!orderIdUsed[orderIds[i]]) { + sumAssetAmount += order.assetAmount; + sumPrice += order.pricePerAsset * order.assetAmount; + orderIdUsed[orderIds[i]] = true; + newOrderIds.push(orderIds[i]); + } + } + inputLength = newOrderIds.length; + + // Cap the asset amount to the sum of the asset amounts of the orders + if (assetAmount > sumAssetAmount) { + assetAmount = sumAssetAmount; + } + + // Set current actor's balance to expected total price plus 100 to avoid revert and + // to provide surplus which should be refunded + uint256 value = sumPrice + 100; + vm.deal(currentActor, value); + + // Take note of buyer's tokens balances and orders' asset amounts before filling orders + // for the assertions later + uint256[] memory orderAssetAmountBefore = new uint256[](inputLength); + for (uint256 i; i < inputLength; ++i) { + IOrderbookDex.Order memory order = dex.getOrder(newOrderIds[i]); + buyerTokenBalanceBefore[order.assetId] = asset.balanceOf(currentActor, order.assetId); + orderAssetAmountBefore[i] = order.assetAmount; + } + + // Execute the fills + dex.fillOrdersExactAsset{value: value}(assetAmount, newOrderIds); + + // Calculate the expected token balance gain for each token + for (uint256 i; i < inputLength; ++i) { + IOrderbookDex.Order memory order = dex.getOrder(newOrderIds[i]); + expectedTokenBalanceGain[order.assetId] += + orderAssetAmountBefore[i] - + order.assetAmount; + } + + // Assert that the buyer's token balances have increased by the expected amount (that was credited from the orders' asset amounts) + for (uint256 i; i < inputLength; ++i) { + IOrderbookDex.Order memory order = dex.getOrder(newOrderIds[i]); + assertEq( + asset.balanceOf(currentActor, order.assetId), + buyerTokenBalanceBefore[order.assetId] + expectedTokenBalanceGain[order.assetId] + ); + } + + // Clean-up + for (uint256 i; i < inputLength; ++i) { + IOrderbookDex.Order memory order = dex.getOrder(newOrderIds[i]); + delete expectedTokenBalanceGain[order.assetId]; + delete orderIdUsed[newOrderIds[i]]; + } + delete newOrderIds; + } + + function cancelSellOrder( + uint256 orderId, + uint256 actorIndexSeed + ) public useActor(actorIndexSeed) { + // If the order does not exist, get the last order id + if (dex.getOrder(orderId).assetAmount == 0) { + orderId = dex.getCurrentOrderId(); + // If there are no orders, return + if (orderId == 0) { + return; + } else { + orderId--; + } + } + + // Assert that you can't cancel an order if you're not the seller + address seller = dex.getOrder(orderId).seller; + if (currentActor != seller) { + vm.expectRevert( + abi.encodeWithSelector(OrderbookDex.Unauthorized.selector, currentActor) + ); + dex.cancelSellOrder(orderId); + } + + // Prank the order's seller + vm.startPrank(seller); + + // Execute the cancel + dex.cancelSellOrder(orderId); + } + + function cancelBatchSellOrder( + uint256[] memory orderIds, + uint256 actorIndexSeed + ) public useActor(actorIndexSeed) { + // Bound the input length to a reasonable limit + uint256 len = bound(orderIds.length, 0, MAX_BATCH_CANCEL_SELL_ORDER); + + // Create new orders if the order does not exist or the seller is not the current actor + uint256[] memory _newOrderIds = new uint256[](len); + for (uint256 i; i < len; ++i) { + IOrderbookDex.Order memory order = dex.getOrder(orderIds[i]); + if (order.assetAmount == 0 || order.seller != currentActor) { + uint256 x = uint256(keccak256(abi.encodePacked(i))); + _newOrderIds[i] = this.createSellOrder(x, x, x, actorIndexSeed); + } else { + _newOrderIds[i] = orderIds[i]; + } + } + + // If this.createSellOrder was called, the prank was stopped, so we need to start it again + if (len > 0) { + vm.startPrank(currentActor); + } + + // Execute the batch cancel + dex.cancelBatchSellOrder(_newOrderIds); + } + + fallback() external payable {} +} + +contract OrderbookDexInvariantTest is CTest, ERC1155Holder { + using Address for address payable; + + CheatCodes vm = CheatCodes(HEVM_ADDRESS); + OrderbookDex public dex; + OrderbookDexHandler public dexHandler; + IInverseAppProjected1155 asset; + AssetHandler public assetHandler; + + function setUp() public { + asset = new InverseAppProjected1155("Gold", "GOLD", address(this)); + dex = new OrderbookDex(asset); + dexHandler = new OrderbookDexHandler(asset, dex); + assetHandler = new AssetHandler(asset, dex); + targetContract(address(assetHandler)); + targetContract(address(dexHandler)); + } + + function invariant_ordersAssetAmountEqualsContractTokenBalance() public { + for (uint256 i; i < dex.getCurrentOrderId(); ++i) { + IOrderbookDex.Order memory order = dex.getOrder(i); + assertEq(order.assetAmount, asset.balanceOf(address(dex), order.assetId)); + } + } + + function invariant_contractDoesNotObtainEther() public { + assertEq(address(dex).balance, 0); + } + + function invariant_orderIdIsIncremental() public { + uint256 currentId = dex.getCurrentOrderId(); + uint256 previousId = dexHandler.previousOrderId(); + if (currentId == previousId) { + assertEq(currentId, 0); + return; + } + assertGe(currentId, previousId); + } + + receive() external payable {} +} From 5408081ff9ac8bbd386d3a460f0e7af8ec0b3a8b Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Thu, 11 Apr 2024 09:30:56 +0200 Subject: [PATCH 16/18] Test for testing gas usage --- .../evm-contracts/test/OrderbookDex.t.sol | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol index 05403a105..fc6c0be6a 100644 --- a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol +++ b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.18; import {CheatCodes} from "../test-lib/cheatcodes.sol"; import {CTest} from "../test-lib/ctest.sol"; +import "../test-lib/console.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol"; @@ -503,5 +504,34 @@ contract OrderbookDexTest is CTest, ERC1155Holder { dex.cancelBatchSellOrder(orderIds); } + // Not really a test, was used to measure gas usage + function test_fillOrders_getGasUsage() public { + uint256 orderCount = 500; + uint256[] memory assetIds = new uint256[](orderCount); + uint256[] memory assetAmounts = new uint256[](orderCount); + uint256[] memory pricesPerAssets = new uint256[](orderCount); + + for (uint256 i = 0; i < orderCount; ++i) { + assetAmounts[i] = i == 0 ? 1 : i; + pricesPerAssets[i] = i == 0 ? 1 : i; + assetIds[i] = asset.mint(assetAmounts[i], ""); + } + uint256[] memory orderIds = dex.createBatchSellOrder( + assetIds, + assetAmounts, + pricesPerAssets + ); + + assertEq(orderIds.length, orderCount); + + uint256 minimumAsset = 1; + uint256 value = type(uint256).max; + vm.deal(address(this), value); + + uint256 startGas = gasleft(); + dex.fillOrdersExactEth{value: value}(minimumAsset, orderIds); + console.log("fill orders gas used", startGas - gasleft()); + } + receive() external payable {} } From d645633ff40a8fd1545537e22d1f603dcf3a269d Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Thu, 11 Apr 2024 16:17:14 +0200 Subject: [PATCH 17/18] Simplify getters and use `@inheritdoc` --- .../contracts/orderbook/IOrderbookDex.sol | 8 +- .../contracts/orderbook/OrderbookDex.sol | 81 +++++++------------ .../evm-contracts/test/OrderbookDex.t.sol | 34 ++++---- .../test/OrderbookDexInvariant.t.sol | 10 +-- 4 files changed, 54 insertions(+), 79 deletions(-) diff --git a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol index 6d1357aed..5b126fe8f 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/IOrderbookDex.sol @@ -48,11 +48,11 @@ interface IOrderbookDex is IERC1155Receiver { /// @param id The order's unique identifier. event OrderCancelled(address indexed seller, uint256 indexed id); - /// @notice Returns the address of the asset that is being traded in this DEX contract. - function getAsset() external view returns (address); + /// @notice The address of the asset that is being traded in this DEX contract. + function asset() external view returns (address); - /// @notice Returns the `orderId` of the next sell order. - function getCurrentOrderId() external view returns (uint256); + /// @notice The `orderId` of the next sell order. + function currentOrderId() external view returns (uint256); /// @notice Returns the Order struct information about an order identified by the `orderId`. function getOrder(uint256 orderId) external view returns (Order memory); diff --git a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol index d66a495b9..35b687203 100644 --- a/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol +++ b/packages/contracts/evm-contracts/contracts/orderbook/OrderbookDex.sol @@ -16,9 +16,11 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { using Address for address payable; using Arrays for uint256[]; - IInverseProjected1155 internal immutable asset; + /// @inheritdoc IOrderbookDex + address public immutable override asset; + /// @inheritdoc IOrderbookDex + uint256 public override currentOrderId; mapping(uint256 orderId => Order) internal orders; - uint256 internal currentOrderId; error OrderDoesNotExist(uint256 orderId); error InsufficientEndAmount(uint256 expectedAmount, uint256 actualAmount); @@ -26,30 +28,16 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { error InvalidInput(uint256 input); error Unauthorized(address sender); - constructor(IInverseProjected1155 _asset) { + constructor(address _asset) { asset = _asset; } - /// @notice Returns the address of the asset that is being traded in this DEX contract. - function getAsset() public view virtual returns (address) { - return address(asset); - } - - /// @notice Returns the `orderId` of the next sell order. - function getCurrentOrderId() public view virtual returns (uint256) { - return currentOrderId; - } - - /// @notice Returns the Order struct information about an order identified by the `orderId`. + /// @inheritdoc IOrderbookDex function getOrder(uint256 orderId) public view virtual returns (Order memory) { return orders[orderId]; } - /// @notice Creates a sell order for the `assetAmount` of `assetId` at `pricePerAsset`. - /// @dev The order information is saved in a mapping `orderId -> Order`, with `orderId` being a unique incremental identifier. - /// MUST transfer the `assetAmount` of `assetId` from the seller to the contract. - /// MUST emit `OrderCreated` event. - /// @return The unique identifier of the created order. + /// @inheritdoc IOrderbookDex function createSellOrder( uint256 assetId, uint256 assetAmount, @@ -58,7 +46,13 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { if (assetAmount == 0 || pricePerAsset == 0) { revert InvalidInput(0); } - asset.safeTransferFrom(msg.sender, address(this), assetId, assetAmount, bytes("")); + IInverseProjected1155(asset).safeTransferFrom( + msg.sender, + address(this), + assetId, + assetAmount, + bytes("") + ); Order memory newOrder = Order({ assetId: assetId, assetAmount: assetAmount, @@ -72,9 +66,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { return orderId; } - /// @notice Creates a batch of sell orders for the `assetAmount` of `assetId` at `pricePerAsset`. - /// @dev This is a batched version of `createSellOrder` that simply iterates through the arrays to call said function. - /// @return The unique identifiers of the created orders. + /// @inheritdoc IOrderbookDex function createBatchSellOrder( uint256[] memory assetIds, uint256[] memory assetAmounts, @@ -97,14 +89,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { return orderIds; } - /// @notice Consecutively fills an array of orders identified by the `orderId` of each order, - /// by providing an exact amount of ETH and requesting a specific minimum amount of asset to receive. - /// @dev Transfers portions of msg.value to the orders' sellers according to the price. - /// The sum of asset amounts of filled orders MUST be at least `minimumAsset`. - /// If msg.value is more than the sum of orders' prices, it MUST refund the excess back to `msg.sender`. - /// MUST decrease the `assetAmount` parameter for the specified order according to how much of it was filled, - /// and transfer that amount of the order's `assetId` to the buyer. - /// MUST emit `OrderFilled` event for each order accordingly. + /// @inheritdoc IOrderbookDex function fillOrdersExactEth( uint256 minimumAsset, uint256[] memory orderIds @@ -128,7 +113,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { order.assetAmount -= assetsToBuy; remainingEth -= assetsToBuy * order.pricePerAsset; totalAssetReceived += assetsToBuy; - asset.safeTransferFrom( + IInverseProjected1155(asset).safeTransferFrom( address(this), msg.sender, order.assetId, @@ -149,14 +134,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { } } - /// @notice Consecutively fills an array of orders identified by the `orderId` of each order, - /// by providing a possibly surplus amount of ETH and requesting an exact amount of asset to receive. - /// @dev Transfers portions of msg.value to the orders' sellers according to the price. - /// The sum of asset amounts of filled orders MUST be exactly `assetAmount`. Excess ETH MUST be returned back to `msg.sender`. - /// MUST decrease the `assetAmount` parameter for the specified order according to how much of it was filled, - /// and transfer that amount of the order's `assetId` to the buyer. - /// MUST emit `OrderFilled` event for each order accordingly. - /// If msg.value is more than the sum of orders' prices, it MUST refund the difference back to `msg.sender`. + /// @inheritdoc IOrderbookDex function fillOrdersExactAsset( uint256 assetAmount, uint256[] memory orderIds @@ -180,7 +158,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { order.assetAmount -= assetsToBuy; remainingEth -= assetsToBuy * order.pricePerAsset; remainingAsset -= assetsToBuy; - asset.safeTransferFrom( + IInverseProjected1155(asset).safeTransferFrom( address(this), msg.sender, order.assetId, @@ -201,11 +179,7 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { } } - /// @notice Cancels the sell order identified by the `orderId`, transferring the order's assets back to the seller. - /// @dev MUST revert if the order's seller is not `msg.sender`. - /// MUST change the `assetAmount` parameter for the specified order to `0`. - /// MUST emit `OrderCancelled` event. - /// MUST transfer the `assetAmount` of `assetId` back to the seller. + /// @inheritdoc IOrderbookDex function cancelSellOrder(uint256 orderId) public virtual { Order storage order = orders[orderId]; if (msg.sender != order.seller) { @@ -213,12 +187,17 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { } uint256 assetAmount = order.assetAmount; delete order.assetAmount; - asset.safeTransferFrom(address(this), msg.sender, order.assetId, assetAmount, bytes("")); + IInverseProjected1155(asset).safeTransferFrom( + address(this), + msg.sender, + order.assetId, + assetAmount, + bytes("") + ); emit OrderCancelled(msg.sender, orderId); } - /// @notice Cancels a batch of sell orders identified by the `orderIds`, transferring the orders' assets back to the seller. - /// @dev This is a batched version of `cancelSellOrder` that simply iterates through the array to call said function. + /// @inheritdoc IOrderbookDex function cancelBatchSellOrder(uint256[] memory orderIds) public virtual { for (uint256 i; i < orderIds.length; ) { cancelSellOrder(orderIds.unsafeMemoryAccess(i)); @@ -235,8 +214,4 @@ contract OrderbookDex is IOrderbookDex, ERC1155Holder, ReentrancyGuard { return interfaceId == type(IOrderbookDex).interfaceId || super.supportsInterface(interfaceId); } - - function _getOrderId(address seller, uint96 orderId) internal view virtual returns (uint256) { - return (uint256(uint160(seller)) << 96) ^ orderId; - } } diff --git a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol index fc6c0be6a..3d78006e4 100644 --- a/packages/contracts/evm-contracts/test/OrderbookDex.t.sol +++ b/packages/contracts/evm-contracts/test/OrderbookDex.t.sol @@ -26,7 +26,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { function setUp() public { asset = new InverseAppProjected1155("Gold", "GOLD", address(this)); - dex = new OrderbookDex(asset); + dex = new OrderbookDex(address(asset)); asset.setApprovalForAll(address(dex), true); vm.deal(alice, 1_000 ether); vm.deal(boris, 1_000 ether); @@ -38,7 +38,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { ) public { vm.assume(assetAmount > 0 && pricePerAsset > 0); - uint256 orderId = dex.getCurrentOrderId(); + uint256 orderId = dex.currentOrderId(); uint256 assetId = asset.mint(assetAmount, ""); vm.expectEmit(true, true, true, true); @@ -70,7 +70,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { assetIds[i] = asset.mint(assetAmounts[i], ""); } - uint256 orderId = dex.getCurrentOrderId(); + uint256 orderId = dex.currentOrderId(); for (uint256 i = 0; i < orderCount; ++i) { vm.expectEmit(true, true, true, true); emit IOrderbookDex.OrderCreated( @@ -133,7 +133,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { address payable seller = payable(vm.addr(uint256(keccak256(abi.encodePacked(i))))); uint256 assetAmount = price / (ordersCount - i); uint256 pricePerAsset = price / assetAmount; - orderIds[i] = dex.getCurrentOrderId(); + orderIds[i] = dex.currentOrderId(); sellers[i] = seller; vm.startPrank(seller); uint256 assetId = asset.mint(assetAmount, ""); @@ -190,7 +190,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { address payable seller = payable(vm.addr(uint256(keccak256(abi.encodePacked(i))))); uint256 assetAmount = price / (ordersCount - i); uint256 pricePerAsset = price / assetAmount; - orderIds[i] = dex.getCurrentOrderId(); + orderIds[i] = dex.currentOrderId(); sellers[i] = seller; vm.startPrank(seller); uint256 assetId = asset.mint(assetAmount, ""); @@ -248,7 +248,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { address buyer = alice; address payable seller = payable(address(this)); vm.deal(buyer, assetAmount * pricePerAsset); - uint256 orderId = dex.getCurrentOrderId(); + uint256 orderId = dex.currentOrderId(); vm.startPrank(seller); uint256 assetId = asset.mint(assetAmount, ""); asset.setApprovalForAll(address(dex), true); @@ -282,7 +282,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { address buyer = alice; address payable seller = payable(address(this)); vm.deal(buyer, assetAmount * pricePerAsset); - uint256 orderId = dex.getCurrentOrderId(); + uint256 orderId = dex.currentOrderId(); vm.startPrank(seller); uint256 assetId = asset.mint(assetAmount, ""); asset.setApprovalForAll(address(dex), true); @@ -313,7 +313,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { vm.assume(pricePerAsset < type(uint256).max / assetAmount / multiplier); vm.deal(alice, assetAmount * pricePerAsset * multiplier); - uint256 orderId = dex.getCurrentOrderId(); + uint256 orderId = dex.currentOrderId(); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); @@ -335,7 +335,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { vm.assume(pricePerAsset < type(uint256).max / assetAmount / multiplier); vm.deal(alice, assetAmount * pricePerAsset * multiplier); - uint256 orderId = dex.getCurrentOrderId(); + uint256 orderId = dex.currentOrderId(); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); @@ -350,7 +350,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { function test_fillOrdersExactEth_wontFillOrderIfOrderWasCancelled() public { uint256 assetAmount = 100; uint256 pricePerAsset = 200; - uint256 orderId = dex.getCurrentOrderId(); + uint256 orderId = dex.currentOrderId(); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); dex.cancelSellOrder(orderId); @@ -367,7 +367,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { function test_fillOrdersExactAsset_wontFillOrderIfOrderWasCancelled() public { uint256 assetAmount = 100; uint256 pricePerAsset = 200; - uint256 orderId = dex.getCurrentOrderId(); + uint256 orderId = dex.currentOrderId(); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); dex.cancelSellOrder(orderId); @@ -384,7 +384,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { function test_fillOrdersExactEth_reverts_ifInsufficientEndAmount() public { uint256 assetAmount = 10; uint256 pricePerAsset = 100; - uint256 orderId = dex.getCurrentOrderId(); + uint256 orderId = dex.currentOrderId(); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); @@ -406,7 +406,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { function test_fillOrdersExactAsset_reverts_ifInsufficientEndAmount() public { uint256 assetAmount = 10; uint256 pricePerAsset = 100; - uint256 orderId = dex.getCurrentOrderId(); + uint256 orderId = dex.currentOrderId(); uint256 assetId = asset.mint(assetAmount, ""); dex.createSellOrder(assetId, assetAmount, pricePerAsset); @@ -434,7 +434,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { function testFuzz_cancelSellOrder_satisfiesRequirements(uint256 assetAmount) public { vm.assume(assetAmount > 0); uint256 assetId = asset.mint(assetAmount, ""); - uint256 orderId = dex.getCurrentOrderId(); + uint256 orderId = dex.currentOrderId(); dex.createSellOrder(assetId, assetAmount, 200); vm.expectEmit(true, true, true, true); @@ -447,7 +447,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { } function test_cancelSellOrder_reverts_ifUnauthorized() public { - uint256 orderId = dex.getCurrentOrderId(); + uint256 orderId = dex.currentOrderId(); uint256 assetId = asset.mint(100, ""); dex.createSellOrder(assetId, 100, 200); @@ -468,7 +468,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { assetIds[i] = asset.mint(assetAmounts[i], ""); } - uint256 orderId = dex.getCurrentOrderId(); + uint256 orderId = dex.currentOrderId(); uint256[] memory orderIds = dex.createBatchSellOrder( assetIds, @@ -492,7 +492,7 @@ contract OrderbookDexTest is CTest, ERC1155Holder { } function test_cancelBatchSellOrder_reverts_ifUnauthorized() public { - uint256 orderId = dex.getCurrentOrderId(); + uint256 orderId = dex.currentOrderId(); uint256 assetId = asset.mint(100, ""); dex.createSellOrder(assetId, 100, 200); diff --git a/packages/contracts/evm-contracts/test/OrderbookDexInvariant.t.sol b/packages/contracts/evm-contracts/test/OrderbookDexInvariant.t.sol index 1d1dc08c5..ddfed6dc9 100644 --- a/packages/contracts/evm-contracts/test/OrderbookDexInvariant.t.sol +++ b/packages/contracts/evm-contracts/test/OrderbookDexInvariant.t.sol @@ -109,7 +109,7 @@ contract OrderbookDexHandler is CTest, ERC1155Holder { asset.setApprovalForAll(address(dex), true); // Take note of the previous order id - previousOrderId = dex.getCurrentOrderId(); + previousOrderId = dex.currentOrderId(); // Execute the sell order creation return dex.createSellOrder(assetId, assetAmount, price); @@ -291,7 +291,7 @@ contract OrderbookDexHandler is CTest, ERC1155Holder { ) public useActor(actorIndexSeed) { // If the order does not exist, get the last order id if (dex.getOrder(orderId).assetAmount == 0) { - orderId = dex.getCurrentOrderId(); + orderId = dex.currentOrderId(); // If there are no orders, return if (orderId == 0) { return; @@ -358,7 +358,7 @@ contract OrderbookDexInvariantTest is CTest, ERC1155Holder { function setUp() public { asset = new InverseAppProjected1155("Gold", "GOLD", address(this)); - dex = new OrderbookDex(asset); + dex = new OrderbookDex(address(asset)); dexHandler = new OrderbookDexHandler(asset, dex); assetHandler = new AssetHandler(asset, dex); targetContract(address(assetHandler)); @@ -366,7 +366,7 @@ contract OrderbookDexInvariantTest is CTest, ERC1155Holder { } function invariant_ordersAssetAmountEqualsContractTokenBalance() public { - for (uint256 i; i < dex.getCurrentOrderId(); ++i) { + for (uint256 i; i < dex.currentOrderId(); ++i) { IOrderbookDex.Order memory order = dex.getOrder(i); assertEq(order.assetAmount, asset.balanceOf(address(dex), order.assetId)); } @@ -377,7 +377,7 @@ contract OrderbookDexInvariantTest is CTest, ERC1155Holder { } function invariant_orderIdIsIncremental() public { - uint256 currentId = dex.getCurrentOrderId(); + uint256 currentId = dex.currentOrderId(); uint256 previousId = dexHandler.previousOrderId(); if (currentId == previousId) { assertEq(currentId, 0); From d788e364ddfb05cf45dcf7b0b5a58873434cb502 Mon Sep 17 00:00:00 2001 From: Matej Poklemba Date: Thu, 11 Apr 2024 16:17:36 +0200 Subject: [PATCH 18/18] Run prettier --- .../evm-contracts/test-lib/StdInvariant.sol | 6 ++- .../evm-contracts/test-lib/ctest.sol | 38 +++++++++++++++---- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/packages/contracts/evm-contracts/test-lib/StdInvariant.sol b/packages/contracts/evm-contracts/test-lib/StdInvariant.sol index fd9d0a1db..124a8666b 100644 --- a/packages/contracts/evm-contracts/test-lib/StdInvariant.sol +++ b/packages/contracts/evm-contracts/test-lib/StdInvariant.sol @@ -74,7 +74,11 @@ abstract contract StdInvariant { targetedArtifacts_ = _targetedArtifacts; } - function targetArtifactSelectors() public view returns (FuzzSelector[] memory targetedArtifactSelectors_) { + function targetArtifactSelectors() + public + view + returns (FuzzSelector[] memory targetedArtifactSelectors_) + { targetedArtifactSelectors_ = _targetedArtifactSelectors; } diff --git a/packages/contracts/evm-contracts/test-lib/ctest.sol b/packages/contracts/evm-contracts/test-lib/ctest.sol index dd052a1f1..ff47d2d9a 100644 --- a/packages/contracts/evm-contracts/test-lib/ctest.sol +++ b/packages/contracts/evm-contracts/test-lib/ctest.sol @@ -588,16 +588,24 @@ contract CTest is StdInvariant { } function console2_log(string memory p0, uint256 p1) private view { - (bool status,) = address(CONSOLE2_ADDRESS).staticcall(abi.encodeWithSignature("log(string,uint256)", p0, p1)); + (bool status, ) = address(CONSOLE2_ADDRESS).staticcall( + abi.encodeWithSignature("log(string,uint256)", p0, p1) + ); status; } function console2_log(string memory p0, string memory p1) private view { - (bool status,) = address(CONSOLE2_ADDRESS).staticcall(abi.encodeWithSignature("log(string,string)", p0, p1)); + (bool status, ) = address(CONSOLE2_ADDRESS).staticcall( + abi.encodeWithSignature("log(string,string)", p0, p1) + ); status; } - function _bound(uint256 x, uint256 min, uint256 max) internal pure virtual returns (uint256 result) { + function _bound( + uint256 x, + uint256 min, + uint256 max + ) internal pure virtual returns (uint256 result) { require(min <= max, "StdUtils bound(uint256,uint256,uint256): Max is less than min."); // If x is between min and max, return x directly. This is to ensure that dictionary values // do not get shifted if the min is nonzero. More info: https://github.com/foundry-rs/forge-std/issues/188 @@ -624,12 +632,20 @@ contract CTest is StdInvariant { } } - function bound(uint256 x, uint256 min, uint256 max) internal view virtual returns (uint256 result) { + function bound( + uint256 x, + uint256 min, + uint256 max + ) internal view virtual returns (uint256 result) { result = _bound(x, min, max); console2_log("Bound Result", result); } - function _bound(int256 x, int256 min, int256 max) internal pure virtual returns (int256 result) { + function _bound( + int256 x, + int256 min, + int256 max + ) internal pure virtual returns (int256 result) { require(min <= max, "StdUtils bound(int256,int256,int256): Max is less than min."); // Shifting all int256 values to uint256 to use _bound function. The range of two types are: @@ -640,13 +656,19 @@ contract CTest is StdInvariant { // If the given integer value is -2**255, we cannot use `-uint256(-x)` because of the overflow. // So, use `~uint256(x) + 1` instead. uint256 _x = x < 0 ? (INT256_MIN_ABS - ~uint256(x) - 1) : (uint256(x) + INT256_MIN_ABS); - uint256 _min = min < 0 ? (INT256_MIN_ABS - ~uint256(min) - 1) : (uint256(min) + INT256_MIN_ABS); - uint256 _max = max < 0 ? (INT256_MIN_ABS - ~uint256(max) - 1) : (uint256(max) + INT256_MIN_ABS); + uint256 _min = min < 0 + ? (INT256_MIN_ABS - ~uint256(min) - 1) + : (uint256(min) + INT256_MIN_ABS); + uint256 _max = max < 0 + ? (INT256_MIN_ABS - ~uint256(max) - 1) + : (uint256(max) + INT256_MIN_ABS); uint256 y = _bound(_x, _min, _max); // To move it back to int256 value, subtract INT256_MIN_ABS at here. - result = y < INT256_MIN_ABS ? int256(~(INT256_MIN_ABS - y) + 1) : int256(y - INT256_MIN_ABS); + result = y < INT256_MIN_ABS + ? int256(~(INT256_MIN_ABS - y) + 1) + : int256(y - INT256_MIN_ABS); } function bound(int256 x, int256 min, int256 max) internal view virtual returns (int256 result) {