From 02c39c9dbb2aa404969fb6dde4e84526a778a484 Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Wed, 3 Nov 2021 09:32:31 -0700 Subject: [PATCH 01/83] dex selector with curve and univ3 implementations --- contracts/interfaces/ICurve.sol | 29 ++ .../interfaces/ICurvePoolRegistration.sol | 13 + contracts/interfaces/IDexRecords.sol | 10 + contracts/interfaces/IUniswapV3SwapRouter.sol | 52 ++++ contracts/interfaces/uniswapQuoter.sol | 15 + contracts/mixins/BytesLib.sol | 121 ++++++++ contracts/mixins/Path.sol | 74 +++++ .../modules/LoanOpenings/LoanOpenings.sol | 1 - .../modules/SwapsExternal/SwapsExternal.sol | 1 - .../CurvePoolRegistration.sol | 21 ++ contracts/swaps/DexRecords.sol | 17 ++ contracts/swaps/ISwapsImpl.sol | 11 +- contracts/swaps/SwapsUser.sol | 26 +- .../swaps/connectors/SwapsImplCurve_ETH.sol | 266 ++++++++++++++++++ .../connectors/SwapsImplUniswapV3_ETH.sol | 222 +++++++++++++++ 15 files changed, 861 insertions(+), 18 deletions(-) create mode 100644 contracts/interfaces/ICurve.sol create mode 100644 contracts/interfaces/ICurvePoolRegistration.sol create mode 100644 contracts/interfaces/IDexRecords.sol create mode 100644 contracts/interfaces/IUniswapV3SwapRouter.sol create mode 100644 contracts/interfaces/uniswapQuoter.sol create mode 100644 contracts/mixins/BytesLib.sol create mode 100644 contracts/mixins/Path.sol create mode 100644 contracts/swaps/CurvePoolRegistration/CurvePoolRegistration.sol create mode 100644 contracts/swaps/DexRecords.sol create mode 100644 contracts/swaps/connectors/SwapsImplCurve_ETH.sol create mode 100644 contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol diff --git a/contracts/interfaces/ICurve.sol b/contracts/interfaces/ICurve.sol new file mode 100644 index 00000000..1f3a3b68 --- /dev/null +++ b/contracts/interfaces/ICurve.sol @@ -0,0 +1,29 @@ +pragma solidity 0.5.17; + +interface ICurve { + function exchange( + uint256 i, + uint256 j, + uint256 dx, + uint256 min_dy + ) external; + function exchange_underlying( + uint256 i, + uint256 j, + uint256 dx, + uint256 min_dy + ) external; + function get_dy( + uint256 i, + uint256 j, + uint256 dx + ) external returns (uint256); + function get_dy_underlying( + uint256 i, + uint256 j, + uint256 dx + ) external returns (uint256); + function underlying_coins( + uint256 + ) external returns (address); +} diff --git a/contracts/interfaces/ICurvePoolRegistration.sol b/contracts/interfaces/ICurvePoolRegistration.sol new file mode 100644 index 00000000..4dacc132 --- /dev/null +++ b/contracts/interfaces/ICurvePoolRegistration.sol @@ -0,0 +1,13 @@ +pragma solidity 0.5.17; + +interface ICurvePoolRegistration { + function addPool( + address tokenPool, + address[] calldata tokensInPool, + uint128[] calldata tokenIDs + ) external; + + function disablePool(address tokenPool) external; + + function CheckPoolValidity(address pool) external view returns (bool); +} diff --git a/contracts/interfaces/IDexRecords.sol b/contracts/interfaces/IDexRecords.sol new file mode 100644 index 00000000..04c17873 --- /dev/null +++ b/contracts/interfaces/IDexRecords.sol @@ -0,0 +1,10 @@ +pragma solidity 0.5.17; + +interface IDexRecords { + function retrieveDexAddress(uint256 dexNumber) + external + view + returns (address); + + function setDexID(address dexAddress) external; +} diff --git a/contracts/interfaces/IUniswapV3SwapRouter.sol b/contracts/interfaces/IUniswapV3SwapRouter.sol new file mode 100644 index 00000000..c73fcc1e --- /dev/null +++ b/contracts/interfaces/IUniswapV3SwapRouter.sol @@ -0,0 +1,52 @@ +pragma solidity 0.5.17; +pragma experimental ABIEncoderV2; + +interface IUniswapV3SwapRouter { + struct ExactInputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + uint160 sqrtPriceLimitX96; + } + struct ExactInputParams { + bytes path; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + } + struct ExactOutputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 deadline; + uint256 amountOut; + uint256 amountInMaximum; + uint160 sqrtPriceLimitX96; + } + struct ExactOutputParams { + bytes path; + address recipient; + uint256 deadline; + uint256 amountOut; + uint256 amountInMaximum; + } + + function exactInput(ExactInputParams calldata params) + external + returns (uint256 amountOut); + + function exactOutput(ExactOutputParams calldata params) + external + returns (uint256 amountIn); + + function multicall(bytes[] calldata data) + external + payable + returns (bytes[] memory results); +} diff --git a/contracts/interfaces/uniswapQuoter.sol b/contracts/interfaces/uniswapQuoter.sol new file mode 100644 index 00000000..1102842d --- /dev/null +++ b/contracts/interfaces/uniswapQuoter.sol @@ -0,0 +1,15 @@ +pragma solidity 0.5.17; + +interface uniswapQuoter { + function quoteExactInput(bytes calldata path, uint256 amountIn) + external + returns ( + uint256 amountOut + ); + + function quoteExactOutput(bytes calldata path, uint256 amountOut) + external + returns ( + uint256 amountIn + ); +} diff --git a/contracts/mixins/BytesLib.sol b/contracts/mixins/BytesLib.sol new file mode 100644 index 00000000..f99dc064 --- /dev/null +++ b/contracts/mixins/BytesLib.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * @title Solidity Bytes Arrays Utils + * @author Gonçalo Sá + * + * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. + * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. + */ +pragma solidity >=0.5.17; + +library BytesLib { + function slice( + bytes memory _bytes, + uint256 _start, + uint256 _length + ) internal pure returns (bytes memory) { + require(_length + 31 >= _length, "slice_overflow"); + require(_start + _length >= _start, "slice_overflow"); + require(_bytes.length >= _start + _length, "slice_outOfBounds"); + + bytes memory tempBytes; + + assembly { + switch iszero(_length) + case 0 { + // Get a location of some free memory and store it in tempBytes as + // Solidity does for memory variables. + tempBytes := mload(0x40) + + // The first word of the slice result is potentially a partial + // word read from the original array. To read it, we calculate + // the length of that partial word and start copying that many + // bytes into the array. The first word we copy will start with + // data we don't care about, but the last `lengthmod` bytes will + // land at the beginning of the contents of the new array. When + // we're done copying, we overwrite the full first word with + // the actual length of the slice. + let lengthmod := and(_length, 31) + + // The multiplication in the next line is necessary + // because when slicing multiples of 32 bytes (lengthmod == 0) + // the following copy loop was copying the origin's length + // and then ending prematurely not copying everything it should. + let mc := add( + add(tempBytes, lengthmod), + mul(0x20, iszero(lengthmod)) + ) + let end := add(mc, _length) + + for { + // The multiplication in the next line has the same exact purpose + // as the one above. + let cc := add( + add( + add(_bytes, lengthmod), + mul(0x20, iszero(lengthmod)) + ), + _start + ) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + mstore(tempBytes, _length) + + //update free-memory pointer + //allocating the array padded to 32 bytes like the compiler does now + mstore(0x40, and(add(mc, 31), not(31))) + } + //if we want a zero-length slice let's just return a zero-length array + default { + tempBytes := mload(0x40) + //zero out the 32 bytes slice we are about to return + //we need to do it because Solidity does not garbage collect + mstore(tempBytes, 0) + + mstore(0x40, add(tempBytes, 0x20)) + } + } + + return tempBytes; + } + + function toAddress(bytes memory _bytes, uint256 _start) + internal + pure + returns (address) + { + require(_start + 20 >= _start, "toAddress_overflow"); + require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); + address tempAddress; + + assembly { + tempAddress := div( + mload(add(add(_bytes, 0x20), _start)), + 0x1000000000000000000000000 + ) + } + + return tempAddress; + } + + function toUint24(bytes memory _bytes, uint256 _start) + internal + pure + returns (uint24) + { + require(_start + 3 >= _start, "toUint24_overflow"); + require(_bytes.length >= _start + 3, "toUint24_outOfBounds"); + uint24 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x3), _start)) + } + + return tempUint; + } +} diff --git a/contracts/mixins/Path.sol b/contracts/mixins/Path.sol new file mode 100644 index 00000000..650742b3 --- /dev/null +++ b/contracts/mixins/Path.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.17; + +import "./BytesLib.sol"; + +/// @title Functions for manipulating path data for multihop swaps +library Path { + using BytesLib for bytes; + + /// @dev The length of the bytes encoded address + uint256 private constant ADDR_SIZE = 20; + /// @dev The length of the bytes encoded fee + uint256 private constant FEE_SIZE = 3; + + /// @dev The offset of a single token address and pool fee + uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE; + /// @dev The offset of an encoded pool key + uint256 private constant POP_OFFSET = NEXT_OFFSET + ADDR_SIZE; + /// @dev The minimum length of an encoding that contains 2 or more pools + uint256 private constant MULTIPLE_POOLS_MIN_LENGTH = + POP_OFFSET + NEXT_OFFSET; + + /// @notice Returns true iff the path contains two or more pools + /// @param path The encoded swap path + /// @return True if path contains two or more pools, otherwise false + function hasMultiplePools(bytes memory path) internal pure returns (bool) { + return path.length >= MULTIPLE_POOLS_MIN_LENGTH; + } + + /// @notice Returns the number of pools in the path + /// @param path The encoded swap path + /// @return The number of pools in the path + function numPools(bytes memory path) internal pure returns (uint256) { + // Ignore the first token address. From then on every fee and token offset indicates a pool. + return ((path.length - ADDR_SIZE) / NEXT_OFFSET); + } + + /// @notice Decodes the first pool in path + /// @param path The bytes encoded swap path + /// @return tokenA The first token of the given pool + /// @return tokenB The second token of the given pool + /// @return fee The fee level of the pool + function decodeFirstPool(bytes memory path) + internal + pure + returns ( + address tokenA, + address tokenB, + uint24 fee + ) + { + tokenA = path.toAddress(0); + fee = path.toUint24(ADDR_SIZE); + tokenB = path.toAddress(NEXT_OFFSET); + } + + /// @notice Gets the segment corresponding to the first pool in the path + /// @param path The bytes encoded swap path + /// @return The segment containing all data necessary to target the first pool in the path + function getFirstPool(bytes memory path) + internal + pure + returns (bytes memory) + { + return path.slice(0, POP_OFFSET); + } + + /// @notice Skips a token + fee element from the buffer and returns the remainder + /// @param path The swap path + /// @return The remaining token + fee elements in the path + function skipToken(bytes memory path) internal pure returns (bytes memory) { + return path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET); + } +} diff --git a/contracts/modules/LoanOpenings/LoanOpenings.sol b/contracts/modules/LoanOpenings/LoanOpenings.sol index 4fe14988..1078079a 100644 --- a/contracts/modules/LoanOpenings/LoanOpenings.sol +++ b/contracts/modules/LoanOpenings/LoanOpenings.sol @@ -114,7 +114,6 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse uint256 interestRate, uint256 newPrincipal) external - view returns (uint256 value) { if (loanTokenSent < newPrincipal) { diff --git a/contracts/modules/SwapsExternal/SwapsExternal.sol b/contracts/modules/SwapsExternal/SwapsExternal.sol index 087790fe..6ba2522c 100644 --- a/contracts/modules/SwapsExternal/SwapsExternal.sol +++ b/contracts/modules/SwapsExternal/SwapsExternal.sol @@ -117,7 +117,6 @@ contract SwapsExternal is State, VaultController, SwapsUser, PausableGuardian { address destToken, uint256 sourceTokenAmount) external - view returns (uint256) { return _swapsExpectedReturn( diff --git a/contracts/swaps/CurvePoolRegistration/CurvePoolRegistration.sol b/contracts/swaps/CurvePoolRegistration/CurvePoolRegistration.sol new file mode 100644 index 00000000..026ddd06 --- /dev/null +++ b/contracts/swaps/CurvePoolRegistration/CurvePoolRegistration.sol @@ -0,0 +1,21 @@ +pragma solidity 0.5.17; + +import "@openzeppelin-2.5.0/ownership/Ownable.sol"; + +contract CurvePoolRegistration is Ownable { + mapping(address => bool) public validPool; + + function addPool( + address tokenPool + ) public { + validPool[tokenPool] = true; + } + + function disablePool(address tokenPool) public { + validPool[tokenPool] = false; + } + + function CheckPoolValidity(address pool) public view returns (bool) { + return validPool[pool]; + } +} diff --git a/contracts/swaps/DexRecords.sol b/contracts/swaps/DexRecords.sol new file mode 100644 index 00000000..f4e81e33 --- /dev/null +++ b/contracts/swaps/DexRecords.sol @@ -0,0 +1,17 @@ +pragma solidity 0.5.17; + +import "@openzeppelin-2.5.0/ownership/Ownable.sol"; + +contract DexRecords is Ownable { + mapping(uint256 => address) public dexes; + uint256 public dexCount = 0; + + function retrieveDexAddress(uint256 number) public view returns (address) { + return dexes[number]; + } + + function setDexID(address dex) public onlyOwner() { + dexCount++; + dexes[dexCount] = dex; + } +} diff --git a/contracts/swaps/ISwapsImpl.sol b/contracts/swaps/ISwapsImpl.sol index 2b1fff15..178c77e7 100644 --- a/contracts/swaps/ISwapsImpl.sol +++ b/contracts/swaps/ISwapsImpl.sol @@ -14,7 +14,8 @@ interface ISwapsImpl { address returnToSenderAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount) + uint256 requiredDestTokenAmount, + bytes calldata payload) external returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed); @@ -27,19 +28,15 @@ interface ISwapsImpl { returns (uint256); function dexAmountOut( - address sourceTokenAddress, - address destTokenAddress, + bytes calldata route, uint256 amountIn) external - view returns (uint256 amountOut, address midToken); function dexAmountIn( - address sourceTokenAddress, - address destTokenAddress, + bytes calldata route, uint256 amountOut) external - view returns (uint256 amountIn, address midToken); function setSwapApprovals( diff --git a/contracts/swaps/SwapsUser.sol b/contracts/swaps/SwapsUser.sol index 44697499..5b407689 100644 --- a/contracts/swaps/SwapsUser.sol +++ b/contracts/swaps/SwapsUser.sol @@ -10,6 +10,7 @@ import "../../interfaces/IPriceFeeds.sol"; import "../events/SwapsEvents.sol"; import "../mixins/FeesHelper.sol"; import "./ISwapsImpl.sol"; +import "../interfaces/IDexRecords.sol"; contract SwapsUser is State, SwapsEvents, FeesHelper { @@ -125,10 +126,10 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { require(vals[0] <= vals[1], "min greater than max"); } - require(loanDataBytes.length == 0, "invalid state"); (destTokenAmountReceived, sourceTokenAmountUsed) = _swapsCall_internal( addrs, - vals + vals, + loanDataBytes ); if (vals[2] == 0) { @@ -160,19 +161,28 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { function _swapsCall_internal( address[5] memory addrs, - uint256[3] memory vals) + uint256[3] memory vals, + bytes memory loanDataBytes) internal returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) { + address swapImplAddress = address(0); + if(loanDataBytes.length==0){ + swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress(1); //if nothing specified, default to first dex option available. ensure it does not require any input data or else this will break + }else{ + (uint256 DexNumber, bytes memory loanDataBytes) = abi.decode(loanDataBytes,(uint256,bytes)); + swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress(DexNumber); + } bytes memory data = abi.encodeWithSelector( - ISwapsImpl(swapsImpl).dexSwap.selector, + ISwapsImpl(swapImplAddress).dexSwap.selector, addrs[0], // sourceToken addrs[1], // destToken addrs[2], // receiverAddress addrs[3], // returnToSenderAddress vals[0], // minSourceTokenAmount vals[1], // maxSourceTokenAmount - vals[2] // requiredDestTokenAmount + vals[2], // requiredDestTokenAmount + loanDataBytes ); bool success; @@ -194,7 +204,6 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { address destToken, uint256 sourceTokenAmount) internal - view returns (uint256 expectedReturn) { uint256 tradingFee = _getTradingFee(sourceTokenAmount); @@ -202,10 +211,9 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { sourceTokenAmount = sourceTokenAmount .sub(tradingFee); } - + (expectedReturn,) = ISwapsImpl(swapsImpl).dexAmountOut( - sourceToken, - destToken, + abi.encode(sourceToken, destToken), sourceTokenAmount ); } diff --git a/contracts/swaps/connectors/SwapsImplCurve_ETH.sol b/contracts/swaps/connectors/SwapsImplCurve_ETH.sol new file mode 100644 index 00000000..e694ce64 --- /dev/null +++ b/contracts/swaps/connectors/SwapsImplCurve_ETH.sol @@ -0,0 +1,266 @@ +/** + * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0. + */ + +pragma solidity 0.5.17; +pragma experimental ABIEncoderV2; +import "../../core/State.sol"; +import "@openzeppelin-2.5.0/token/ERC20/SafeERC20.sol"; +import "../ISwapsImpl.sol"; +import "../../interfaces/ICurve.sol"; +import "../../mixins/Path.sol"; +import "../../interfaces/ICurvePoolRegistration.sol"; + +contract SwapsImplCurve_ETH is State, ISwapsImpl { + using SafeERC20 for IERC20; + using Path for bytes; + using BytesLib for bytes; + address public constant PoolRegistry = address(0); //set to address for monitoring Curve pools + bytes4 public constant ExchangeUnderlyingSig = bytes4(keccak256("exchange_underlying(uint256,uint256,uint256,uint256)")); + bytes4 public constant ExchangeSig = bytes4(keccak256("exchange(uint256,uint256,uint256,uint256)")); + bytes4 public constant GetDySig = bytes4(keccak256("get_dy(uint256,uint256,uint256)")); + bytes4 public constant GetDyUnderlyingSig = bytes4(keccak256("get_dy_underlying(uint256,uint256,uint256)")); + function dexSwap( + address sourceTokenAddress, + address destTokenAddress, + address receiverAddress, + address returnToSenderAddress, + uint256 minSourceTokenAmount, + uint256 maxSourceTokenAmount, + uint256 requiredDestTokenAmount, + bytes memory payload + ) + public + + returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) + { + require(sourceTokenAddress != destTokenAddress, "source == dest"); + require( + supportedTokens[sourceTokenAddress] && + supportedTokens[destTokenAddress], + "invalid tokens" + ); + + IERC20 sourceToken = IERC20(sourceTokenAddress); + address _thisAddress = address(this); + (sourceTokenAmountUsed, destTokenAmountReceived) = _swapWithCurve( + sourceTokenAddress, + destTokenAddress, + receiverAddress, + minSourceTokenAmount, + maxSourceTokenAmount, + requiredDestTokenAmount, + payload + ); + + if ( + returnToSenderAddress != _thisAddress && + sourceTokenAmountUsed < maxSourceTokenAmount + ) { + // send unused source token back + sourceToken.safeTransfer( + returnToSenderAddress, + maxSourceTokenAmount - sourceTokenAmountUsed + ); + } + } + + function dexExpectedRate( + address sourceTokenAddress, + address destTokenAddress, + uint256 sourceTokenAmount + ) public view returns (uint256 expectedRate) { + revert("unsupported"); + } + + function dexAmountOut(bytes memory route, uint256 amountIn) + public + + returns (uint256 amountOut, address midToken) + { + if (amountIn == 0) { + amountOut = 0; + } else if (amountIn != 0) { + amountOut = _getAmountOut(amountIn, route); + } + } + + function dexAmountIn(bytes memory route, uint256 amountOut) + public + + returns (uint256 amountIn, address midToken) + { + if (amountOut != 0) { + amountIn = _getAmountIn(amountOut, route); + + if (amountIn == uint256(-1)) { + amountIn = 0; + } + } else { + amountIn = 0; + } + } + + function _getAmountOut(uint256 amountIn, bytes memory path) + public + returns (uint256) + { + (bytes4 sig, address curvePool, uint128 tokenIn, uint128 tokenOut) = abi.decode( + path, + (bytes4, address, uint128, uint128) + ); + uint256 amountOut; + if(sig == GetDySig || sig == ExchangeSig){ + amountOut = ICurve(curvePool).get_dy( + tokenOut, + tokenIn, + amountIn + ); + }else if(sig == GetDyUnderlyingSig || sig == ExchangeUnderlyingSig){ + amountOut = ICurve(curvePool).get_dy_underlying( + tokenOut, + tokenIn, + amountIn + ); + }else{ + revert("Unsupported Signature"); + } + if (amountOut == 0) { + amountOut = uint256(-1); + } + return amountOut; + } + + function _getAmountIn(uint256 amountOut, bytes memory path) + public + returns (uint256) + { + (bytes4 sig, address curvePool, uint128 tokenIn, uint128 tokenOut) = abi.decode( + path, + (bytes4, address, uint128, uint128) + ); + uint256 amountIn; + if(sig == GetDySig || sig == ExchangeSig){ + amountIn = ICurve(curvePool).get_dy( + tokenOut, + tokenIn, + amountOut + ); + }else if(sig == GetDyUnderlyingSig || sig == ExchangeUnderlyingSig){ + amountIn = ICurve(curvePool).get_dy_underlying( + tokenOut, + tokenIn, + amountOut + ); + }else{ + revert("Unsupported Signature"); + } + if (amountIn == 0) { + amountIn = uint256(-1); + } + return amountIn; + } + + function setSwapApprovals(address[] memory tokens) public { + require( + ICurvePoolRegistration(PoolRegistry).CheckPoolValidity(tokens[0]) + ); + for (uint256 i = 1; i < tokens.length; i++) { + IERC20(tokens[i]).safeApprove(tokens[0], 0); + IERC20(tokens[i]).safeApprove(tokens[0], uint256(-1)); + } + } + + function _getDexNumber(address pool, uint256 tokenID) + internal + returns (address) + { + return ICurve(pool).underlying_coins(tokenID); + } + + function _swapWithCurve( + address sourceTokenAddress, + address destTokenAddress, + address receiverAddress, + uint256 minSourceTokenAmount, + uint256 sourceTokenAmount, + uint256 requiredDestTokenAmount, + bytes memory payload + ) + internal + returns (uint256 sourceTokenAmountUsed, uint256 destTokenAmountReceived) + { + if (requiredDestTokenAmount != 0) { + (bytes4 sig, address curvePool, uint128 tokenIn, uint128 tokenOut) = abi.decode( + payload, + (bytes4, address, uint128, uint128) + ); + require( + ICurvePoolRegistration(PoolRegistry).CheckPoolValidity( + curvePool + ) + ); + require(sourceTokenAddress == _getDexNumber(curvePool, tokenIn)); + require(destTokenAddress == _getDexNumber(curvePool, tokenOut)); + (uint256 amountIn, ) = dexAmountIn( + payload, + requiredDestTokenAmount + ); + require( + amountIn >= minSourceTokenAmount && + amountIn <= sourceTokenAmount + ); + if(sig==ExchangeUnderlyingSig){ + ICurve(curvePool).exchange_underlying(tokenIn, tokenOut, amountIn, 1); + }else if(sig==ExchangeSig){ + ICurve(curvePool).exchange_underlying(tokenIn, tokenOut, amountIn, 1); + }else{ + revert("Unsupported Signature"); + } + if (receiverAddress != address(this)) { + IERC20(destTokenAddress).safeTransfer( + receiverAddress, + requiredDestTokenAmount + ); + } + sourceTokenAmountUsed = amountIn; + destTokenAmountReceived = requiredDestTokenAmount; + } else { + (bytes4 sig, address curvePool, uint128 tokenIn, uint128 tokenOut) = abi.decode( + payload, + (bytes4, address, uint128, uint128) + ); + require( + ICurvePoolRegistration(PoolRegistry).CheckPoolValidity( + curvePool + ) + ); + require(sourceTokenAddress == _getDexNumber(curvePool, tokenIn)); + require(destTokenAddress == _getDexNumber(curvePool, tokenOut)); + (uint256 recv, ) = dexAmountOut(payload, minSourceTokenAmount); + if(sig == ExchangeUnderlyingSig){ + ICurve(curvePool).exchange_underlying( + tokenIn, + tokenOut, + minSourceTokenAmount, + 1 + ); + }else if(sig == ExchangeSig){ + ICurve(curvePool).exchange( + tokenIn, + tokenOut, + minSourceTokenAmount, + 1 + ); + }else{ + revert("Unsupported Signature"); + } + if (receiverAddress != address(this)) { + IERC20(destTokenAddress).safeTransfer(receiverAddress, recv); + } + sourceTokenAmountUsed = minSourceTokenAmount; + destTokenAmountReceived = recv; + } + } +} diff --git a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol new file mode 100644 index 00000000..1933d38f --- /dev/null +++ b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol @@ -0,0 +1,222 @@ +/** + * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0. + */ + +pragma solidity 0.5.17; +pragma experimental ABIEncoderV2; +import "../../core/State.sol"; +import "@openzeppelin-2.5.0/token/ERC20/SafeERC20.sol"; +import "../ISwapsImpl.sol"; +import "../../interfaces/IUniswapV3SwapRouter.sol"; +import "../../interfaces/uniswapQuoter.sol"; +import "../../mixins/Path.sol"; + +contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { + using SafeERC20 for IERC20; + using Path for bytes; + using BytesLib for bytes; + address public constant uniswapSwapRouter = + 0xE592427A0AEce92De3Edee1F18E0157C05861564; //mainnet + address public constant uniswapQuoteContract = + 0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6; //mainnet + + function dexSwap( + address sourceTokenAddress, + address destTokenAddress, + address receiverAddress, + address returnToSenderAddress, + uint256 minSourceTokenAmount, + uint256 maxSourceTokenAmount, + uint256 requiredDestTokenAmount, + bytes memory payload + ) + public + + returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) + { + require(sourceTokenAddress != destTokenAddress, "source == dest"); + require( + supportedTokens[sourceTokenAddress] && + supportedTokens[destTokenAddress], + "invalid tokens" + ); + + IERC20 sourceToken = IERC20(sourceTokenAddress); + address _thisAddress = address(this); + (sourceTokenAmountUsed, destTokenAmountReceived) = _swapWithUni( + sourceTokenAddress, + destTokenAddress, + receiverAddress, + maxSourceTokenAmount, + requiredDestTokenAmount, + payload + ); + + if ( + returnToSenderAddress != _thisAddress && + sourceTokenAmountUsed < maxSourceTokenAmount + ) { + // send unused source token back + sourceToken.safeTransfer( + returnToSenderAddress, + maxSourceTokenAmount - sourceTokenAmountUsed + ); + } + } + + function dexExpectedRate( + address sourceTokenAddress, + address destTokenAddress, + uint256 sourceTokenAmount + ) public view returns (uint256 expectedRate) { + revert("unsupported"); + } + + function dexAmountOut(bytes memory route, uint256 amountIn) + public + + returns (uint256 amountOut, address midToken) + { + if (amountIn == 0) { + amountOut = 0; + } else if (amountIn != 0) { + amountOut = _getAmountOut(amountIn, route); + } + } + + function dexAmountIn(bytes memory route, uint256 amountOut) + public + + returns (uint256 amountIn, address midToken) + { + if (amountOut != 0) { + amountIn = _getAmountIn(amountOut, route); + + if (amountIn == uint256(-1)) { + amountIn = 0; + } + } else { + amountIn = 0; + } + } + + function _getAmountOut(uint256 amountIn, bytes memory path) + public + returns (uint256) + { + uint256 amountOut = uniswapQuoter(uniswapQuoteContract) + .quoteExactInput(path, amountIn); + if (amountOut == 0) { + amountOut = uint256(-1); + } + return amountOut; + } + + function _getAmountIn(uint256 amountOut, bytes memory path) + public + returns (uint256) + { + uint256 amountIn = uniswapQuoter(uniswapQuoteContract) + .quoteExactOutput(path, amountOut); + if (amountIn == 0) { + amountIn = uint256(-1); + } + return amountIn; + } + + function setSwapApprovals(address[] memory tokens) public { + for (uint256 i = 0; i < tokens.length; i++) { + IERC20(tokens[i]).safeApprove(uniswapSwapRouter, 0); + IERC20(tokens[i]).safeApprove(uniswapSwapRouter, uint256(-1)); + } + } + + function _swapWithUni( + address sourceTokenAddress, + address destTokenAddress, + address receiverAddress, + uint256 sourceTokenAmount, + uint256 requiredDestTokenAmount, + bytes memory payload + ) + internal + returns (uint256 sourceTokenAmountUsed, uint256 destTokenAmountReceived) + { + if (requiredDestTokenAmount != 0) { + IUniswapV3SwapRouter.ExactOutputParams[] memory exactParams = abi + .decode(payload, (IUniswapV3SwapRouter.ExactOutputParams[])); + bytes[] memory encodedTXs = new bytes[](exactParams.length); + uint256 totalAmountsOut = 0; + uint256 totalAmountsInMax = 0; + for (uint256 x = 0; x < exactParams.length; x++) { + require(receiverAddress == exactParams[x].recipient); + address tokenIn = exactParams[x].path.toAddress(0); + require(tokenIn == destTokenAddress, "improper destination"); + require( + exactParams[x].path.toAddress( + exactParams[x].path.length - 20 + ) == sourceTokenAddress, + "improper source" + ); + totalAmountsOut = totalAmountsOut + exactParams[x].amountOut; + totalAmountsInMax = + totalAmountsInMax + + exactParams[x].amountInMaximum; + encodedTXs[x] = abi.encodeWithSelector( + IUniswapV3SwapRouter(uniswapSwapRouter) + .exactOutput + .selector, + exactParams[x] + ); + } + require( + totalAmountsOut == requiredDestTokenAmount && + totalAmountsInMax <= sourceTokenAmount + ); + bytes[] memory trueAmountsIn = IUniswapV3SwapRouter( + uniswapSwapRouter + ).multicall(encodedTXs); + uint256 totaledAmountIn = 0; + for (uint256 x = 0; x < trueAmountsIn.length; x++) { + uint256 tempAmountIn = abi.decode(trueAmountsIn[x], (uint256)); + totaledAmountIn = totaledAmountIn + tempAmountIn; + } + sourceTokenAmountUsed = totaledAmountIn; + destTokenAmountReceived = requiredDestTokenAmount; + } else { + IUniswapV3SwapRouter.ExactInputParams[] memory exactParams = abi + .decode(payload, (IUniswapV3SwapRouter.ExactInputParams[])); + bytes[] memory encodedTXs = new bytes[](exactParams.length); + uint256 totalAmounts = 0; + for (uint256 x = 0; x < exactParams.length; x++) { + require(receiverAddress == exactParams[x].recipient); + address tokenIn = exactParams[x].path.toAddress(0); + require(tokenIn == sourceTokenAddress, "improper route"); + address tokenOut = exactParams[x].path.toAddress( + exactParams[x].path.length - 20 + ); + require(tokenOut == destTokenAddress, "improper destination"); + totalAmounts = totalAmounts + exactParams[x].amountIn; + encodedTXs[x] = abi.encodeWithSelector( + IUniswapV3SwapRouter(uniswapSwapRouter).exactInput.selector, + exactParams[x] + ); + } + sourceTokenAmountUsed = totalAmounts; + require(totalAmounts == sourceTokenAmount, "improper swap amounts"); + bytes[] memory trueAmountsOut = IUniswapV3SwapRouter( + uniswapSwapRouter + ).multicall(encodedTXs); + uint256 totaledAmountOut = 0; + for (uint256 x = 0; x < trueAmountsOut.length; x++) { + uint256 tempAmountOut = abi.decode( + trueAmountsOut[x], + (uint256) + ); + totaledAmountOut = totaledAmountOut + tempAmountOut; + } + destTokenAmountReceived = totaledAmountOut; + } + } +} From cbfb1e04a5b4d5b8eafe2a5eec1f1de3a2b8c5f5 Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Sun, 14 Nov 2021 00:51:54 -0800 Subject: [PATCH 02/83] Implementation fixes --- .../modules/LoanOpenings/LoanOpenings.sol | 6 +- .../modules/SwapsExternal/SwapsExternal.sol | 6 +- contracts/swaps/SwapsUser.sol | 66 +++++++++++++------ 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/contracts/modules/LoanOpenings/LoanOpenings.sol b/contracts/modules/LoanOpenings/LoanOpenings.sol index 1078079a..c9b5d3a9 100644 --- a/contracts/modules/LoanOpenings/LoanOpenings.sol +++ b/contracts/modules/LoanOpenings/LoanOpenings.sol @@ -112,7 +112,8 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse uint256 loanTokenSent, uint256 collateralTokenSent, uint256 interestRate, - uint256 newPrincipal) + uint256 newPrincipal, + bytes calldata payload) external returns (uint256 value) { @@ -138,7 +139,8 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse loanToken, collateralToken, loanTokenSent - .sub(interestAmountRequired) + .sub(interestAmountRequired), + payload ); if (value != 0) { return collateralTokenSent diff --git a/contracts/modules/SwapsExternal/SwapsExternal.sol b/contracts/modules/SwapsExternal/SwapsExternal.sol index 6ba2522c..44e72895 100644 --- a/contracts/modules/SwapsExternal/SwapsExternal.sol +++ b/contracts/modules/SwapsExternal/SwapsExternal.sol @@ -115,14 +115,16 @@ contract SwapsExternal is State, VaultController, SwapsUser, PausableGuardian { function getSwapExpectedReturn( address sourceToken, address destToken, - uint256 sourceTokenAmount) + uint256 sourceTokenAmount, + bytes calldata payload) external returns (uint256) { return _swapsExpectedReturn( sourceToken, destToken, - sourceTokenAmount + sourceTokenAmount, + payload ); } } \ No newline at end of file diff --git a/contracts/swaps/SwapsUser.sol b/contracts/swaps/SwapsUser.sol index 5b407689..255fa913 100644 --- a/contracts/swaps/SwapsUser.sol +++ b/contracts/swaps/SwapsUser.sol @@ -4,6 +4,7 @@ */ pragma solidity 0.5.17; +pragma experimental ABIEncoderV2; import "../core/State.sol"; import "../../interfaces/IPriceFeeds.sol"; @@ -166,27 +167,40 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { internal returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) { - address swapImplAddress = address(0); + bytes memory data; + address swapImplAddress; if(loanDataBytes.length==0){ swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress(1); //if nothing specified, default to first dex option available. ensure it does not require any input data or else this will break + data = abi.encodeWithSelector( + ISwapsImpl(swapImplAddress).dexSwap.selector, + addrs[0], // sourceToken + addrs[1], // destToken + addrs[2], // receiverAddress + addrs[3], // returnToSenderAddress + vals[0], // minSourceTokenAmount + vals[1], // maxSourceTokenAmount + vals[2], // requiredDestTokenAmount + loanDataBytes + ); }else{ - (uint256 DexNumber, bytes memory loanDataBytes) = abi.decode(loanDataBytes,(uint256,bytes)); + (uint256 DexNumber, bytes memory SwapData) = abi.decode(loanDataBytes,(uint256,bytes)); swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress(DexNumber); + data = abi.encodeWithSelector( + ISwapsImpl(swapImplAddress).dexSwap.selector, + addrs[0], // sourceToken + addrs[1], // destToken + addrs[2], // receiverAddress + addrs[3], // returnToSenderAddress + vals[0], // minSourceTokenAmount + vals[1], // maxSourceTokenAmount + vals[2], // requiredDestTokenAmount + SwapData + ); } - bytes memory data = abi.encodeWithSelector( - ISwapsImpl(swapImplAddress).dexSwap.selector, - addrs[0], // sourceToken - addrs[1], // destToken - addrs[2], // receiverAddress - addrs[3], // returnToSenderAddress - vals[0], // minSourceTokenAmount - vals[1], // maxSourceTokenAmount - vals[2], // requiredDestTokenAmount - loanDataBytes - ); + bool success; - (success, data) = swapsImpl.delegatecall(data); + (success, data) = swapImplAddress.delegatecall(data); if (!success) { assembly { let ptr := mload(0x40) @@ -202,7 +216,8 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { function _swapsExpectedReturn( address sourceToken, address destToken, - uint256 sourceTokenAmount) + uint256 sourceTokenAmount, + bytes memory payload) internal returns (uint256 expectedReturn) { @@ -211,11 +226,22 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { sourceTokenAmount = sourceTokenAmount .sub(tradingFee); } - - (expectedReturn,) = ISwapsImpl(swapsImpl).dexAmountOut( - abi.encode(sourceToken, destToken), - sourceTokenAmount - ); + if(payload.length==0){ + address swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress(1); //default dex address + bytes memory dataToSend = abi.encode(sourceToken, destToken); + (expectedReturn,) = ISwapsImpl(swapImplAddress).dexAmountOut( + dataToSend, + sourceTokenAmount + ); + }else{ + (uint256 DexNumber, bytes memory dataToSend) = abi.decode(payload,(uint256,bytes)); + address swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress(DexNumber); + (expectedReturn,) = ISwapsImpl(swapImplAddress).dexAmountOut( + dataToSend, + sourceTokenAmount + ); + } + } function _checkSwapSize( From 03739d992fcb3c5913b5414864da2aa44e88c42e Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Wed, 8 Dec 2021 18:42:48 -0800 Subject: [PATCH 03/83] Init commit --- contracts/orderbook/Enumerates/EnumLimits.sol | 79 +++ contracts/orderbook/Enumerates/EnumOrders.sol | 79 +++ .../orderbook/Enumerates/EnumTraders.sol | 79 +++ contracts/orderbook/IWalletFactor.sol | 42 ++ .../orderbook/Keepers/IUniswapV2Router.sol | 38 ++ .../orderbook/Keepers/OrderBookInterface.sol | 46 ++ contracts/orderbook/Keepers/OrderKeeper.sol | 89 +++ .../orderbook/Keepers/OrderKeeperClear.sol | 50 ++ contracts/orderbook/Logic/OrderBook.sol | 573 ++++++++++++++++++ contracts/orderbook/Logic/OrderBookData.sol | 102 ++++ .../orderbook/Logic/OrderBookMarketOrders.sol | 136 +++++ .../orderbook/Logic/OrderBookOrderPlace.sol | 277 +++++++++ .../orderbook/Logic/UniswapInterfaces.sol | 23 + contracts/orderbook/Logic/dexSwaps.sol | 21 + contracts/orderbook/OrderBookProxy.sol | 55 ++ contracts/orderbook/OrderVault/Deposits.sol | 54 ++ contracts/orderbook/OrderVault/IDeposits.sol | 22 + .../orderbook/Storage/OrderBookEvents.sol | 43 ++ .../orderbook/Storage/OrderBookStorage.sol | 23 + contracts/orderbook/WrappedToken.sol | 5 + contracts/orderbook/bZxInterfaces/IBZX.sol | 83 +++ .../orderbook/bZxInterfaces/ILoanToken.sol | 20 + .../orderbook/bZxInterfaces/IPriceFeeds.sol | 75 +++ contracts/utils/ExponentMath.sol | 15 + 24 files changed, 2029 insertions(+) create mode 100644 contracts/orderbook/Enumerates/EnumLimits.sol create mode 100644 contracts/orderbook/Enumerates/EnumOrders.sol create mode 100644 contracts/orderbook/Enumerates/EnumTraders.sol create mode 100644 contracts/orderbook/IWalletFactor.sol create mode 100644 contracts/orderbook/Keepers/IUniswapV2Router.sol create mode 100644 contracts/orderbook/Keepers/OrderBookInterface.sol create mode 100644 contracts/orderbook/Keepers/OrderKeeper.sol create mode 100644 contracts/orderbook/Keepers/OrderKeeperClear.sol create mode 100644 contracts/orderbook/Logic/OrderBook.sol create mode 100644 contracts/orderbook/Logic/OrderBookData.sol create mode 100644 contracts/orderbook/Logic/OrderBookMarketOrders.sol create mode 100644 contracts/orderbook/Logic/OrderBookOrderPlace.sol create mode 100644 contracts/orderbook/Logic/UniswapInterfaces.sol create mode 100644 contracts/orderbook/Logic/dexSwaps.sol create mode 100644 contracts/orderbook/OrderBookProxy.sol create mode 100644 contracts/orderbook/OrderVault/Deposits.sol create mode 100644 contracts/orderbook/OrderVault/IDeposits.sol create mode 100644 contracts/orderbook/Storage/OrderBookEvents.sol create mode 100644 contracts/orderbook/Storage/OrderBookStorage.sol create mode 100644 contracts/orderbook/WrappedToken.sol create mode 100644 contracts/orderbook/bZxInterfaces/IBZX.sol create mode 100644 contracts/orderbook/bZxInterfaces/ILoanToken.sol create mode 100644 contracts/orderbook/bZxInterfaces/IPriceFeeds.sol create mode 100644 contracts/utils/ExponentMath.sol diff --git a/contracts/orderbook/Enumerates/EnumLimits.sol b/contracts/orderbook/Enumerates/EnumLimits.sol new file mode 100644 index 00000000..3ee2c0dd --- /dev/null +++ b/contracts/orderbook/Enumerates/EnumLimits.sol @@ -0,0 +1,79 @@ +pragma solidity ^0.8.4; + +library OrderRecords { + struct orderSet { + mapping(uint256 => uint256) idx; + uint256[] vals; + } + + function addOrderNum(orderSet storage hist, uint256 val) + internal + returns (bool) + { + if (inVals(hist, val) == false) { + hist.vals.push(val); + hist.idx[val] = length(hist); + return true; + } else { + return false; + } + } + + function removeOrderNum(orderSet storage hist, uint256 val) + internal + returns (bool) + { + if (inVals(hist, val)) { + uint256 delIdx = hist.idx[val] - 1; + uint256 lastIdx = hist.vals.length - 1; + if (delIdx != lastIdx) { + uint256 tempVal = hist.vals[lastIdx]; + hist.vals[delIdx] = tempVal; + hist.idx[tempVal] = delIdx + 1; + } + delete hist.idx[val]; + hist.vals.pop(); + return true; + } else { + return false; + } + } + + function inVals(orderSet storage hist, uint256 val) + internal + view + returns (bool) + { + return hist.idx[val] != 0; + } + + function length(orderSet storage hist) internal view returns (uint256) { + return hist.vals.length; + } + + function get(orderSet storage hist, uint256 idx) + internal + view + returns (uint256) + { + return hist.vals[idx]; + } + + function enums( + orderSet storage hist, + uint256 start, + uint256 len + ) internal view returns (uint256[] memory out) { + uint256 stop = start + len; + require(stop >= start); + stop = hist.vals.length < stop ? hist.vals.length : stop; + if (start >= stop || stop == 0) { + return out; + } + out = new uint256[](stop - start); + for (uint256 i = start; i < stop; i++) { + out[i - start] = hist.vals[i]; + } + return out; + } +} diff --git a/contracts/orderbook/Enumerates/EnumOrders.sol b/contracts/orderbook/Enumerates/EnumOrders.sol new file mode 100644 index 00000000..1c5064f0 --- /dev/null +++ b/contracts/orderbook/Enumerates/EnumOrders.sol @@ -0,0 +1,79 @@ +pragma solidity ^0.8.4; + +library OrderEntry { + struct orderSet { + mapping(bytes32 => uint256) idx; + bytes32[] vals; + } + + function addTrade(orderSet storage hist, bytes32 val) + internal + returns (bool) + { + if (inVals(hist, val) == false) { + hist.vals.push(val); + hist.idx[val] = length(hist); + return true; + } else { + return false; + } + } + + function removeTrade(orderSet storage hist, bytes32 val) + internal + returns (bool) + { + if (inVals(hist, val)) { + uint256 delIdx = hist.idx[val] - 1; + uint256 lastIdx = hist.vals.length - 1; + if (delIdx != lastIdx) { + bytes32 tempVal = hist.vals[lastIdx]; + hist.vals[delIdx] = tempVal; + hist.idx[tempVal] = delIdx + 1; + } + delete hist.idx[val]; + hist.vals.pop(); + return true; + } else { + return false; + } + } + + function inVals(orderSet storage hist, bytes32 val) + internal + view + returns (bool) + { + return hist.idx[val] != 0; + } + + function length(orderSet storage hist) internal view returns (uint256) { + return hist.vals.length; + } + + function get(orderSet storage hist, uint256 idx) + internal + view + returns (bytes32) + { + return hist.vals[idx]; + } + + function enums( + orderSet storage hist, + uint256 start, + uint256 len + ) internal view returns (bytes32[] memory out) { + uint256 stop = start + len; + require(stop >= start); + stop = hist.vals.length < stop ? hist.vals.length : stop; + if (start >= stop || stop == 0) { + return out; + } + out = new bytes32[](stop - start); + for (uint256 i = start; i < stop; i++) { + out[i - start] = hist.vals[i]; + } + return out; + } +} diff --git a/contracts/orderbook/Enumerates/EnumTraders.sol b/contracts/orderbook/Enumerates/EnumTraders.sol new file mode 100644 index 00000000..97478ee2 --- /dev/null +++ b/contracts/orderbook/Enumerates/EnumTraders.sol @@ -0,0 +1,79 @@ +pragma solidity ^0.8.4; + +library ActiveTraders { + struct orderSet { + mapping(address => uint256) idx; + address[] vals; + } + + function addTrader(orderSet storage hist, address val) + internal + returns (bool) + { + if (inVals(hist, val) == false) { + hist.vals.push(val); + hist.idx[val] = length(hist); + return true; + } else { + return false; + } + } + + function removeTrader(orderSet storage hist, address val) + internal + returns (bool) + { + if (inVals(hist, val)) { + uint256 delIdx = hist.idx[val] - 1; + uint256 lastIdx = hist.vals.length - 1; + if (delIdx != lastIdx) { + address tempVal = hist.vals[lastIdx]; + hist.vals[delIdx] = tempVal; + hist.idx[tempVal] = delIdx + 1; + } + delete hist.idx[val]; + hist.vals.pop(); + return true; + } else { + return false; + } + } + + function inVals(orderSet storage hist, address val) + internal + view + returns (bool) + { + return hist.idx[val] != 0; + } + + function length(orderSet storage hist) internal view returns (uint256) { + return hist.vals.length; + } + + function get(orderSet storage hist, uint256 idx) + internal + view + returns (address) + { + return hist.vals[idx]; + } + + function enums( + orderSet storage hist, + uint256 start, + uint256 len + ) internal view returns (address[] memory out) { + uint256 stop = start + len; + require(stop >= start); + stop = hist.vals.length < stop ? hist.vals.length : stop; + if (start >= stop || stop == 0) { + return out; + } + out = new address[](stop - start); + for (uint256 i = start; i < stop; i++) { + out[i - start] = hist.vals[i]; + } + return out; + } +} diff --git a/contracts/orderbook/IWalletFactor.sol b/contracts/orderbook/IWalletFactor.sol new file mode 100644 index 00000000..39878f22 --- /dev/null +++ b/contracts/orderbook/IWalletFactor.sol @@ -0,0 +1,42 @@ +pragma solidity ^0.8.4; + +interface IWalletFactory { + enum OrderType { + LIMIT_OPEN, + LIMIT_CLOSE, + MARKET_STOP + } + struct OpenOrder { + address trader; + bytes32 loanID; + address iToken; + address loanTokenAddress; + uint256 price; + uint256 leverage; + uint256 loanTokenAmount; + uint256 collateralTokenAmount; + bool isActive; + address base; + OrderType orderType; + bool isCollateral; + uint256 orderID; + bytes loanData; + } + + struct OrderQueue { + address trader; + uint256 orderID; + } + + function getRouter() external view returns (address); + + function placeOrder(OpenOrder calldata Order) external; + + function amendOrder(OpenOrder calldata Order, uint256 orderID) external; + + function cancelOrder(uint256 orderID) external; + + function getSwapAddress() external view returns (address); + + function getFeed() external view returns (address); +} diff --git a/contracts/orderbook/Keepers/IUniswapV2Router.sol b/contracts/orderbook/Keepers/IUniswapV2Router.sol new file mode 100644 index 00000000..72632b99 --- /dev/null +++ b/contracts/orderbook/Keepers/IUniswapV2Router.sol @@ -0,0 +1,38 @@ +/** + * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0. + */ + +pragma solidity ^0.8.4; + +interface uniswapV2Router { + // 0x38ed1739 + function swapExactTokensForTokens( + uint256 amountIn, + uint256 amountOutMin, + address[] calldata path, + address to, + uint256 deadline + ) external returns (uint256[] memory amounts); + + // 0x8803dbee + function swapTokensForExactTokens( + uint256 amountOut, + uint256 amountInMax, + address[] calldata path, + address to, + uint256 deadline + ) external returns (uint256[] memory amounts); + + // 0x1f00ca74 + function getAmountsIn(uint256 amountOut, address[] calldata path) + external + view + returns (uint256[] memory amounts); + + // 0xd06ca61f + function getAmountsOut(uint256 amountIn, address[] calldata path) + external + view + returns (uint256[] memory amounts); +} diff --git a/contracts/orderbook/Keepers/OrderBookInterface.sol b/contracts/orderbook/Keepers/OrderBookInterface.sol new file mode 100644 index 00000000..38c56c74 --- /dev/null +++ b/contracts/orderbook/Keepers/OrderBookInterface.sol @@ -0,0 +1,46 @@ +pragma solidity ^0.8.4; + +interface IOrderBook { + function getOrders(uint256 start, uint256 count) + external + view + returns (OpenOrder[] memory); + + function prelimCheck(address trader, uint256 orderID) + external + view + returns (bool); + + function executeOrder( + address payable keeper, + address trader, + uint256 orderID + ) external; + function cancelOrderProtocol(address trader, uint256 orderID) external; + function clearOrder(address trader, uint256 orderID) + external + view + returns (bool); + function getTotalActiveOrders() external view returns (uint256); + + struct OpenOrder { + address trader; + bytes32 loanID; + address iToken; + address loanTokenAddress; + uint256 price; + uint256 leverage; + uint256 loanTokenAmount; + uint256 collateralTokenAmount; + bool isActive; + address base; + uint256 orderType; + bool isCollateral; + uint256 orderID; + bytes loanData; + } + struct OrderQueue { + address trader; + uint256 orderID; + } +} diff --git a/contracts/orderbook/Keepers/OrderKeeper.sol b/contracts/orderbook/Keepers/OrderKeeper.sol new file mode 100644 index 00000000..db74f86a --- /dev/null +++ b/contracts/orderbook/Keepers/OrderKeeper.sol @@ -0,0 +1,89 @@ +pragma solidity ^0.8.4; +import "./OrderBookInterface.sol"; +import "./IUniswapV2Router.sol"; +import "../WrappedToken.sol"; +import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; + +contract OrderKeeper { + address factory; + address constant LINK = 0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39; + address constant WETH = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; + address swapAddress = 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506; + + constructor(address factoryAddress) { + factory = factoryAddress; + } + + function checkUpkeep(bytes calldata checkData) + public + view + returns (bool upkeepNeeded, bytes memory performData) + { + IOrderBook.OpenOrder[] memory listOfMainOrders = IOrderBook(factory) + .getOrders(0, IOrderBook(factory).getTotalActiveOrders()); + for (uint256 x = 0; x < listOfMainOrders.length; x++) { + if ( + IOrderBook(factory).prelimCheck( + listOfMainOrders[x].trader, + listOfMainOrders[x].orderID + ) == true + ) { + upkeepNeeded = true; + performData = abi.encode( + listOfMainOrders[x].trader, + listOfMainOrders[x].orderID + ); + return (upkeepNeeded, performData); + } + } + return (upkeepNeeded, performData); + } + + function performUpkeep(bytes calldata performData) public { + (address trader, uint256 orderId) = abi.decode( + performData, + (address, uint256) + ); + //emit OrderExecuted(trader,orderId); + IOrderBook(factory).executeOrder( + payable(address(this)), + trader, + orderId + ); + } + + function handleFees(address[] memory tokenAddress) public { + address[] memory path; + path = new address[](3); + path[1] = WETH; + path[2] = LINK; + for (uint256 x = 0; x < tokenAddress.length; x++) { + if (tokenAddress[x] != WETH) { + path[0] = tokenAddress[x]; + uniswapV2Router(swapAddress).swapExactTokensForTokens( + IERC20Metadata(tokenAddress[x]).balanceOf(address(this)), + 1, + path, + address(this), + block.timestamp + ); + } else { + address[] memory pathWETH; + pathWETH = new address[](2); + pathWETH[0] = WETH; + pathWETH[1] = LINK; + uniswapV2Router(swapAddress).swapExactTokensForTokens( + IERC20Metadata(tokenAddress[x]).balanceOf(address(this)), + 1, + pathWETH, + address(this), + block.timestamp + ); + } + } + } + + function handleETHFees() public { + WrappedToken(WETH).deposit{value: address(this).balance}(); + } +} diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol new file mode 100644 index 00000000..5e90cf7f --- /dev/null +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -0,0 +1,50 @@ +pragma solidity ^0.8.4; +import "./OrderBookInterface.sol"; +import "./IUniswapV2Router.sol"; +import "../WrappedToken.sol"; +import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; + +contract OrderKeeperClear { + address factory; + + constructor(address factoryAddress) { + factory = factoryAddress; + } + + function checkUpkeep(bytes calldata checkData) + public + view + returns (bool upkeepNeeded, bytes memory performData) + { + IOrderBook.OpenOrder[] memory listOfMainOrders = IOrderBook(factory) + .getOrders(0, IOrderBook(factory).getTotalActiveOrders()); + for (uint256 x = 0; x < listOfMainOrders.length; x++) { + if ( + IOrderBook(factory).clearOrder( + listOfMainOrders[x].trader, + listOfMainOrders[x].orderID + ) == true + ) { + upkeepNeeded = true; + performData = abi.encode( + listOfMainOrders[x].trader, + listOfMainOrders[x].orderID + ); + return (upkeepNeeded, performData); + } + } + return (upkeepNeeded, performData); + } + + function performUpkeep(bytes calldata performData) public { + (address trader, uint256 orderId) = abi.decode( + performData, + (address, uint256) + ); + //emit OrderExecuted(trader,orderId); + IOrderBook(factory).cancelOrderProtocol( + trader, + orderId + ); + } +} diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol new file mode 100644 index 00000000..b60e8db1 --- /dev/null +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -0,0 +1,573 @@ +pragma solidity ^0.8.4; +import "../Storage/OrderBookEvents.sol"; +import "../Storage/OrderBookStorage.sol"; +import "./dexSwaps.sol"; +import "./UniswapInterfaces.sol"; +import "../OrderVault/IDeposits.sol"; +import "../../utils/ExponentMath.sol"; + +contract OrderBook is OrderBookEvents, OrderBookStorage { + using ExponentMath for uint256; + function executeTradeOpen( + address trader, + uint256 orderID, + address keeper, + address usedToken + ) internal returns (uint256 success) { + IWalletFactory.OpenOrder memory internalOrder = HistoricalOrders[ + trader + ][orderID]; + IDeposits(vault).withdraw(trader, orderID); + SafeERC20.safeTransferFrom( + IERC20(usedToken), + trader, + address(this), + IERC20Metadata(usedToken).balanceOf(trader) + ); + (bool result, bytes memory data) = HistoricalOrders[trader][orderID] + .iToken + .call( + abi.encodeWithSelector( + LoanTokenI(internalOrder.iToken).marginTrade.selector, + internalOrder.loanID, + internalOrder.leverage, + internalOrder.loanTokenAmount, + internalOrder.collateralTokenAmount, + internalOrder.base, + address(this), + internalOrder.loanData + ) + ); + if (result == true) { + (bytes32 loanID, , ) = abi.decode( + data, + (bytes32, uint256, uint256) + ); + if (OrderEntry.inVals(ActiveTrades[trader], loanID) == false) { + OrderEntry.addTrade(ActiveTrades[trader], loanID); + } + } + success = gasleft(); + } + + function executeTradeClose( + address trader, + address payable keeper, + bytes32 loanID, + uint256 amount, + bool iscollateral, + address loanTokenAddress, + address collateralAddress, + uint256 startGas, + bytes memory arbData + ) internal returns (bool success) { + address usedToken; + usedToken = iscollateral ? collateralAddress : loanTokenAddress; + uint256 traderB = IERC20Metadata(usedToken).balanceOf(trader); + SafeERC20.safeTransferFrom( + IERC20(usedToken), + trader, + address(this), + traderB + ); + if (IBZX(bZxRouterAddress).getLoan(loanID).collateral == amount) { + OrderEntry.removeTrade(ActiveTrades[trader], loanID); + } + bZxRouterAddress.call( + abi.encodeWithSelector( + IBZX(bZxRouterAddress).closeWithSwap.selector, + loanID, + address(this), + amount, + iscollateral, + arbData + ) + ); + if (usedToken == wrapToken) { + WrappedToken(wrapToken).deposit{value: address(this).balance}(); + } + uint256 gasUsed = ((startGas - gasleft()) * gasPrice(usedToken)) / + (10**36); + SafeERC20.safeTransfer(IERC20(usedToken), keeper, gasUsed); + SafeERC20.safeTransfer( + IERC20(usedToken), + trader, + IERC20Metadata(usedToken).balanceOf(address(this)) + ); + + success = true; + } + + function getSwapAddress() public view returns (address) { + return StateI(bZxRouterAddress).swapsImpl(); + } + + function currentSwapRate(address start, address end) + public + view + returns (uint256 executionPrice) + { + (executionPrice, ) = IPriceFeeds(getFeed()).queryRate(end, start); + } + + function getFeed() public view returns (address) { + return StateI(bZxRouterAddress).priceFeeds(); + } + + function isActiveLoan(bytes32 ID) internal view returns (bool) { + (, , , , , , , , , , , bool active) = IBZX(bZxRouterAddress).loans(ID); + return active; + } + + function dexSwapRate(IWalletFactory.OpenOrder memory order) + public + view + returns (uint256) + { + uint256 tradeSize; + if (order.orderType == IWalletFactory.OrderType.LIMIT_OPEN) { + if (order.loanTokenAmount > 0) { + tradeSize = (order.loanTokenAmount * order.leverage) / 1 ether; + } else { + (tradeSize, ) = dexSwaps(getSwapAddress()).dexAmountOut( + order.base, + order.loanTokenAddress, + order.collateralTokenAmount + ); + if (tradeSize == 0) { + return 0; + } + tradeSize = (tradeSize * order.leverage) / 1 ether; + } + } + (uint256 fSwapRate, ) = order.orderType == + IWalletFactory.OrderType.LIMIT_OPEN + ? dexSwaps(getSwapAddress()).dexAmountOut( + order.loanTokenAddress, + order.base, + tradeSize + ) + : dexSwaps(getSwapAddress()).dexAmountOut( + order.base, + order.loanTokenAddress, + order.collateralTokenAmount + ); + if (fSwapRate == 0) { + return 0; + } + return + order.orderType == IWalletFactory.OrderType.LIMIT_OPEN + ? (tradeSize.TenExp(18 - int8(IERC20Metadata(order.loanTokenAddress).decimals())) * 1 ether) / + (fSwapRate.TenExp(18 - int8(IERC20Metadata(order.base).decimals()))) + : (1 ether *(fSwapRate.TenExp(18 - int8(IERC20Metadata(order.loanTokenAddress).decimals())))) / + (order.collateralTokenAmount.TenExp(18 - int8(IERC20Metadata(order.base).decimals()))); + } + + function dexSwapCheck( + uint256 collateralTokenAmount, + uint256 loanTokenAmount, + address loanTokenAddress, + address base, + uint256 leverage, + IWalletFactory.OrderType orderType + ) public view returns (uint256) { + uint256 tradeSize; + if (orderType == IWalletFactory.OrderType.LIMIT_OPEN) { + if (loanTokenAmount > 0) { + tradeSize = (loanTokenAmount * leverage) / 1 ether; + } else { + (tradeSize, ) = dexSwaps(getSwapAddress()).dexAmountOut( + base, + loanTokenAddress, + collateralTokenAmount + ); + if (tradeSize == 0) { + return 0; + } + tradeSize = (tradeSize * leverage) / 1 ether; + } + } + (uint256 fSwapRate, ) = orderType == IWalletFactory.OrderType.LIMIT_OPEN + ? dexSwaps(getSwapAddress()).dexAmountOut( + loanTokenAddress, + base, + tradeSize + ) + : dexSwaps(getSwapAddress()).dexAmountOut( + base, + loanTokenAddress, + collateralTokenAmount + ); + if (fSwapRate == 0) { + return 0; + } + return + orderType == IWalletFactory.OrderType.LIMIT_OPEN + ? (tradeSize.TenExp(18 - int8(IERC20Metadata(loanTokenAddress).decimals())) * + 1 ether) / + (fSwapRate.TenExp(18 - int8(IERC20Metadata(base).decimals()))) + : (1 ether * + (fSwapRate.TenExp(18 - + int8(IERC20Metadata(loanTokenAddress).decimals())))) / + (collateralTokenAmount.TenExp(18 - int8(IERC20Metadata(base).decimals()))); + } + + function gasPrice(address payToken) public view returns (uint256) { + return IPriceFeeds(getFeed()).getFastGasPrice(payToken) * 2; + } + + function clearOrder(address trader, uint256 orderID) + public + view + returns (bool) + { + if(orderExpiration[trader][orderID] < block.timestamp){ + return true; + } + uint256 swapRate = currentSwapRate(HistoricalOrders[trader][orderID].loanTokenAddress, HistoricalOrders[trader][orderID].base); + if(!(HistoricalOrders[trader][orderID].price > swapRate + ? (HistoricalOrders[trader][orderID].price - swapRate) < (HistoricalOrders[trader][orderID].price * 4) / 100 + : (swapRate - HistoricalOrders[trader][orderID].price) < (swapRate * 4) / 100)){ + return true; + } + return false; + } + + function prelimCheck(address trader, uint256 orderID) + public + view + returns (bool) + { + if (orderExpiration[trader][orderID] < block.timestamp) { + return false; + } + if ( + HistoricalOrders[trader][orderID].orderType == + IWalletFactory.OrderType.LIMIT_OPEN + ) { + if ( + HistoricalOrders[trader][orderID].loanID == 0 || + isActiveLoan(HistoricalOrders[trader][orderID].loanID) + ) {} else { + return false; + } + uint256 tAmount = HistoricalOrders[trader][orderID] + .collateralTokenAmount > 0 + ? HistoricalOrders[trader][orderID].collateralTokenAmount + + (gasPrice(HistoricalOrders[trader][orderID].base) * + 2300000) / + 10**36 + : HistoricalOrders[trader][orderID].loanTokenAmount + + (gasPrice( + HistoricalOrders[trader][orderID].loanTokenAddress + ) * 2300000) / + 10**36; + address tokenUsed = HistoricalOrders[trader][orderID] + .collateralTokenAmount > 0 + ? HistoricalOrders[trader][orderID].base + : HistoricalOrders[trader][orderID].loanTokenAddress; + if ( + tAmount > + IERC20Metadata(tokenUsed).balanceOf(trader) + + IDeposits(vault).getDeposit(trader, orderID) + ) { + return false; + } + uint256 dSwapValue = dexSwapCheck( + HistoricalOrders[trader][orderID].collateralTokenAmount, + HistoricalOrders[trader][orderID].loanTokenAmount, + HistoricalOrders[trader][orderID].loanTokenAddress, + HistoricalOrders[trader][orderID].base, + HistoricalOrders[trader][orderID].leverage, + IWalletFactory.OrderType.LIMIT_OPEN + ); + + if ( + HistoricalOrders[trader][orderID].price >= dSwapValue && + dSwapValue > 0 + ) { + return true; + } + } else if ( + HistoricalOrders[trader][orderID].orderType == + IWalletFactory.OrderType.LIMIT_CLOSE + ) { + if (!isActiveLoan(HistoricalOrders[trader][orderID].loanID)) { + return false; + } + uint256 tAmount = HistoricalOrders[trader][orderID].isCollateral + ? (gasPrice(HistoricalOrders[trader][orderID].base) * 1600000) / + 10**36 + : (gasPrice( + HistoricalOrders[trader][orderID].loanTokenAddress + ) * 1600000) / 10**36; + address tokenUsed = HistoricalOrders[trader][orderID].isCollateral + ? HistoricalOrders[trader][orderID].base + : HistoricalOrders[trader][orderID].loanTokenAddress; + if (tAmount > IERC20Metadata(tokenUsed).balanceOf(trader)) { + return false; + } + if ( + HistoricalOrders[trader][orderID].price <= + dexSwapCheck( + HistoricalOrders[trader][orderID].collateralTokenAmount, + HistoricalOrders[trader][orderID].loanTokenAmount, + HistoricalOrders[trader][orderID].loanTokenAddress, + HistoricalOrders[trader][orderID].base, + HistoricalOrders[trader][orderID].leverage, + IWalletFactory.OrderType.LIMIT_CLOSE + ) + ) { + return true; + } + } else { + if (!isActiveLoan(HistoricalOrders[trader][orderID].loanID)) { + return false; + } + uint256 tAmount = HistoricalOrders[trader][orderID].isCollateral + ? (gasPrice(HistoricalOrders[trader][orderID].base) * 1600000) / + 10**36 + : (gasPrice( + HistoricalOrders[trader][orderID].loanTokenAddress + ) * 1600000) / 10**36; + address tokenUsed = HistoricalOrders[trader][orderID].isCollateral + ? HistoricalOrders[trader][orderID].base + : HistoricalOrders[trader][orderID].loanTokenAddress; + if (tAmount > IERC20Metadata(tokenUsed).balanceOf(trader)) { + return false; + } + if ( + HistoricalOrders[trader][orderID].price >= + currentSwapRate( + HistoricalOrders[trader][orderID].base, + HistoricalOrders[trader][orderID].loanTokenAddress + ) + ) { + return true; + } + } + return false; + } + + function currentDexRate(address dest, address src) + public + view + returns (uint256) + { + uint256 dexRate; + if (src == wrapToken || dest == wrapToken) { + address pairAddress = UniswapFactory(UniFactoryContract).getPair( + src, + dest + ); + (uint112 reserve0, uint112 reserve1, ) = UniswapPair(pairAddress) + .getReserves(); + uint256 res0 = uint256(reserve0); + uint256 res1 = uint256(reserve1); + dexRate = UniswapPair(pairAddress).token0() == src + ? (res0.TenExp(18 - + int8(IERC20Metadata(UniswapPair(pairAddress).token0()) + .decimals()) + + 18)) / + (res1.TenExp(18 - + int8(IERC20Metadata( + UniswapPair(pairAddress).token1() + ).decimals()))) + : ((res1.TenExp(18 - + int8(IERC20Metadata(UniswapPair(pairAddress).token1()) + .decimals()) + + 18)) / res0).TenExp(18 - + int8(IERC20Metadata(UniswapPair(pairAddress).token0()) + .decimals())); + } else { + address pairAddress0 = UniswapFactory(UniFactoryContract).getPair( + src, + wrapToken + ); + (uint112 reserve0, uint112 reserve1, ) = UniswapPair(pairAddress0) + .getReserves(); + uint256 res0 = uint256(reserve0); + uint256 res1 = uint256(reserve1); + uint256 midSwapRate = UniswapPair(pairAddress0).token0() == + wrapToken + ? (res1.TenExp(18 - + int8(IERC20Metadata(UniswapPair(pairAddress0).token1()) + .decimals()) + + 18)) / + (res0.TenExp(18 - + int8(IERC20Metadata( + UniswapPair(pairAddress0).token0() + ).decimals()))) + : (res0.TenExp(18 - int8(IERC20Metadata(UniswapPair(pairAddress0).token0()).decimals()) + 18)) / + (res1.TenExp(18 - int8(IERC20Metadata(UniswapPair(pairAddress0).token0()).decimals()))); + address pairAddress1 = UniswapFactory(UniFactoryContract).getPair( + dest, + wrapToken + ); + (uint112 reserve2, uint112 reserve3, ) = UniswapPair(pairAddress1) + .getReserves(); + uint256 res2 = uint256(reserve2); + uint256 res3 = uint256(reserve3); + dexRate = UniswapPair(pairAddress1).token0() == wrapToken + ? ((10**36 / + ((res3.TenExp(18 - + int8(IERC20Metadata( + UniswapPair(pairAddress1).token1() + ).decimals()) + + 18)) / + (res2.TenExp(18 - + int8(IERC20Metadata( + UniswapPair(pairAddress1).token0() + ).decimals()))))) * midSwapRate) / 10**18 + : ((10**36 / + ((res2.TenExp(18 - + int8(IERC20Metadata( + UniswapPair(pairAddress1).token0() + ).decimals()) + + 18)) / + (res3.TenExp(18 - + int8(IERC20Metadata( + UniswapPair(pairAddress1).token1() + ).decimals()))))) * midSwapRate) / 10**18; + } + return dexRate; + } + + function priceCheck(address loanTokenAddress, address base) + public + view + returns (bool) + { + uint256 dexRate = currentDexRate(base, loanTokenAddress); + uint256 indexRate = currentSwapRate(base, loanTokenAddress); + return + dexRate >= indexRate + ? ((dexRate - indexRate) * 1000) / dexRate <= 5 ? true : false + : ((indexRate - dexRate) * 1000) / indexRate <= 5 + ? true + : false; + } + + function executeOrder( + address payable keeper, + address trader, + uint256 orderID + ) public { + uint256 startGas = gasleft(); + require(HistoricalOrders[trader][orderID].isActive, "non active"); + //HistoricalOrders[trader][orderID].collateralTokenAmount > 0 ? checkCollateralAllowance(HistoricalOrders[trader][orderID]) : checkLoanTokenAllowance(HistoricalOrders[trader][orderID]); + if ( + HistoricalOrders[trader][orderID].orderType == + IWalletFactory.OrderType.LIMIT_OPEN + ) { + require( + HistoricalOrders[trader][orderID].price >= + dexSwapRate(HistoricalOrders[trader][orderID]), + "invalid swap rate" + ); + address usedToken = HistoricalOrders[trader][orderID] + .collateralTokenAmount > + HistoricalOrders[trader][orderID].loanTokenAmount + ? HistoricalOrders[trader][orderID].base + : HistoricalOrders[trader][orderID].loanTokenAddress; + + SafeERC20.safeTransfer( + IERC20(usedToken), + keeper, + ((startGas - + executeTradeOpen(trader, orderID, keeper, usedToken)) * + gasPrice(usedToken)) / (10**36) + ); + SafeERC20.safeTransfer( + IERC20(usedToken), + trader, + IERC20Metadata(usedToken).balanceOf(address(this)) + ); + HistoricalOrders[trader][orderID].isActive = false; + OrderRecords.removeOrderNum( + AllOrderIDs, + matchingID[trader][orderID] + ); + OrderRecords.removeOrderNum(HistOrders[trader], orderID); + if (OrderRecords.length(HistOrders[trader]) == 0) { + ActiveTraders.removeTrader(activeTraders, trader); + } + emit OrderExecuted(trader, orderID); + return; + } + if ( + HistoricalOrders[trader][orderID].orderType == + IWalletFactory.OrderType.LIMIT_CLOSE + ) { + require( + HistoricalOrders[trader][orderID].price <= + dexSwapRate(HistoricalOrders[trader][orderID]), + "invalid swap rate" + ); + executeTradeClose( + trader, + keeper, + HistoricalOrders[trader][orderID].loanID, + HistoricalOrders[trader][orderID].collateralTokenAmount, + HistoricalOrders[trader][orderID].isCollateral, + HistoricalOrders[trader][orderID].loanTokenAddress, + HistoricalOrders[trader][orderID].base, + startGas, + HistoricalOrders[trader][orderID].loanData + ); + HistoricalOrders[trader][orderID].isActive = false; + OrderRecords.removeOrderNum( + AllOrderIDs, + matchingID[trader][orderID] + ); + OrderRecords.removeOrderNum(HistOrders[trader], orderID); + if (OrderRecords.length(HistOrders[trader]) == 0) { + ActiveTraders.removeTrader(activeTraders, trader); + } + emit OrderExecuted(trader, orderID); + return; + } + if ( + HistoricalOrders[trader][orderID].orderType == + IWalletFactory.OrderType.MARKET_STOP + ) { + require( + HistoricalOrders[trader][orderID].price >= + currentSwapRate( + HistoricalOrders[trader][orderID].base, + HistoricalOrders[trader][orderID].loanTokenAddress + ) && + priceCheck( + HistoricalOrders[trader][orderID].loanTokenAddress, + HistoricalOrders[trader][orderID].base + ), + "invalid swap rate" + ); + executeTradeClose( + trader, + keeper, + HistoricalOrders[trader][orderID].loanID, + HistoricalOrders[trader][orderID].collateralTokenAmount, + HistoricalOrders[trader][orderID].isCollateral, + HistoricalOrders[trader][orderID].loanTokenAddress, + HistoricalOrders[trader][orderID].base, + startGas, + HistoricalOrders[trader][orderID].loanData + ); + HistoricalOrders[trader][orderID].isActive = false; + OrderRecords.removeOrderNum( + AllOrderIDs, + matchingID[trader][orderID] + ); + OrderRecords.removeOrderNum(HistOrders[trader], orderID); + if (OrderRecords.length(HistOrders[trader]) == 0) { + ActiveTraders.removeTrader(activeTraders, trader); + } + emit OrderExecuted(trader, orderID); + return; + } + } + function setVaultAddress(address nVault) onlyOwner() public{ + vault = nVault; + } +} diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol new file mode 100644 index 00000000..0c6e5b38 --- /dev/null +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -0,0 +1,102 @@ +pragma solidity ^0.8.4; +import "../Storage/OrderBookEvents.sol"; +import "../Storage/OrderBookStorage.sol"; + +contract OrderBookData is OrderBookEvents, OrderBookStorage { + function getRouter() public view returns (address) { + return bZxRouterAddress; + } + + function adjustAllowance(address spender, address token) public { + require( + IBZX(bZxRouterAddress).isLoanPool(spender) || + bZxRouterAddress == spender || vault == spender, + "invalid spender" + ); + IERC20Metadata(token).approve(spender, type(uint256).max); + } + + function getActiveOrders( + address smartWallet, + uint256 start, + uint256 count + ) public view returns (IWalletFactory.OpenOrder[] memory fullList) { + uint256[] memory idSet = OrderRecords.enums( + HistOrders[smartWallet], + start, + count + ); + + fullList = new IWalletFactory.OpenOrder[](idSet.length); + for (uint256 i = 0; i < idSet.length; i++) { + fullList[i] = HistoricalOrders[smartWallet][idSet[i]]; + } + return fullList; + } + + function getOrderByOrderID(address smartWallet, uint256 orderID) + public + view + returns (IWalletFactory.OpenOrder memory) + { + return HistoricalOrders[smartWallet][orderID]; + } + + function getActiveOrderIDs( + address smartWallet, + uint256 start, + uint256 count + ) public view returns (uint256[] memory) { + return OrderRecords.enums(HistOrders[smartWallet], start, count); + } + + function getTotalOrders(address smartWallet) public view returns (uint256) { + return OrderRecords.length(HistOrders[smartWallet]); + } + + function getTradersWithOrders(uint256 start, uint256 count) + public + view + returns (address[] memory) + { + return ActiveTraders.enums(activeTraders, start, count); + } + + function getTotalTradersWithOrders() public view returns (uint256) { + return ActiveTraders.length(activeTraders); + } + + function getTotalActiveOrders() public view returns (uint256) { + return OrderRecords.length(AllOrderIDs); + } + + function getOrders(uint256 start, uint256 count) + public + view + returns (IWalletFactory.OpenOrder[] memory fullList) + { + uint256[] memory idSet = OrderRecords.enums(AllOrderIDs, start, count); + + fullList = new IWalletFactory.OpenOrder[](idSet.length); + for (uint256 i = 0; i < idSet.length; i++) { + fullList[i] = getOrderByOrderID( + AllOrders[idSet[i]].trader, + AllOrders[idSet[i]].orderID + ); + } + return fullList; + } + + function getActiveTrades(address trader) + public + view + returns (bytes32[] memory) + { + return + OrderEntry.enums( + ActiveTrades[trader], + 0, + OrderEntry.length(ActiveTrades[trader]) + ); + } +} diff --git a/contracts/orderbook/Logic/OrderBookMarketOrders.sol b/contracts/orderbook/Logic/OrderBookMarketOrders.sol new file mode 100644 index 00000000..1c29ae50 --- /dev/null +++ b/contracts/orderbook/Logic/OrderBookMarketOrders.sol @@ -0,0 +1,136 @@ +pragma solidity ^0.8.4; +import "../Storage/OrderBookEvents.sol"; +import "../Storage/OrderBookStorage.sol"; + +contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { + function marketOpen( + bytes32 loanID, + uint256 leverage, + uint256 loanTokenAmount, + uint256 collateralTokenAmount, + address iToken, + address base, + bytes memory loanData + ) public { + executeMarketOpen( + msg.sender, + loanID, + leverage, + loanTokenAmount, + collateralTokenAmount, + iToken, + base, + loanData + ); + } + + function marketClose( + bytes32 loanID, + uint256 amount, + bool iscollateral, + address loanTokenAddress, + address collateralAddress, + bytes memory arbData + ) public { + executeMarketClose( + msg.sender, + loanID, + amount, + iscollateral, + loanTokenAddress, + collateralAddress, + arbData + ); + } + + function executeMarketOpen( + address trader, + bytes32 lID, + uint256 leverage, + uint256 loanTokenAmount, + uint256 collateralTokenAmount, + address iToken, + address base, + bytes memory loanData + ) internal { + address usedToken = collateralTokenAmount > loanTokenAmount + ? base + : LoanTokenI(iToken).loanTokenAddress(); + uint256 transferAmount = collateralTokenAmount > loanTokenAmount + ? collateralTokenAmount + : loanTokenAmount; + SafeERC20.safeTransferFrom( + IERC20(usedToken), + trader, + address(this), + transferAmount + ); + loanData = ""; + bytes32 loanID = LoanTokenI(iToken) + .marginTrade( + lID, + leverage, + loanTokenAmount, + collateralTokenAmount, + base, + address(this), + loanData + ) + .LoanId; + if (OrderEntry.inVals(ActiveTrades[trader], loanID) == false) { + OrderEntry.addTrade(ActiveTrades[trader], loanID); + } + } + + function executeMarketClose( + address trader, + bytes32 loanID, + uint256 amount, + bool iscollateral, + address loanTokenAddress, + address collateralAddress, + bytes memory arbData + ) internal { + address usedToken; + arbData = ""; + if ( + (iscollateral == true && collateralAddress != wrapToken) || + (iscollateral == false && loanTokenAddress != wrapToken) + ) { + usedToken = iscollateral ? collateralAddress : loanTokenAddress; + uint256 traderB = IERC20Metadata(usedToken).balanceOf(trader); + SafeERC20.safeTransferFrom( + IERC20(usedToken), + trader, + address(this), + traderB + ); + } else { + usedToken = address(0); + } + if (IBZX(bZxRouterAddress).getLoan(loanID).collateral == amount) { + OrderEntry.removeTrade(ActiveTrades[trader], loanID); + } + IBZX(bZxRouterAddress).closeWithSwap( + loanID, + address(this), + amount, + iscollateral, + arbData + ); + if (usedToken != address(0)) { + SafeERC20.safeTransfer( + IERC20(usedToken), + trader, + IERC20Metadata(usedToken).balanceOf(address(this)) + ); + } else { + WrappedToken(wrapToken).deposit{value: address(this).balance}(); + SafeERC20.safeTransfer( + IERC20(wrapToken), + trader, + IERC20Metadata(wrapToken).balanceOf(address(this)) + ); + } + } +} diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol new file mode 100644 index 00000000..457cdcf4 --- /dev/null +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -0,0 +1,277 @@ +pragma solidity ^0.8.4; +import "../Storage/OrderBookEvents.sol"; +import "../Storage/OrderBookStorage.sol"; +import "../OrderVault/IDeposits.sol"; +contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { + + function currentSwapRate(address start, address end) + public + view + returns (uint256 executionPrice) + { + (executionPrice, ) = IPriceFeeds(StateI(bZxRouterAddress).priceFeeds()) + .queryRate(end, start); + } + + function collateralTokenMatch(IWalletFactory.OpenOrder memory checkOrder) + internal + view + returns (bool) + { + return + IBZX(bZxRouterAddress).getLoan(checkOrder.loanID).collateralToken == + checkOrder.base; + } + + function loanTokenMatch(IWalletFactory.OpenOrder memory checkOrder) + internal + view + returns (bool) + { + return + IBZX(bZxRouterAddress).getLoan(checkOrder.loanID).loanToken == + checkOrder.loanTokenAddress; + } + + function placeOrder(IWalletFactory.OpenOrder memory Order) public { + require( + (Order.loanTokenAmount == 0 && Order.collateralTokenAmount > 0) || + (Order.loanTokenAmount > 0 && Order.collateralTokenAmount == 0), + "only one token can be used" + ); + //uint256 swapRate = currentSwapRate(Order.loanTokenAddress, Order.base); + require(IBZX(bZxRouterAddress).supportedTokens(Order.loanTokenAddress) && IBZX(bZxRouterAddress).supportedTokens(Order.base), "invalid pair"); + /*require( + Order.price > swapRate + ? (Order.price - swapRate) < (Order.price * 4) / 100 + : (swapRate - Order.price) < (swapRate * 4) / 100, + "price too far away" + );*/ + require( + Order.loanID != 0 + ? collateralTokenMatch(Order) && loanTokenMatch(Order) + : true, + "incorrect collateral and/or loan token specified" + ); + require( + Order.orderType == IWalletFactory.OrderType.LIMIT_OPEN + ? Order.loanID == 0 || OrderEntry.inVals(ActiveTrades[msg.sender], Order.loanID) + : OrderEntry.inVals(ActiveTrades[msg.sender], Order.loanID), + "inactive loan" + ); + uint256 amountUsed = Order.loanTokenAmount + + Order.collateralTokenAmount; //one is always 0 so correct amount and no overflow issues + address usedToken = Order.loanTokenAmount > + Order.collateralTokenAmount + ? Order.loanTokenAddress + : Order.base; + require(currentSwapRate(usedToken,USDC)*amountUsed/10**(IERC20Metadata(usedToken).decimals()) > MIN_AMOUNT_IN_USDC); + HistoricalOrderIDs[msg.sender]++; + mainOBID++; + Order.orderID = HistoricalOrderIDs[msg.sender]; + Order.trader = msg.sender; + Order.isActive = true; + Order.loanData = ""; + orderExpiration[msg.sender][Order.orderID] = block.timestamp + DAYS_14; + HistoricalOrders[msg.sender][HistoricalOrderIDs[msg.sender]] = Order; + AllOrders[mainOBID].trader = msg.sender; + AllOrders[mainOBID].orderID = Order.orderID; + OrderRecords.addOrderNum( + HistOrders[msg.sender], + HistoricalOrderIDs[msg.sender] + ); + OrderRecords.addOrderNum(AllOrderIDs, mainOBID); + matchingID[msg.sender][HistoricalOrderIDs[msg.sender]] = mainOBID; + if (ActiveTraders.inVals(activeTraders, msg.sender) == false) { + ActiveTraders.addTrader(activeTraders, msg.sender); + } + if (Order.orderType == IWalletFactory.OrderType.LIMIT_OPEN) { + + + SafeERC20.safeTransferFrom( + IERC20(usedToken), + msg.sender, + address(this), + amountUsed + ); + IDeposits(vault).deposit( + Order.orderID, + amountUsed, + msg.sender, + usedToken + ); + } + emit OrderPlaced( + msg.sender, + Order.orderType, + Order.price, + HistoricalOrderIDs[msg.sender], + Order.base, + Order.loanTokenAddress + ); + } + + function amendOrder(IWalletFactory.OpenOrder memory Order, uint256 orderID) + public + { + require( + (Order.loanTokenAmount == 0 && Order.collateralTokenAmount > 0) || + (Order.loanTokenAmount > 0 && Order.collateralTokenAmount == 0), + "only one token can be used" + ); + //uint256 swapRate = currentSwapRate(Order.loanTokenAddress, Order.base); + require(Order.base==HistoricalOrders[msg.sender][orderID].base&&Order.loanTokenAddress==HistoricalOrders[msg.sender][orderID].loanTokenAddress, "invalid tokens"); + /*require( + Order.price > swapRate + ? (Order.price - swapRate) < (Order.price * 4) / 100 + : (swapRate - Order.price) < (swapRate * 4) / 100, + "price too far away" + );*/ + require(Order.trader == msg.sender, "trader of order != sender"); + require( + Order.orderID == HistoricalOrders[msg.sender][orderID].orderID, + "improper ID" + ); + require( + HistoricalOrders[msg.sender][orderID].isActive == true, + "inactive order specified" + ); + require( + Order.orderType == IWalletFactory.OrderType.LIMIT_OPEN + ? Order.loanID == 0 || OrderEntry.inVals(ActiveTrades[msg.sender], Order.loanID) + : OrderEntry.inVals(ActiveTrades[msg.sender], Order.loanID), + "inactive loan" + ); + orderExpiration[msg.sender][Order.orderID] = block.timestamp + DAYS_14; + Order.loanData = ""; + if (Order.orderType == IWalletFactory.OrderType.LIMIT_OPEN) { + uint256 amountUsed = Order.loanTokenAmount + + Order.collateralTokenAmount; //one is always 0 so correct amount and no overflow issues + address usedToken = Order.loanTokenAmount > + Order.collateralTokenAmount + ? Order.loanTokenAddress + : Order.base; + uint256 storedAmount = IDeposits(vault).getDeposit( + msg.sender, + Order.orderID + ); + require( + usedToken == + IDeposits(vault).getTokenUsed(msg.sender, Order.orderID) + ); + IDeposits(vault).withdraw(msg.sender, Order.orderID); + if (storedAmount > amountUsed) { + IDeposits(vault).deposit( + Order.orderID, + amountUsed, + msg.sender, + usedToken + ); + } else { + SafeERC20.safeTransferFrom( + IERC20(usedToken), + msg.sender, + address(this), + amountUsed - storedAmount + ); + IDeposits(vault).deposit( + Order.orderID, + amountUsed, + msg.sender, + usedToken + ); + } + SafeERC20.safeTransfer( + IERC20(usedToken), + msg.sender, + IERC20Metadata(usedToken).balanceOf(address(this)) + ); + } + HistoricalOrders[msg.sender][orderID] = Order; + + emit OrderAmended( + msg.sender, + Order.orderType, + Order.price, + orderID, + Order.base, + Order.loanTokenAddress + ); + } + + function cancelOrder(uint256 orderID) public { + require( + HistoricalOrders[msg.sender][orderID].isActive == true, + "inactive order" + ); + HistoricalOrders[msg.sender][orderID].isActive = false; + OrderRecords.removeOrderNum(HistOrders[msg.sender], orderID); + OrderRecords.removeOrderNum( + AllOrderIDs, + matchingID[msg.sender][orderID] + ); + if (OrderRecords.length(HistOrders[msg.sender]) == 0) { + ActiveTraders.removeTrader(activeTraders, msg.sender); + } + if ( + HistoricalOrders[msg.sender][orderID].orderType == + IWalletFactory.OrderType.LIMIT_OPEN + ) { + address usedToken = IDeposits(vault).getTokenUsed( + msg.sender, + orderID + ); + IDeposits(vault).withdraw(msg.sender, orderID); + SafeERC20.safeTransfer( + IERC20(usedToken), + msg.sender, + IERC20Metadata(usedToken).balanceOf(address(this)) + ); + } + emit OrderCancelled(msg.sender, orderID); + } + + function cancelOrderProtocol(address trader, uint256 orderID) public { + require( + HistoricalOrders[trader][orderID].isActive == true, + "inactive order" + ); + require(orderExpiration[trader][orderID] < block.timestamp); + uint256 swapRate = currentSwapRate(HistoricalOrders[trader][orderID].loanTokenAddress, HistoricalOrders[trader][orderID].base); + require( + HistoricalOrders[trader][orderID].price > swapRate + ? (HistoricalOrders[trader][orderID].price - swapRate) < (HistoricalOrders[trader][orderID].price * 4) / 100 + : (swapRate - HistoricalOrders[trader][orderID].price) < (swapRate * 4) / 100, + "price too far away" + ); + HistoricalOrders[trader][orderID].isActive = false; + OrderRecords.removeOrderNum(HistOrders[trader], orderID); + OrderRecords.removeOrderNum( + AllOrderIDs, + matchingID[trader][orderID] + ); + if (OrderRecords.length(HistOrders[trader]) == 0) { + ActiveTraders.removeTrader(activeTraders, trader); + } + if ( + HistoricalOrders[trader][orderID].orderType == + IWalletFactory.OrderType.LIMIT_OPEN + ) { + address usedToken = IDeposits(vault).getTokenUsed( + trader, + orderID + ); + IDeposits(vault).withdraw(trader, orderID); + SafeERC20.safeTransfer( + IERC20(usedToken), + trader, + IERC20Metadata(usedToken).balanceOf(address(this)) + ); + } + emit OrderCancelled(trader, orderID); + } + + function minimumAmount() public view returns(uint256){ + return MIN_AMOUNT_IN_USDC; + } +} diff --git a/contracts/orderbook/Logic/UniswapInterfaces.sol b/contracts/orderbook/Logic/UniswapInterfaces.sol new file mode 100644 index 00000000..6391f3bd --- /dev/null +++ b/contracts/orderbook/Logic/UniswapInterfaces.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.8.4; + +interface UniswapFactory { + function getPair(address tokenA, address tokenB) + external + view + returns (address pair); +} + +interface UniswapPair { + function token0() external view returns (address); + + function token1() external view returns (address); + + function getReserves() + external + view + returns ( + uint112 reserve0, + uint112 reserve1, + uint32 blockTimestampLast + ); +} diff --git a/contracts/orderbook/Logic/dexSwaps.sol b/contracts/orderbook/Logic/dexSwaps.sol new file mode 100644 index 00000000..c4e99935 --- /dev/null +++ b/contracts/orderbook/Logic/dexSwaps.sol @@ -0,0 +1,21 @@ +pragma solidity ^0.8.4; + +interface dexSwaps { + function dexExpectedRate( + address sourceTokenAddress, + address destTokenAddress, + uint256 sourceTokenAmount + ) external view virtual returns (uint256); + + function dexAmountOut( + address sourceTokenAddress, + address destTokenAddress, + uint256 amountIn + ) external view virtual returns (uint256 amountOut, address midToken); + + function dexAmountIn( + address sourceTokenAddress, + address destTokenAddress, + uint256 amountOut + ) external view virtual returns (uint256 amountIn, address midToken); +} diff --git a/contracts/orderbook/OrderBookProxy.sol b/contracts/orderbook/OrderBookProxy.sol new file mode 100644 index 00000000..fbd4c303 --- /dev/null +++ b/contracts/orderbook/OrderBookProxy.sol @@ -0,0 +1,55 @@ +pragma solidity ^0.8.4; +import "./Storage/OrderBookStorage.sol"; +import "./Storage/OrderBookEvents.sol"; + +contract OrderBookProxy is OrderBookEvents, OrderBookStorage { + mapping(bytes4 => address) internal implMatch; + + constructor(address bzx) { + bZxRouterAddress = bzx; + } + + fallback() external payable { + if (gasleft() <= 2300) { + return; + } + + address impl = implMatch[msg.sig]; + + bytes memory data = msg.data; + assembly { + let result := delegatecall( + gas(), + impl, + add(data, 0x20), + mload(data), + 0, + 0 + ) + let size := returndatasize() + let ptr := mload(0x40) + returndatacopy(ptr, 0, size) + switch result + case 0 { + revert(ptr, size) + } + default { + return(ptr, size) + } + } + } + + function setTargets(bytes4[] calldata sigs, address[] calldata targets) + public + onlyOwner + { + require(sigs.length == targets.length); + for (uint256 i = 0; i < targets.length; i++) { + implMatch[sigs[i]] = targets[i]; + } + } + + function getTarget(bytes4 sig) public view returns (address) { + return implMatch[sig]; + } +} diff --git a/contracts/orderbook/OrderVault/Deposits.sol b/contracts/orderbook/OrderVault/Deposits.sol new file mode 100644 index 00000000..b20c69b1 --- /dev/null +++ b/contracts/orderbook/OrderVault/Deposits.sol @@ -0,0 +1,54 @@ +pragma solidity ^0.8.4; +import "@openzeppelin-4.3.2/access/Ownable.sol"; +import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; + +contract Deposits is Ownable { + mapping(address => mapping(uint256 => uint256)) storedBalance; + mapping(address => mapping(uint256 => address)) correctTokenAddress; + address public OrderBook = address(0); + function deposit( + uint256 orderID, + uint256 TokenAmount, + address trader, + address token + ) public { + require(msg.sender==OrderBook,"unauthorized"); + storedBalance[trader][orderID] += TokenAmount; + correctTokenAddress[trader][orderID] = token; + SafeERC20.safeTransferFrom( + IERC20(token), + msg.sender, + address(this), + TokenAmount + ); + } + function SetOrderBook(address n) public onlyOwner{ + OrderBook=n; + } + function withdraw(address trader, uint256 orderID) public { + require(msg.sender==OrderBook,"unauthorized"); + SafeERC20.safeTransfer( + IERC20(correctTokenAddress[trader][orderID]), + msg.sender, + storedBalance[trader][orderID] + ); + storedBalance[trader][orderID] = 0; + } + + function getDeposit(address trader, uint256 orderID) + public + view + returns (uint256) + { + return storedBalance[trader][orderID]; + } + + function getTokenUsed(address trader, uint256 orderID) + public + view + returns (address) + { + return correctTokenAddress[trader][orderID]; + } +} diff --git a/contracts/orderbook/OrderVault/IDeposits.sol b/contracts/orderbook/OrderVault/IDeposits.sol new file mode 100644 index 00000000..f7a2f225 --- /dev/null +++ b/contracts/orderbook/OrderVault/IDeposits.sol @@ -0,0 +1,22 @@ +pragma solidity ^0.8.4; + +interface IDeposits { + function deposit( + uint256 orderID, + uint256 TokenAmount, + address trader, + address token + ) external; + + function withdraw(address trader, uint256 orderID) external; + + function getDeposit(address trader, uint256 orderID) + external + view + returns (uint256); + + function getTokenUsed(address trader, uint256 orderID) + external + view + returns (address); +} diff --git a/contracts/orderbook/Storage/OrderBookEvents.sol b/contracts/orderbook/Storage/OrderBookEvents.sol new file mode 100644 index 00000000..c3b0d285 --- /dev/null +++ b/contracts/orderbook/Storage/OrderBookEvents.sol @@ -0,0 +1,43 @@ +pragma solidity ^0.8.4; +import "../Enumerates/EnumLimits.sol"; +import "../Enumerates/EnumTraders.sol"; +import "../Enumerates/EnumOrders.sol"; +import "../IWalletFactor.sol"; +import "@openzeppelin-4.3.2/access/Ownable.sol"; + +contract OrderBookEvents is Ownable { + using OrderRecords for OrderRecords.orderSet; + mapping(address => bool) internal hasSmartWallet; + mapping(address => address) internal smartWalletOwnership; + mapping(address => bool) internal isSmartWallet; + mapping(address => mapping(uint256 => IWalletFactory.OpenOrder)) + internal HistoricalOrders; + mapping(uint256 => IWalletFactory.OrderQueue) AllOrders; + mapping(address => mapping(uint256 => uint256)) internal orderExpiration; + mapping(address => OrderRecords.orderSet) internal HistOrders; + mapping(address => OrderEntry.orderSet) internal ActiveTrades; + mapping(address => uint256) internal HistoricalOrderIDs; + mapping(address => mapping(uint256 => uint256)) internal matchingID; + mapping(bytes32 => address) internal loanIDOwnership; + mapping(address => mapping(address => uint256)) internal allocatedBalances; + OrderRecords.orderSet internal AllOrderIDs; + ActiveTraders.orderSet internal activeTraders; + event OrderCancelled(address indexed smartWallet, uint256 nonce); + event OrderPlaced( + address indexed smartWallet, + IWalletFactory.OrderType indexed OrderType, + uint256 indexed execPrice, + uint256 orderID, + address collateralTokenAddress, + address loanTokenAddress + ); + event OrderExecuted(address indexed smartWallet, uint256 nonce); + event OrderAmended( + address indexed smartWallet, + IWalletFactory.OrderType indexed OrderType, + uint256 indexed execPrice, + uint256 orderID, + address collateralTokenAddress, + address loanTokenAddress + ); +} diff --git a/contracts/orderbook/Storage/OrderBookStorage.sol b/contracts/orderbook/Storage/OrderBookStorage.sol new file mode 100644 index 00000000..66c47640 --- /dev/null +++ b/contracts/orderbook/Storage/OrderBookStorage.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.8.4; + +import "../WrappedToken.sol"; +import "../bZxInterfaces/IPriceFeeds.sol"; +import "../bZxInterfaces/ILoanToken.sol"; +import "../bZxInterfaces/IBZX.sol"; +import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; + +contract OrderBookStorage { + address internal vault = address(0); + address internal bZxRouterAddress = address(0); + address internal walletGen; + address internal constant wrapToken = + 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; + address internal smartWalletLogic; + address internal constant UniFactoryContract = + 0xc35DADB65012eC5796536bD9864eD8773aBc74C4; + uint256 internal mainOBID = 0; + uint256 internal DAYS_14 = 86400 * 14; + uint256 internal MIN_AMOUNT_IN_USDC = 1*10**15; + address internal USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; +} diff --git a/contracts/orderbook/WrappedToken.sol b/contracts/orderbook/WrappedToken.sol new file mode 100644 index 00000000..41fb03ef --- /dev/null +++ b/contracts/orderbook/WrappedToken.sol @@ -0,0 +1,5 @@ +pragma solidity ^0.8.4; + +abstract contract WrappedToken { + function deposit() public payable virtual; +} diff --git a/contracts/orderbook/bZxInterfaces/IBZX.sol b/contracts/orderbook/bZxInterfaces/IBZX.sol new file mode 100644 index 00000000..727c430d --- /dev/null +++ b/contracts/orderbook/bZxInterfaces/IBZX.sol @@ -0,0 +1,83 @@ +pragma solidity ^0.8.4; + +abstract contract IBZX { + enum LoanType { + All, + Margin, + NonMargin + } + struct Loan { + bytes32 id; // id of the loan + bytes32 loanParamsId; // the linked loan params id + bytes32 pendingTradesId; // the linked pending trades id + uint256 principal; // total borrowed amount outstanding + uint256 collateral; // total collateral escrowed for the loan + uint256 startTimestamp; // loan start time + uint256 endTimestamp; // for active loans, this is the expected loan end time, for in-active loans, is the actual (past) end time + uint256 startMargin; // initial margin when the loan opened + uint256 startRate; // reference rate when the loan opened for converting collateralToken to loanToken + address borrower; // borrower of this loan + address lender; // lender of this loan + bool active; // if false, the loan has been fully closed + } + mapping(bytes32 => Loan) public loans; + struct LoanReturnData { + bytes32 loanId; // id of the loan + uint96 endTimestamp; // loan end timestamp + address loanToken; // loan token address + address collateralToken; // collateral token address + uint256 principal; // principal amount of the loan + uint256 collateral; // collateral amount of the loan + uint256 interestOwedPerDay; // interest owned per day + uint256 interestDepositRemaining; // remaining unspent interest + uint256 startRate; // collateralToLoanRate + uint256 startMargin; // margin with which loan was open + uint256 maintenanceMargin; // maintenance margin + uint256 currentMargin; /// current margin + uint256 maxLoanTerm; // maximum term of the loan + uint256 maxLiquidatable; // current max liquidatable + uint256 maxSeizable; // current max seizable + uint256 depositValueAsLoanToken; // net value of deposit denominated as loanToken + uint256 depositValueAsCollateralToken; // net value of deposit denominated as collateralToken + } + + function closeWithSwap( + bytes32 loanId, + address receiver, + uint256 swapAmount, // denominated in collateralToken + bool returnTokenIsCollateral, // true: withdraws collateralToken, false: withdraws loanToken + bytes calldata loanDataBytes + ) + external + virtual + returns ( + uint256 loanCloseAmount, + uint256 withdrawAmount, + address withdrawToken + ); + + function getUserLoans( + address user, + uint256 start, + uint256 count, + LoanType loanType, + bool isLender, + bool unsafeOnly + ) external view virtual returns (LoanReturnData[] memory loansData); + + function getUserLoansCount(address user, bool isLender) + external + view + virtual + returns (uint256); + + function getLoan(bytes32 loanId) + external + view + virtual + returns (LoanReturnData memory loanData); + + function isLoanPool(address loanPool) external view virtual returns (bool); + + function supportedTokens(address Token) external virtual view returns (bool); +} diff --git a/contracts/orderbook/bZxInterfaces/ILoanToken.sol b/contracts/orderbook/bZxInterfaces/ILoanToken.sol new file mode 100644 index 00000000..42e2e981 --- /dev/null +++ b/contracts/orderbook/bZxInterfaces/ILoanToken.sol @@ -0,0 +1,20 @@ +pragma solidity ^0.8.4; + +abstract contract LoanTokenI { + struct LoanOpenData { + bytes32 LoanId; + uint256 principal; + uint256 collateral; + } + address public loanTokenAddress; + + function marginTrade( + bytes32 loanId, + uint256 leverageAmount, + uint256 loanTokenSent, + uint256 collateralTokenSent, + address collateralTokenAddress, + address trader, + bytes memory loanDataBytes + ) external payable virtual returns (LoanOpenData memory); +} diff --git a/contracts/orderbook/bZxInterfaces/IPriceFeeds.sol b/contracts/orderbook/bZxInterfaces/IPriceFeeds.sol new file mode 100644 index 00000000..5484b845 --- /dev/null +++ b/contracts/orderbook/bZxInterfaces/IPriceFeeds.sol @@ -0,0 +1,75 @@ +pragma solidity ^0.8.4; + +interface IPriceFeeds { + function queryRate(address sourceToken, address destToken) + external + view + returns (uint256 rate, uint256 precision); + + function queryPrecision(address sourceToken, address destToken) + external + view + returns (uint256 precision); + + function queryReturn( + address sourceToken, + address destToken, + uint256 sourceAmount + ) external view returns (uint256 destAmount); + + function checkPriceDisagreement( + address sourceToken, + address destToken, + uint256 sourceAmount, + uint256 destAmount, + uint256 maxSlippage + ) external view returns (uint256 sourceToDestSwapRate); + + function amountInEth(address Token, uint256 amount) + external + view + returns (uint256 ethAmount); + + function getMaxDrawdown( + address loanToken, + address collateralToken, + uint256 loanAmount, + uint256 collateralAmount, + uint256 maintenanceMargin + ) external view returns (uint256); + + function getCurrentMarginAndCollateralSize( + address loanToken, + address collateralToken, + uint256 loanAmount, + uint256 collateralAmount + ) + external + view + returns (uint256 currentMargin, uint256 collateralInEthAmount); + + function getCurrentMargin( + address loanToken, + address collateralToken, + uint256 loanAmount, + uint256 collateralAmount + ) + external + view + returns (uint256 currentMargin, uint256 collateralToLoanRate); + + function shouldLiquidate( + address loanToken, + address collateralToken, + uint256 loanAmount, + uint256 collateralAmount, + uint256 maintenanceMargin + ) external view returns (bool); + + function getFastGasPrice(address payToken) external view returns (uint256); +} + +abstract contract StateI { + address public priceFeeds; + address public swapsImpl; +} diff --git a/contracts/utils/ExponentMath.sol b/contracts/utils/ExponentMath.sol new file mode 100644 index 00000000..bab557e8 --- /dev/null +++ b/contracts/utils/ExponentMath.sol @@ -0,0 +1,15 @@ +pragma solidity ^0.8.4; + + +library ExponentMath{ + + function TenExp(uint256 number, int8 pow) public pure returns (uint256){ + if(pow < 0){ + number=number/10**(uint8(pow*-1)); + }else{ + number=number*10**uint8(pow); + } + return number; + } + +} \ No newline at end of file From 54dcb269453c7192f8070b7af9ca2dd1e8c6d28f Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Wed, 8 Dec 2021 18:53:31 -0800 Subject: [PATCH 04/83] passed tests --- contracts/interfaces/ICurve.sol | 19 ++++--- .../interfaces/ICurvePoolRegistration.sol | 2 + .../modules/LoanClosings/LoanClosings.sol | 4 +- .../modules/LoanClosings/LoanClosingsBase.sol | 1 - .../CurvePoolRegistration.sol | 10 +++- contracts/swaps/ISwapsImpl.sol | 1 - contracts/swaps/SwapsUser.sol | 1 - .../swaps/connectors/SwapsImplCurve_ETH.sol | 55 +++++++++++-------- contracts/swaps/connectors/SwapsImplKyber.sol | 5 +- .../connectors/SwapsImplUniswapV2_BSC.sol | 5 +- .../connectors/SwapsImplUniswapV2_ETH.sol | 5 +- .../connectors/SwapsImplUniswapV2_POLYGON.sol | 5 +- .../connectors/SwapsImplUniswapV3_ETH.sol | 26 ++++++--- 13 files changed, 82 insertions(+), 57 deletions(-) diff --git a/contracts/interfaces/ICurve.sol b/contracts/interfaces/ICurve.sol index 1f3a3b68..940623b7 100644 --- a/contracts/interfaces/ICurve.sol +++ b/contracts/interfaces/ICurve.sol @@ -2,28 +2,31 @@ pragma solidity 0.5.17; interface ICurve { function exchange( - uint256 i, - uint256 j, + int128 i, + int128 j, uint256 dx, uint256 min_dy ) external; function exchange_underlying( - uint256 i, - uint256 j, + int128 i, + int128 j, uint256 dx, uint256 min_dy ) external; function get_dy( - uint256 i, - uint256 j, + int128 i, + int128 j, uint256 dx ) external returns (uint256); function get_dy_underlying( - uint256 i, - uint256 j, + int128 i, + int128 j, uint256 dx ) external returns (uint256); function underlying_coins( uint256 ) external returns (address); + function coins( + uint256 + ) external returns (address); } diff --git a/contracts/interfaces/ICurvePoolRegistration.sol b/contracts/interfaces/ICurvePoolRegistration.sol index 4dacc132..438061fd 100644 --- a/contracts/interfaces/ICurvePoolRegistration.sol +++ b/contracts/interfaces/ICurvePoolRegistration.sol @@ -10,4 +10,6 @@ interface ICurvePoolRegistration { function disablePool(address tokenPool) external; function CheckPoolValidity(address pool) external view returns (bool); + + function getPoolType(address tokenPool) external view returns (uint256); } diff --git a/contracts/modules/LoanClosings/LoanClosings.sol b/contracts/modules/LoanClosings/LoanClosings.sol index a8de1b66..b81870ba 100644 --- a/contracts/modules/LoanClosings/LoanClosings.sol +++ b/contracts/modules/LoanClosings/LoanClosings.sol @@ -90,7 +90,7 @@ contract LoanClosings is LoanClosingsBase { address receiver, uint256 swapAmount, // denominated in collateralToken bool returnTokenIsCollateral, // true: withdraws collateralToken, false: withdraws loanToken - bytes memory /*loanDataBytes*/) // for future use + bytes memory loanDataBytes) // for future use public nonReentrant returns ( @@ -104,7 +104,7 @@ contract LoanClosings is LoanClosingsBase { receiver, swapAmount, returnTokenIsCollateral, - "" // loanDataBytes + loanDataBytes ); } } diff --git a/contracts/modules/LoanClosings/LoanClosingsBase.sol b/contracts/modules/LoanClosings/LoanClosingsBase.sol index a0393b83..ff9315f2 100644 --- a/contracts/modules/LoanClosings/LoanClosingsBase.sol +++ b/contracts/modules/LoanClosings/LoanClosingsBase.sol @@ -405,7 +405,6 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes .div(loanLocal.collateral); } require(loanCloseAmount != 0, "loanCloseAmount == 0"); - uint256 loanCloseAmountLessInterest = _settleInterestToPrincipal( loanLocal, loanParamsLocal, diff --git a/contracts/swaps/CurvePoolRegistration/CurvePoolRegistration.sol b/contracts/swaps/CurvePoolRegistration/CurvePoolRegistration.sol index 026ddd06..c46a89e4 100644 --- a/contracts/swaps/CurvePoolRegistration/CurvePoolRegistration.sol +++ b/contracts/swaps/CurvePoolRegistration/CurvePoolRegistration.sol @@ -4,11 +4,11 @@ import "@openzeppelin-2.5.0/ownership/Ownable.sol"; contract CurvePoolRegistration is Ownable { mapping(address => bool) public validPool; + mapping(address => uint256) public poolType; - function addPool( - address tokenPool - ) public { + function addPool(address tokenPool, uint256 PoolT) public { validPool[tokenPool] = true; + poolType[tokenPool] = PoolT; } function disablePool(address tokenPool) public { @@ -18,4 +18,8 @@ contract CurvePoolRegistration is Ownable { function CheckPoolValidity(address pool) public view returns (bool) { return validPool[pool]; } + + function getPoolType(address tokenPool) public view returns (uint256) { + return poolType[tokenPool]; + } } diff --git a/contracts/swaps/ISwapsImpl.sol b/contracts/swaps/ISwapsImpl.sol index 178c77e7..7134a3db 100644 --- a/contracts/swaps/ISwapsImpl.sol +++ b/contracts/swaps/ISwapsImpl.sol @@ -38,7 +38,6 @@ interface ISwapsImpl { uint256 amountOut) external returns (uint256 amountIn, address midToken); - function setSwapApprovals( address[] calldata tokens) external; diff --git a/contracts/swaps/SwapsUser.sol b/contracts/swaps/SwapsUser.sol index 255fa913..421c2c4e 100644 --- a/contracts/swaps/SwapsUser.sol +++ b/contracts/swaps/SwapsUser.sol @@ -198,7 +198,6 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { ); } - bool success; (success, data) = swapImplAddress.delegatecall(data); if (!success) { diff --git a/contracts/swaps/connectors/SwapsImplCurve_ETH.sol b/contracts/swaps/connectors/SwapsImplCurve_ETH.sol index e694ce64..f5d73591 100644 --- a/contracts/swaps/connectors/SwapsImplCurve_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplCurve_ETH.sol @@ -16,7 +16,7 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { using SafeERC20 for IERC20; using Path for bytes; using BytesLib for bytes; - address public constant PoolRegistry = address(0); //set to address for monitoring Curve pools + address public constant PoolRegistry = 0x63fea6E447F120B8Faf85B53cdaD8348e645D80E; //set to address for monitoring Curve pools bytes4 public constant ExchangeUnderlyingSig = bytes4(keccak256("exchange_underlying(uint256,uint256,uint256,uint256)")); bytes4 public constant ExchangeSig = bytes4(keccak256("exchange(uint256,uint256,uint256,uint256)")); bytes4 public constant GetDySig = bytes4(keccak256("get_dy(uint256,uint256,uint256)")); @@ -113,14 +113,14 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { uint256 amountOut; if(sig == GetDySig || sig == ExchangeSig){ amountOut = ICurve(curvePool).get_dy( - tokenOut, - tokenIn, + int128(tokenOut), + int128(tokenIn), amountIn ); }else if(sig == GetDyUnderlyingSig || sig == ExchangeUnderlyingSig){ amountOut = ICurve(curvePool).get_dy_underlying( - tokenOut, - tokenIn, + int128(tokenOut), + int128(tokenIn), amountIn ); }else{ @@ -143,14 +143,14 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { uint256 amountIn; if(sig == GetDySig || sig == ExchangeSig){ amountIn = ICurve(curvePool).get_dy( - tokenOut, - tokenIn, + int128(tokenOut), + int128(tokenIn), amountOut ); }else if(sig == GetDyUnderlyingSig || sig == ExchangeUnderlyingSig){ amountIn = ICurve(curvePool).get_dy_underlying( - tokenOut, - tokenIn, + int128(tokenOut), + int128(tokenIn), amountOut ); }else{ @@ -162,13 +162,13 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { return amountIn; } - function setSwapApprovals(address[] memory tokens) public { + function setSwapApprovalsCurve(address spender,address[] memory tokens) public { require( - ICurvePoolRegistration(PoolRegistry).CheckPoolValidity(tokens[0]) + ICurvePoolRegistration(PoolRegistry).CheckPoolValidity(spender) ); for (uint256 i = 1; i < tokens.length; i++) { - IERC20(tokens[i]).safeApprove(tokens[0], 0); - IERC20(tokens[i]).safeApprove(tokens[0], uint256(-1)); + IERC20(tokens[i]).safeApprove(spender, 0); + IERC20(tokens[i]).safeApprove(spender, uint256(-1)); } } @@ -176,7 +176,13 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { internal returns (address) { - return ICurve(pool).underlying_coins(tokenID); + address token = address(0); + if(ICurvePoolRegistration(PoolRegistry).getPoolType(pool) == 0){ + token = ICurve(pool).underlying_coins(tokenID); + }else{ + token = ICurve(pool).coins(tokenID); + } + return token; } function _swapWithCurve( @@ -208,13 +214,12 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { requiredDestTokenAmount ); require( - amountIn >= minSourceTokenAmount && - amountIn <= sourceTokenAmount + amountIn <= sourceTokenAmount, "too much" ); if(sig==ExchangeUnderlyingSig){ - ICurve(curvePool).exchange_underlying(tokenIn, tokenOut, amountIn, 1); + ICurve(curvePool).exchange_underlying(int128(tokenIn), int128(tokenOut), amountIn, 1); }else if(sig==ExchangeSig){ - ICurve(curvePool).exchange_underlying(tokenIn, tokenOut, amountIn, 1); + ICurve(curvePool).exchange(int128(tokenIn), int128(tokenOut), amountIn, 1); }else{ revert("Unsupported Signature"); } @@ -236,20 +241,22 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { curvePool ) ); - require(sourceTokenAddress == _getDexNumber(curvePool, tokenIn)); - require(destTokenAddress == _getDexNumber(curvePool, tokenOut)); + + require(sourceTokenAddress == _getDexNumber(curvePool, tokenIn),"source token number off"); + require(destTokenAddress == _getDexNumber(curvePool, tokenOut),"dest token number off"); + (uint256 recv, ) = dexAmountOut(payload, minSourceTokenAmount); if(sig == ExchangeUnderlyingSig){ ICurve(curvePool).exchange_underlying( - tokenIn, - tokenOut, + int128(tokenIn), + int128(tokenOut), minSourceTokenAmount, 1 ); }else if(sig == ExchangeSig){ ICurve(curvePool).exchange( - tokenIn, - tokenOut, + int128(tokenIn), + int128(tokenOut), minSourceTokenAmount, 1 ); diff --git a/contracts/swaps/connectors/SwapsImplKyber.sol b/contracts/swaps/connectors/SwapsImplKyber.sol index 3ee4cd4e..f523059c 100644 --- a/contracts/swaps/connectors/SwapsImplKyber.sol +++ b/contracts/swaps/connectors/SwapsImplKyber.sol @@ -98,12 +98,13 @@ contract SwapsImplKyber is State, ISwapsImpl { } function setSwapApprovals( + address spender, address[] memory tokens) public { for (uint256 i = 0; i < tokens.length; i++) { - IERC20(tokens[i]).safeApprove(kyberContract, 0); - IERC20(tokens[i]).safeApprove(kyberContract, uint256(-1)); + IERC20(tokens[i]).safeApprove(spender, 0); + IERC20(tokens[i]).safeApprove(spender, uint256(-1)); } } diff --git a/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol b/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol index 19da0ca7..94acb365 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol @@ -221,12 +221,13 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { } function setSwapApprovals( + address spender, address[] memory tokens) public { for (uint256 i = 0; i < tokens.length; i++) { - IERC20(tokens[i]).safeApprove(uniswapRouter, 0); - IERC20(tokens[i]).safeApprove(uniswapRouter, uint256(-1)); + IERC20(tokens[i]).safeApprove(spender, 0); + IERC20(tokens[i]).safeApprove(spender, uint256(-1)); } } diff --git a/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol b/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol index a838d079..70f9f005 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol @@ -240,12 +240,13 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { } function setSwapApprovals( + address spender, address[] memory tokens) public { for (uint256 i = 0; i < tokens.length; i++) { - IERC20(tokens[i]).safeApprove(uniswapRouter, 0); - IERC20(tokens[i]).safeApprove(uniswapRouter, uint256(-1)); + IERC20(tokens[i]).safeApprove(spender, 0); + IERC20(tokens[i]).safeApprove(spender, uint256(-1)); } } diff --git a/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol b/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol index 29b7e218..ac7d5ca3 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol @@ -258,12 +258,13 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } function setSwapApprovals( + address spender, address[] memory tokens) public { for (uint256 i = 0; i < tokens.length; i++) { - IERC20(tokens[i]).safeApprove(uniswapRouter, 0); - IERC20(tokens[i]).safeApprove(uniswapRouter, uint256(-1)); + IERC20(tokens[i]).safeApprove(spender, 0); + IERC20(tokens[i]).safeApprove(spender, uint256(-1)); } } diff --git a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol index 1933d38f..6108be2a 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol @@ -48,6 +48,7 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { sourceTokenAddress, destTokenAddress, receiverAddress, + minSourceTokenAmount, maxSourceTokenAmount, requiredDestTokenAmount, payload @@ -136,7 +137,8 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { address sourceTokenAddress, address destTokenAddress, address receiverAddress, - uint256 sourceTokenAmount, + uint256 minSourceTokenAmount, + uint256 maxSourceTokenAmount, uint256 requiredDestTokenAmount, bytes memory payload ) @@ -159,24 +161,28 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { ) == sourceTokenAddress, "improper source" ); - totalAmountsOut = totalAmountsOut + exactParams[x].amountOut; - totalAmountsInMax = - totalAmountsInMax + - exactParams[x].amountInMaximum; + exactParams[x].amountOut = requiredDestTokenAmount.mul(exactParams[x].amountOut).div(100); + exactParams[x].amountInMaximum = maxSourceTokenAmount.mul(exactParams[x].amountInMaximum).div(100); + totalAmountsOut = totalAmountsOut.add(exactParams[x].amountOut); + totalAmountsInMax = totalAmountsInMax.add(exactParams[x].amountInMaximum); + encodedTXs[x] = abi.encodeWithSelector( IUniswapV3SwapRouter(uniswapSwapRouter) .exactOutput .selector, exactParams[x] ); + } require( totalAmountsOut == requiredDestTokenAmount && - totalAmountsInMax <= sourceTokenAmount + totalAmountsInMax <= maxSourceTokenAmount ); - bytes[] memory trueAmountsIn = IUniswapV3SwapRouter( + + bytes[] memory trueAmountsIn = IUniswapV3SwapRouter( uniswapSwapRouter ).multicall(encodedTXs); + uint256 totaledAmountIn = 0; for (uint256 x = 0; x < trueAmountsIn.length; x++) { uint256 tempAmountIn = abi.decode(trueAmountsIn[x], (uint256)); @@ -184,6 +190,7 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { } sourceTokenAmountUsed = totaledAmountIn; destTokenAmountReceived = requiredDestTokenAmount; + } else { IUniswapV3SwapRouter.ExactInputParams[] memory exactParams = abi .decode(payload, (IUniswapV3SwapRouter.ExactInputParams[])); @@ -197,14 +204,15 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { exactParams[x].path.length - 20 ); require(tokenOut == destTokenAddress, "improper destination"); - totalAmounts = totalAmounts + exactParams[x].amountIn; + exactParams[x].amountIn = exactParams[x].amountIn.mul(minSourceTokenAmount).div(100); //amountIn on data is % of funds to use per route. Total should add to source token amount or else it fails. take into consideration rounding + totalAmounts = totalAmounts.add(exactParams[x].amountIn); encodedTXs[x] = abi.encodeWithSelector( IUniswapV3SwapRouter(uniswapSwapRouter).exactInput.selector, exactParams[x] ); } sourceTokenAmountUsed = totalAmounts; - require(totalAmounts == sourceTokenAmount, "improper swap amounts"); + require(totalAmounts == minSourceTokenAmount, "improper swap amounts"); bytes[] memory trueAmountsOut = IUniswapV3SwapRouter( uniswapSwapRouter ).multicall(encodedTXs); From 040e8bc4a273998148893f8478436a2f54443b89 Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Mon, 27 Dec 2021 14:00:43 -0800 Subject: [PATCH 05/83] Formatting --- contracts/interfaces/ICurve.sol | 15 +- .../interfaces/ICurvePoolRegistration.sol | 6 +- contracts/interfaces/IDexRecords.sol | 2 +- .../{uniswapQuoter.sol => IUniswapQuoter.sol} | 12 +- contracts/interfaces/IUniswapV3SwapRouter.sol | 2 +- .../modules/LoanClosings/LoanClosings.sol | 68 +-- .../modules/LoanClosings/LoanClosingsBase.sol | 518 +++++++++--------- .../modules/LoanOpenings/LoanOpenings.sol | 513 +++++++++-------- contracts/swaps/DexRecords.sol | 2 +- contracts/swaps/ISwapsImpl.sol | 29 +- contracts/swaps/SwapsUser.sol | 182 +++--- .../swaps/connectors/SwapsImplCurve_ETH.sol | 217 ++++---- contracts/swaps/connectors/SwapsImplKyber.sol | 105 ++-- .../connectors/SwapsImplUniswapV2_BSC.sol | 96 ++-- .../connectors/SwapsImplUniswapV2_ETH.sol | 104 ++-- .../connectors/SwapsImplUniswapV2_POLYGON.sol | 96 ++-- .../connectors/SwapsImplUniswapV3_ETH.sol | 59 +- 17 files changed, 1015 insertions(+), 1011 deletions(-) rename contracts/interfaces/{uniswapQuoter.sol => IUniswapQuoter.sol} (51%) diff --git a/contracts/interfaces/ICurve.sol b/contracts/interfaces/ICurve.sol index 940623b7..15685c98 100644 --- a/contracts/interfaces/ICurve.sol +++ b/contracts/interfaces/ICurve.sol @@ -1,4 +1,4 @@ -pragma solidity 0.5.17; +pragma solidity >=0.5.17; interface ICurve { function exchange( @@ -7,26 +7,27 @@ interface ICurve { uint256 dx, uint256 min_dy ) external; + function exchange_underlying( int128 i, int128 j, uint256 dx, uint256 min_dy ) external; + function get_dy( int128 i, int128 j, uint256 dx ) external returns (uint256); + function get_dy_underlying( int128 i, int128 j, uint256 dx ) external returns (uint256); - function underlying_coins( - uint256 - ) external returns (address); - function coins( - uint256 - ) external returns (address); + + function underlying_coins(uint256) external returns (address); + + function coins(uint256) external returns (address); } diff --git a/contracts/interfaces/ICurvePoolRegistration.sol b/contracts/interfaces/ICurvePoolRegistration.sol index 438061fd..d2c38ee5 100644 --- a/contracts/interfaces/ICurvePoolRegistration.sol +++ b/contracts/interfaces/ICurvePoolRegistration.sol @@ -1,4 +1,4 @@ -pragma solidity 0.5.17; +pragma solidity >=0.5.17; interface ICurvePoolRegistration { function addPool( @@ -10,6 +10,6 @@ interface ICurvePoolRegistration { function disablePool(address tokenPool) external; function CheckPoolValidity(address pool) external view returns (bool); - - function getPoolType(address tokenPool) external view returns (uint256); + + function getPoolType(address tokenPool) external view returns (uint256); } diff --git a/contracts/interfaces/IDexRecords.sol b/contracts/interfaces/IDexRecords.sol index 04c17873..6b0f5386 100644 --- a/contracts/interfaces/IDexRecords.sol +++ b/contracts/interfaces/IDexRecords.sol @@ -1,4 +1,4 @@ -pragma solidity 0.5.17; +pragma solidity >=0.5.17; interface IDexRecords { function retrieveDexAddress(uint256 dexNumber) diff --git a/contracts/interfaces/uniswapQuoter.sol b/contracts/interfaces/IUniswapQuoter.sol similarity index 51% rename from contracts/interfaces/uniswapQuoter.sol rename to contracts/interfaces/IUniswapQuoter.sol index 1102842d..96a4d3e3 100644 --- a/contracts/interfaces/uniswapQuoter.sol +++ b/contracts/interfaces/IUniswapQuoter.sol @@ -1,15 +1,11 @@ -pragma solidity 0.5.17; +pragma solidity >=0.5.17; -interface uniswapQuoter { +interface IUniswapQuoter { function quoteExactInput(bytes calldata path, uint256 amountIn) external - returns ( - uint256 amountOut - ); + returns (uint256 amountOut); function quoteExactOutput(bytes calldata path, uint256 amountOut) external - returns ( - uint256 amountIn - ); + returns (uint256 amountIn); } diff --git a/contracts/interfaces/IUniswapV3SwapRouter.sol b/contracts/interfaces/IUniswapV3SwapRouter.sol index c73fcc1e..d6eaf3f0 100644 --- a/contracts/interfaces/IUniswapV3SwapRouter.sol +++ b/contracts/interfaces/IUniswapV3SwapRouter.sol @@ -1,4 +1,4 @@ -pragma solidity 0.5.17; +pragma solidity >=0.5.17; pragma experimental ABIEncoderV2; interface IUniswapV3SwapRouter { diff --git a/contracts/modules/LoanClosings/LoanClosings.sol b/contracts/modules/LoanClosings/LoanClosings.sol index b81870ba..a2017139 100644 --- a/contracts/modules/LoanClosings/LoanClosings.sol +++ b/contracts/modules/LoanClosings/LoanClosings.sol @@ -8,14 +8,8 @@ pragma experimental ABIEncoderV2; import "./LoanClosingsBase.sol"; - contract LoanClosings is LoanClosingsBase { - - function initialize( - address target) - external - onlyOwner - { + function initialize(address target) external onlyOwner { _setTarget(this.liquidate.selector, target); _setTarget(this.rollover.selector, target); _setTarget(this.closeWithDeposit.selector, target); @@ -25,7 +19,8 @@ contract LoanClosings is LoanClosingsBase { function liquidate( bytes32 loanId, address receiver, - uint256 closeAmount) // denominated in loanToken + uint256 closeAmount // denominated in loanToken + ) external payable nonReentrant @@ -35,40 +30,31 @@ contract LoanClosings is LoanClosingsBase { address seizedToken ) { - return _liquidate( - loanId, - receiver, - closeAmount - ); + return _liquidate(loanId, receiver, closeAmount); } function rollover( bytes32 loanId, - bytes calldata /*loanDataBytes*/) // for future use - external - nonReentrant - returns ( - address rebateToken, - uint256 gasRebate - ) - { - uint256 startingGas = gasleft() + - 21576; // estimated used gas ignoring loanDataBytes: 21000 + (4+32) * 16 + bytes calldata /*loanDataBytes*/ // for future use + ) external nonReentrant returns (address rebateToken, uint256 gasRebate) { + uint256 startingGas = gasleft() + 21576; // estimated used gas ignoring loanDataBytes: 21000 + (4+32) * 16 // restrict to EOAs to prevent griefing attacks, during interest rate recalculation require(msg.sender == tx.origin, "only EOAs can call"); - return _rollover( - loanId, - startingGas, - "" // loanDataBytes - ); + return + _rollover( + loanId, + startingGas, + "" // loanDataBytes + ); } function closeWithDeposit( bytes32 loanId, address receiver, - uint256 depositAmount) // denominated in loanToken + uint256 depositAmount // denominated in loanToken + ) public payable nonReentrant @@ -78,11 +64,7 @@ contract LoanClosings is LoanClosingsBase { address withdrawToken ) { - return _closeWithDeposit( - loanId, - receiver, - depositAmount - ); + return _closeWithDeposit(loanId, receiver, depositAmount); } function closeWithSwap( @@ -90,7 +72,8 @@ contract LoanClosings is LoanClosingsBase { address receiver, uint256 swapAmount, // denominated in collateralToken bool returnTokenIsCollateral, // true: withdraws collateralToken, false: withdraws loanToken - bytes memory loanDataBytes) // for future use + bytes memory loanDataBytes // for future use + ) public nonReentrant returns ( @@ -99,12 +82,13 @@ contract LoanClosings is LoanClosingsBase { address withdrawToken ) { - return _closeWithSwap( - loanId, - receiver, - swapAmount, - returnTokenIsCollateral, - loanDataBytes - ); + return + _closeWithSwap( + loanId, + receiver, + swapAmount, + returnTokenIsCollateral, + loanDataBytes + ); } } diff --git a/contracts/modules/LoanClosings/LoanClosingsBase.sol b/contracts/modules/LoanClosings/LoanClosingsBase.sol index ff9315f2..a90f5cab 100644 --- a/contracts/modules/LoanClosings/LoanClosingsBase.sol +++ b/contracts/modules/LoanClosings/LoanClosingsBase.sol @@ -15,9 +15,15 @@ import "../../swaps/SwapsUser.sol"; import "../../interfaces/ILoanPool.sol"; import "../../governance/PausableGuardian.sol"; - -contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, InterestUser, SwapsUser, LiquidationHelper, PausableGuardian { - +contract LoanClosingsBase is + State, + LoanClosingsEvents, + VaultController, + InterestUser, + SwapsUser, + LiquidationHelper, + PausableGuardian +{ enum CloseTypes { Deposit, Swap, @@ -27,7 +33,8 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes function _liquidate( bytes32 loanId, address receiver, - uint256 closeAmount) + uint256 closeAmount + ) internal pausable returns ( @@ -65,13 +72,15 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes currentMargin, loanParamsLocal.maintenanceMargin, collateralToLoanRate, - liquidationIncentivePercent[loanParamsLocal.loanToken][loanParamsLocal.collateralToken] + liquidationIncentivePercent[loanParamsLocal.loanToken][ + loanParamsLocal.collateralToken + ] ); if (loanCloseAmount < maxLiquidatable) { - seizedAmount = maxSeizable - .mul(loanCloseAmount) - .div(maxLiquidatable); + seizedAmount = maxSeizable.mul(loanCloseAmount).div( + maxLiquidatable + ); } else { if (loanCloseAmount > maxLiquidatable) { // adjust down the close amount to the max @@ -117,14 +126,9 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes seizedToken = loanParamsLocal.collateralToken; if (seizedAmount != 0) { - loanLocal.collateral = loanLocal.collateral - .sub(seizedAmount); + loanLocal.collateral = loanLocal.collateral.sub(seizedAmount); - _withdrawAsset( - seizedToken, - receiver, - seizedAmount - ); + _withdrawAsset(seizedToken, receiver, seizedAmount); } _emitClosingEvents( @@ -138,23 +142,14 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes CloseTypes.Liquidation ); - _closeLoan( - loanLocal, - loanCloseAmount - ); + _closeLoan(loanLocal, loanCloseAmount); } function _rollover( bytes32 loanId, uint256 startingGas, - bytes memory /*loanDataBytes*/) // for future use - internal - pausable - returns ( - address rebateToken, - uint256 gasRebate - ) - { + bytes memory /*loanDataBytes*/ // for future use + ) internal pausable returns (address rebateToken, uint256 gasRebate) { Loan memory loanLocal = loans[loanId]; require(loanLocal.active, "loan is closed"); require( @@ -169,13 +164,12 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes LoanParams memory loanParamsLocal = loanParams[loanLocal.loanParamsId]; // pay outstanding interest to lender - _payInterest( - loanLocal.lender, - loanParamsLocal.loanToken - ); + _payInterest(loanLocal.lender, loanParamsLocal.loanToken); LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id]; - LenderInterest storage lenderInterestLocal = lenderInterest[loanLocal.lender][loanParamsLocal.loanToken]; + LenderInterest storage lenderInterestLocal = lenderInterest[ + loanLocal.lender + ][loanParamsLocal.loanToken]; _settleFeeRewardForInterestExpense( loanInterestLocal, @@ -189,26 +183,28 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes uint256 backInterestTime; uint256 backInterestOwed; if (block.timestamp > loanLocal.endTimestamp) { - backInterestTime = block.timestamp - .sub(loanLocal.endTimestamp); - backInterestOwed = backInterestTime - .mul(loanInterestLocal.owedPerDay); - backInterestOwed = backInterestOwed - .div(24 hours); + backInterestTime = block.timestamp.sub(loanLocal.endTimestamp); + backInterestOwed = backInterestTime.mul( + loanInterestLocal.owedPerDay + ); + backInterestOwed = backInterestOwed.div(24 hours); } uint256 maxDuration = loanParamsLocal.maxLoanTerm; if (maxDuration != 0) { // fixed-term loan, so need to query iToken for latest variable rate - uint256 owedPerDay = loanLocal.principal + uint256 owedPerDay = loanLocal + .principal .mul(ILoanPool(loanLocal.lender).borrowInterestRate()) .div(DAYS_IN_A_YEAR * WEI_PERCENT_PRECISION); - lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay - .add(owedPerDay); - lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay - .sub(loanInterestLocal.owedPerDay); + lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay.add( + owedPerDay + ); + lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay.sub( + loanInterestLocal.owedPerDay + ); loanInterestLocal.owedPerDay = owedPerDay; } else { @@ -217,33 +213,33 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes } if (backInterestTime >= maxDuration) { - maxDuration = backInterestTime - .add(24 hours); // adds an extra 24 hours + maxDuration = backInterestTime.add(24 hours); // adds an extra 24 hours } // update loan end time - loanLocal.endTimestamp = loanLocal.endTimestamp - .add(maxDuration); + loanLocal.endTimestamp = loanLocal.endTimestamp.add(maxDuration); - uint256 interestAmountRequired = loanLocal.endTimestamp - .sub(block.timestamp); - interestAmountRequired = interestAmountRequired - .mul(loanInterestLocal.owedPerDay); - interestAmountRequired = interestAmountRequired - .div(24 hours); + uint256 interestAmountRequired = loanLocal.endTimestamp.sub( + block.timestamp + ); + interestAmountRequired = interestAmountRequired.mul( + loanInterestLocal.owedPerDay + ); + interestAmountRequired = interestAmountRequired.div(24 hours); - loanInterestLocal.depositTotal = loanInterestLocal.depositTotal - .add(interestAmountRequired); + loanInterestLocal.depositTotal = loanInterestLocal.depositTotal.add( + interestAmountRequired + ); - lenderInterestLocal.owedTotal = lenderInterestLocal.owedTotal - .add(interestAmountRequired); + lenderInterestLocal.owedTotal = lenderInterestLocal.owedTotal.add( + interestAmountRequired + ); // add backInterestOwed - interestAmountRequired = interestAmountRequired - .add(backInterestOwed); + interestAmountRequired = interestAmountRequired.add(backInterestOwed); // collect interest - (,uint256 sourceTokenAmountUsed,) = _doCollateralSwap( + (, uint256 sourceTokenAmountUsed, ) = _doCollateralSwap( loanLocal, loanParamsLocal, loanLocal.collateral, @@ -251,8 +247,7 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes true, // returnTokenIsCollateral "" ); - loanLocal.collateral = loanLocal.collateral - .sub(sourceTokenAmountUsed); + loanLocal.collateral = loanLocal.collateral.sub(sourceTokenAmountUsed); if (backInterestOwed != 0) { // pay out backInterestOwed @@ -273,16 +268,14 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes if (gasRebate != 0) { // pay out gas rebate to caller // the preceeding logic should ensure gasRebate <= collateral, but just in case, will use SafeMath here - loanLocal.collateral = loanLocal.collateral - .sub(gasRebate, "gasRebate too high"); + loanLocal.collateral = loanLocal.collateral.sub( + gasRebate, + "gasRebate too high" + ); rebateToken = loanParamsLocal.collateralToken; - _withdrawAsset( - rebateToken, - msg.sender, - gasRebate - ); + _withdrawAsset(rebateToken, msg.sender, gasRebate); } } @@ -298,7 +291,8 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes function _closeWithDeposit( bytes32 loanId, address receiver, - uint256 depositAmount) // denominated in loanToken + uint256 depositAmount // denominated in loanToken + ) internal pausable returns ( @@ -310,11 +304,7 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes require(depositAmount != 0, "depositAmount == 0"); Loan memory loanLocal = loans[loanId]; - _checkAuthorized( - loanLocal.id, - loanLocal.active, - loanLocal.borrower - ); + _checkAuthorized(loanLocal.id, loanLocal.active, loanLocal.borrower); if (receiver == address(0)) { receiver = msg.sender; @@ -323,9 +313,9 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes LoanParams memory loanParamsLocal = loanParams[loanLocal.loanParamsId]; // can't close more than the full principal - loanCloseAmount = depositAmount > loanLocal.principal ? - loanLocal.principal : - depositAmount; + loanCloseAmount = depositAmount > loanLocal.principal + ? loanLocal.principal + : depositAmount; uint256 loanCloseAmountLessInterest = _settleInterestToPrincipal( loanLocal, @@ -348,11 +338,7 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes withdrawToken = loanParamsLocal.collateralToken; loanLocal.collateral = 0; - _withdrawAsset( - withdrawToken, - receiver, - withdrawAmount - ); + _withdrawAsset(withdrawToken, receiver, withdrawAmount); } _finalizeClose( @@ -370,7 +356,8 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes address receiver, uint256 swapAmount, bool returnTokenIsCollateral, - bytes memory loanDataBytes) + bytes memory loanDataBytes + ) internal pausable returns ( @@ -382,11 +369,7 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes require(swapAmount != 0, "swapAmount == 0"); Loan memory loanLocal = loans[loanId]; - _checkAuthorized( - loanLocal.id, - loanLocal.active, - loanLocal.borrower - ); + _checkAuthorized(loanLocal.id, loanLocal.active, loanLocal.borrower); if (receiver == address(0)) { receiver = msg.sender; @@ -400,9 +383,9 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes loanCloseAmount = loanLocal.principal; if (swapAmount != loanLocal.collateral) { - loanCloseAmount = loanCloseAmount - .mul(swapAmount) - .div(loanLocal.collateral); + loanCloseAmount = loanCloseAmount.mul(swapAmount).div( + loanLocal.collateral + ); } require(loanCloseAmount != 0, "loanCloseAmount == 0"); uint256 loanCloseAmountLessInterest = _settleInterestToPrincipal( @@ -414,7 +397,11 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes uint256 usedCollateral; uint256 collateralToLoanSwapRate; - (usedCollateral, withdrawAmount, collateralToLoanSwapRate) = _coverPrincipalWithSwap( + ( + usedCollateral, + withdrawAmount, + collateralToLoanSwapRate + ) = _coverPrincipalWithSwap( loanLocal, loanParamsLocal, swapAmount, @@ -433,20 +420,15 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes } if (usedCollateral != 0) { - loanLocal.collateral = loanLocal.collateral - .sub(usedCollateral); + loanLocal.collateral = loanLocal.collateral.sub(usedCollateral); } - withdrawToken = returnTokenIsCollateral ? - loanParamsLocal.collateralToken : - loanParamsLocal.loanToken; + withdrawToken = returnTokenIsCollateral + ? loanParamsLocal.collateralToken + : loanParamsLocal.loanToken; if (withdrawAmount != 0) { - _withdrawAsset( - withdrawToken, - receiver, - withdrawAmount - ); + _withdrawAsset(withdrawToken, receiver, withdrawAmount); } _finalizeClose( @@ -462,9 +444,8 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes function _updateDepositAmount( bytes32 loanId, uint256 principalBefore, - uint256 principalAfter) - internal - { + uint256 principalAfter + ) internal { uint256 depositValueAsLoanToken; uint256 depositValueAsCollateralToken; bytes32 slot = keccak256(abi.encode(loanId, LoanDepositValueID)); @@ -475,11 +456,17 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes sstore(add(slot, 1), 0) } default { - depositValueAsLoanToken := div(mul(sload(slot), principalAfter), principalBefore) + depositValueAsLoanToken := div( + mul(sload(slot), principalAfter), + principalBefore + ) sstore(slot, depositValueAsLoanToken) slot := add(slot, 1) - depositValueAsCollateralToken := div(mul(sload(slot), principalAfter), principalBefore) + depositValueAsCollateralToken := div( + mul(sload(slot), principalAfter), + principalBefore + ) sstore(slot, depositValueAsCollateralToken) } } @@ -494,14 +481,11 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes function _checkAuthorized( bytes32 _id, bool _active, - address _borrower) - internal - view - { + address _borrower + ) internal view { require(_active, "loan is closed"); require( - msg.sender == _borrower || - delegatedManagers[_id][msg.sender], + msg.sender == _borrower || delegatedManagers[_id][msg.sender], "unauthorized" ); } @@ -510,10 +494,8 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes Loan memory loanLocal, LoanParams memory loanParamsLocal, uint256 loanCloseAmount, - address receiver) - internal - returns (uint256) - { + address receiver + ) internal returns (uint256) { uint256 loanCloseAmountLessInterest = loanCloseAmount; uint256 interestRefundToBorrower = _settleInterest( @@ -545,11 +527,7 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes loanCloseAmountLessInterest = 0; // refund overage - vaultWithdraw( - loanToken, - receiver, - interestRefundToBorrower - ); + vaultWithdraw(loanToken, receiver, interestRefundToBorrower); } if (interestAppliedToPrincipal != 0) { @@ -567,17 +545,11 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes function _returnPrincipalWithDeposit( address loanToken, address receiver, - uint256 principalNeeded) - internal - { + uint256 principalNeeded + ) internal { if (principalNeeded != 0) { if (msg.value == 0) { - vaultTransfer( - loanToken, - msg.sender, - receiver, - principalNeeded - ); + vaultTransfer(loanToken, msg.sender, receiver, principalNeeded); } else { require(loanToken == address(wethToken), "wrong asset sent"); require(msg.value >= principalNeeded, "not enough ether"); @@ -592,10 +564,7 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes } if (msg.value > principalNeeded) { // refund overage - Address.sendValue( - msg.sender, - msg.value - principalNeeded - ); + Address.sendValue(msg.sender, msg.value - principalNeeded); } } } else { @@ -609,13 +578,22 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes uint256 swapAmount, uint256 principalNeeded, bool returnTokenIsCollateral, - bytes memory loanDataBytes) + bytes memory loanDataBytes + ) internal - returns (uint256 usedCollateral, uint256 withdrawAmount, uint256 collateralToLoanSwapRate) + returns ( + uint256 usedCollateral, + uint256 withdrawAmount, + uint256 collateralToLoanSwapRate + ) { uint256 destTokenAmountReceived; uint256 sourceTokenAmountUsed; - (destTokenAmountReceived, sourceTokenAmountUsed, collateralToLoanSwapRate) = _doCollateralSwap( + ( + destTokenAmountReceived, + sourceTokenAmountUsed, + collateralToLoanSwapRate + ) = _doCollateralSwap( loanLocal, loanParamsLocal, swapAmount, @@ -633,17 +611,17 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes destTokenAmountReceived - principalNeeded ); } - withdrawAmount = swapAmount > sourceTokenAmountUsed ? - swapAmount - sourceTokenAmountUsed : - 0; + withdrawAmount = swapAmount > sourceTokenAmountUsed + ? swapAmount - sourceTokenAmountUsed + : 0; } else { require(sourceTokenAmountUsed == swapAmount, "swap error"); withdrawAmount = destTokenAmountReceived - principalNeeded; } - usedCollateral = sourceTokenAmountUsed > swapAmount ? - sourceTokenAmountUsed : - swapAmount; + usedCollateral = sourceTokenAmountUsed > swapAmount + ? sourceTokenAmountUsed + : swapAmount; } function _doCollateralSwap( @@ -652,46 +630,53 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes uint256 swapAmount, uint256 principalNeeded, bool returnTokenIsCollateral, - bytes memory loanDataBytes) + bytes memory loanDataBytes + ) internal - returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed, uint256 collateralToLoanSwapRate) + returns ( + uint256 destTokenAmountReceived, + uint256 sourceTokenAmountUsed, + uint256 collateralToLoanSwapRate + ) { - (destTokenAmountReceived, sourceTokenAmountUsed, collateralToLoanSwapRate) = _loanSwap( + ( + destTokenAmountReceived, + sourceTokenAmountUsed, + collateralToLoanSwapRate + ) = _loanSwap( loanLocal.id, loanParamsLocal.collateralToken, loanParamsLocal.loanToken, loanLocal.borrower, swapAmount, // minSourceTokenAmount loanLocal.collateral, // maxSourceTokenAmount - returnTokenIsCollateral ? - principalNeeded : // requiredDestTokenAmount - 0, + returnTokenIsCollateral + ? principalNeeded // requiredDestTokenAmount + : 0, false, // bypassFee loanDataBytes ); - require(destTokenAmountReceived >= principalNeeded, "insufficient dest amount"); - require(sourceTokenAmountUsed <= loanLocal.collateral, "excessive source amount"); + require( + destTokenAmountReceived >= principalNeeded, + "insufficient dest amount" + ); + require( + sourceTokenAmountUsed <= loanLocal.collateral, + "excessive source amount" + ); } // withdraws asset to receiver function _withdrawAsset( address assetToken, address receiver, - uint256 assetAmount) - internal - { + uint256 assetAmount + ) internal { if (assetAmount != 0) { if (assetToken == address(wethToken)) { - vaultEtherWithdraw( - receiver, - assetAmount - ); + vaultEtherWithdraw(receiver, assetAmount); } else { - vaultWithdraw( - assetToken, - receiver, - assetAmount - ); + vaultWithdraw(assetToken, receiver, assetAmount); } } } @@ -701,10 +686,8 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes address collateralToken, uint256 principal, uint256 collateral, - bool silentFail) - internal - returns (uint256 currentMargin, uint256 collateralToLoanRate) - { + bool silentFail + ) internal returns (uint256 currentMargin, uint256 collateralToLoanRate) { address _priceFeeds = priceFeeds; (bool success, bytes memory data) = _priceFeeds.staticcall( abi.encodeWithSelector( @@ -731,10 +714,9 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes uint256 loanCloseAmount, uint256 collateralCloseAmount, uint256 collateralToLoanSwapRate, - CloseTypes closeType) - internal - { - (uint256 principalBefore, uint256 principalAfter) = _closeLoan( + CloseTypes closeType + ) internal { + (uint256 principalBefore, uint256 principalAfter) = _closeLoan( loanLocal, loanCloseAmount ); @@ -751,16 +733,12 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes //// Note: We can safely skip the margin check if closing via closeWithDeposit or if closing the loan in full by any method //// require( closeType == CloseTypes.Deposit || - principalAfter == 0 || // loan fully closed - currentMargin > loanParamsLocal.maintenanceMargin, + principalAfter == 0 || // loan fully closed + currentMargin > loanParamsLocal.maintenanceMargin, "unhealthy position" ); - _updateDepositAmount( - loanLocal.id, - principalBefore, - principalAfter - ); + _updateDepositAmount(loanLocal.id, principalBefore, principalAfter); _emitClosingEvents( loanParamsLocal, @@ -774,9 +752,7 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes ); } - function _closeLoan( - Loan memory loanLocal, - uint256 loanCloseAmount) + function _closeLoan(Loan memory loanLocal, uint256 loanCloseAmount) internal returns (uint256 principalBefore, uint256 principalAfter) { @@ -793,8 +769,7 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes lenderLoanSets[loanLocal.lender].removeBytes32(loanLocal.id); borrowerLoanSets[loanLocal.borrower].removeBytes32(loanLocal.id); } else { - principalAfter = principalBefore - .sub(loanCloseAmount); + principalAfter = principalBefore.sub(loanCloseAmount); loanLocal.principal = principalAfter; } @@ -804,18 +779,15 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes function _settleInterest( LoanParams memory loanParamsLocal, Loan memory loanLocal, - uint256 closePrincipal) - internal - returns (uint256) - { + uint256 closePrincipal + ) internal returns (uint256) { // pay outstanding interest to lender - _payInterest( - loanLocal.lender, - loanParamsLocal.loanToken - ); + _payInterest(loanLocal.lender, loanParamsLocal.loanToken); LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id]; - LenderInterest storage lenderInterestLocal = lenderInterest[loanLocal.lender][loanParamsLocal.loanToken]; + LenderInterest storage lenderInterestLocal = lenderInterest[ + loanLocal.lender + ][loanParamsLocal.loanToken]; uint256 interestTime = block.timestamp; if (interestTime > loanLocal.endTimestamp) { @@ -832,7 +804,8 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes uint256 owedPerDayRefund; if (closePrincipal < loanLocal.principal) { - owedPerDayRefund = loanInterestLocal.owedPerDay + owedPerDayRefund = loanInterestLocal + .owedPerDay .mul(closePrincipal) .div(loanLocal.principal); } else { @@ -840,34 +813,39 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes } // update stored owedPerDay - loanInterestLocal.owedPerDay = loanInterestLocal.owedPerDay - .sub(owedPerDayRefund); - lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay - .sub(owedPerDayRefund); + loanInterestLocal.owedPerDay = loanInterestLocal.owedPerDay.sub( + owedPerDayRefund + ); + lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay.sub( + owedPerDayRefund + ); // update borrower interest - uint256 interestRefundToBorrower = loanLocal.endTimestamp - .sub(interestTime); - interestRefundToBorrower = interestRefundToBorrower - .mul(owedPerDayRefund); - interestRefundToBorrower = interestRefundToBorrower - .div(24 hours); + uint256 interestRefundToBorrower = loanLocal.endTimestamp.sub( + interestTime + ); + interestRefundToBorrower = interestRefundToBorrower.mul( + owedPerDayRefund + ); + interestRefundToBorrower = interestRefundToBorrower.div(24 hours); if (closePrincipal < loanLocal.principal) { - loanInterestLocal.depositTotal = loanInterestLocal.depositTotal - .sub(interestRefundToBorrower); + loanInterestLocal.depositTotal = loanInterestLocal.depositTotal.sub( + interestRefundToBorrower + ); } else { loanInterestLocal.depositTotal = 0; } // update remaining lender interest values - lenderInterestLocal.principalTotal = lenderInterestLocal.principalTotal + lenderInterestLocal.principalTotal = lenderInterestLocal + .principalTotal .sub(closePrincipal); uint256 owedTotal = lenderInterestLocal.owedTotal; - lenderInterestLocal.owedTotal = owedTotal > interestRefundToBorrower ? - owedTotal - interestRefundToBorrower : - 0; + lenderInterestLocal.owedTotal = owedTotal > interestRefundToBorrower + ? owedTotal - interestRefundToBorrower + : 0; return interestRefundToBorrower; } @@ -875,20 +853,18 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes function _getRebate( address collateralToken, uint256 collateral, - uint256 startingGas) - internal - view - returns (uint256 gasRebate) - { + uint256 startingGas + ) internal view returns (uint256 gasRebate) { // gets the gas rebate denominated in collateralToken - gasRebate = SafeMath.mul( - IPriceFeeds(priceFeeds).getFastGasPrice(collateralToken) * 2, - startingGas - gasleft() - ).div(WEI_PRECISION * WEI_PRECISION); + gasRebate = SafeMath + .mul( + IPriceFeeds(priceFeeds).getFastGasPrice(collateralToken) * 2, + startingGas - gasleft() + ) + .div(WEI_PRECISION * WEI_PRECISION); // gas rebate cannot exceed available collateral - gasRebate = gasRebate - .min256(collateral); + gasRebate = gasRebate.min256(collateral); } function _finalizeRollover( @@ -896,20 +872,19 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes LoanParams memory loanParamsLocal, uint256 sourceTokenAmountUsed, uint256 interestAmountRequired, - uint256 gasRebate) - internal - { + uint256 gasRebate + ) internal { emit Rollover( - loanLocal.borrower, // user (borrower) - msg.sender, // caller - loanLocal.id, // loanId - loanLocal.lender, // lender - loanParamsLocal.loanToken, // loanToken - loanParamsLocal.collateralToken, // collateralToken - sourceTokenAmountUsed, // collateralAmountUsed - interestAmountRequired, // interestAmountAdded - loanLocal.endTimestamp, // loanEndTimestamp - gasRebate // gasRebate + loanLocal.borrower, // user (borrower) + msg.sender, // caller + loanLocal.id, // loanId + loanLocal.lender, // lender + loanParamsLocal.loanToken, // loanToken + loanParamsLocal.collateralToken, // collateralToken + sourceTokenAmountUsed, // collateralAmountUsed + interestAmountRequired, // interestAmountAdded + loanLocal.endTimestamp, // loanEndTimestamp + gasRebate // gasRebate ); loans[loanLocal.id] = loanLocal; @@ -923,26 +898,28 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes uint256 collateralToLoanRate, uint256 collateralToLoanSwapRate, uint256 currentMargin, - CloseTypes closeType) - internal - { + CloseTypes closeType + ) internal { if (closeType == CloseTypes.Deposit) { emit CloseWithDeposit( - loanLocal.borrower, // user (borrower) - loanLocal.lender, // lender - loanLocal.id, // loanId - msg.sender, // closer - loanParamsLocal.loanToken, // loanToken - loanParamsLocal.collateralToken, // collateralToken - loanCloseAmount, // loanCloseAmount - collateralCloseAmount, // collateralCloseAmount - collateralToLoanRate, // collateralToLoanRate - currentMargin // currentMargin + loanLocal.borrower, // user (borrower) + loanLocal.lender, // lender + loanLocal.id, // loanId + msg.sender, // closer + loanParamsLocal.loanToken, // loanToken + loanParamsLocal.collateralToken, // collateralToken + loanCloseAmount, // loanCloseAmount + collateralCloseAmount, // collateralCloseAmount + collateralToLoanRate, // collateralToLoanRate + currentMargin // currentMargin ); } else if (closeType == CloseTypes.Swap) { // exitPrice = 1 / collateralToLoanSwapRate if (collateralToLoanSwapRate != 0) { - collateralToLoanSwapRate = SafeMath.div(WEI_PRECISION * WEI_PRECISION, collateralToLoanSwapRate); + collateralToLoanSwapRate = SafeMath.div( + WEI_PRECISION * WEI_PRECISION, + collateralToLoanSwapRate + ); } // currentLeverage = 100 / currentMargin @@ -951,29 +928,30 @@ contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, Interes } emit CloseWithSwap( - loanLocal.borrower, // user (trader) - loanLocal.lender, // lender - loanLocal.id, // loanId - loanParamsLocal.collateralToken, // collateralToken - loanParamsLocal.loanToken, // loanToken - msg.sender, // closer - collateralCloseAmount, // positionCloseSize - loanCloseAmount, // loanCloseAmount - collateralToLoanSwapRate, // exitPrice (1 / collateralToLoanSwapRate) - currentMargin // currentLeverage + loanLocal.borrower, // user (trader) + loanLocal.lender, // lender + loanLocal.id, // loanId + loanParamsLocal.collateralToken, // collateralToken + loanParamsLocal.loanToken, // loanToken + msg.sender, // closer + collateralCloseAmount, // positionCloseSize + loanCloseAmount, // loanCloseAmount + collateralToLoanSwapRate, // exitPrice (1 / collateralToLoanSwapRate) + currentMargin // currentLeverage ); - } else { // closeType == CloseTypes.Liquidation + } else { + // closeType == CloseTypes.Liquidation emit Liquidate( - loanLocal.borrower, // user (borrower) - msg.sender, // liquidator - loanLocal.id, // loanId - loanLocal.lender, // lender - loanParamsLocal.loanToken, // loanToken - loanParamsLocal.collateralToken, // collateralToken - loanCloseAmount, // loanCloseAmount - collateralCloseAmount, // collateralCloseAmount - collateralToLoanRate, // collateralToLoanRate - currentMargin // currentMargin + loanLocal.borrower, // user (borrower) + msg.sender, // liquidator + loanLocal.id, // loanId + loanLocal.lender, // lender + loanParamsLocal.loanToken, // loanToken + loanParamsLocal.collateralToken, // collateralToken + loanCloseAmount, // loanCloseAmount + collateralCloseAmount, // collateralCloseAmount + collateralToLoanRate, // collateralToLoanRate + currentMargin // currentMargin ); } } diff --git a/contracts/modules/LoanOpenings/LoanOpenings.sol b/contracts/modules/LoanOpenings/LoanOpenings.sol index c9b5d3a9..b3c9077d 100644 --- a/contracts/modules/LoanOpenings/LoanOpenings.sol +++ b/contracts/modules/LoanOpenings/LoanOpenings.sol @@ -13,14 +13,15 @@ import "../../mixins/InterestUser.sol"; import "../../swaps/SwapsUser.sol"; import "../../governance/PausableGuardian.sol"; - -contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUser, SwapsUser, PausableGuardian { - - function initialize( - address target) - external - onlyOwner - { +contract LoanOpenings is + State, + LoanOpeningsEvents, + VaultController, + InterestUser, + SwapsUser, + PausableGuardian +{ + function initialize(address target) external onlyOwner { _setTarget(this.borrowOrTradeFromPool.selector, target); _setTarget(this.setDelegatedManager.selector, target); _setTarget(this.getEstimatedMarginExposure.selector, target); @@ -37,27 +38,28 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse bool isTorqueLoan, uint256 initialMargin, address[4] calldata sentAddresses, - // lender: must match loan if loanId provided - // borrower: must match loan if loanId provided - // receiver: receiver of funds (address(0) assumes borrower address) - // manager: delegated manager of loan unless address(0) + // lender: must match loan if loanId provided + // borrower: must match loan if loanId provided + // receiver: receiver of funds (address(0) assumes borrower address) + // manager: delegated manager of loan unless address(0) uint256[5] calldata sentValues, - // newRate: new loan interest rate - // newPrincipal: new loan size (borrowAmount + any borrowed interest) - // torqueInterest: new amount of interest to escrow for Torque loan (determines initial loan length) - // loanTokenReceived: total loanToken deposit (amount not sent to borrower in the case of Torque loans) - // collateralTokenReceived: total collateralToken deposit - bytes calldata loanDataBytes) - external - payable - nonReentrant - pausable - returns (LoanOpenData memory) - { - require(msg.value == 0 || loanDataBytes.length != 0, "loanDataBytes required with ether"); + // newRate: new loan interest rate + // newPrincipal: new loan size (borrowAmount + any borrowed interest) + // torqueInterest: new amount of interest to escrow for Torque loan (determines initial loan length) + // loanTokenReceived: total loanToken deposit (amount not sent to borrower in the case of Torque loans) + // collateralTokenReceived: total collateralToken deposit + bytes calldata loanDataBytes + ) external payable nonReentrant pausable returns (LoanOpenData memory) { + require( + msg.value == 0 || loanDataBytes.length != 0, + "loanDataBytes required with ether" + ); // only callable by loan pools - require(loanPoolToUnderlying[msg.sender] != address(0), "not authorized"); + require( + loanPoolToUnderlying[msg.sender] != address(0), + "not authorized" + ); LoanParams memory loanParamsLocal = loanParams[loanParamsId]; require(loanParamsLocal.id != 0, "loanParams not exists"); @@ -66,7 +68,7 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse require(isTorqueLoan, "initialMargin == 0"); initialMargin = loanParamsLocal.minInitialMargin; } - + // get required collateral uint256 collateralAmountRequired = _getRequiredCollateral( loanParamsLocal.loanToken, @@ -75,35 +77,30 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse initialMargin, isTorqueLoan ); + require(collateralAmountRequired != 0, "collateral is 0"); - return _borrowOrTrade( - loanParamsLocal, - loanId, - isTorqueLoan, - collateralAmountRequired, - initialMargin, - sentAddresses, - sentValues, - loanDataBytes - ); + return + _borrowOrTrade( + loanParamsLocal, + loanId, + isTorqueLoan, + collateralAmountRequired, + initialMargin, + sentAddresses, + sentValues, + loanDataBytes + ); } function setDelegatedManager( bytes32 loanId, address delegated, - bool toggle) - external - pausable - { + bool toggle + ) external pausable { require(loans[loanId].borrower == msg.sender, "unauthorized"); - _setDelegatedManager( - loanId, - msg.sender, - delegated, - toggle - ); + _setDelegatedManager(loanId, msg.sender, delegated, toggle); } function getEstimatedMarginExposure( @@ -113,23 +110,21 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse uint256 collateralTokenSent, uint256 interestRate, uint256 newPrincipal, - bytes calldata payload) - external - returns (uint256 value) - { + bytes calldata payload + ) external returns (uint256 value) { if (loanTokenSent < newPrincipal) { return 0; } uint256 maxLoanTerm = 2419200; // 28 days - uint256 owedPerDay = newPrincipal - .mul(interestRate) - .div(DAYS_IN_A_YEAR * WEI_PERCENT_PRECISION); + uint256 owedPerDay = newPrincipal.mul(interestRate).div( + DAYS_IN_A_YEAR * WEI_PERCENT_PRECISION + ); - uint256 interestAmountRequired = maxLoanTerm - .mul(owedPerDay) - .div(1 days); + uint256 interestAmountRequired = maxLoanTerm.mul(owedPerDay).div( + 1 days + ); if (loanTokenSent < interestAmountRequired) { return 0; @@ -138,13 +133,11 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse value = _swapsExpectedReturn( loanToken, collateralToken, - loanTokenSent - .sub(interestAmountRequired), - payload + loanTokenSent.sub(interestAmountRequired), + payload ); if (value != 0) { - return collateralTokenSent - .add(value); + return collateralTokenSent.add(value); } } @@ -153,11 +146,8 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse address collateralToken, uint256 newPrincipal, uint256 marginAmount, - bool isTorqueLoan) - public - view - returns (uint256 collateralAmountRequired) - { + bool isTorqueLoan + ) public view returns (uint256 collateralAmountRequired) { if (marginAmount != 0) { collateralAmountRequired = _getRequiredCollateral( loanToken, @@ -167,9 +157,9 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse isTorqueLoan ); - uint256 feePercent = isTorqueLoan ? - borrowingFeePercent : - tradingFeePercent; + uint256 feePercent = isTorqueLoan + ? borrowingFeePercent + : tradingFeePercent; if (collateralAmountRequired != 0 && feePercent != 0) { collateralAmountRequired = collateralAmountRequired .mul(WEI_PERCENT_PRECISION) @@ -182,21 +172,19 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse function getRequiredCollateralByParams( bytes32 loanParamsId, - uint256 newPrincipal) - public - view - returns (uint256 collateralAmountRequired) - { + uint256 newPrincipal + ) public view returns (uint256 collateralAmountRequired) { LoanParams memory loanParamsLocal = loanParams[loanParamsId]; - return getRequiredCollateral( - loanParamsLocal.loanToken, - loanParamsLocal.collateralToken, - newPrincipal, - loanParamsLocal.minInitialMargin, // marginAmount - loanParamsLocal.maxLoanTerm == 0 ? // isTorqueLoan - true : - false - ); + return + getRequiredCollateral( + loanParamsLocal.loanToken, + loanParamsLocal.collateralToken, + newPrincipal, + loanParamsLocal.minInitialMargin, // marginAmount + loanParamsLocal.maxLoanTerm == 0 // isTorqueLoan + ? true + : false + ); } function getBorrowAmount( @@ -204,15 +192,11 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse address collateralToken, uint256 collateralTokenAmount, uint256 marginAmount, - bool isTorqueLoan) - public - view - returns (uint256 borrowAmount) - { + bool isTorqueLoan + ) public view returns (uint256 borrowAmount) { if (marginAmount != 0) { if (isTorqueLoan) { - marginAmount = marginAmount - .add(WEI_PERCENT_PRECISION); // adjust for over-collateralized loan + marginAmount = marginAmount.add(WEI_PERCENT_PRECISION); // adjust for over-collateralized loan } if (loanToken == collateralToken) { @@ -220,10 +204,13 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse .mul(WEI_PERCENT_PRECISION) .div(marginAmount); } else { - (uint256 sourceToDestRate, uint256 sourceToDestPrecision) = IPriceFeeds(priceFeeds).queryRate( - collateralToken, - loanToken - ); + ( + uint256 sourceToDestRate, + uint256 sourceToDestPrecision + ) = IPriceFeeds(priceFeeds).queryRate( + collateralToken, + loanToken + ); if (sourceToDestPrecision != 0) { borrowAmount = collateralTokenAmount .mul(WEI_PERCENT_PRECISION) @@ -233,9 +220,9 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse } } - uint256 feePercent = isTorqueLoan ? - borrowingFeePercent : - tradingFeePercent; + uint256 feePercent = isTorqueLoan + ? borrowingFeePercent + : tradingFeePercent; if (borrowAmount != 0 && feePercent != 0) { borrowAmount = borrowAmount .mul( @@ -248,21 +235,19 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse function getBorrowAmountByParams( bytes32 loanParamsId, - uint256 collateralTokenAmount) - public - view - returns (uint256 borrowAmount) - { + uint256 collateralTokenAmount + ) public view returns (uint256 borrowAmount) { LoanParams memory loanParamsLocal = loanParams[loanParamsId]; - return getBorrowAmount( - loanParamsLocal.loanToken, - loanParamsLocal.collateralToken, - collateralTokenAmount, - loanParamsLocal.minInitialMargin, // marginAmount - loanParamsLocal.maxLoanTerm == 0 ? // isTorqueLoan - true : - false - ); + return + getBorrowAmount( + loanParamsLocal.loanToken, + loanParamsLocal.collateralToken, + collateralTokenAmount, + loanParamsLocal.minInitialMargin, // marginAmount + loanParamsLocal.maxLoanTerm == 0 // isTorqueLoan + ? true + : false + ); } function _borrowOrTrade( @@ -272,27 +257,33 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse uint256 collateralAmountRequired, uint256 initialMargin, address[4] memory sentAddresses, - // lender: must match loan if loanId provided - // borrower: must match loan if loanId provided - // receiver: receiver of funds (address(0) assumes borrower address) - // manager: delegated manager of loan unless address(0) + // lender: must match loan if loanId provided + // borrower: must match loan if loanId provided + // receiver: receiver of funds (address(0) assumes borrower address) + // manager: delegated manager of loan unless address(0) uint256[5] memory sentValues, - // newRate: new loan interest rate - // newPrincipal: new loan size (borrowAmount + any borrowed interest) - // torqueInterest: new amount of interest to escrow for Torque loan (determines initial loan length) - // loanTokenReceived: total loanToken deposit - // collateralTokenReceived: total collateralToken deposit - bytes memory loanDataBytes) - internal - returns (LoanOpenData memory) - { - require (loanParamsLocal.collateralToken != loanParamsLocal.loanToken, "collateral/loan match"); - require (initialMargin >= loanParamsLocal.minInitialMargin, "initialMargin too low"); + // newRate: new loan interest rate + // newPrincipal: new loan size (borrowAmount + any borrowed interest) + // torqueInterest: new amount of interest to escrow for Torque loan (determines initial loan length) + // loanTokenReceived: total loanToken deposit + // collateralTokenReceived: total collateralToken deposit + bytes memory loanDataBytes + ) internal returns (LoanOpenData memory) { + + require( + loanParamsLocal.collateralToken != loanParamsLocal.loanToken, + "collateral/loan match" + ); + require( + initialMargin >= loanParamsLocal.minInitialMargin, + "initialMargin too low" + ); // maxLoanTerm == 0 indicates a Torqueloan and requres that torqueInterest != 0 - require(loanParamsLocal.maxLoanTerm != 0 || - sentValues[2] != 0, // torqueInterest - "invalid interest"); + require( + loanParamsLocal.maxLoanTerm != 0 || sentValues[2] != 0, // torqueInterest + "invalid interest" + ); // initialize loan Loan storage loanLocal = _initializeLoan( @@ -302,25 +293,23 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse sentAddresses, sentValues ); - if (loanId == 0) { // loanId is defined for new loans loanId = loanLocal.id; } - + // get required interest uint256 amount = _initializeInterest( loanParamsLocal, loanLocal, sentValues[0], // newRate sentValues[1], // newPrincipal, - sentValues[2] // torqueInterest + sentValues[2] // torqueInterest ); - + // substract out interest from usable loanToken sent - sentValues[3] = sentValues[3] - .sub(amount); - + sentValues[3] = sentValues[3].sub(amount); + if (isTorqueLoan) { require(sentValues[3] == 0, "surplus loan token"); @@ -340,7 +329,8 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse // update collateral after trade // sentValues[3] is repurposed to hold loanToCollateralSwapRate to avoid stack too deep error uint256 receivedAmount; - (receivedAmount,,sentValues[3]) = _loanSwap( + + (receivedAmount, , sentValues[3]) = _loanSwap( loanId, loanParamsLocal.loanToken, loanParamsLocal.collateralToken, @@ -351,10 +341,10 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse false, // bypassFee loanDataBytes ); - sentValues[4] = sentValues[4] // collateralTokenReceived - .add(receivedAmount); + //require(1==2,"check4"); + sentValues[4] = sentValues[4].add(receivedAmount); // collateralTokenReceived } - + // settle collateral require( _isCollateralSatisfied( @@ -368,16 +358,19 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse "collateral insufficient" ); - loanLocal.collateral = loanLocal.collateral - .add(sentValues[4]) - .sub(amount); // borrowingFee + loanLocal.collateral = loanLocal.collateral.add(sentValues[4]).sub( + amount + ); // borrowingFee if (isTorqueLoan) { // reclaiming varaible -> interestDuration sentValues[2] = loanLocal.endTimestamp.sub(block.timestamp); } else { // reclaiming varaible -> entryLeverage = 100 / initialMargin - sentValues[2] = SafeMath.div(WEI_PRECISION * WEI_PERCENT_PRECISION, initialMargin); + sentValues[2] = SafeMath.div( + WEI_PRECISION * WEI_PERCENT_PRECISION, + initialMargin + ); } _finalizeOpen( @@ -388,11 +381,12 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse isTorqueLoan ); - return LoanOpenData({ - loanId: loanId, - principal: sentValues[1], - collateral: sentValues[4] - }); + return + LoanOpenData({ + loanId: loanId, + principal: sentValues[1], + collateral: sentValues[4] + }); } function _finalizeOpen( @@ -400,15 +394,16 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse Loan storage loanLocal, address[4] memory sentAddresses, uint256[5] memory sentValues, - bool isTorqueLoan) - internal - { - (uint256 initialMargin, uint256 collateralToLoanRate) = IPriceFeeds(priceFeeds).getCurrentMargin( - loanParamsLocal.loanToken, - loanParamsLocal.collateralToken, - loanLocal.principal, - loanLocal.collateral - ); + bool isTorqueLoan + ) internal { + (uint256 initialMargin, uint256 collateralToLoanRate) = IPriceFeeds( + priceFeeds + ).getCurrentMargin( + loanParamsLocal.loanToken, + loanParamsLocal.collateralToken, + loanLocal.principal, + loanLocal.collateral + ); require( initialMargin > loanParamsLocal.maintenanceMargin, "unhealthy position" @@ -440,40 +435,42 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse uint256[5] memory sentValues, uint256 collateralToLoanRate, uint256 margin, - bool isTorqueLoan) - internal - { + bool isTorqueLoan + ) internal { if (isTorqueLoan) { emit Borrow( - sentAddresses[1], // user (borrower) - sentAddresses[0], // lender - loanLocal.id, // loanId - loanParamsLocal.loanToken, // loanToken - loanParamsLocal.collateralToken, // collateralToken - sentValues[1], // newPrincipal - sentValues[4], // newCollateral - sentValues[0], // interestRate - sentValues[2], // interestDuration - collateralToLoanRate, // collateralToLoanRate, - margin // currentMargin + sentAddresses[1], // user (borrower) + sentAddresses[0], // lender + loanLocal.id, // loanId + loanParamsLocal.loanToken, // loanToken + loanParamsLocal.collateralToken, // collateralToken + sentValues[1], // newPrincipal + sentValues[4], // newCollateral + sentValues[0], // interestRate + sentValues[2], // interestDuration + collateralToLoanRate, // collateralToLoanRate, + margin // currentMargin ); } else { // currentLeverage = 100 / currentMargin - margin = SafeMath.div(WEI_PRECISION * WEI_PERCENT_PRECISION, margin); + margin = SafeMath.div( + WEI_PRECISION * WEI_PERCENT_PRECISION, + margin + ); emit Trade( - sentAddresses[1], // user (trader) - sentAddresses[0], // lender - loanLocal.id, // loanId - loanParamsLocal.collateralToken, // collateralToken - loanParamsLocal.loanToken, // loanToken - sentValues[4], // positionSize - sentValues[1], // borrowedAmount - sentValues[0], // interestRate, - loanLocal.endTimestamp, // settlementDate - sentValues[3], // entryPrice (loanToCollateralSwapRate) - sentValues[2], // entryLeverage - margin // currentLeverage + sentAddresses[1], // user (trader) + sentAddresses[0], // lender + loanLocal.id, // loanId + loanParamsLocal.collateralToken, // collateralToken + loanParamsLocal.loanToken, // loanToken + sentValues[4], // positionSize + sentValues[1], // borrowedAmount + sentValues[0], // interestRate, + loanLocal.endTimestamp, // settlementDate + sentValues[3], // entryPrice (loanToCollateralSwapRate) + sentValues[2], // entryLeverage + margin // currentLeverage ); } } @@ -482,17 +479,11 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse bytes32 loanId, address delegator, address delegated, - bool toggle) - internal - { + bool toggle + ) internal { delegatedManagers[loanId][delegated] = toggle; - emit DelegatedManagerSet( - loanId, - delegator, - delegated, - toggle - ); + emit DelegatedManagerSet(loanId, delegator, delegated, toggle); } function _isCollateralSatisfied( @@ -501,11 +492,8 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse uint256 initialMargin, uint256 newCollateral, uint256 collateralAmountRequired, - uint256 borrowingFee) - internal - view - returns (bool) - { + uint256 borrowingFee + ) internal view returns (bool) { // allow at most 2% under-collateralized collateralAmountRequired = collateralAmountRequired .mul(98 ether) @@ -522,8 +510,8 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse loanLocal.collateral, initialMargin ); - return newCollateral - .add(maxDrawdown) >= collateralAmountRequired; + return + newCollateral.add(maxDrawdown) >= collateralAmountRequired; } else { return false; } @@ -536,10 +524,8 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse bytes32 loanId, uint256 initialMargin, address[4] memory sentAddresses, - uint256[5] memory sentValues) - internal - returns (Loan storage sloanLocal) - { + uint256[5] memory sentValues + ) internal returns (Loan storage sloanLocal) { require(loanParamsLocal.active, "loanParams disabled"); address lender = sentAddresses[0]; @@ -548,12 +534,14 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse uint256 newPrincipal = sentValues[1]; if (loanId == 0) { - loanId = keccak256(abi.encodePacked( - loanParamsLocal.id, - lender, - borrower, - block.timestamp - )); + loanId = keccak256( + abi.encodePacked( + loanParamsLocal.id, + lender, + borrower, + block.timestamp + ) + ); sloanLocal = loans[loanId]; require(sloanLocal.id == 0, "loan exists"); @@ -576,22 +564,22 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse borrowerLoanSets[borrower].addBytes32(loanId); } else { sloanLocal = loans[loanId]; - require(sloanLocal.active && block.timestamp < sloanLocal.endTimestamp, "loan has ended"); + require( + sloanLocal.active && block.timestamp < sloanLocal.endTimestamp, + "loan has ended" + ); require(sloanLocal.borrower == borrower, "borrower mismatch"); require(sloanLocal.lender == lender, "lender mismatch"); - require(sloanLocal.loanParamsId == loanParamsLocal.id, "loanParams mismatch"); + require( + sloanLocal.loanParamsId == loanParamsLocal.id, + "loanParams mismatch" + ); - sloanLocal.principal = sloanLocal.principal - .add(newPrincipal); + sloanLocal.principal = sloanLocal.principal.add(newPrincipal); } if (manager != address(0)) { - _setDelegatedManager( - loanId, - borrower, - manager, - true - ); + _setDelegatedManager(loanId, borrower, manager, true); } } @@ -600,18 +588,15 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse Loan storage loanLocal, uint256 newRate, uint256 newPrincipal, - uint256 torqueInterest) // ignored for fixed-term loans - internal - returns (uint256 interestAmountRequired) - { + uint256 torqueInterest // ignored for fixed-term loans + ) internal returns (uint256 interestAmountRequired) { // pay outstanding interest to lender - _payInterest( - loanLocal.lender, - loanParamsLocal.loanToken - ); + _payInterest(loanLocal.lender, loanParamsLocal.loanToken); LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id]; - LenderInterest storage lenderInterestLocal = lenderInterest[loanLocal.lender][loanParamsLocal.loanToken]; + LenderInterest storage lenderInterestLocal = lenderInterest[ + loanLocal.lender + ][loanParamsLocal.loanToken]; uint256 maxLoanTerm = loanParamsLocal.maxLoanTerm; @@ -625,21 +610,22 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse uint256 previousDepositRemaining; if (maxLoanTerm == 0 && loanLocal.endTimestamp != 0) { - previousDepositRemaining = loanLocal.endTimestamp - .sub(block.timestamp) // block.timestamp < endTimestamp was confirmed earlier - .mul(loanInterestLocal.owedPerDay) - .div(1 days); + previousDepositRemaining = loanLocal + .endTimestamp + .sub(block.timestamp).mul(loanInterestLocal.owedPerDay).div(1 days); // block.timestamp < endTimestamp was confirmed earlier } - uint256 owedPerDay = newPrincipal - .mul(newRate) - .div(DAYS_IN_A_YEAR * WEI_PERCENT_PRECISION); + uint256 owedPerDay = newPrincipal.mul(newRate).div( + DAYS_IN_A_YEAR * WEI_PERCENT_PRECISION + ); // update stored owedPerDay - loanInterestLocal.owedPerDay = loanInterestLocal.owedPerDay - .add(owedPerDay); - lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay - .add(owedPerDay); + loanInterestLocal.owedPerDay = loanInterestLocal.owedPerDay.add( + owedPerDay + ); + lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay.add( + owedPerDay + ); if (maxLoanTerm == 0) { // indefinite-term (Torque) loan @@ -651,8 +637,7 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse .div(loanInterestLocal.owedPerDay) .add(block.timestamp); - maxLoanTerm = loanLocal.endTimestamp - .sub(block.timestamp); + maxLoanTerm = loanLocal.endTimestamp.sub(block.timestamp); // loan term has to at least be greater than one hour require(maxLoanTerm > 1 hours, "loan too short"); @@ -662,24 +647,27 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse // fixed-term loan if (loanLocal.endTimestamp == 0) { - loanLocal.endTimestamp = block.timestamp - .add(maxLoanTerm); + loanLocal.endTimestamp = block.timestamp.add(maxLoanTerm); } - interestAmountRequired = loanLocal.endTimestamp + interestAmountRequired = loanLocal + .endTimestamp .sub(block.timestamp) .mul(owedPerDay) .div(1 days); } - loanInterestLocal.depositTotal = loanInterestLocal.depositTotal - .add(interestAmountRequired); + loanInterestLocal.depositTotal = loanInterestLocal.depositTotal.add( + interestAmountRequired + ); // update remaining lender interest values - lenderInterestLocal.principalTotal = lenderInterestLocal.principalTotal + lenderInterestLocal.principalTotal = lenderInterestLocal + .principalTotal .add(newPrincipal); - lenderInterestLocal.owedTotal = lenderInterestLocal.owedTotal - .add(interestAmountRequired); + lenderInterestLocal.owedTotal = lenderInterestLocal.owedTotal.add( + interestAmountRequired + ); } function _getRequiredCollateral( @@ -687,20 +675,17 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestUse address collateralToken, uint256 newPrincipal, uint256 marginAmount, - bool isTorqueLoan) - internal - view - returns (uint256 collateralTokenAmount) - { + bool isTorqueLoan + ) internal view returns (uint256 collateralTokenAmount) { if (loanToken == collateralToken) { - collateralTokenAmount = newPrincipal - .mul(marginAmount) - .divCeil(WEI_PERCENT_PRECISION); - } else { - (uint256 sourceToDestRate, uint256 sourceToDestPrecision) = IPriceFeeds(priceFeeds).queryRate( - collateralToken, - loanToken + collateralTokenAmount = newPrincipal.mul(marginAmount).divCeil( + WEI_PERCENT_PRECISION ); + } else { + ( + uint256 sourceToDestRate, + uint256 sourceToDestPrecision + ) = IPriceFeeds(priceFeeds).queryRate(collateralToken, loanToken); if (sourceToDestRate != 0) { collateralTokenAmount = newPrincipal .mul(sourceToDestPrecision) diff --git a/contracts/swaps/DexRecords.sol b/contracts/swaps/DexRecords.sol index f4e81e33..637bdf9f 100644 --- a/contracts/swaps/DexRecords.sol +++ b/contracts/swaps/DexRecords.sol @@ -10,7 +10,7 @@ contract DexRecords is Ownable { return dexes[number]; } - function setDexID(address dex) public onlyOwner() { + function setDexID(address dex) public onlyOwner { dexCount++; dexes[dexCount] = dex; } diff --git a/contracts/swaps/ISwapsImpl.sol b/contracts/swaps/ISwapsImpl.sol index 7134a3db..16efe7ae 100644 --- a/contracts/swaps/ISwapsImpl.sol +++ b/contracts/swaps/ISwapsImpl.sol @@ -3,8 +3,7 @@ * Licensed under the Apache License, Version 2.0. */ -pragma solidity 0.5.17; - +pragma solidity >=0.5.17; interface ISwapsImpl { function dexSwap( @@ -15,30 +14,28 @@ interface ISwapsImpl { uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, uint256 requiredDestTokenAmount, - bytes calldata payload) + bytes calldata payload + ) external - returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed); + returns ( + uint256 destTokenAmountReceived, + uint256 sourceTokenAmountUsed + ); function dexExpectedRate( address sourceTokenAddress, address destTokenAddress, - uint256 sourceTokenAmount) - external - view - returns (uint256); + uint256 sourceTokenAmount + ) external view returns (uint256); - function dexAmountOut( - bytes calldata route, - uint256 amountIn) + function dexAmountOut(bytes calldata route, uint256 amountIn) external returns (uint256 amountOut, address midToken); - function dexAmountIn( - bytes calldata route, - uint256 amountOut) + function dexAmountIn(bytes calldata route, uint256 amountOut) external returns (uint256 amountIn, address midToken); - function setSwapApprovals( - address[] calldata tokens) + + function setSwapApprovals(address spender, address[] calldata tokens) external; } diff --git a/contracts/swaps/SwapsUser.sol b/contracts/swaps/SwapsUser.sol index 421c2c4e..512d3280 100644 --- a/contracts/swaps/SwapsUser.sol +++ b/contracts/swaps/SwapsUser.sol @@ -13,9 +13,7 @@ import "../mixins/FeesHelper.sol"; import "./ISwapsImpl.sol"; import "../interfaces/IDexRecords.sol"; - contract SwapsUser is State, SwapsEvents, FeesHelper { - function _loanSwap( bytes32 loanId, address sourceToken, @@ -25,10 +23,16 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { uint256 maxSourceTokenAmount, uint256 requiredDestTokenAmount, bool bypassFee, - bytes memory loanDataBytes) + bytes memory loanDataBytes + ) internal - returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed, uint256 sourceToDestSwapRate) + returns ( + uint256 destTokenAmountReceived, + uint256 sourceTokenAmountUsed, + uint256 sourceToDestSwapRate + ) { + (destTokenAmountReceived, sourceTokenAmountUsed) = _swapsCall( [ sourceToken, @@ -46,7 +50,7 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { bypassFee, loanDataBytes ); - + // will revert if swap size too large _checkSwapSize(sourceToken, sourceTokenAmountUsed); @@ -74,10 +78,8 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { uint256[3] memory vals, bytes32 loanId, bool miscBool, // bypassFee - bytes memory loanDataBytes) - internal - returns (uint256, uint256) - { + bytes memory loanDataBytes + ) internal returns (uint256, uint256) { //addrs[0]: sourceToken //addrs[1]: destToken //addrs[2]: receiver @@ -93,7 +95,8 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { uint256 sourceTokenAmountUsed; uint256 tradingFee; - if (!miscBool) { // bypassFee + if (!miscBool) { + // bypassFee if (vals[2] == 0) { // condition: vals[0] will always be used as sourceAmount @@ -106,8 +109,7 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { tradingFee ); - vals[0] = vals[0] - .sub(tradingFee); + vals[0] = vals[0].sub(tradingFee); } } else { // condition: unknown sourceAmount will be used @@ -115,8 +117,7 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { tradingFee = _getTradingFee(vals[2]); if (tradingFee != 0) { - vals[2] = vals[2] - .add(tradingFee); + vals[2] = vals[2].add(tradingFee); } } } @@ -126,16 +127,19 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { } else { require(vals[0] <= vals[1], "min greater than max"); } - + (destTokenAmountReceived, sourceTokenAmountUsed) = _swapsCall_internal( addrs, vals, - loanDataBytes + loanDataBytes ); - + if (vals[2] == 0) { // there's no minimum destTokenAmount, but all of vals[0] (minSourceTokenAmount) must be spent, and amount spent can't exceed vals[0] - require(sourceTokenAmountUsed == vals[0], "swap too large to fill"); + require( + sourceTokenAmountUsed == vals[0], + "swap too large to fill test" + ); if (tradingFee != 0) { sourceTokenAmountUsed = sourceTokenAmountUsed + tradingFee; // will never overflow @@ -143,7 +147,10 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { } else { // there's a minimum destTokenAmount required, but sourceTokenAmountUsed won't be greater than vals[1] (maxSourceTokenAmount) require(sourceTokenAmountUsed <= vals[1], "swap fill too large"); - require(destTokenAmountReceived >= vals[2], "insufficient swap liquidity"); + require( + destTokenAmountReceived >= vals[2], + "insufficient swap liquidity" + ); if (tradingFee != 0) { _payTradingFee( @@ -163,43 +170,54 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { function _swapsCall_internal( address[5] memory addrs, uint256[3] memory vals, - bytes memory loanDataBytes) + bytes memory loanDataBytes + ) internal returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) { - bytes memory data; - address swapImplAddress; - if(loanDataBytes.length==0){ - swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress(1); //if nothing specified, default to first dex option available. ensure it does not require any input data or else this will break - data = abi.encodeWithSelector( - ISwapsImpl(swapImplAddress).dexSwap.selector, - addrs[0], // sourceToken - addrs[1], // destToken - addrs[2], // receiverAddress - addrs[3], // returnToSenderAddress - vals[0], // minSourceTokenAmount - vals[1], // maxSourceTokenAmount - vals[2], // requiredDestTokenAmount - loanDataBytes - ); - }else{ - (uint256 DexNumber, bytes memory SwapData) = abi.decode(loanDataBytes,(uint256,bytes)); - swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress(DexNumber); - data = abi.encodeWithSelector( - ISwapsImpl(swapImplAddress).dexSwap.selector, - addrs[0], // sourceToken - addrs[1], // destToken - addrs[2], // receiverAddress - addrs[3], // returnToSenderAddress - vals[0], // minSourceTokenAmount - vals[1], // maxSourceTokenAmount - vals[2], // requiredDestTokenAmount - SwapData - ); - } - + bytes memory data; + address swapImplAddress; + + if (loanDataBytes.length == 0) { + swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress(1); //if nothing specified, default to first dex option available. ensure it does not require any input data or else this will break + data = abi.encodeWithSelector( + ISwapsImpl(swapImplAddress).dexSwap.selector, + addrs[0], // sourceToken + addrs[1], // destToken + addrs[2], // receiverAddress + addrs[3], // returnToSenderAddress + vals[0], // minSourceTokenAmount + vals[1], // maxSourceTokenAmount + vals[2], // requiredDestTokenAmount + loanDataBytes + ); + } else { + (uint256 DexNumber, bytes memory SwapData) = abi.decode( + loanDataBytes, + (uint256, bytes) + ); + + swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress( + DexNumber + ); + + data = abi.encodeWithSelector( + ISwapsImpl(swapImplAddress).dexSwap.selector, + addrs[0], // sourceToken + addrs[1], // destToken + addrs[2], // receiverAddress + addrs[3], // returnToSenderAddress + vals[0], // minSourceTokenAmount + vals[1], // maxSourceTokenAmount + vals[2], // requiredDestTokenAmount + SwapData + ); + + } + bool success; (success, data) = swapImplAddress.delegatecall(data); + if (!success) { assembly { let ptr := mload(0x40) @@ -208,44 +226,47 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { revert(ptr, size) } } - - (destTokenAmountReceived, sourceTokenAmountUsed) = abi.decode(data, (uint256, uint256)); + (destTokenAmountReceived, sourceTokenAmountUsed) = abi.decode( + data, + (uint256, uint256) + ); } function _swapsExpectedReturn( address sourceToken, address destToken, uint256 sourceTokenAmount, - bytes memory payload) - internal - returns (uint256 expectedReturn) - { + bytes memory payload + ) internal returns (uint256 expectedReturn) { uint256 tradingFee = _getTradingFee(sourceTokenAmount); if (tradingFee != 0) { - sourceTokenAmount = sourceTokenAmount - .sub(tradingFee); + sourceTokenAmount = sourceTokenAmount.sub(tradingFee); + } + if (payload.length == 0) { + address swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress( + 1 + ); //default dex address + bytes memory dataToSend = abi.encode(sourceToken, destToken); + (expectedReturn, ) = ISwapsImpl(swapImplAddress).dexAmountOut( + dataToSend, + sourceTokenAmount + ); + } else { + (uint256 DexNumber, bytes memory dataToSend) = abi.decode( + payload, + (uint256, bytes) + ); + address swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress( + DexNumber + ); + (expectedReturn, ) = ISwapsImpl(swapImplAddress).dexAmountOut( + dataToSend, + sourceTokenAmount + ); } - if(payload.length==0){ - address swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress(1); //default dex address - bytes memory dataToSend = abi.encode(sourceToken, destToken); - (expectedReturn,) = ISwapsImpl(swapImplAddress).dexAmountOut( - dataToSend, - sourceTokenAmount - ); - }else{ - (uint256 DexNumber, bytes memory dataToSend) = abi.decode(payload,(uint256,bytes)); - address swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress(DexNumber); - (expectedReturn,) = ISwapsImpl(swapImplAddress).dexAmountOut( - dataToSend, - sourceTokenAmount - ); - } - } - function _checkSwapSize( - address tokenAddress, - uint256 amount) + function _checkSwapSize(address tokenAddress, uint256 amount) internal view { @@ -255,7 +276,10 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { if (tokenAddress == address(wethToken)) { amountInEth = amount; } else { - amountInEth = IPriceFeeds(priceFeeds).amountInEth(tokenAddress, amount); + amountInEth = IPriceFeeds(priceFeeds).amountInEth( + tokenAddress, + amount + ); } require(amountInEth <= _maxSwapSize, "swap too large"); } diff --git a/contracts/swaps/connectors/SwapsImplCurve_ETH.sol b/contracts/swaps/connectors/SwapsImplCurve_ETH.sol index f5d73591..9edefce1 100644 --- a/contracts/swaps/connectors/SwapsImplCurve_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplCurve_ETH.sol @@ -12,15 +12,23 @@ import "../../interfaces/ICurve.sol"; import "../../mixins/Path.sol"; import "../../interfaces/ICurvePoolRegistration.sol"; -contract SwapsImplCurve_ETH is State, ISwapsImpl { +contract SwapsImplCurve_ETH is State { using SafeERC20 for IERC20; using Path for bytes; using BytesLib for bytes; - address public constant PoolRegistry = 0x63fea6E447F120B8Faf85B53cdaD8348e645D80E; //set to address for monitoring Curve pools - bytes4 public constant ExchangeUnderlyingSig = bytes4(keccak256("exchange_underlying(uint256,uint256,uint256,uint256)")); - bytes4 public constant ExchangeSig = bytes4(keccak256("exchange(uint256,uint256,uint256,uint256)")); - bytes4 public constant GetDySig = bytes4(keccak256("get_dy(uint256,uint256,uint256)")); - bytes4 public constant GetDyUnderlyingSig = bytes4(keccak256("get_dy_underlying(uint256,uint256,uint256)")); + address public constant PoolRegistry = + 0x18E317A7D70d8fBf8e6E893616b52390EbBdb629; //set to address for monitoring Curve pools + bytes4 public constant ExchangeUnderlyingSig = + bytes4( + keccak256("exchange_underlying(uint256,uint256,uint256,uint256)") + ); + bytes4 public constant ExchangeSig = + bytes4(keccak256("exchange(uint256,uint256,uint256,uint256)")); + bytes4 public constant GetDySig = + bytes4(keccak256("get_dy(uint256,uint256,uint256)")); + bytes4 public constant GetDyUnderlyingSig = + bytes4(keccak256("get_dy_underlying(uint256,uint256,uint256)")); + function dexSwap( address sourceTokenAddress, address destTokenAddress, @@ -32,7 +40,6 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { bytes memory payload ) public - returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) { require(sourceTokenAddress != destTokenAddress, "source == dest"); @@ -70,13 +77,12 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { address sourceTokenAddress, address destTokenAddress, uint256 sourceTokenAmount - ) public view returns (uint256 expectedRate) { + ) public view returns (uint256 expectedRate) { revert("unsupported"); } function dexAmountOut(bytes memory route, uint256 amountIn) public - returns (uint256 amountOut, address midToken) { if (amountIn == 0) { @@ -88,7 +94,6 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { function dexAmountIn(bytes memory route, uint256 amountOut) public - returns (uint256 amountIn, address midToken) { if (amountOut != 0) { @@ -106,26 +111,24 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { public returns (uint256) { - (bytes4 sig, address curvePool, uint128 tokenIn, uint128 tokenOut) = abi.decode( - path, - (bytes4, address, uint128, uint128) - ); - uint256 amountOut; - if(sig == GetDySig || sig == ExchangeSig){ - amountOut = ICurve(curvePool).get_dy( - int128(tokenOut), - int128(tokenIn), - amountIn - ); - }else if(sig == GetDyUnderlyingSig || sig == ExchangeUnderlyingSig){ - amountOut = ICurve(curvePool).get_dy_underlying( - int128(tokenOut), - int128(tokenIn), - amountIn - ); - }else{ - revert("Unsupported Signature"); - } + (bytes4 sig, address curvePool, uint128 tokenIn, uint128 tokenOut) = abi + .decode(path, (bytes4, address, uint128, uint128)); + uint256 amountOut; + if (sig == GetDySig || sig == ExchangeSig) { + amountOut = ICurve(curvePool).get_dy( + int128(tokenOut), + int128(tokenIn), + amountIn + ); + } else if (sig == GetDyUnderlyingSig || sig == ExchangeUnderlyingSig) { + amountOut = ICurve(curvePool).get_dy_underlying( + int128(tokenOut), + int128(tokenIn), + amountIn + ); + } else { + revert("Unsupported Signature"); + } if (amountOut == 0) { amountOut = uint256(-1); } @@ -136,39 +139,37 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { public returns (uint256) { - (bytes4 sig, address curvePool, uint128 tokenIn, uint128 tokenOut) = abi.decode( - path, - (bytes4, address, uint128, uint128) - ); - uint256 amountIn; - if(sig == GetDySig || sig == ExchangeSig){ - amountIn = ICurve(curvePool).get_dy( - int128(tokenOut), - int128(tokenIn), - amountOut - ); - }else if(sig == GetDyUnderlyingSig || sig == ExchangeUnderlyingSig){ - amountIn = ICurve(curvePool).get_dy_underlying( - int128(tokenOut), - int128(tokenIn), - amountOut - ); - }else{ - revert("Unsupported Signature"); - } + (bytes4 sig, address curvePool, uint128 tokenIn, uint128 tokenOut) = abi + .decode(path, (bytes4, address, uint128, uint128)); + uint256 amountIn; + if (sig == GetDySig || sig == ExchangeSig) { + amountIn = ICurve(curvePool).get_dy( + int128(tokenOut), + int128(tokenIn), + amountOut + ); + } else if (sig == GetDyUnderlyingSig || sig == ExchangeUnderlyingSig) { + amountIn = ICurve(curvePool).get_dy_underlying( + int128(tokenOut), + int128(tokenIn), + amountOut + ); + } else { + revert("Unsupported Signature"); + } if (amountIn == 0) { amountIn = uint256(-1); } return amountIn; } - function setSwapApprovalsCurve(address spender,address[] memory tokens) public { + function setSwapApprovals(address[] memory tokens) public { require( - ICurvePoolRegistration(PoolRegistry).CheckPoolValidity(spender) + ICurvePoolRegistration(PoolRegistry).CheckPoolValidity(tokens[0]) ); for (uint256 i = 1; i < tokens.length; i++) { - IERC20(tokens[i]).safeApprove(spender, 0); - IERC20(tokens[i]).safeApprove(spender, uint256(-1)); + IERC20(tokens[i]).safeApprove(tokens[0], 0); + IERC20(tokens[i]).safeApprove(tokens[0], uint256(-1)); } } @@ -176,13 +177,13 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { internal returns (address) { - address token = address(0); - if(ICurvePoolRegistration(PoolRegistry).getPoolType(pool) == 0){ - token = ICurve(pool).underlying_coins(tokenID); - }else{ - token = ICurve(pool).coins(tokenID); - } - return token; + address token = address(0); + if (ICurvePoolRegistration(PoolRegistry).getPoolType(pool) == 0) { + token = ICurve(pool).underlying_coins(tokenID); + } else { + token = ICurve(pool).coins(tokenID); + } + return token; } function _swapWithCurve( @@ -198,10 +199,12 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { returns (uint256 sourceTokenAmountUsed, uint256 destTokenAmountReceived) { if (requiredDestTokenAmount != 0) { - (bytes4 sig, address curvePool, uint128 tokenIn, uint128 tokenOut) = abi.decode( - payload, - (bytes4, address, uint128, uint128) - ); + ( + bytes4 sig, + address curvePool, + uint128 tokenIn, + uint128 tokenOut + ) = abi.decode(payload, (bytes4, address, uint128, uint128)); require( ICurvePoolRegistration(PoolRegistry).CheckPoolValidity( curvePool @@ -213,16 +216,24 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { payload, requiredDestTokenAmount ); - require( - amountIn <= sourceTokenAmount, "too much" - ); - if(sig==ExchangeUnderlyingSig){ - ICurve(curvePool).exchange_underlying(int128(tokenIn), int128(tokenOut), amountIn, 1); - }else if(sig==ExchangeSig){ - ICurve(curvePool).exchange(int128(tokenIn), int128(tokenOut), amountIn, 1); - }else{ - revert("Unsupported Signature"); - } + require(amountIn <= sourceTokenAmount, "too much"); + if (sig == ExchangeUnderlyingSig) { + ICurve(curvePool).exchange_underlying( + int128(tokenIn), + int128(tokenOut), + amountIn, + 1 + ); + } else if (sig == ExchangeSig) { + ICurve(curvePool).exchange( + int128(tokenIn), + int128(tokenOut), + amountIn, + 1 + ); + } else { + revert("Unsupported Signature"); + } if (receiverAddress != address(this)) { IERC20(destTokenAddress).safeTransfer( receiverAddress, @@ -232,37 +243,45 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { sourceTokenAmountUsed = amountIn; destTokenAmountReceived = requiredDestTokenAmount; } else { - (bytes4 sig, address curvePool, uint128 tokenIn, uint128 tokenOut) = abi.decode( - payload, - (bytes4, address, uint128, uint128) - ); + ( + bytes4 sig, + address curvePool, + uint128 tokenIn, + uint128 tokenOut + ) = abi.decode(payload, (bytes4, address, uint128, uint128)); require( ICurvePoolRegistration(PoolRegistry).CheckPoolValidity( curvePool ) ); - - require(sourceTokenAddress == _getDexNumber(curvePool, tokenIn),"source token number off"); - require(destTokenAddress == _getDexNumber(curvePool, tokenOut),"dest token number off"); + + require( + sourceTokenAddress == _getDexNumber(curvePool, tokenIn), + "source token number off" + ); + require( + destTokenAddress == _getDexNumber(curvePool, tokenOut), + "dest token number off" + ); (uint256 recv, ) = dexAmountOut(payload, minSourceTokenAmount); - if(sig == ExchangeUnderlyingSig){ - ICurve(curvePool).exchange_underlying( - int128(tokenIn), - int128(tokenOut), - minSourceTokenAmount, - 1 - ); - }else if(sig == ExchangeSig){ - ICurve(curvePool).exchange( - int128(tokenIn), - int128(tokenOut), - minSourceTokenAmount, - 1 - ); - }else{ - revert("Unsupported Signature"); - } + if (sig == ExchangeUnderlyingSig) { + ICurve(curvePool).exchange_underlying( + int128(tokenIn), + int128(tokenOut), + minSourceTokenAmount, + 1 + ); + } else if (sig == ExchangeSig) { + ICurve(curvePool).exchange( + int128(tokenIn), + int128(tokenOut), + minSourceTokenAmount, + 1 + ); + } else { + revert("Unsupported Signature"); + } if (receiverAddress != address(this)) { IERC20(destTokenAddress).safeTransfer(receiverAddress, recv); } diff --git a/contracts/swaps/connectors/SwapsImplKyber.sol b/contracts/swaps/connectors/SwapsImplKyber.sol index f523059c..ecf2afb2 100644 --- a/contracts/swaps/connectors/SwapsImplKyber.sol +++ b/contracts/swaps/connectors/SwapsImplKyber.sol @@ -10,17 +10,18 @@ import "../../../interfaces/IPriceFeeds.sol"; import "@openzeppelin-2.5.0/token/ERC20/SafeERC20.sol"; import "../ISwapsImpl.sol"; - -contract SwapsImplKyber is State, ISwapsImpl { +contract SwapsImplKyber is State { using SafeERC20 for IERC20; - address internal constant feeWallet = 0x13ddAC8d492E463073934E2a101e419481970299; + address internal constant feeWallet = + 0x13ddAC8d492E463073934E2a101e419481970299; + + address public constant kyberContract = + 0x818E6FECD516Ecc3849DAf6845e3EC868087B755; // mainnet - address public constant kyberContract = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755; // mainnet //address public constant kyberContract = 0x692f391bCc85cefCe8C237C01e1f636BbD70EA4D; // kovan //address public constant kyberContract = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755; // ropsten - function dexSwap( address sourceTokenAddress, address destTokenAddress, @@ -28,12 +29,17 @@ contract SwapsImplKyber is State, ISwapsImpl { address returnToSenderAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount) + uint256 requiredDestTokenAmount + ) public returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) { require(sourceTokenAddress != destTokenAddress, "source == dest"); - require(supportedTokens[sourceTokenAddress] && supportedTokens[destTokenAddress], "invalid tokens"); + require( + supportedTokens[sourceTokenAddress] && + supportedTokens[destTokenAddress], + "invalid tokens" + ); bytes memory txnData = _getSwapTxnData( sourceTokenAddress, @@ -57,13 +63,18 @@ contract SwapsImplKyber is State, ISwapsImpl { assembly { destTokenAmountReceived := mload(add(returnData, 32)) } - sourceTokenAmountUsed = sourceBalanceBefore.sub(sourceToken.balanceOf(_thisAddress)); + sourceTokenAmountUsed = sourceBalanceBefore.sub( + sourceToken.balanceOf(_thisAddress) + ); - if (returnToSenderAddress != _thisAddress && sourceTokenAmountUsed < maxSourceTokenAmount) { + if ( + returnToSenderAddress != _thisAddress && + sourceTokenAmountUsed < maxSourceTokenAmount + ) { // send unused source token back sourceToken.safeTransfer( returnToSenderAddress, - maxSourceTokenAmount-sourceTokenAmountUsed + maxSourceTokenAmount - sourceTokenAmountUsed ); } } @@ -71,11 +82,8 @@ contract SwapsImplKyber is State, ISwapsImpl { function dexExpectedRate( address sourceTokenAddress, address destTokenAddress, - uint256 sourceTokenAmount) - public - view - returns (uint256 expectedRate) - { + uint256 sourceTokenAmount + ) public view returns (uint256 expectedRate) { if (sourceTokenAddress == destTokenAddress) { expectedRate = WEI_PRECISION; } else if (sourceTokenAmount != 0) { @@ -97,14 +105,10 @@ contract SwapsImplKyber is State, ISwapsImpl { return expectedRate; } - function setSwapApprovals( - address spender, - address[] memory tokens) - public - { + function setSwapApprovals(address[] memory tokens) public { for (uint256 i = 0; i < tokens.length; i++) { - IERC20(tokens[i]).safeApprove(spender, 0); - IERC20(tokens[i]).safeApprove(spender, uint256(-1)); + IERC20(tokens[i]).safeApprove(kyberContract, 0); + IERC20(tokens[i]).safeApprove(kyberContract, uint256(-1)); } } @@ -114,31 +118,29 @@ contract SwapsImplKyber is State, ISwapsImpl { address receiverAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount) - internal - view - returns (bytes memory) - { + uint256 requiredDestTokenAmount + ) internal view returns (bytes memory) { uint256 estimatedSourceAmount; if (requiredDestTokenAmount != 0) { - uint256 sourceToDestPrecision = IPriceFeeds(priceFeeds).queryPrecision( - sourceTokenAddress, - destTokenAddress - ); + uint256 sourceToDestPrecision = IPriceFeeds(priceFeeds) + .queryPrecision(sourceTokenAddress, destTokenAddress); if (sourceToDestPrecision == 0) { return ""; } - uint256 bufferMultiplier = sourceBufferPercent - .add(WEI_PERCENT_PRECISION); + uint256 bufferMultiplier = sourceBufferPercent.add( + WEI_PERCENT_PRECISION + ); estimatedSourceAmount = requiredDestTokenAmount .mul(sourceToDestPrecision) - .div(dexExpectedRate( - sourceTokenAddress, - destTokenAddress, - minSourceTokenAmount - )); + .div( + dexExpectedRate( + sourceTokenAddress, + destTokenAddress, + minSourceTokenAmount + ) + ); if (estimatedSourceAmount == 0) { return ""; } @@ -154,18 +156,19 @@ contract SwapsImplKyber is State, ISwapsImpl { estimatedSourceAmount = minSourceTokenAmount; } - return abi.encodeWithSelector( - 0x29589f61, // keccak("tradeWithHint(address,uint256,address,address,uint256,uint256,address,bytes)") - sourceTokenAddress, - estimatedSourceAmount, - destTokenAddress, - receiverAddress, - requiredDestTokenAmount == 0 || requiredDestTokenAmount > 10**28 ? // maxDestAmount - 10**28 : - requiredDestTokenAmount, - 0, // minConversionRate - feeWallet, - "" // hint - ); + return + abi.encodeWithSelector( + 0x29589f61, // keccak("tradeWithHint(address,uint256,address,address,uint256,uint256,address,bytes)") + sourceTokenAddress, + estimatedSourceAmount, + destTokenAddress, + receiverAddress, + requiredDestTokenAmount == 0 || requiredDestTokenAmount > 10**28 // maxDestAmount + ? 10**28 + : requiredDestTokenAmount, + 0, // minConversionRate + feeWallet, + "" // hint + ); } } diff --git a/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol b/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol index 94acb365..e07c2fbc 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol @@ -10,13 +10,13 @@ import "../../interfaces/IUniswapV2Router.sol"; import "@openzeppelin-2.5.0/token/ERC20/SafeERC20.sol"; import "../ISwapsImpl.sol"; - contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { using SafeERC20 for IERC20; // bsc (PancakeSwap) //address public constant uniswapRouter = 0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F; // PancakeSwap v1 - address public constant uniswapRouter = 0x10ED43C718714eb63d5aA57B78B54704E256024E; // PancakeSwap v2 + address public constant uniswapRouter = + 0x10ED43C718714eb63d5aA57B78B54704E256024E; // PancakeSwap v2 address public constant busd = 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56; address public constant usdt = 0x55d398326f99059fF775485246999027B3197955; @@ -28,12 +28,17 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { address returnToSenderAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount) + uint256 requiredDestTokenAmount + ) public returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) { require(sourceTokenAddress != destTokenAddress, "source == dest"); - require(supportedTokens[sourceTokenAddress] && supportedTokens[destTokenAddress], "invalid tokens"); + require( + supportedTokens[sourceTokenAddress] && + supportedTokens[destTokenAddress], + "invalid tokens" + ); IERC20 sourceToken = IERC20(sourceTokenAddress); address _thisAddress = address(this); @@ -47,11 +52,14 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { requiredDestTokenAmount ); - if (returnToSenderAddress != _thisAddress && sourceTokenAmountUsed < maxSourceTokenAmount) { + if ( + returnToSenderAddress != _thisAddress && + sourceTokenAmountUsed < maxSourceTokenAmount + ) { // send unused source token back sourceToken.safeTransfer( returnToSenderAddress, - maxSourceTokenAmount-sourceTokenAmountUsed + maxSourceTokenAmount - sourceTokenAmountUsed ); } } @@ -59,22 +67,16 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { function dexExpectedRate( address sourceTokenAddress, address destTokenAddress, - uint256 sourceTokenAmount) - public - view - returns (uint256 expectedRate) - { + uint256 sourceTokenAmount + ) public view returns (uint256 expectedRate) { revert("unsupported"); } function dexAmountOut( address sourceTokenAddress, address destTokenAddress, - uint256 amountIn) - public - view - returns (uint256 amountOut, address midToken) - { + uint256 amountIn + ) public view returns (uint256 amountOut, address midToken) { if (sourceTokenAddress == destTokenAddress) { amountOut = amountIn; } else if (amountIn != 0) { @@ -88,8 +90,11 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { path = new address[](3); path[0] = sourceTokenAddress; path[2] = destTokenAddress; - - if (sourceTokenAddress != address(wethToken) && destTokenAddress != address(wethToken)) { + + if ( + sourceTokenAddress != address(wethToken) && + destTokenAddress != address(wethToken) + ) { path[1] = address(wethToken); tmpValue = _getAmountOut(amountIn, path); if (tmpValue > amountOut) { @@ -121,11 +126,8 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { function dexAmountIn( address sourceTokenAddress, address destTokenAddress, - uint256 amountOut) - public - view - returns (uint256 amountIn, address midToken) - { + uint256 amountOut + ) public view returns (uint256 amountIn, address midToken) { if (sourceTokenAddress == destTokenAddress) { amountIn = amountOut; } else if (amountOut != 0) { @@ -139,8 +141,11 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { path = new address[](3); path[0] = sourceTokenAddress; path[2] = destTokenAddress; - - if (sourceTokenAddress != address(wethToken) && destTokenAddress != address(wethToken)) { + + if ( + sourceTokenAddress != address(wethToken) && + destTokenAddress != address(wethToken) + ) { path[1] = address(wethToken); tmpValue = _getAmountIn(amountOut, path); if (tmpValue < amountIn) { @@ -173,9 +178,7 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { } } - function _getAmountOut( - uint256 amountIn, - address[] memory path) + function _getAmountOut(uint256 amountIn, address[] memory path) public view returns (uint256 amountOut) @@ -195,9 +198,7 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { } } - function _getAmountIn( - uint256 amountOut, - address[] memory path) + function _getAmountIn(uint256 amountOut, address[] memory path) public view returns (uint256 amountIn) @@ -220,14 +221,10 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { } } - function setSwapApprovals( - address spender, - address[] memory tokens) - public - { + function setSwapApprovals(address[] memory tokens) public { for (uint256 i = 0; i < tokens.length; i++) { - IERC20(tokens[i]).safeApprove(spender, 0); - IERC20(tokens[i]).safeApprove(spender, uint256(-1)); + IERC20(tokens[i]).safeApprove(uniswapRouter, 0); + IERC20(tokens[i]).safeApprove(uniswapRouter, uint256(-1)); } } @@ -237,7 +234,8 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { address receiverAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount) + uint256 requiredDestTokenAmount + ) internal returns (uint256 sourceTokenAmountUsed, uint256 destTokenAmountReceived) { @@ -251,7 +249,10 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { if (sourceTokenAmountUsed == 0) { return (0, 0); } - require(sourceTokenAmountUsed <= maxSourceTokenAmount, "source amount too high"); + require( + sourceTokenAmountUsed <= maxSourceTokenAmount, + "source amount too high" + ); } else { sourceTokenAmountUsed = minSourceTokenAmount; (destTokenAmountReceived, midToken) = dexAmountOut( @@ -276,13 +277,14 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { path[1] = destTokenAddress; } - uint256[] memory amounts = IUniswapV2Router(uniswapRouter).swapExactTokensForTokens( - sourceTokenAmountUsed, - 1, // amountOutMin - path, - receiverAddress, - block.timestamp - ); + uint256[] memory amounts = IUniswapV2Router(uniswapRouter) + .swapExactTokensForTokens( + sourceTokenAmountUsed, + 1, // amountOutMin + path, + receiverAddress, + block.timestamp + ); destTokenAmountReceived = amounts[amounts.length - 1]; } diff --git a/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol b/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol index 70f9f005..b1526e93 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol @@ -10,18 +10,17 @@ import "../../interfaces/IUniswapV2Router.sol"; import "@openzeppelin-2.5.0/token/ERC20/SafeERC20.sol"; import "../ISwapsImpl.sol"; - -contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { +contract SwapsImplUniswapV2_ETH is State { using SafeERC20 for IERC20; // mainnet //address public constant uniswapRouter = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; // uniswap - address public constant uniswapRouter = 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F; // sushiswap + address public constant uniswapRouter = + 0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F; // sushiswap address public constant dai = 0x6B175474E89094C44Da98b954EedeAC495271d0F; address public constant usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; address public constant usdt = 0xdAC17F958D2ee523a2206206994597C13D831ec7; - function dexSwap( address sourceTokenAddress, address destTokenAddress, @@ -29,16 +28,22 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { address returnToSenderAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount) + uint256 requiredDestTokenAmount, + bytes memory /*payload*/ + ) public returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) { require(sourceTokenAddress != destTokenAddress, "source == dest"); - require(supportedTokens[sourceTokenAddress] && supportedTokens[destTokenAddress], "invalid tokens"); + require( + supportedTokens[sourceTokenAddress] && + supportedTokens[destTokenAddress], + "invalid tokens" + ); IERC20 sourceToken = IERC20(sourceTokenAddress); address _thisAddress = address(this); - + (sourceTokenAmountUsed, destTokenAmountReceived) = _swapWithUni( sourceTokenAddress, destTokenAddress, @@ -47,12 +52,15 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { maxSourceTokenAmount, requiredDestTokenAmount ); - - if (returnToSenderAddress != _thisAddress && sourceTokenAmountUsed < maxSourceTokenAmount) { + + if ( + returnToSenderAddress != _thisAddress && + sourceTokenAmountUsed < maxSourceTokenAmount + ) { // send unused source token back sourceToken.safeTransfer( returnToSenderAddress, - maxSourceTokenAmount-sourceTokenAmountUsed + maxSourceTokenAmount - sourceTokenAmountUsed ); } } @@ -60,22 +68,16 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { function dexExpectedRate( address sourceTokenAddress, address destTokenAddress, - uint256 sourceTokenAmount) - public - view - returns (uint256 expectedRate) - { + uint256 sourceTokenAmount + ) public view returns (uint256 expectedRate) { revert("unsupported"); } function dexAmountOut( address sourceTokenAddress, address destTokenAddress, - uint256 amountIn) - public - view - returns (uint256 amountOut, address midToken) - { + uint256 amountIn + ) public view returns (uint256 amountOut, address midToken) { if (sourceTokenAddress == destTokenAddress) { amountOut = amountIn; } else if (amountIn != 0) { @@ -89,8 +91,11 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { path = new address[](3); path[0] = sourceTokenAddress; path[2] = destTokenAddress; - - if (sourceTokenAddress != address(wethToken) && destTokenAddress != address(wethToken)) { + + if ( + sourceTokenAddress != address(wethToken) && + destTokenAddress != address(wethToken) + ) { path[1] = address(wethToken); tmpValue = _getAmountOut(amountIn, path); if (tmpValue > amountOut) { @@ -131,11 +136,8 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { function dexAmountIn( address sourceTokenAddress, address destTokenAddress, - uint256 amountOut) - public - view - returns (uint256 amountIn, address midToken) - { + uint256 amountOut + ) public view returns (uint256 amountIn, address midToken) { if (sourceTokenAddress == destTokenAddress) { amountIn = amountOut; } else if (amountOut != 0) { @@ -149,8 +151,11 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { path = new address[](3); path[0] = sourceTokenAddress; path[2] = destTokenAddress; - - if (sourceTokenAddress != address(wethToken) && destTokenAddress != address(wethToken)) { + + if ( + sourceTokenAddress != address(wethToken) && + destTokenAddress != address(wethToken) + ) { path[1] = address(wethToken); tmpValue = _getAmountIn(amountOut, path); if (tmpValue < amountIn) { @@ -192,9 +197,7 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { } } - function _getAmountOut( - uint256 amountIn, - address[] memory path) + function _getAmountOut(uint256 amountIn, address[] memory path) public view returns (uint256 amountOut) @@ -214,9 +217,7 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { } } - function _getAmountIn( - uint256 amountOut, - address[] memory path) + function _getAmountIn(uint256 amountOut, address[] memory path) public view returns (uint256 amountIn) @@ -239,14 +240,10 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { } } - function setSwapApprovals( - address spender, - address[] memory tokens) - public - { + function setSwapApprovals(address[] memory tokens) public { for (uint256 i = 0; i < tokens.length; i++) { - IERC20(tokens[i]).safeApprove(spender, 0); - IERC20(tokens[i]).safeApprove(spender, uint256(-1)); + IERC20(tokens[i]).safeApprove(uniswapRouter, 0); + IERC20(tokens[i]).safeApprove(uniswapRouter, uint256(-1)); } } @@ -256,7 +253,8 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { address receiverAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount) + uint256 requiredDestTokenAmount + ) internal returns (uint256 sourceTokenAmountUsed, uint256 destTokenAmountReceived) { @@ -270,7 +268,10 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { if (sourceTokenAmountUsed == 0) { return (0, 0); } - require(sourceTokenAmountUsed <= maxSourceTokenAmount, "source amount too high"); + require( + sourceTokenAmountUsed <= maxSourceTokenAmount, + "source amount too high" + ); } else { sourceTokenAmountUsed = minSourceTokenAmount; (destTokenAmountReceived, midToken) = dexAmountOut( @@ -295,13 +296,14 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { path[1] = destTokenAddress; } - uint256[] memory amounts = IUniswapV2Router(uniswapRouter).swapExactTokensForTokens( - sourceTokenAmountUsed, - 1, // amountOutMin - path, - receiverAddress, - block.timestamp - ); + uint256[] memory amounts = IUniswapV2Router(uniswapRouter) + .swapExactTokensForTokens( + sourceTokenAmountUsed, + 1, // amountOutMin + path, + receiverAddress, + block.timestamp + ); destTokenAmountReceived = amounts[amounts.length - 1]; } diff --git a/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol b/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol index ac7d5ca3..806c5c57 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol @@ -10,11 +10,11 @@ import "../../interfaces/IUniswapV2Router.sol"; import "@openzeppelin-2.5.0/token/ERC20/SafeERC20.sol"; import "../ISwapsImpl.sol"; - contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { using SafeERC20 for IERC20; - address public constant uniswapRouter = 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506; // Sushiswap + address public constant uniswapRouter = + 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506; // Sushiswap //address public constant uniswapRouter = 0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff; // QuickSwap address public constant eth = 0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619; @@ -29,12 +29,17 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { address returnToSenderAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount) + uint256 requiredDestTokenAmount + ) public returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) { require(sourceTokenAddress != destTokenAddress, "source == dest"); - require(supportedTokens[sourceTokenAddress] && supportedTokens[destTokenAddress], "invalid tokens"); + require( + supportedTokens[sourceTokenAddress] && + supportedTokens[destTokenAddress], + "invalid tokens" + ); IERC20 sourceToken = IERC20(sourceTokenAddress); address _thisAddress = address(this); @@ -48,11 +53,14 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { requiredDestTokenAmount ); - if (returnToSenderAddress != _thisAddress && sourceTokenAmountUsed < maxSourceTokenAmount) { + if ( + returnToSenderAddress != _thisAddress && + sourceTokenAmountUsed < maxSourceTokenAmount + ) { // send unused source token back sourceToken.safeTransfer( returnToSenderAddress, - maxSourceTokenAmount-sourceTokenAmountUsed + maxSourceTokenAmount - sourceTokenAmountUsed ); } } @@ -60,22 +68,16 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { function dexExpectedRate( address sourceTokenAddress, address destTokenAddress, - uint256 sourceTokenAmount) - public - view - returns (uint256 expectedRate) - { + uint256 sourceTokenAmount + ) public view returns (uint256 expectedRate) { revert("unsupported"); } function dexAmountOut( address sourceTokenAddress, address destTokenAddress, - uint256 amountIn) - public - view - returns (uint256 amountOut, address midToken) - { + uint256 amountIn + ) public view returns (uint256 amountOut, address midToken) { if (sourceTokenAddress == destTokenAddress) { amountOut = amountIn; } else if (amountIn != 0) { @@ -89,8 +91,11 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { path = new address[](3); path[0] = sourceTokenAddress; path[2] = destTokenAddress; - - if (sourceTokenAddress != address(wethToken) && destTokenAddress != address(wethToken)) { + + if ( + sourceTokenAddress != address(wethToken) && + destTokenAddress != address(wethToken) + ) { path[1] = address(wethToken); tmpValue = _getAmountOut(amountIn, path); if (tmpValue > amountOut) { @@ -140,11 +145,8 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { function dexAmountIn( address sourceTokenAddress, address destTokenAddress, - uint256 amountOut) - public - view - returns (uint256 amountIn, address midToken) - { + uint256 amountOut + ) public view returns (uint256 amountIn, address midToken) { if (sourceTokenAddress == destTokenAddress) { amountIn = amountOut; } else if (amountOut != 0) { @@ -158,8 +160,11 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { path = new address[](3); path[0] = sourceTokenAddress; path[2] = destTokenAddress; - - if (sourceTokenAddress != address(wethToken) && destTokenAddress != address(wethToken)) { + + if ( + sourceTokenAddress != address(wethToken) && + destTokenAddress != address(wethToken) + ) { path[1] = address(wethToken); tmpValue = _getAmountIn(amountOut, path); if (tmpValue < amountIn) { @@ -210,9 +215,7 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } } - function _getAmountOut( - uint256 amountIn, - address[] memory path) + function _getAmountOut(uint256 amountIn, address[] memory path) public view returns (uint256 amountOut) @@ -232,9 +235,7 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } } - function _getAmountIn( - uint256 amountOut, - address[] memory path) + function _getAmountIn(uint256 amountOut, address[] memory path) public view returns (uint256 amountIn) @@ -257,14 +258,10 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } } - function setSwapApprovals( - address spender, - address[] memory tokens) - public - { + function setSwapApprovals(address[] memory tokens) public { for (uint256 i = 0; i < tokens.length; i++) { - IERC20(tokens[i]).safeApprove(spender, 0); - IERC20(tokens[i]).safeApprove(spender, uint256(-1)); + IERC20(tokens[i]).safeApprove(uniswapRouter, 0); + IERC20(tokens[i]).safeApprove(uniswapRouter, uint256(-1)); } } @@ -274,7 +271,8 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { address receiverAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount) + uint256 requiredDestTokenAmount + ) internal returns (uint256 sourceTokenAmountUsed, uint256 destTokenAmountReceived) { @@ -288,7 +286,10 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { if (sourceTokenAmountUsed == 0) { return (0, 0); } - require(sourceTokenAmountUsed <= maxSourceTokenAmount, "source amount too high"); + require( + sourceTokenAmountUsed <= maxSourceTokenAmount, + "source amount too high" + ); } else { sourceTokenAmountUsed = minSourceTokenAmount; (destTokenAmountReceived, midToken) = dexAmountOut( @@ -313,13 +314,14 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { path[1] = destTokenAddress; } - uint256[] memory amounts = IUniswapV2Router(uniswapRouter).swapExactTokensForTokens( - sourceTokenAmountUsed, - 1, // amountOutMin - path, - receiverAddress, - block.timestamp - ); + uint256[] memory amounts = IUniswapV2Router(uniswapRouter) + .swapExactTokensForTokens( + sourceTokenAmountUsed, + 1, // amountOutMin + path, + receiverAddress, + block.timestamp + ); destTokenAmountReceived = amounts[amounts.length - 1]; } diff --git a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol index 6108be2a..342db656 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol @@ -9,10 +9,10 @@ import "../../core/State.sol"; import "@openzeppelin-2.5.0/token/ERC20/SafeERC20.sol"; import "../ISwapsImpl.sol"; import "../../interfaces/IUniswapV3SwapRouter.sol"; -import "../../interfaces/uniswapQuoter.sol"; +import "../../interfaces/IUniswapQuoter.sol"; import "../../mixins/Path.sol"; -contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { +contract SwapsImplUniswapV3_ETH is State { using SafeERC20 for IERC20; using Path for bytes; using BytesLib for bytes; @@ -32,7 +32,6 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { bytes memory payload ) public - returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) { require(sourceTokenAddress != destTokenAddress, "source == dest"); @@ -48,7 +47,7 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { sourceTokenAddress, destTokenAddress, receiverAddress, - minSourceTokenAmount, + minSourceTokenAmount, maxSourceTokenAmount, requiredDestTokenAmount, payload @@ -70,13 +69,12 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { address sourceTokenAddress, address destTokenAddress, uint256 sourceTokenAmount - ) public view returns (uint256 expectedRate) { + ) public view returns (uint256 expectedRate) { revert("unsupported"); } function dexAmountOut(bytes memory route, uint256 amountIn) public - returns (uint256 amountOut, address midToken) { if (amountIn == 0) { @@ -88,7 +86,6 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { function dexAmountIn(bytes memory route, uint256 amountOut) public - returns (uint256 amountIn, address midToken) { if (amountOut != 0) { @@ -106,8 +103,10 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { public returns (uint256) { - uint256 amountOut = uniswapQuoter(uniswapQuoteContract) - .quoteExactInput(path, amountIn); + uint256 amountOut = IUniswapQuoter(uniswapQuoteContract).quoteExactInput( + path, + amountIn + ); if (amountOut == 0) { amountOut = uint256(-1); } @@ -118,15 +117,17 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { public returns (uint256) { - uint256 amountIn = uniswapQuoter(uniswapQuoteContract) - .quoteExactOutput(path, amountOut); + uint256 amountIn = IUniswapQuoter(uniswapQuoteContract).quoteExactOutput( + path, + amountOut + ); if (amountIn == 0) { amountIn = uint256(-1); } return amountIn; } - function setSwapApprovals(address[] memory tokens) public { + function setSwapApprovals(address[] memory tokens) public { for (uint256 i = 0; i < tokens.length; i++) { IERC20(tokens[i]).safeApprove(uniswapSwapRouter, 0); IERC20(tokens[i]).safeApprove(uniswapSwapRouter, uint256(-1)); @@ -137,7 +138,7 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { address sourceTokenAddress, address destTokenAddress, address receiverAddress, - uint256 minSourceTokenAmount, + uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, uint256 requiredDestTokenAmount, bytes memory payload @@ -161,28 +162,33 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { ) == sourceTokenAddress, "improper source" ); - exactParams[x].amountOut = requiredDestTokenAmount.mul(exactParams[x].amountOut).div(100); - exactParams[x].amountInMaximum = maxSourceTokenAmount.mul(exactParams[x].amountInMaximum).div(100); + exactParams[x].amountOut = requiredDestTokenAmount + .mul(exactParams[x].amountOut) + .div(100); + exactParams[x].amountInMaximum = maxSourceTokenAmount + .mul(exactParams[x].amountInMaximum) + .div(100); totalAmountsOut = totalAmountsOut.add(exactParams[x].amountOut); - totalAmountsInMax = totalAmountsInMax.add(exactParams[x].amountInMaximum); - + totalAmountsInMax = totalAmountsInMax.add( + exactParams[x].amountInMaximum + ); + encodedTXs[x] = abi.encodeWithSelector( IUniswapV3SwapRouter(uniswapSwapRouter) .exactOutput .selector, exactParams[x] ); - } require( totalAmountsOut == requiredDestTokenAmount && totalAmountsInMax <= maxSourceTokenAmount ); - - bytes[] memory trueAmountsIn = IUniswapV3SwapRouter( + + bytes[] memory trueAmountsIn = IUniswapV3SwapRouter( uniswapSwapRouter ).multicall(encodedTXs); - + uint256 totaledAmountIn = 0; for (uint256 x = 0; x < trueAmountsIn.length; x++) { uint256 tempAmountIn = abi.decode(trueAmountsIn[x], (uint256)); @@ -190,7 +196,6 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { } sourceTokenAmountUsed = totaledAmountIn; destTokenAmountReceived = requiredDestTokenAmount; - } else { IUniswapV3SwapRouter.ExactInputParams[] memory exactParams = abi .decode(payload, (IUniswapV3SwapRouter.ExactInputParams[])); @@ -204,7 +209,10 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { exactParams[x].path.length - 20 ); require(tokenOut == destTokenAddress, "improper destination"); - exactParams[x].amountIn = exactParams[x].amountIn.mul(minSourceTokenAmount).div(100); //amountIn on data is % of funds to use per route. Total should add to source token amount or else it fails. take into consideration rounding + exactParams[x].amountIn = exactParams[x] + .amountIn + .mul(minSourceTokenAmount) + .div(100); //amountIn on data is % of funds to use per route. Total should add to source token amount or else it fails. take into consideration rounding totalAmounts = totalAmounts.add(exactParams[x].amountIn); encodedTXs[x] = abi.encodeWithSelector( IUniswapV3SwapRouter(uniswapSwapRouter).exactInput.selector, @@ -212,7 +220,10 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { ); } sourceTokenAmountUsed = totalAmounts; - require(totalAmounts == minSourceTokenAmount, "improper swap amounts"); + require( + totalAmounts == minSourceTokenAmount, + "improper swap amounts" + ); bytes[] memory trueAmountsOut = IUniswapV3SwapRouter( uniswapSwapRouter ).multicall(encodedTXs); From a7af5e025044f44a9126cf3072d63f92e863d1e8 Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Mon, 27 Dec 2021 14:02:11 -0800 Subject: [PATCH 06/83] Test Case --- tests/test_selector.py | 91 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 tests/test_selector.py diff --git a/tests/test_selector.py b/tests/test_selector.py new file mode 100644 index 00000000..ff486eee --- /dev/null +++ b/tests/test_selector.py @@ -0,0 +1,91 @@ +from brownie import * +from eth_abi import encode_abi, is_encodable, encode_single +from eth_abi.packed import encode_single_packed, encode_abi_packed +from hexbytes import HexBytes +import pytest +timelock = '0xfedC4dD5247B93feb41e899A09C44cFaBec29Cbc' +impersonate = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266' +trader = '0xE78388b4CE79068e89Bf8aA7f218eF6b9AB0e9d0' +Gas = 130e9 +def test_initialize(): #run this first and replace the contract address of PoolRegistry in SwapsImplCurve_ETH.sol then run test_main() + registration = CurvePoolRegistration.deploy({'from':impersonate,'gas_price':Gas}) + registration.addPool('0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7',1,{'from':impersonate,'gas_price':Gas}) + print(registration.address) + assert(False) + +def test_main(): + mainState = Contract.from_abi("bZxProtocol","0xD8Ee69652E4e4838f2531732a46d1f7F584F0b7f",bZxProtocol.abi) + dexSelector = DexRecords.deploy({'from':impersonate,'gas_price':Gas}) + impl = SwapsImplCurve_ETH.deploy({'from':impersonate,'gas_price':Gas}) + impl2 = SwapsImplUniswapV2_ETH.deploy({'from':impersonate,'gas_price':Gas}) + impl3 = SwapsImplUniswapV3_ETH.deploy({'from':impersonate,'gas_price':Gas}) + dexSelector.setDexID(impl2.address,{'from':impersonate,'gas_price':Gas}) + dexSelector.setDexID(impl.address,{'from':impersonate,'gas_price':Gas}) + dexSelector.setDexID(impl3.address,{'from':impersonate,'gas_price':Gas}) + web3.eth.sendTransaction({ "from": impersonate, "to": timelock, "value": str(1*10**18), "gas": "21000", 'gas_price':Gas }) #provide ETH for execs + cc = ChangeSwapImpl.deploy({'from':impersonate,'gas_price':Gas}) + mainState.replaceContract(cc.address,{'from':timelock,'gas_price':Gas}) + changeImplProtocol = Contract.from_abi("Protocol","0xD8Ee69652E4e4838f2531732a46d1f7F584F0b7f",ChangeSwapImpl.abi) + changeImplProtocol.changeImpl(dexSelector.address,{'from':timelock,'gas_price':Gas}) + loanClose = LoanClosings.deploy({'from':timelock,'gas_price':Gas}) + loanOpen = LoanOpenings.deploy({'from':timelock,'gas_price':Gas}) + mainState.replaceContract(loanClose.address,{'from':timelock,'gas_price':Gas}) + mainState.replaceContract(loanOpen.address,{'from':timelock,'gas_price':Gas}) + trade_univ2(mainState,dexSelector) + trade_curve(mainState,dexSelector) + trade_univ3(mainState,dexSelector) + assert(False) + + + +def trade_curve(mainState,dexSelector): + mainState = interface.IBZx(mainState.address) + mainState.setTargets(["setSwapApprovals(address[])"],[dexSelector.dexes.call(2)],{'from':timelock,'gas_price':Gas}) + swaps1 = Contract.from_abi('Impl',mainState.address,SwapsImplCurve_ETH.abi) + swaps1.setSwapApprovals(['0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7','0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48','0xdac17f958d2ee523a2206206994597c13d831ec7'],{'from':timelock,'gas_price':Gas}) + swapImpl = Contract.from_abi("v3",dexSelector.retrieveDexAddress.call(2),SwapsImplCurve_ETH.abi) + sendOut = encode_abi(['bytes4','address','uint128','uint128'],[HexBytes(swapImpl.ExchangeSig.call()),"0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7",1,2]) + sendOut = encode_abi(['uint256','bytes'],[2,sendOut]) + iToken = Contract.from_abi('i','0x32E4c68B3A4a813b710595AebA7f6B7604Ab9c15',LoanTokenLogicStandard.abi) + interface.IERC20('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48').approve(iToken.address,1000000000000e6,{'from':trader,'gas_price':Gas}) + tradeReturn = iToken.marginTrade(0,2e18,100000e6,0,"0xdac17f958d2ee523a2206206994597c13d831ec7",trader,sendOut.hex(),{'from':trader,'gas_price':Gas}).return_value + print(""+str(tradeReturn[1])+" "+str(tradeReturn[2])) #prints principal and collateral + sendOut = encode_abi(['bytes4','address','uint128','uint128'],[HexBytes(swapImpl.ExchangeSig.call()),"0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7",2,1]) + sendOut = encode_abi(['uint256','bytes'],[2,sendOut]) + mainState.closeWithSwap(tradeReturn[0],trader,10e6,True,sendOut.hex(),{'from':trader,'gas_price':Gas}) + + +def trade_univ3(mainState,dexSelector): + mainState = interface.IBZx(mainState.address) + mainState.setTargets(["setSwapApprovals(address[])"],[dexSelector.dexes.call(3)],{'from':timelock,'gas_price':Gas}) + swaps1 = Contract.from_abi('Impl',mainState.address,SwapsImplUniswapV3_ETH.abi) + swaps1.setSwapApprovals(['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48','0xdac17f958d2ee523a2206206994597c13d831ec7'],{'from':timelock,'gas_price':Gas}) + + route = encode_abi_packed(['address','uint24','address'],["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",500,"0xdAC17F958D2ee523a2206206994597C13D831ec7"]) + totalPassage = encode_abi(['(bytes,address,uint256,uint256,uint256)[]'],[[(route,mainState.address,1643224769,100,100)]]) + sendOut = encode_abi(['uint256','bytes'],[3,totalPassage]) + iToken = Contract.from_abi('i','0x32E4c68B3A4a813b710595AebA7f6B7604Ab9c15',LoanTokenLogicStandard.abi) + + tradeReturn = iToken.marginTrade(0,2e18,100000e6,0,"0xdAC17F958D2ee523a2206206994597C13D831ec7",trader,sendOut.hex(),{'from':trader,'gas_price':Gas}).return_value + print(""+str(tradeReturn[1])+" "+str(tradeReturn[2])) #prints principal and collateral + route = encode_abi_packed(['address','uint24','address'],["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",500,"0xdAC17F958D2ee523a2206206994597C13D831ec7"]) + totalPassage = encode_abi(['(bytes,address,uint256,uint256,uint256)[]'],[[(route,mainState.address,1643224769,100,100)]]) + sendOut = encode_abi(['uint256','bytes'],[3,totalPassage]) + mainState.closeWithSwap(tradeReturn[0],trader,10e6,True,sendOut.hex(),{'from':trader,'gas_price':Gas}) + +def trade_univ2(mainState,dexSelector): + mainState = interface.IBZx(mainState.address) + mainState.setTargets(["setSwapApprovals(address[])"],[dexSelector.dexes.call(1)],{'from':timelock,'gas_price':Gas}) + swaps1 = Contract.from_abi('Impl',mainState.address,SwapsImplUniswapV2_ETH.abi) + swaps1.setSwapApprovals(['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48','0xdac17f958d2ee523a2206206994597c13d831ec7'],{'from':timelock,'gas_price':Gas}) + iToken = Contract.from_abi('i','0x32E4c68B3A4a813b710595AebA7f6B7604Ab9c15',LoanTokenLogicStandard.abi) + interface.IERC20('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48').approve(iToken.address,1000000000000e6,{'from':trader,'gas_price':50e9}) + tradeReturn = iToken.marginTrade(0,2e18,100000e6,0,"0xdac17f958d2ee523a2206206994597c13d831ec7",trader,HexBytes(''),{'from':trader,'gas_price':Gas}).return_value + print(""+str(tradeReturn[1])+" "+str(tradeReturn[2])) #prints principal and collateral + mainState.closeWithSwap(tradeReturn[0],trader,10e6,True,HexBytes(''),{'from':trader,'gas_price':Gas}) + + + + + + From fdd5fc84020272ec5d2e06b301a23cef36cf12ed Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Mon, 27 Dec 2021 18:27:31 -0800 Subject: [PATCH 07/83] standardized inputs --- contracts/swaps/ISwapsImpl.sol | 2 +- .../swaps/connectors/SwapsImplCurve_ETH.sol | 2 +- .../connectors/SwapsImplUniswapV2_ETH.sol | 22 +++++++++---------- .../connectors/SwapsImplUniswapV3_ETH.sol | 2 +- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/contracts/swaps/ISwapsImpl.sol b/contracts/swaps/ISwapsImpl.sol index 16efe7ae..6823081c 100644 --- a/contracts/swaps/ISwapsImpl.sol +++ b/contracts/swaps/ISwapsImpl.sol @@ -36,6 +36,6 @@ interface ISwapsImpl { external returns (uint256 amountIn, address midToken); - function setSwapApprovals(address spender, address[] calldata tokens) + function setSwapApprovals(address[] calldata tokens) external; } diff --git a/contracts/swaps/connectors/SwapsImplCurve_ETH.sol b/contracts/swaps/connectors/SwapsImplCurve_ETH.sol index 9edefce1..40bb6aa4 100644 --- a/contracts/swaps/connectors/SwapsImplCurve_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplCurve_ETH.sol @@ -12,7 +12,7 @@ import "../../interfaces/ICurve.sol"; import "../../mixins/Path.sol"; import "../../interfaces/ICurvePoolRegistration.sol"; -contract SwapsImplCurve_ETH is State { +contract SwapsImplCurve_ETH is State, ISwapsImpl { using SafeERC20 for IERC20; using Path for bytes; using BytesLib for bytes; diff --git a/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol b/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol index b1526e93..d1f81a9c 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol @@ -10,7 +10,7 @@ import "../../interfaces/IUniswapV2Router.sol"; import "@openzeppelin-2.5.0/token/ERC20/SafeERC20.sol"; import "../ISwapsImpl.sol"; -contract SwapsImplUniswapV2_ETH is State { +contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { using SafeERC20 for IERC20; // mainnet @@ -29,7 +29,7 @@ contract SwapsImplUniswapV2_ETH is State { uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, uint256 requiredDestTokenAmount, - bytes memory /*payload*/ + bytes memory payload ) public returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) @@ -74,10 +74,10 @@ contract SwapsImplUniswapV2_ETH is State { } function dexAmountOut( - address sourceTokenAddress, - address destTokenAddress, + bytes memory payload, uint256 amountIn - ) public view returns (uint256 amountOut, address midToken) { + ) public returns (uint256 amountOut, address midToken) { + (address sourceTokenAddress, address destTokenAddress) = abi.decode(payload,(address,address)); if (sourceTokenAddress == destTokenAddress) { amountOut = amountIn; } else if (amountIn != 0) { @@ -134,10 +134,10 @@ contract SwapsImplUniswapV2_ETH is State { } function dexAmountIn( - address sourceTokenAddress, - address destTokenAddress, + bytes memory payload, uint256 amountOut - ) public view returns (uint256 amountIn, address midToken) { + ) public returns (uint256 amountIn, address midToken) { + (address sourceTokenAddress, address destTokenAddress) = abi.decode(payload,(address,address)); if (sourceTokenAddress == destTokenAddress) { amountIn = amountOut; } else if (amountOut != 0) { @@ -261,8 +261,7 @@ contract SwapsImplUniswapV2_ETH is State { address midToken; if (requiredDestTokenAmount != 0) { (sourceTokenAmountUsed, midToken) = dexAmountIn( - sourceTokenAddress, - destTokenAddress, + abi.encode(sourceTokenAddress,destTokenAddress), requiredDestTokenAmount ); if (sourceTokenAmountUsed == 0) { @@ -275,8 +274,7 @@ contract SwapsImplUniswapV2_ETH is State { } else { sourceTokenAmountUsed = minSourceTokenAmount; (destTokenAmountReceived, midToken) = dexAmountOut( - sourceTokenAddress, - destTokenAddress, + abi.encode(sourceTokenAddress,destTokenAddress), sourceTokenAmountUsed ); if (destTokenAmountReceived == 0) { diff --git a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol index 342db656..04e84335 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol @@ -12,7 +12,7 @@ import "../../interfaces/IUniswapV3SwapRouter.sol"; import "../../interfaces/IUniswapQuoter.sol"; import "../../mixins/Path.sol"; -contract SwapsImplUniswapV3_ETH is State { +contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { using SafeERC20 for IERC20; using Path for bytes; using BytesLib for bytes; From d3f0f24e37dfb6cd24c2a2aa37d06fe229bd3cb3 Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Mon, 27 Dec 2021 19:15:24 -0800 Subject: [PATCH 08/83] further cleanup --- .../connectors/SwapsImplUniswapV2_BSC.sol | 21 +++++++++---------- .../connectors/SwapsImplUniswapV2_POLYGON.sol | 18 +++++++--------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol b/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol index e07c2fbc..e758e400 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol @@ -28,7 +28,8 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { address returnToSenderAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount + uint256 requiredDestTokenAmount, + bytes memory payload ) public returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) @@ -73,10 +74,10 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { } function dexAmountOut( - address sourceTokenAddress, - address destTokenAddress, + bytes memory payload, uint256 amountIn - ) public view returns (uint256 amountOut, address midToken) { + ) public returns (uint256 amountOut, address midToken) { + (address sourceTokenAddress, address destTokenAddress) = abi.decode(payload,(address,address)); if (sourceTokenAddress == destTokenAddress) { amountOut = amountIn; } else if (amountIn != 0) { @@ -124,10 +125,10 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { } function dexAmountIn( - address sourceTokenAddress, - address destTokenAddress, + bytes memory payload, uint256 amountOut - ) public view returns (uint256 amountIn, address midToken) { + ) public returns (uint256 amountIn, address midToken) { + (address sourceTokenAddress, address destTokenAddress) = abi.decode(payload,(address,address)); if (sourceTokenAddress == destTokenAddress) { amountIn = amountOut; } else if (amountOut != 0) { @@ -242,8 +243,7 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { address midToken; if (requiredDestTokenAmount != 0) { (sourceTokenAmountUsed, midToken) = dexAmountIn( - sourceTokenAddress, - destTokenAddress, + abi.encode(sourceTokenAddress, destTokenAddress), requiredDestTokenAmount ); if (sourceTokenAmountUsed == 0) { @@ -256,8 +256,7 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { } else { sourceTokenAmountUsed = minSourceTokenAmount; (destTokenAmountReceived, midToken) = dexAmountOut( - sourceTokenAddress, - destTokenAddress, + abi.encode(sourceTokenAddress, destTokenAddress), sourceTokenAmountUsed ); if (destTokenAmountReceived == 0) { diff --git a/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol b/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol index 806c5c57..d4c6aa7f 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol @@ -74,10 +74,10 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } function dexAmountOut( - address sourceTokenAddress, - address destTokenAddress, + bytes memory payload, uint256 amountIn - ) public view returns (uint256 amountOut, address midToken) { + ) public returns (uint256 amountOut, address midToken) { + (address sourceTokenAddress, address destTokenAddress) = abi.decode(payload,(address,address)); if (sourceTokenAddress == destTokenAddress) { amountOut = amountIn; } else if (amountIn != 0) { @@ -143,10 +143,10 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } function dexAmountIn( - address sourceTokenAddress, - address destTokenAddress, + bytes memory payload, uint256 amountOut - ) public view returns (uint256 amountIn, address midToken) { + ) public returns (uint256 amountIn, address midToken) { + (address sourceTokenAddress, address destTokenAddress) = abi.decode(payload,(address,address)); if (sourceTokenAddress == destTokenAddress) { amountIn = amountOut; } else if (amountOut != 0) { @@ -279,8 +279,7 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { address midToken; if (requiredDestTokenAmount != 0) { (sourceTokenAmountUsed, midToken) = dexAmountIn( - sourceTokenAddress, - destTokenAddress, + abi.encode(sourceTokenAddress, destTokenAddress), requiredDestTokenAmount ); if (sourceTokenAmountUsed == 0) { @@ -293,8 +292,7 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } else { sourceTokenAmountUsed = minSourceTokenAmount; (destTokenAmountReceived, midToken) = dexAmountOut( - sourceTokenAddress, - destTokenAddress, + abi.encode(sourceTokenAddress, destTokenAddress), sourceTokenAmountUsed ); if (destTokenAmountReceived == 0) { From 9ef357098bc6eb131dd0a09ffd63e9485883e8ea Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Mon, 27 Dec 2021 19:32:37 -0800 Subject: [PATCH 09/83] Formatting Specification --- interfaces/IBZx.sol | 21 +++++++++++++++++++++ interfaces/IToken.sol | 20 ++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/interfaces/IBZx.sol b/interfaces/IBZx.sol index c45e868d..30b5b7f6 100644 --- a/interfaces/IBZx.sol +++ b/interfaces/IBZx.sol @@ -406,6 +406,27 @@ interface IBZx { uint256 swapAmount, // denominated in collateralToken bool returnTokenIsCollateral, // true: withdraws collateralToken, false: withdraws loanToken bytes calldata loanDataBytes + /* Format: + Uniswap v3: encode (uint256,bytes) + uint256 is the ID for uniswap v3 implementation on dex selector contract + bytes is payload for the routes for swapping on uniswap v3 + Format: encode ExactInputParams[] or ExactOutputParams[] + Allows for multiple swap routes and arbitrarily long swap route as long as start is the source asset and end is destination asset + amountIn/amountOut and amountOutMinimum/amountInMaximum are the percent value to allocate balance for each route IE 100% alloc would be 100 and 100 for the inputs + excess swap amount not accounted for due to incorrectly inputting percentages results in adding the swap amount to the first route + Sushiswap: enter nothing OR encode (uint256,bytes) + uint256 is 1 (sushiswap is always ID 1) + bytes is blank + Curve: encode (uint256,bytes) + uint256 is the ID for curve implementation on dex selector contract + bytes is payload for curve swap route and tokens + Format: encode (bytes4,address,uint128,uint128) + bytes4 is signature for style of execution (depends on Curve pool type) + address: curve pool address + uint128: source asset ID for curve pool (is validated to ensure the source asset matches the ID specified) + uint128: dest asset ID for curve pool (is validated to ensure the source asset matches the ID specified) + */ + ) external returns ( diff --git a/interfaces/IToken.sol b/interfaces/IToken.sol index 4fb07b2a..0c2bd974 100644 --- a/interfaces/IToken.sol +++ b/interfaces/IToken.sol @@ -72,6 +72,26 @@ interface IToken { address collateralTokenAddress, address trader, bytes memory loanDataBytes // arbitrary order data + /* Format: + Uniswap v3: encode (uint256,bytes) + uint256 is the ID for uniswap v3 implementation on dex selector contract + bytes is payload for the routes for swapping on uniswap v3 + Format: encode ExactInputParams[] or ExactOutputParams[] + Allows for multiple swap routes and arbitrarily long swap route as long as start is the source asset and end is destination asset + amountIn/amountOut and amountOutMinimum/amountInMaximum are the percent value to allocate balance for each route IE 100% alloc would be 100 and 100 for the inputs + excess swap amount not accounted for due to incorrectly inputting percentages results in adding the swap amount to the first route + Sushiswap: enter nothing OR encode (uint256,bytes) + uint256 is 1 (sushiswap is always ID 1) + bytes is blank + Curve: encode (uint256,bytes) + uint256 is the ID for curve implementation on dex selector contract + bytes is payload for curve swap route and tokens + Format: encode (bytes4,address,uint128,uint128) + bytes4 is signature for style of execution (depends on Curve pool type) + address: curve pool address + uint128: source asset ID for curve pool (is validated to ensure the source asset matches the ID specified) + uint128: dest asset ID for curve pool (is validated to ensure the source asset matches the ID specified) + */ ) external payable returns (LoanOpenData memory); function marginTradeWithGasToken( From 9f264136dbef4707ca5a3bb573c59349e41a41a1 Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Mon, 27 Dec 2021 22:07:43 -0800 Subject: [PATCH 10/83] edge case handling --- .../modules/LoanOpenings/LoanOpenings.sol | 1 - .../connectors/SwapsImplUniswapV3_ETH.sol | 30 +++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/contracts/modules/LoanOpenings/LoanOpenings.sol b/contracts/modules/LoanOpenings/LoanOpenings.sol index b3c9077d..9610ce33 100644 --- a/contracts/modules/LoanOpenings/LoanOpenings.sol +++ b/contracts/modules/LoanOpenings/LoanOpenings.sol @@ -341,7 +341,6 @@ contract LoanOpenings is false, // bypassFee loanDataBytes ); - //require(1==2,"check4"); sentValues[4] = sentValues[4].add(receivedAmount); // collateralTokenReceived } diff --git a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol index 04e84335..48cb0e17 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol @@ -180,10 +180,26 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { exactParams[x] ); } + if(totalAmountsOut Date: Wed, 29 Dec 2021 12:08:53 -0800 Subject: [PATCH 11/83] resolve conflicts --- .../modules/LoanClosings/LoanClosings.sol | 64 +- .../modules/LoanClosings/LoanClosingsBase.sol | 687 +++++------------- .../modules/LoanOpenings/LoanOpenings.sol | 571 ++++++--------- interfaces/IBZx.sol | 5 +- 4 files changed, 442 insertions(+), 885 deletions(-) diff --git a/contracts/modules/LoanClosings/LoanClosings.sol b/contracts/modules/LoanClosings/LoanClosings.sol index a2017139..05d4da92 100644 --- a/contracts/modules/LoanClosings/LoanClosings.sol +++ b/contracts/modules/LoanClosings/LoanClosings.sol @@ -8,10 +8,15 @@ pragma experimental ABIEncoderV2; import "./LoanClosingsBase.sol"; + contract LoanClosings is LoanClosingsBase { - function initialize(address target) external onlyOwner { + + function initialize( + address target) + external + onlyOwner + { _setTarget(this.liquidate.selector, target); - _setTarget(this.rollover.selector, target); _setTarget(this.closeWithDeposit.selector, target); _setTarget(this.closeWithSwap.selector, target); } @@ -19,8 +24,7 @@ contract LoanClosings is LoanClosingsBase { function liquidate( bytes32 loanId, address receiver, - uint256 closeAmount // denominated in loanToken - ) + uint256 closeAmount) // denominated in loanToken external payable nonReentrant @@ -30,31 +34,17 @@ contract LoanClosings is LoanClosingsBase { address seizedToken ) { - return _liquidate(loanId, receiver, closeAmount); - } - - function rollover( - bytes32 loanId, - bytes calldata /*loanDataBytes*/ // for future use - ) external nonReentrant returns (address rebateToken, uint256 gasRebate) { - uint256 startingGas = gasleft() + 21576; // estimated used gas ignoring loanDataBytes: 21000 + (4+32) * 16 - - // restrict to EOAs to prevent griefing attacks, during interest rate recalculation - require(msg.sender == tx.origin, "only EOAs can call"); - - return - _rollover( - loanId, - startingGas, - "" // loanDataBytes - ); + return _liquidate( + loanId, + receiver, + closeAmount + ); } function closeWithDeposit( bytes32 loanId, address receiver, - uint256 depositAmount // denominated in loanToken - ) + uint256 depositAmount) // denominated in loanToken public payable nonReentrant @@ -64,7 +54,11 @@ contract LoanClosings is LoanClosingsBase { address withdrawToken ) { - return _closeWithDeposit(loanId, receiver, depositAmount); + return _closeWithDeposit( + loanId, + receiver, + depositAmount + ); } function closeWithSwap( @@ -72,8 +66,7 @@ contract LoanClosings is LoanClosingsBase { address receiver, uint256 swapAmount, // denominated in collateralToken bool returnTokenIsCollateral, // true: withdraws collateralToken, false: withdraws loanToken - bytes memory loanDataBytes // for future use - ) + bytes memory loanDataBytes) // for future use public nonReentrant returns ( @@ -82,13 +75,12 @@ contract LoanClosings is LoanClosingsBase { address withdrawToken ) { - return - _closeWithSwap( - loanId, - receiver, - swapAmount, - returnTokenIsCollateral, - loanDataBytes - ); + return _closeWithSwap( + loanId, + receiver, + swapAmount, + returnTokenIsCollateral, + loanDataBytes + ); } -} +} \ No newline at end of file diff --git a/contracts/modules/LoanClosings/LoanClosingsBase.sol b/contracts/modules/LoanClosings/LoanClosingsBase.sol index a90f5cab..fafcfdca 100644 --- a/contracts/modules/LoanClosings/LoanClosingsBase.sol +++ b/contracts/modules/LoanClosings/LoanClosingsBase.sol @@ -9,21 +9,15 @@ pragma experimental ABIEncoderV2; import "../../core/State.sol"; import "../../events/LoanClosingsEvents.sol"; import "../../mixins/VaultController.sol"; -import "../../mixins/InterestUser.sol"; +import "../../mixins/InterestHandler.sol"; import "../../mixins/LiquidationHelper.sol"; import "../../swaps/SwapsUser.sol"; import "../../interfaces/ILoanPool.sol"; import "../../governance/PausableGuardian.sol"; -contract LoanClosingsBase is - State, - LoanClosingsEvents, - VaultController, - InterestUser, - SwapsUser, - LiquidationHelper, - PausableGuardian -{ + +contract LoanClosingsBase is State, LoanClosingsEvents, VaultController, InterestHandler, SwapsUser, LiquidationHelper, PausableGuardian { + enum CloseTypes { Deposit, Swap, @@ -33,8 +27,7 @@ contract LoanClosingsBase is function _liquidate( bytes32 loanId, address receiver, - uint256 closeAmount - ) + uint256 closeAmount) internal pausable returns ( @@ -48,6 +41,8 @@ contract LoanClosingsBase is LoanParams memory loanParamsLocal = loanParams[loanLocal.loanParamsId]; + loanLocal.principal = _settleInterest(loanLocal.lender, loanParamsLocal.loanToken, loanId); + (uint256 currentMargin, uint256 collateralToLoanRate) = _getCurrentMargin( loanParamsLocal.loanToken, loanParamsLocal.collateralToken, @@ -72,15 +67,13 @@ contract LoanClosingsBase is currentMargin, loanParamsLocal.maintenanceMargin, collateralToLoanRate, - liquidationIncentivePercent[loanParamsLocal.loanToken][ - loanParamsLocal.collateralToken - ] + liquidationIncentivePercent[loanParamsLocal.loanToken][loanParamsLocal.collateralToken] ); if (loanCloseAmount < maxLiquidatable) { - seizedAmount = maxSeizable.mul(loanCloseAmount).div( - maxLiquidatable - ); + seizedAmount = maxSeizable + .mul(loanCloseAmount) + .div(maxLiquidatable); } else { if (loanCloseAmount > maxLiquidatable) { // adjust down the close amount to the max @@ -91,44 +84,27 @@ contract LoanClosingsBase is require(loanCloseAmount != 0, "nothing to liquidate"); - // liquidator deposits the principal being closed + // liquidator pays off the principal being closed _returnPrincipalWithDeposit( loanParamsLocal.loanToken, - address(this), + loanLocal.lender, loanCloseAmount ); - // a portion of the principal is repaid to the lender out of interest refunded - uint256 loanCloseAmountLessInterest = _settleInterestToPrincipal( - loanLocal, - loanParamsLocal, - loanCloseAmount, - loanLocal.borrower - ); - - if (loanCloseAmount > loanCloseAmountLessInterest) { - // full interest refund goes to the borrower - vaultWithdraw( - loanParamsLocal.loanToken, - loanLocal.borrower, - loanCloseAmount - loanCloseAmountLessInterest - ); - } - - if (loanCloseAmountLessInterest != 0) { - vaultWithdraw( - loanParamsLocal.loanToken, - loanLocal.lender, - loanCloseAmountLessInterest - ); - } + poolTotalPrincipal[loanLocal.lender] = poolTotalPrincipal[loanLocal.lender] + .sub(loanCloseAmount); seizedToken = loanParamsLocal.collateralToken; if (seizedAmount != 0) { - loanLocal.collateral = loanLocal.collateral.sub(seizedAmount); + loanLocal.collateral = loanLocal.collateral + .sub(seizedAmount); - _withdrawAsset(seizedToken, receiver, seizedAmount); + _withdrawAsset( + seizedToken, + receiver, + seizedAmount + ); } _emitClosingEvents( @@ -142,157 +118,16 @@ contract LoanClosingsBase is CloseTypes.Liquidation ); - _closeLoan(loanLocal, loanCloseAmount); - } - - function _rollover( - bytes32 loanId, - uint256 startingGas, - bytes memory /*loanDataBytes*/ // for future use - ) internal pausable returns (address rebateToken, uint256 gasRebate) { - Loan memory loanLocal = loans[loanId]; - require(loanLocal.active, "loan is closed"); - require( - block.timestamp > loanLocal.endTimestamp.sub(1 hours), - "healthy position" - ); - require( - loanPoolToUnderlying[loanLocal.lender] != address(0), - "invalid lender" - ); - - LoanParams memory loanParamsLocal = loanParams[loanLocal.loanParamsId]; - - // pay outstanding interest to lender - _payInterest(loanLocal.lender, loanParamsLocal.loanToken); - - LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id]; - LenderInterest storage lenderInterestLocal = lenderInterest[ - loanLocal.lender - ][loanParamsLocal.loanToken]; - - _settleFeeRewardForInterestExpense( - loanInterestLocal, - loanLocal.id, - loanParamsLocal.loanToken, - loanLocal.borrower, - block.timestamp - ); - - // Handle back interest: calculates interest owned since the loan endtime passed but the loan remained open - uint256 backInterestTime; - uint256 backInterestOwed; - if (block.timestamp > loanLocal.endTimestamp) { - backInterestTime = block.timestamp.sub(loanLocal.endTimestamp); - backInterestOwed = backInterestTime.mul( - loanInterestLocal.owedPerDay - ); - backInterestOwed = backInterestOwed.div(24 hours); - } - - uint256 maxDuration = loanParamsLocal.maxLoanTerm; - - if (maxDuration != 0) { - // fixed-term loan, so need to query iToken for latest variable rate - uint256 owedPerDay = loanLocal - .principal - .mul(ILoanPool(loanLocal.lender).borrowInterestRate()) - .div(DAYS_IN_A_YEAR * WEI_PERCENT_PRECISION); - - lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay.add( - owedPerDay - ); - lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay.sub( - loanInterestLocal.owedPerDay - ); - - loanInterestLocal.owedPerDay = owedPerDay; - } else { - // loanInterestLocal.owedPerDay doesn't change - maxDuration = ONE_MONTH; - } - - if (backInterestTime >= maxDuration) { - maxDuration = backInterestTime.add(24 hours); // adds an extra 24 hours - } - - // update loan end time - loanLocal.endTimestamp = loanLocal.endTimestamp.add(maxDuration); - - uint256 interestAmountRequired = loanLocal.endTimestamp.sub( - block.timestamp - ); - interestAmountRequired = interestAmountRequired.mul( - loanInterestLocal.owedPerDay - ); - interestAmountRequired = interestAmountRequired.div(24 hours); - - loanInterestLocal.depositTotal = loanInterestLocal.depositTotal.add( - interestAmountRequired - ); - - lenderInterestLocal.owedTotal = lenderInterestLocal.owedTotal.add( - interestAmountRequired - ); - - // add backInterestOwed - interestAmountRequired = interestAmountRequired.add(backInterestOwed); - - // collect interest - (, uint256 sourceTokenAmountUsed, ) = _doCollateralSwap( + _closeLoan( loanLocal, - loanParamsLocal, - loanLocal.collateral, - interestAmountRequired, - true, // returnTokenIsCollateral - "" - ); - loanLocal.collateral = loanLocal.collateral.sub(sourceTokenAmountUsed); - - if (backInterestOwed != 0) { - // pay out backInterestOwed - - _payInterestTransfer( - loanLocal.lender, - loanParamsLocal.loanToken, - backInterestOwed - ); - } - - if (msg.sender != loanLocal.borrower) { - gasRebate = _getRebate( - loanParamsLocal.collateralToken, - loanLocal.collateral, - startingGas - ); - if (gasRebate != 0) { - // pay out gas rebate to caller - // the preceeding logic should ensure gasRebate <= collateral, but just in case, will use SafeMath here - loanLocal.collateral = loanLocal.collateral.sub( - gasRebate, - "gasRebate too high" - ); - - rebateToken = loanParamsLocal.collateralToken; - - _withdrawAsset(rebateToken, msg.sender, gasRebate); - } - } - - _finalizeRollover( - loanLocal, - loanParamsLocal, - sourceTokenAmountUsed, - interestAmountRequired, - gasRebate + loanCloseAmount ); } function _closeWithDeposit( bytes32 loanId, address receiver, - uint256 depositAmount // denominated in loanToken - ) + uint256 depositAmount) // denominated in loanToken internal pausable returns ( @@ -304,7 +139,11 @@ contract LoanClosingsBase is require(depositAmount != 0, "depositAmount == 0"); Loan memory loanLocal = loans[loanId]; - _checkAuthorized(loanLocal.id, loanLocal.active, loanLocal.borrower); + _checkAuthorized( + loanLocal.id, + loanLocal.active, + loanLocal.borrower + ); if (receiver == address(0)) { receiver = msg.sender; @@ -312,24 +151,22 @@ contract LoanClosingsBase is LoanParams memory loanParamsLocal = loanParams[loanLocal.loanParamsId]; - // can't close more than the full principal - loanCloseAmount = depositAmount > loanLocal.principal - ? loanLocal.principal - : depositAmount; + loanLocal.principal = _settleInterest(loanLocal.lender, loanParamsLocal.loanToken, loanId); - uint256 loanCloseAmountLessInterest = _settleInterestToPrincipal( - loanLocal, - loanParamsLocal, - loanCloseAmount, - receiver - ); + // can't close more than the full principal + loanCloseAmount = depositAmount > loanLocal.principal ? + loanLocal.principal : + depositAmount; - if (loanCloseAmountLessInterest != 0) { + if (loanCloseAmount != 0) { _returnPrincipalWithDeposit( loanParamsLocal.loanToken, loanLocal.lender, - loanCloseAmountLessInterest + loanCloseAmount ); + + poolTotalPrincipal[loanLocal.lender] = poolTotalPrincipal[loanLocal.lender] + .sub(loanCloseAmount); } if (loanCloseAmount == loanLocal.principal) { @@ -338,7 +175,11 @@ contract LoanClosingsBase is withdrawToken = loanParamsLocal.collateralToken; loanLocal.collateral = 0; - _withdrawAsset(withdrawToken, receiver, withdrawAmount); + _withdrawAsset( + withdrawToken, + receiver, + withdrawAmount + ); } _finalizeClose( @@ -356,8 +197,7 @@ contract LoanClosingsBase is address receiver, uint256 swapAmount, bool returnTokenIsCollateral, - bytes memory loanDataBytes - ) + bytes memory loanDataBytes) internal pausable returns ( @@ -369,7 +209,11 @@ contract LoanClosingsBase is require(swapAmount != 0, "swapAmount == 0"); Loan memory loanLocal = loans[loanId]; - _checkAuthorized(loanLocal.id, loanLocal.active, loanLocal.borrower); + _checkAuthorized( + loanLocal.id, + loanLocal.active, + loanLocal.borrower + ); if (receiver == address(0)) { receiver = msg.sender; @@ -377,58 +221,58 @@ contract LoanClosingsBase is LoanParams memory loanParamsLocal = loanParams[loanLocal.loanParamsId]; + loanLocal.principal = _settleInterest(loanLocal.lender, loanParamsLocal.loanToken, loanId); + if (swapAmount > loanLocal.collateral) { swapAmount = loanLocal.collateral; } loanCloseAmount = loanLocal.principal; if (swapAmount != loanLocal.collateral) { - loanCloseAmount = loanCloseAmount.mul(swapAmount).div( - loanLocal.collateral - ); + loanCloseAmount = loanCloseAmount + .mul(swapAmount) + .div(loanLocal.collateral); } require(loanCloseAmount != 0, "loanCloseAmount == 0"); - uint256 loanCloseAmountLessInterest = _settleInterestToPrincipal( - loanLocal, - loanParamsLocal, - loanCloseAmount, - receiver - ); uint256 usedCollateral; uint256 collateralToLoanSwapRate; - ( - usedCollateral, - withdrawAmount, - collateralToLoanSwapRate - ) = _coverPrincipalWithSwap( + (usedCollateral, withdrawAmount, collateralToLoanSwapRate) = _coverPrincipalWithSwap( loanLocal, loanParamsLocal, swapAmount, - loanCloseAmountLessInterest, + loanCloseAmount, returnTokenIsCollateral, loanDataBytes ); - if (loanCloseAmountLessInterest != 0) { + if (loanCloseAmount != 0) { // Repays principal to lender vaultWithdraw( loanParamsLocal.loanToken, loanLocal.lender, - loanCloseAmountLessInterest + loanCloseAmount ); + + poolTotalPrincipal[loanLocal.lender] = poolTotalPrincipal[loanLocal.lender] + .sub(loanCloseAmount); } if (usedCollateral != 0) { - loanLocal.collateral = loanLocal.collateral.sub(usedCollateral); + loanLocal.collateral = loanLocal.collateral + .sub(usedCollateral); } - withdrawToken = returnTokenIsCollateral - ? loanParamsLocal.collateralToken - : loanParamsLocal.loanToken; + withdrawToken = returnTokenIsCollateral ? + loanParamsLocal.collateralToken : + loanParamsLocal.loanToken; if (withdrawAmount != 0) { - _withdrawAsset(withdrawToken, receiver, withdrawAmount); + _withdrawAsset( + withdrawToken, + receiver, + withdrawAmount + ); } _finalizeClose( @@ -444,8 +288,9 @@ contract LoanClosingsBase is function _updateDepositAmount( bytes32 loanId, uint256 principalBefore, - uint256 principalAfter - ) internal { + uint256 principalAfter) + internal + { uint256 depositValueAsLoanToken; uint256 depositValueAsCollateralToken; bytes32 slot = keccak256(abi.encode(loanId, LoanDepositValueID)); @@ -456,17 +301,11 @@ contract LoanClosingsBase is sstore(add(slot, 1), 0) } default { - depositValueAsLoanToken := div( - mul(sload(slot), principalAfter), - principalBefore - ) + depositValueAsLoanToken := div(mul(sload(slot), principalAfter), principalBefore) sstore(slot, depositValueAsLoanToken) slot := add(slot, 1) - depositValueAsCollateralToken := div( - mul(sload(slot), principalAfter), - principalBefore - ) + depositValueAsCollateralToken := div(mul(sload(slot), principalAfter), principalBefore) sstore(slot, depositValueAsCollateralToken) } } @@ -481,75 +320,33 @@ contract LoanClosingsBase is function _checkAuthorized( bytes32 _id, bool _active, - address _borrower - ) internal view { + address _borrower) + internal + view + { require(_active, "loan is closed"); require( - msg.sender == _borrower || delegatedManagers[_id][msg.sender], + msg.sender == _borrower || + delegatedManagers[_id][msg.sender], "unauthorized" ); } - function _settleInterestToPrincipal( - Loan memory loanLocal, - LoanParams memory loanParamsLocal, - uint256 loanCloseAmount, - address receiver - ) internal returns (uint256) { - uint256 loanCloseAmountLessInterest = loanCloseAmount; - - uint256 interestRefundToBorrower = _settleInterest( - loanParamsLocal, - loanLocal, - loanCloseAmountLessInterest - ); - - address loanToken = loanParamsLocal.loanToken; - - uint256 interestAppliedToPrincipal; - if (loanCloseAmountLessInterest >= interestRefundToBorrower) { - // apply all of borrower interest refund torwards principal - interestAppliedToPrincipal = interestRefundToBorrower; - - // principal needed is reduced by this amount - loanCloseAmountLessInterest -= interestRefundToBorrower; - - // no interest refund remaining - interestRefundToBorrower = 0; - } else { - // principal fully covered by excess interest - interestAppliedToPrincipal = loanCloseAmountLessInterest; - - // amount refunded is reduced by this amount - interestRefundToBorrower -= loanCloseAmountLessInterest; - - // principal fully covered by excess interest - loanCloseAmountLessInterest = 0; - - // refund overage - vaultWithdraw(loanToken, receiver, interestRefundToBorrower); - } - - if (interestAppliedToPrincipal != 0) { - vaultWithdraw( - loanToken, - loanLocal.lender, - interestAppliedToPrincipal - ); - } - - return loanCloseAmountLessInterest; - } - // The receiver always gets back an ERC20 (even WETH) function _returnPrincipalWithDeposit( address loanToken, address receiver, - uint256 principalNeeded - ) internal { + uint256 principalNeeded) + internal + { if (principalNeeded != 0) { if (msg.value == 0) { - vaultTransfer(loanToken, msg.sender, receiver, principalNeeded); + vaultTransfer( + loanToken, + msg.sender, + receiver, + principalNeeded + ); } else { require(loanToken == address(wethToken), "wrong asset sent"); require(msg.value >= principalNeeded, "not enough ether"); @@ -564,7 +361,10 @@ contract LoanClosingsBase is } if (msg.value > principalNeeded) { // refund overage - Address.sendValue(msg.sender, msg.value - principalNeeded); + Address.sendValue( + msg.sender, + msg.value - principalNeeded + ); } } } else { @@ -578,22 +378,13 @@ contract LoanClosingsBase is uint256 swapAmount, uint256 principalNeeded, bool returnTokenIsCollateral, - bytes memory loanDataBytes - ) + bytes memory loanDataBytes) internal - returns ( - uint256 usedCollateral, - uint256 withdrawAmount, - uint256 collateralToLoanSwapRate - ) + returns (uint256 usedCollateral, uint256 withdrawAmount, uint256 collateralToLoanSwapRate) { uint256 destTokenAmountReceived; uint256 sourceTokenAmountUsed; - ( - destTokenAmountReceived, - sourceTokenAmountUsed, - collateralToLoanSwapRate - ) = _doCollateralSwap( + (destTokenAmountReceived, sourceTokenAmountUsed, collateralToLoanSwapRate) = _doCollateralSwap( loanLocal, loanParamsLocal, swapAmount, @@ -611,17 +402,17 @@ contract LoanClosingsBase is destTokenAmountReceived - principalNeeded ); } - withdrawAmount = swapAmount > sourceTokenAmountUsed - ? swapAmount - sourceTokenAmountUsed - : 0; + withdrawAmount = swapAmount > sourceTokenAmountUsed ? + swapAmount - sourceTokenAmountUsed : + 0; } else { require(sourceTokenAmountUsed == swapAmount, "swap error"); withdrawAmount = destTokenAmountReceived - principalNeeded; } - usedCollateral = sourceTokenAmountUsed > swapAmount - ? sourceTokenAmountUsed - : swapAmount; + usedCollateral = sourceTokenAmountUsed > swapAmount ? + sourceTokenAmountUsed : + swapAmount; } function _doCollateralSwap( @@ -630,53 +421,46 @@ contract LoanClosingsBase is uint256 swapAmount, uint256 principalNeeded, bool returnTokenIsCollateral, - bytes memory loanDataBytes - ) + bytes memory loanDataBytes) internal - returns ( - uint256 destTokenAmountReceived, - uint256 sourceTokenAmountUsed, - uint256 collateralToLoanSwapRate - ) + returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed, uint256 collateralToLoanSwapRate) { - ( - destTokenAmountReceived, - sourceTokenAmountUsed, - collateralToLoanSwapRate - ) = _loanSwap( + (destTokenAmountReceived, sourceTokenAmountUsed, collateralToLoanSwapRate) = _loanSwap( loanLocal.id, loanParamsLocal.collateralToken, loanParamsLocal.loanToken, loanLocal.borrower, swapAmount, // minSourceTokenAmount loanLocal.collateral, // maxSourceTokenAmount - returnTokenIsCollateral - ? principalNeeded // requiredDestTokenAmount - : 0, + returnTokenIsCollateral ? + principalNeeded : // requiredDestTokenAmount + 0, false, // bypassFee loanDataBytes ); - require( - destTokenAmountReceived >= principalNeeded, - "insufficient dest amount" - ); - require( - sourceTokenAmountUsed <= loanLocal.collateral, - "excessive source amount" - ); + require(destTokenAmountReceived >= principalNeeded, "insufficient dest amount"); + require(sourceTokenAmountUsed <= loanLocal.collateral, "excessive source amount"); } // withdraws asset to receiver function _withdrawAsset( address assetToken, address receiver, - uint256 assetAmount - ) internal { + uint256 assetAmount) + internal + { if (assetAmount != 0) { if (assetToken == address(wethToken)) { - vaultEtherWithdraw(receiver, assetAmount); + vaultEtherWithdraw( + receiver, + assetAmount + ); } else { - vaultWithdraw(assetToken, receiver, assetAmount); + vaultWithdraw( + assetToken, + receiver, + assetAmount + ); } } } @@ -686,8 +470,10 @@ contract LoanClosingsBase is address collateralToken, uint256 principal, uint256 collateral, - bool silentFail - ) internal returns (uint256 currentMargin, uint256 collateralToLoanRate) { + bool silentFail) + internal + returns (uint256 currentMargin, uint256 collateralToLoanRate) + { address _priceFeeds = priceFeeds; (bool success, bytes memory data) = _priceFeeds.staticcall( abi.encodeWithSelector( @@ -714,9 +500,10 @@ contract LoanClosingsBase is uint256 loanCloseAmount, uint256 collateralCloseAmount, uint256 collateralToLoanSwapRate, - CloseTypes closeType - ) internal { - (uint256 principalBefore, uint256 principalAfter) = _closeLoan( + CloseTypes closeType) + internal + { + (uint256 principalBefore, uint256 principalAfter) = _closeLoan( loanLocal, loanCloseAmount ); @@ -733,12 +520,16 @@ contract LoanClosingsBase is //// Note: We can safely skip the margin check if closing via closeWithDeposit or if closing the loan in full by any method //// require( closeType == CloseTypes.Deposit || - principalAfter == 0 || // loan fully closed - currentMargin > loanParamsLocal.maintenanceMargin, + principalAfter == 0 || // loan fully closed + currentMargin > loanParamsLocal.maintenanceMargin, "unhealthy position" ); - _updateDepositAmount(loanLocal.id, principalBefore, principalAfter); + _updateDepositAmount( + loanLocal.id, + principalBefore, + principalAfter + ); _emitClosingEvents( loanParamsLocal, @@ -752,7 +543,9 @@ contract LoanClosingsBase is ); } - function _closeLoan(Loan memory loanLocal, uint256 loanCloseAmount) + function _closeLoan( + Loan memory loanLocal, + uint256 loanCloseAmount) internal returns (uint256 principalBefore, uint256 principalAfter) { @@ -769,127 +562,14 @@ contract LoanClosingsBase is lenderLoanSets[loanLocal.lender].removeBytes32(loanLocal.id); borrowerLoanSets[loanLocal.borrower].removeBytes32(loanLocal.id); } else { - principalAfter = principalBefore.sub(loanCloseAmount); + principalAfter = principalBefore + .sub(loanCloseAmount); loanLocal.principal = principalAfter; } loans[loanLocal.id] = loanLocal; } - function _settleInterest( - LoanParams memory loanParamsLocal, - Loan memory loanLocal, - uint256 closePrincipal - ) internal returns (uint256) { - // pay outstanding interest to lender - _payInterest(loanLocal.lender, loanParamsLocal.loanToken); - - LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id]; - LenderInterest storage lenderInterestLocal = lenderInterest[ - loanLocal.lender - ][loanParamsLocal.loanToken]; - - uint256 interestTime = block.timestamp; - if (interestTime > loanLocal.endTimestamp) { - interestTime = loanLocal.endTimestamp; - } - - _settleFeeRewardForInterestExpense( - loanInterestLocal, - loanLocal.id, - loanParamsLocal.loanToken, - loanLocal.borrower, - interestTime - ); - - uint256 owedPerDayRefund; - if (closePrincipal < loanLocal.principal) { - owedPerDayRefund = loanInterestLocal - .owedPerDay - .mul(closePrincipal) - .div(loanLocal.principal); - } else { - owedPerDayRefund = loanInterestLocal.owedPerDay; - } - - // update stored owedPerDay - loanInterestLocal.owedPerDay = loanInterestLocal.owedPerDay.sub( - owedPerDayRefund - ); - lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay.sub( - owedPerDayRefund - ); - - // update borrower interest - uint256 interestRefundToBorrower = loanLocal.endTimestamp.sub( - interestTime - ); - interestRefundToBorrower = interestRefundToBorrower.mul( - owedPerDayRefund - ); - interestRefundToBorrower = interestRefundToBorrower.div(24 hours); - - if (closePrincipal < loanLocal.principal) { - loanInterestLocal.depositTotal = loanInterestLocal.depositTotal.sub( - interestRefundToBorrower - ); - } else { - loanInterestLocal.depositTotal = 0; - } - - // update remaining lender interest values - lenderInterestLocal.principalTotal = lenderInterestLocal - .principalTotal - .sub(closePrincipal); - - uint256 owedTotal = lenderInterestLocal.owedTotal; - lenderInterestLocal.owedTotal = owedTotal > interestRefundToBorrower - ? owedTotal - interestRefundToBorrower - : 0; - - return interestRefundToBorrower; - } - - function _getRebate( - address collateralToken, - uint256 collateral, - uint256 startingGas - ) internal view returns (uint256 gasRebate) { - // gets the gas rebate denominated in collateralToken - gasRebate = SafeMath - .mul( - IPriceFeeds(priceFeeds).getFastGasPrice(collateralToken) * 2, - startingGas - gasleft() - ) - .div(WEI_PRECISION * WEI_PRECISION); - - // gas rebate cannot exceed available collateral - gasRebate = gasRebate.min256(collateral); - } - - function _finalizeRollover( - Loan memory loanLocal, - LoanParams memory loanParamsLocal, - uint256 sourceTokenAmountUsed, - uint256 interestAmountRequired, - uint256 gasRebate - ) internal { - emit Rollover( - loanLocal.borrower, // user (borrower) - msg.sender, // caller - loanLocal.id, // loanId - loanLocal.lender, // lender - loanParamsLocal.loanToken, // loanToken - loanParamsLocal.collateralToken, // collateralToken - sourceTokenAmountUsed, // collateralAmountUsed - interestAmountRequired, // interestAmountAdded - loanLocal.endTimestamp, // loanEndTimestamp - gasRebate // gasRebate - ); - - loans[loanLocal.id] = loanLocal; - } - function _emitClosingEvents( LoanParams memory loanParamsLocal, Loan memory loanLocal, @@ -898,28 +578,26 @@ contract LoanClosingsBase is uint256 collateralToLoanRate, uint256 collateralToLoanSwapRate, uint256 currentMargin, - CloseTypes closeType - ) internal { + CloseTypes closeType) + internal + { if (closeType == CloseTypes.Deposit) { emit CloseWithDeposit( - loanLocal.borrower, // user (borrower) - loanLocal.lender, // lender - loanLocal.id, // loanId - msg.sender, // closer - loanParamsLocal.loanToken, // loanToken - loanParamsLocal.collateralToken, // collateralToken - loanCloseAmount, // loanCloseAmount - collateralCloseAmount, // collateralCloseAmount - collateralToLoanRate, // collateralToLoanRate - currentMargin // currentMargin + loanLocal.borrower, // user (borrower) + loanLocal.lender, // lender + loanLocal.id, // loanId + msg.sender, // closer + loanParamsLocal.loanToken, // loanToken + loanParamsLocal.collateralToken, // collateralToken + loanCloseAmount, // loanCloseAmount + collateralCloseAmount, // collateralCloseAmount + collateralToLoanRate, // collateralToLoanRate + currentMargin // currentMargin ); } else if (closeType == CloseTypes.Swap) { // exitPrice = 1 / collateralToLoanSwapRate if (collateralToLoanSwapRate != 0) { - collateralToLoanSwapRate = SafeMath.div( - WEI_PRECISION * WEI_PRECISION, - collateralToLoanSwapRate - ); + collateralToLoanSwapRate = SafeMath.div(WEI_PRECISION * WEI_PRECISION, collateralToLoanSwapRate); } // currentLeverage = 100 / currentMargin @@ -928,31 +606,30 @@ contract LoanClosingsBase is } emit CloseWithSwap( - loanLocal.borrower, // user (trader) - loanLocal.lender, // lender - loanLocal.id, // loanId - loanParamsLocal.collateralToken, // collateralToken - loanParamsLocal.loanToken, // loanToken - msg.sender, // closer - collateralCloseAmount, // positionCloseSize - loanCloseAmount, // loanCloseAmount - collateralToLoanSwapRate, // exitPrice (1 / collateralToLoanSwapRate) - currentMargin // currentLeverage + loanLocal.borrower, // user (trader) + loanLocal.lender, // lender + loanLocal.id, // loanId + loanParamsLocal.collateralToken, // collateralToken + loanParamsLocal.loanToken, // loanToken + msg.sender, // closer + collateralCloseAmount, // positionCloseSize + loanCloseAmount, // loanCloseAmount + collateralToLoanSwapRate, // exitPrice (1 / collateralToLoanSwapRate) + currentMargin // currentLeverage ); - } else { - // closeType == CloseTypes.Liquidation + } else { // closeType == CloseTypes.Liquidation emit Liquidate( - loanLocal.borrower, // user (borrower) - msg.sender, // liquidator - loanLocal.id, // loanId - loanLocal.lender, // lender - loanParamsLocal.loanToken, // loanToken - loanParamsLocal.collateralToken, // collateralToken - loanCloseAmount, // loanCloseAmount - collateralCloseAmount, // collateralCloseAmount - collateralToLoanRate, // collateralToLoanRate - currentMargin // currentMargin + loanLocal.borrower, // user (borrower) + msg.sender, // liquidator + loanLocal.id, // loanId + loanLocal.lender, // lender + loanParamsLocal.loanToken, // loanToken + loanParamsLocal.collateralToken, // collateralToken + loanCloseAmount, // loanCloseAmount + collateralCloseAmount, // collateralCloseAmount + collateralToLoanRate, // collateralToLoanRate + currentMargin // currentMargin ); } } -} +} \ No newline at end of file diff --git a/contracts/modules/LoanOpenings/LoanOpenings.sol b/contracts/modules/LoanOpenings/LoanOpenings.sol index 9610ce33..6bac8106 100644 --- a/contracts/modules/LoanOpenings/LoanOpenings.sol +++ b/contracts/modules/LoanOpenings/LoanOpenings.sol @@ -9,19 +9,18 @@ pragma experimental ABIEncoderV2; import "../../core/State.sol"; import "../../events/LoanOpeningsEvents.sol"; import "../../mixins/VaultController.sol"; -import "../../mixins/InterestUser.sol"; +import "../../mixins/InterestHandler.sol"; import "../../swaps/SwapsUser.sol"; import "../../governance/PausableGuardian.sol"; -contract LoanOpenings is - State, - LoanOpeningsEvents, - VaultController, - InterestUser, - SwapsUser, - PausableGuardian -{ - function initialize(address target) external onlyOwner { + +contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestHandler, SwapsUser, PausableGuardian { + + function initialize( + address target) + external + onlyOwner + { _setTarget(this.borrowOrTradeFromPool.selector, target); _setTarget(this.setDelegatedManager.selector, target); _setTarget(this.getEstimatedMarginExposure.selector, target); @@ -38,28 +37,27 @@ contract LoanOpenings is bool isTorqueLoan, uint256 initialMargin, address[4] calldata sentAddresses, - // lender: must match loan if loanId provided - // borrower: must match loan if loanId provided - // receiver: receiver of funds (address(0) assumes borrower address) - // manager: delegated manager of loan unless address(0) + // lender: must match loan if loanId provided + // borrower: must match loan if loanId provided + // receiver: receiver of funds (address(0) assumes borrower address) + // manager: delegated manager of loan unless address(0) uint256[5] calldata sentValues, - // newRate: new loan interest rate - // newPrincipal: new loan size (borrowAmount + any borrowed interest) - // torqueInterest: new amount of interest to escrow for Torque loan (determines initial loan length) - // loanTokenReceived: total loanToken deposit (amount not sent to borrower in the case of Torque loans) - // collateralTokenReceived: total collateralToken deposit - bytes calldata loanDataBytes - ) external payable nonReentrant pausable returns (LoanOpenData memory) { - require( - msg.value == 0 || loanDataBytes.length != 0, - "loanDataBytes required with ether" - ); + // newRate: new loan interest rate + // newPrincipal: new loan size (borrowAmount + any borrowed interest) + // torqueInterest: new amount of interest to escrow for Torque loan (determines initial loan length) + // loanTokenReceived: total loanToken deposit (amount not sent to borrower in the case of Torque loans) + // collateralTokenReceived: total collateralToken deposit + bytes calldata loanDataBytes) + external + payable + nonReentrant + pausable + returns (LoanOpenData memory) + { + require(msg.value == 0 || loanDataBytes.length != 0, "loanDataBytes required with ether"); // only callable by loan pools - require( - loanPoolToUnderlying[msg.sender] != address(0), - "not authorized" - ); + require(loanPoolToUnderlying[msg.sender] != address(0), "not authorized"); LoanParams memory loanParamsLocal = loanParams[loanParamsId]; require(loanParamsLocal.id != 0, "loanParams not exists"); @@ -68,7 +66,7 @@ contract LoanOpenings is require(isTorqueLoan, "initialMargin == 0"); initialMargin = loanParamsLocal.minInitialMargin; } - + // get required collateral uint256 collateralAmountRequired = _getRequiredCollateral( loanParamsLocal.loanToken, @@ -77,30 +75,35 @@ contract LoanOpenings is initialMargin, isTorqueLoan ); - require(collateralAmountRequired != 0, "collateral is 0"); - return - _borrowOrTrade( - loanParamsLocal, - loanId, - isTorqueLoan, - collateralAmountRequired, - initialMargin, - sentAddresses, - sentValues, - loanDataBytes - ); + return _borrowOrTrade( + loanParamsLocal, + loanId, + isTorqueLoan, + collateralAmountRequired, + initialMargin, + sentAddresses, + sentValues, + loanDataBytes + ); } function setDelegatedManager( bytes32 loanId, address delegated, - bool toggle - ) external pausable { + bool toggle) + external + pausable + { require(loans[loanId].borrower == msg.sender, "unauthorized"); - _setDelegatedManager(loanId, msg.sender, delegated, toggle); + _setDelegatedManager( + loanId, + msg.sender, + delegated, + toggle + ); } function getEstimatedMarginExposure( @@ -108,36 +111,20 @@ contract LoanOpenings is address collateralToken, uint256 loanTokenSent, uint256 collateralTokenSent, - uint256 interestRate, - uint256 newPrincipal, - bytes calldata payload - ) external returns (uint256 value) { - if (loanTokenSent < newPrincipal) { - return 0; - } - - uint256 maxLoanTerm = 2419200; // 28 days - - uint256 owedPerDay = newPrincipal.mul(interestRate).div( - DAYS_IN_A_YEAR * WEI_PERCENT_PRECISION - ); - - uint256 interestAmountRequired = maxLoanTerm.mul(owedPerDay).div( - 1 days - ); - - if (loanTokenSent < interestAmountRequired) { - return 0; - } - + uint256 /*interestRate*/, + uint256 /*newPrincipal*/) + external + view + returns (uint256 value) + { value = _swapsExpectedReturn( loanToken, collateralToken, - loanTokenSent.sub(interestAmountRequired), - payload + loanTokenSent ); if (value != 0) { - return collateralTokenSent.add(value); + return collateralTokenSent + .add(value); } } @@ -146,8 +133,11 @@ contract LoanOpenings is address collateralToken, uint256 newPrincipal, uint256 marginAmount, - bool isTorqueLoan - ) public view returns (uint256 collateralAmountRequired) { + bool isTorqueLoan) + public + view + returns (uint256 collateralAmountRequired) + { if (marginAmount != 0) { collateralAmountRequired = _getRequiredCollateral( loanToken, @@ -157,9 +147,9 @@ contract LoanOpenings is isTorqueLoan ); - uint256 feePercent = isTorqueLoan - ? borrowingFeePercent - : tradingFeePercent; + uint256 feePercent = isTorqueLoan ? + borrowingFeePercent : + tradingFeePercent; if (collateralAmountRequired != 0 && feePercent != 0) { collateralAmountRequired = collateralAmountRequired .mul(WEI_PERCENT_PRECISION) @@ -172,19 +162,21 @@ contract LoanOpenings is function getRequiredCollateralByParams( bytes32 loanParamsId, - uint256 newPrincipal - ) public view returns (uint256 collateralAmountRequired) { + uint256 newPrincipal) + public + view + returns (uint256 collateralAmountRequired) + { LoanParams memory loanParamsLocal = loanParams[loanParamsId]; - return - getRequiredCollateral( - loanParamsLocal.loanToken, - loanParamsLocal.collateralToken, - newPrincipal, - loanParamsLocal.minInitialMargin, // marginAmount - loanParamsLocal.maxLoanTerm == 0 // isTorqueLoan - ? true - : false - ); + return getRequiredCollateral( + loanParamsLocal.loanToken, + loanParamsLocal.collateralToken, + newPrincipal, + loanParamsLocal.minInitialMargin, // marginAmount + loanParamsLocal.maxLoanTerm == 0 ? // isTorqueLoan + true : + false + ); } function getBorrowAmount( @@ -192,11 +184,15 @@ contract LoanOpenings is address collateralToken, uint256 collateralTokenAmount, uint256 marginAmount, - bool isTorqueLoan - ) public view returns (uint256 borrowAmount) { + bool isTorqueLoan) + public + view + returns (uint256 borrowAmount) + { if (marginAmount != 0) { if (isTorqueLoan) { - marginAmount = marginAmount.add(WEI_PERCENT_PRECISION); // adjust for over-collateralized loan + marginAmount = marginAmount + .add(WEI_PERCENT_PRECISION); // adjust for over-collateralized loan } if (loanToken == collateralToken) { @@ -204,13 +200,10 @@ contract LoanOpenings is .mul(WEI_PERCENT_PRECISION) .div(marginAmount); } else { - ( - uint256 sourceToDestRate, - uint256 sourceToDestPrecision - ) = IPriceFeeds(priceFeeds).queryRate( - collateralToken, - loanToken - ); + (uint256 sourceToDestRate, uint256 sourceToDestPrecision) = IPriceFeeds(priceFeeds).queryRate( + collateralToken, + loanToken + ); if (sourceToDestPrecision != 0) { borrowAmount = collateralTokenAmount .mul(WEI_PERCENT_PRECISION) @@ -220,9 +213,9 @@ contract LoanOpenings is } } - uint256 feePercent = isTorqueLoan - ? borrowingFeePercent - : tradingFeePercent; + uint256 feePercent = isTorqueLoan ? + borrowingFeePercent : + tradingFeePercent; if (borrowAmount != 0 && feePercent != 0) { borrowAmount = borrowAmount .mul( @@ -235,19 +228,21 @@ contract LoanOpenings is function getBorrowAmountByParams( bytes32 loanParamsId, - uint256 collateralTokenAmount - ) public view returns (uint256 borrowAmount) { + uint256 collateralTokenAmount) + public + view + returns (uint256 borrowAmount) + { LoanParams memory loanParamsLocal = loanParams[loanParamsId]; - return - getBorrowAmount( - loanParamsLocal.loanToken, - loanParamsLocal.collateralToken, - collateralTokenAmount, - loanParamsLocal.minInitialMargin, // marginAmount - loanParamsLocal.maxLoanTerm == 0 // isTorqueLoan - ? true - : false - ); + return getBorrowAmount( + loanParamsLocal.loanToken, + loanParamsLocal.collateralToken, + collateralTokenAmount, + loanParamsLocal.minInitialMargin, // marginAmount + loanParamsLocal.maxLoanTerm == 0 ? // isTorqueLoan + true : + false + ); } function _borrowOrTrade( @@ -257,33 +252,29 @@ contract LoanOpenings is uint256 collateralAmountRequired, uint256 initialMargin, address[4] memory sentAddresses, - // lender: must match loan if loanId provided - // borrower: must match loan if loanId provided - // receiver: receiver of funds (address(0) assumes borrower address) - // manager: delegated manager of loan unless address(0) + // lender: must match loan if loanId provided + // borrower: must match loan if loanId provided + // receiver: receiver of funds (address(0) assumes borrower address) + // manager: delegated manager of loan unless address(0) uint256[5] memory sentValues, - // newRate: new loan interest rate - // newPrincipal: new loan size (borrowAmount + any borrowed interest) - // torqueInterest: new amount of interest to escrow for Torque loan (determines initial loan length) - // loanTokenReceived: total loanToken deposit - // collateralTokenReceived: total collateralToken deposit - bytes memory loanDataBytes - ) internal returns (LoanOpenData memory) { - - require( - loanParamsLocal.collateralToken != loanParamsLocal.loanToken, - "collateral/loan match" - ); - require( - initialMargin >= loanParamsLocal.minInitialMargin, - "initialMargin too low" - ); + // newRate: new loan interest rate + // newPrincipal: new loan size (borrowAmount + any borrowed interest) + // torqueInterest: new amount of interest to escrow for Torque loan (determines initial loan length) + // loanTokenReceived: total loanToken deposit + // collateralTokenReceived: total collateralToken deposit + bytes memory loanDataBytes) + internal + returns (LoanOpenData memory) + { + require (loanParamsLocal.collateralToken != loanParamsLocal.loanToken, "collateral/loan match"); + require (initialMargin >= loanParamsLocal.minInitialMargin, "initialMargin too low"); // maxLoanTerm == 0 indicates a Torqueloan and requres that torqueInterest != 0 - require( - loanParamsLocal.maxLoanTerm != 0 || sentValues[2] != 0, // torqueInterest - "invalid interest" - ); + /*require(loanParamsLocal.maxLoanTerm != 0 || + sentValues[2] != 0, // torqueInterest + "invalid interest");*/ + + require(loanId != 0, "invalid loanId"); // initialize loan Loan storage loanLocal = _initializeLoan( @@ -293,23 +284,11 @@ contract LoanOpenings is sentAddresses, sentValues ); - if (loanId == 0) { - // loanId is defined for new loans - loanId = loanLocal.id; - } - - // get required interest - uint256 amount = _initializeInterest( - loanParamsLocal, - loanLocal, - sentValues[0], // newRate - sentValues[1], // newPrincipal, - sentValues[2] // torqueInterest - ); - - // substract out interest from usable loanToken sent - sentValues[3] = sentValues[3].sub(amount); - + + poolTotalPrincipal[loanLocal.lender] = poolTotalPrincipal[loanLocal.lender] + .add(sentValues[1]); // newPrincipal*/ + + uint256 amount; if (isTorqueLoan) { require(sentValues[3] == 0, "surplus loan token"); @@ -329,8 +308,7 @@ contract LoanOpenings is // update collateral after trade // sentValues[3] is repurposed to hold loanToCollateralSwapRate to avoid stack too deep error uint256 receivedAmount; - - (receivedAmount, , sentValues[3]) = _loanSwap( + (receivedAmount,,sentValues[3]) = _loanSwap( loanId, loanParamsLocal.loanToken, loanParamsLocal.collateralToken, @@ -341,9 +319,10 @@ contract LoanOpenings is false, // bypassFee loanDataBytes ); - sentValues[4] = sentValues[4].add(receivedAmount); // collateralTokenReceived + sentValues[4] = sentValues[4] // collateralTokenReceived + .add(receivedAmount); } - + // settle collateral require( _isCollateralSatisfied( @@ -357,19 +336,13 @@ contract LoanOpenings is "collateral insufficient" ); - loanLocal.collateral = loanLocal.collateral.add(sentValues[4]).sub( - amount - ); // borrowingFee + loanLocal.collateral = loanLocal.collateral + .add(sentValues[4]) + .sub(amount); // borrowingFee - if (isTorqueLoan) { - // reclaiming varaible -> interestDuration - sentValues[2] = loanLocal.endTimestamp.sub(block.timestamp); - } else { + if (!isTorqueLoan) { // reclaiming varaible -> entryLeverage = 100 / initialMargin - sentValues[2] = SafeMath.div( - WEI_PRECISION * WEI_PERCENT_PRECISION, - initialMargin - ); + sentValues[2] = SafeMath.div(WEI_PRECISION * WEI_PERCENT_PRECISION, initialMargin); } _finalizeOpen( @@ -380,12 +353,11 @@ contract LoanOpenings is isTorqueLoan ); - return - LoanOpenData({ - loanId: loanId, - principal: sentValues[1], - collateral: sentValues[4] - }); + return LoanOpenData({ + loanId: loanId, + principal: sentValues[1], + collateral: sentValues[4] + }); } function _finalizeOpen( @@ -393,16 +365,15 @@ contract LoanOpenings is Loan storage loanLocal, address[4] memory sentAddresses, uint256[5] memory sentValues, - bool isTorqueLoan - ) internal { - (uint256 initialMargin, uint256 collateralToLoanRate) = IPriceFeeds( - priceFeeds - ).getCurrentMargin( - loanParamsLocal.loanToken, - loanParamsLocal.collateralToken, - loanLocal.principal, - loanLocal.collateral - ); + bool isTorqueLoan) + internal + { + (uint256 initialMargin, uint256 collateralToLoanRate) = IPriceFeeds(priceFeeds).getCurrentMargin( + loanParamsLocal.loanToken, + loanParamsLocal.collateralToken, + loanLocal.principal, + loanLocal.collateral + ); require( initialMargin > loanParamsLocal.maintenanceMargin, "unhealthy position" @@ -434,42 +405,40 @@ contract LoanOpenings is uint256[5] memory sentValues, uint256 collateralToLoanRate, uint256 margin, - bool isTorqueLoan - ) internal { + bool isTorqueLoan) + internal + { if (isTorqueLoan) { emit Borrow( - sentAddresses[1], // user (borrower) - sentAddresses[0], // lender - loanLocal.id, // loanId - loanParamsLocal.loanToken, // loanToken - loanParamsLocal.collateralToken, // collateralToken - sentValues[1], // newPrincipal - sentValues[4], // newCollateral - sentValues[0], // interestRate - sentValues[2], // interestDuration - collateralToLoanRate, // collateralToLoanRate, - margin // currentMargin + sentAddresses[1], // user (borrower) + sentAddresses[0], // lender + loanLocal.id, // loanId + loanParamsLocal.loanToken, // loanToken + loanParamsLocal.collateralToken, // collateralToken + sentValues[1], // newPrincipal + sentValues[4], // newCollateral + 0, // interestRate (old value: sentValues[0]) + 0, // interestDuration (old value: sentValues[2]) + collateralToLoanRate, // collateralToLoanRate, + margin // currentMargin ); } else { // currentLeverage = 100 / currentMargin - margin = SafeMath.div( - WEI_PRECISION * WEI_PERCENT_PRECISION, - margin - ); + margin = SafeMath.div(WEI_PRECISION * WEI_PERCENT_PRECISION, margin); emit Trade( - sentAddresses[1], // user (trader) - sentAddresses[0], // lender - loanLocal.id, // loanId - loanParamsLocal.collateralToken, // collateralToken - loanParamsLocal.loanToken, // loanToken - sentValues[4], // positionSize - sentValues[1], // borrowedAmount - sentValues[0], // interestRate, - loanLocal.endTimestamp, // settlementDate - sentValues[3], // entryPrice (loanToCollateralSwapRate) - sentValues[2], // entryLeverage - margin // currentLeverage + sentAddresses[1], // user (trader) + sentAddresses[0], // lender + loanLocal.id, // loanId + loanParamsLocal.collateralToken, // collateralToken + loanParamsLocal.loanToken, // loanToken + sentValues[4], // positionSize + sentValues[1], // borrowedAmount + 0, // interestRate (old value: sentValues[0]) + 0, // settlementDate (old value: loanLocal.endTimestamp) + sentValues[3], // entryPrice (loanToCollateralSwapRate) + sentValues[2], // entryLeverage + margin // currentLeverage ); } } @@ -478,11 +447,17 @@ contract LoanOpenings is bytes32 loanId, address delegator, address delegated, - bool toggle - ) internal { + bool toggle) + internal + { delegatedManagers[loanId][delegated] = toggle; - emit DelegatedManagerSet(loanId, delegator, delegated, toggle); + emit DelegatedManagerSet( + loanId, + delegator, + delegated, + toggle + ); } function _isCollateralSatisfied( @@ -491,8 +466,11 @@ contract LoanOpenings is uint256 initialMargin, uint256 newCollateral, uint256 collateralAmountRequired, - uint256 borrowingFee - ) internal view returns (bool) { + uint256 borrowingFee) + internal + view + returns (bool) + { // allow at most 2% under-collateralized collateralAmountRequired = collateralAmountRequired .mul(98 ether) @@ -509,8 +487,8 @@ contract LoanOpenings is loanLocal.collateral, initialMargin ); - return - newCollateral.add(maxDrawdown) >= collateralAmountRequired; + return newCollateral + .add(maxDrawdown) >= collateralAmountRequired; } else { return false; } @@ -523,8 +501,10 @@ contract LoanOpenings is bytes32 loanId, uint256 initialMargin, address[4] memory sentAddresses, - uint256[5] memory sentValues - ) internal returns (Loan storage sloanLocal) { + uint256[5] memory sentValues) + internal + returns (Loan storage sloanLocal) + { require(loanParamsLocal.active, "loanParams disabled"); address lender = sentAddresses[0]; @@ -532,19 +512,10 @@ contract LoanOpenings is address manager = sentAddresses[3]; uint256 newPrincipal = sentValues[1]; - if (loanId == 0) { - loanId = keccak256( - abi.encodePacked( - loanParamsLocal.id, - lender, - borrower, - block.timestamp - ) - ); - - sloanLocal = loans[loanId]; - require(sloanLocal.id == 0, "loan exists"); + sloanLocal = loans[loanId]; + if (sloanLocal.id == 0) { + // new loan sloanLocal.id = loanId; sloanLocal.loanParamsId = loanParamsLocal.id; sloanLocal.principal = newPrincipal; @@ -562,111 +533,26 @@ contract LoanOpenings is lenderLoanSets[lender].addBytes32(loanId); borrowerLoanSets[borrower].addBytes32(loanId); } else { + // existing loan sloanLocal = loans[loanId]; - require( - sloanLocal.active && block.timestamp < sloanLocal.endTimestamp, - "loan has ended" - ); + //require(sloanLocal.active && block.timestamp < sloanLocal.endTimestamp, "loan has ended"); + require(sloanLocal.active, "loan has ended"); require(sloanLocal.borrower == borrower, "borrower mismatch"); require(sloanLocal.lender == lender, "lender mismatch"); - require( - sloanLocal.loanParamsId == loanParamsLocal.id, - "loanParams mismatch" - ); + require(sloanLocal.loanParamsId == loanParamsLocal.id, "loanParams mismatch"); - sloanLocal.principal = sloanLocal.principal.add(newPrincipal); + sloanLocal.principal = sloanLocal.principal + .add(newPrincipal); } if (manager != address(0)) { - _setDelegatedManager(loanId, borrower, manager, true); - } - } - - function _initializeInterest( - LoanParams memory loanParamsLocal, - Loan storage loanLocal, - uint256 newRate, - uint256 newPrincipal, - uint256 torqueInterest // ignored for fixed-term loans - ) internal returns (uint256 interestAmountRequired) { - // pay outstanding interest to lender - _payInterest(loanLocal.lender, loanParamsLocal.loanToken); - - LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id]; - LenderInterest storage lenderInterestLocal = lenderInterest[ - loanLocal.lender - ][loanParamsLocal.loanToken]; - - uint256 maxLoanTerm = loanParamsLocal.maxLoanTerm; - - _settleFeeRewardForInterestExpense( - loanInterestLocal, - loanLocal.id, - loanParamsLocal.loanToken, - loanLocal.borrower, - block.timestamp - ); - - uint256 previousDepositRemaining; - if (maxLoanTerm == 0 && loanLocal.endTimestamp != 0) { - previousDepositRemaining = loanLocal - .endTimestamp - .sub(block.timestamp).mul(loanInterestLocal.owedPerDay).div(1 days); // block.timestamp < endTimestamp was confirmed earlier - } - - uint256 owedPerDay = newPrincipal.mul(newRate).div( - DAYS_IN_A_YEAR * WEI_PERCENT_PRECISION - ); - - // update stored owedPerDay - loanInterestLocal.owedPerDay = loanInterestLocal.owedPerDay.add( - owedPerDay - ); - lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay.add( - owedPerDay - ); - - if (maxLoanTerm == 0) { - // indefinite-term (Torque) loan - - // torqueInterest != 0 was confirmed earlier - loanLocal.endTimestamp = torqueInterest - .add(previousDepositRemaining) - .mul(1 days) - .div(loanInterestLocal.owedPerDay) - .add(block.timestamp); - - maxLoanTerm = loanLocal.endTimestamp.sub(block.timestamp); - - // loan term has to at least be greater than one hour - require(maxLoanTerm > 1 hours, "loan too short"); - - interestAmountRequired = torqueInterest; - } else { - // fixed-term loan - - if (loanLocal.endTimestamp == 0) { - loanLocal.endTimestamp = block.timestamp.add(maxLoanTerm); - } - - interestAmountRequired = loanLocal - .endTimestamp - .sub(block.timestamp) - .mul(owedPerDay) - .div(1 days); + _setDelegatedManager( + loanId, + borrower, + manager, + true + ); } - - loanInterestLocal.depositTotal = loanInterestLocal.depositTotal.add( - interestAmountRequired - ); - - // update remaining lender interest values - lenderInterestLocal.principalTotal = lenderInterestLocal - .principalTotal - .add(newPrincipal); - lenderInterestLocal.owedTotal = lenderInterestLocal.owedTotal.add( - interestAmountRequired - ); } function _getRequiredCollateral( @@ -674,17 +560,20 @@ contract LoanOpenings is address collateralToken, uint256 newPrincipal, uint256 marginAmount, - bool isTorqueLoan - ) internal view returns (uint256 collateralTokenAmount) { + bool isTorqueLoan) + internal + view + returns (uint256 collateralTokenAmount) + { if (loanToken == collateralToken) { - collateralTokenAmount = newPrincipal.mul(marginAmount).divCeil( - WEI_PERCENT_PRECISION - ); + collateralTokenAmount = newPrincipal + .mul(marginAmount) + .divCeil(WEI_PERCENT_PRECISION); } else { - ( - uint256 sourceToDestRate, - uint256 sourceToDestPrecision - ) = IPriceFeeds(priceFeeds).queryRate(collateralToken, loanToken); + (uint256 sourceToDestRate, uint256 sourceToDestPrecision) = IPriceFeeds(priceFeeds).queryRate( + collateralToken, + loanToken + ); if (sourceToDestRate != 0) { collateralTokenAmount = newPrincipal .mul(sourceToDestPrecision) @@ -700,4 +589,4 @@ contract LoanOpenings is .add(collateralTokenAmount); } } -} +} \ No newline at end of file diff --git a/interfaces/IBZx.sol b/interfaces/IBZx.sol index 30b5b7f6..7190f143 100644 --- a/interfaces/IBZx.sol +++ b/interfaces/IBZx.sol @@ -405,8 +405,8 @@ interface IBZx { address receiver, uint256 swapAmount, // denominated in collateralToken bool returnTokenIsCollateral, // true: withdraws collateralToken, false: withdraws loanToken - bytes calldata loanDataBytes - /* Format: + bytes calldata loanDataBytes + /** Format: Uniswap v3: encode (uint256,bytes) uint256 is the ID for uniswap v3 implementation on dex selector contract bytes is payload for the routes for swapping on uniswap v3 @@ -426,7 +426,6 @@ interface IBZx { uint128: source asset ID for curve pool (is validated to ensure the source asset matches the ID specified) uint128: dest asset ID for curve pool (is validated to ensure the source asset matches the ID specified) */ - ) external returns ( From ac8ad8fbe13affad5a87f4ba0395c6a446217f84 Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Wed, 29 Dec 2021 12:39:58 -0800 Subject: [PATCH 12/83] cleanup --- contracts/core/State.sol | 8 + .../core/objects/LenderInterestStruct.sol | 10 +- contracts/core/objects/LoanInterestStruct.sol | 6 +- contracts/interfaces/ILoanPool.sol | 9 +- contracts/mixins/FeesHelper.sol | 1 + contracts/mixins/InterestHandler.sol | 134 ++++++++++ contracts/mixins/InterestUser.sol | 77 ------ .../modules/LoanClosings/LoanClosings.sol | 2 +- .../modules/LoanMaintenance/LoanCleanup.sol | 123 --------- .../LoanMaintenance/LoanMaintenance.sol | 238 +----------------- .../modules/LoanOpenings/LoanOpenings.sol | 7 +- 11 files changed, 175 insertions(+), 440 deletions(-) create mode 100644 contracts/mixins/InterestHandler.sol delete mode 100644 contracts/mixins/InterestUser.sol delete mode 100644 contracts/modules/LoanMaintenance/LoanCleanup.sol diff --git a/contracts/core/State.sol b/contracts/core/State.sol index b6e6def2..b7aefeb5 100644 --- a/contracts/core/State.sol +++ b/contracts/core/State.sol @@ -77,6 +77,14 @@ contract State is Constants, Objects, ReentrancyGuard, Ownable { uint256 public maxSwapSize = 1500 ether; // maximum supported swap size in ETH + /**** new interest model start */ + mapping(address => uint256) public poolLastUpdateTime; // per itoken + mapping(address => uint256) public poolTotalPrincipal; // per itoken + mapping(address => uint256) public poolRatePerTokenStored; // per itoken + mapping(bytes32 => uint256) public loanRatePerTokenPaid; // per loan + /**** new interest model end */ + + function _setTarget( bytes4 sig, address target) diff --git a/contracts/core/objects/LenderInterestStruct.sol b/contracts/core/objects/LenderInterestStruct.sol index 428c53b5..b16d6acd 100644 --- a/contracts/core/objects/LenderInterestStruct.sol +++ b/contracts/core/objects/LenderInterestStruct.sol @@ -8,10 +8,10 @@ pragma solidity 0.5.17; contract LenderInterestStruct { struct LenderInterest { - uint256 principalTotal; // total borrowed amount outstanding of asset - uint256 owedPerDay; // interest owed per day for all loans of asset - uint256 owedTotal; // total interest owed for all loans of asset (assuming they go to full term) - uint256 paidTotal; // total interest paid so far for asset - uint256 updatedTimestamp; // last update + uint256 principalTotal; // total borrowed amount outstanding of asset (DEPRECIATED) + uint256 owedPerDay; // interest owed per day for all loans of asset (DEPRECIATED) + uint256 owedTotal; // total interest owed for all loans of asset (DEPRECIATED) + uint256 paidTotal; // total interest paid so far for asset (DEPRECIATED) + uint256 updatedTimestamp; // last update (DEPRECIATED) } } diff --git a/contracts/core/objects/LoanInterestStruct.sol b/contracts/core/objects/LoanInterestStruct.sol index d002ad90..14cd752d 100644 --- a/contracts/core/objects/LoanInterestStruct.sol +++ b/contracts/core/objects/LoanInterestStruct.sol @@ -8,8 +8,8 @@ pragma solidity 0.5.17; contract LoanInterestStruct { struct LoanInterest { - uint256 owedPerDay; // interest owed per day for loan - uint256 depositTotal; // total escrowed interest for loan - uint256 updatedTimestamp; // last update + uint256 owedPerDay; // interest owed per day for loan (DEPRECIATED) + uint256 depositTotal; // total escrowed interest for loan (DEPRECIATED) + uint256 updatedTimestamp; // last update (DEPRECIATED) } } diff --git a/contracts/interfaces/ILoanPool.sol b/contracts/interfaces/ILoanPool.sol index 00fd1e05..280b6641 100644 --- a/contracts/interfaces/ILoanPool.sol +++ b/contracts/interfaces/ILoanPool.sol @@ -17,6 +17,13 @@ interface ILoanPool { view returns (uint256); + function _nextBorrowInterestRate( + uint256 totalBorrow, + uint256 newBorrow) + external + view + returns (uint256 nextRate); + function totalAssetSupply() external view @@ -27,4 +34,4 @@ interface ILoanPool { external view returns (uint256); -} +} \ No newline at end of file diff --git a/contracts/mixins/FeesHelper.sol b/contracts/mixins/FeesHelper.sol index e4a3adbd..77e67644 100644 --- a/contracts/mixins/FeesHelper.sol +++ b/contracts/mixins/FeesHelper.sol @@ -102,6 +102,7 @@ contract FeesHelper is State, FeesEvents { } } + // TODO !! // settles and pays borrowers based on the fees generated by their interest payments function _settleFeeRewardForInterestExpense( LoanInterest storage loanInterestLocal, diff --git a/contracts/mixins/InterestHandler.sol b/contracts/mixins/InterestHandler.sol new file mode 100644 index 00000000..d3132633 --- /dev/null +++ b/contracts/mixins/InterestHandler.sol @@ -0,0 +1,134 @@ +/** + * Copyright 2017-2021, bZxDao. All Rights Reserved. + * Licensed under the Apache License, Version 2.0. + */ + +pragma solidity 0.5.17; + +import "../core/State.sol"; +import "./FeesHelper.sol"; +import "../interfaces/ILoanPool.sol"; + + +contract InterestHandler is State, FeesHelper { + using SafeERC20 for IERC20; + + // returns up to date loan principal (with interest) or 0 is not applicable + function _settleInterest( + address pool, + address loanToken, + bytes32 loanId) + internal + returns (uint256 _loanPrincipalTotal) + { + uint256 _loanRatePerTokenPaid; + ( + poolTotalPrincipal[pool], + poolRatePerTokenStored[pool], + _loanRatePerTokenPaid, + _loanPrincipalTotal) = _settleInterest2( + pool, + loanToken, + loanId + ); + + if (loanId != 0) { + loanRatePerTokenPaid[loanId] = _loanRatePerTokenPaid; + loans[loanId].principal = _loanPrincipalTotal; + } + + poolLastUpdateTime[pool] = block.timestamp; + } + + function _getTotalPrincipal( + address pool, + address loanToken) + internal + view + returns (uint256 _poolTotalPrincipal) + { + (_poolTotalPrincipal,,,) = _settleInterest2( + pool, + loanToken, + 0 + ); + } + + function _getLoanPrincipal( + address pool, + address loanToken, + bytes32 loanId) + internal + view + returns (uint256 _loanPrincipalTotal) + { + (,,,_loanPrincipalTotal) = _settleInterest2( + pool, + loanToken, + loanId + ); + } + + function _settleInterest2( + address pool, + address loanToken, + bytes32 loanId) + internal + view + returns ( + uint256 _poolTotalPrincipal, + uint256 _poolRatePerTokenStored, + uint256 _loanRatePerTokenPaid, + uint256 _loanPrincipalTotal) + { + _poolTotalPrincipal = poolTotalPrincipal[pool]; + uint256 _poolVariableRatePerTokenNewAmount = _getRatePerTokenNewAmount(_poolTotalPrincipal, pool); + if (_poolTotalPrincipal != 0) { + _poolTotalPrincipal = _getUpdatedPrincipal( + _poolTotalPrincipal, + _poolVariableRatePerTokenNewAmount + ); + + _poolRatePerTokenStored = poolRatePerTokenStored[pool] + .add(_poolVariableRatePerTokenNewAmount); + } + + if (loanId != 0) { + _loanPrincipalTotal = loans[loanId].principal; + if (_loanPrincipalTotal != 0) { + _loanPrincipalTotal = _getUpdatedPrincipal( + _loanPrincipalTotal, + _poolRatePerTokenStored.sub(loanRatePerTokenPaid[loanId]) // _loanRatePerTokenUnpaid + ); + + _loanRatePerTokenPaid = _poolRatePerTokenStored; + } + } + } + + function _getRatePerTokenNewAmount( + uint256 _poolPrincipalTotal, + address pool) + internal + view + returns (uint256) + { + return block.timestamp + .sub(poolLastUpdateTime[pool]) + .mul(ILoanPool(pool)._nextBorrowInterestRate(_poolPrincipalTotal, 0)) // rate per year + .div(31536000); // seconds in a year + } + + function _getUpdatedPrincipal( + uint256 _principal, + uint256 _ratePerTokenNewAmount) + internal + view + returns (uint256) + { + return _principal + .mul(_ratePerTokenNewAmount) + .div(WEI_PERCENT_PRECISION) + .add(_principal); + } +} diff --git a/contracts/mixins/InterestUser.sol b/contracts/mixins/InterestUser.sol deleted file mode 100644 index 903f0f0c..00000000 --- a/contracts/mixins/InterestUser.sol +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2017-2021, bZxDao. All Rights Reserved. - * Licensed under the Apache License, Version 2.0. - */ - -pragma solidity 0.5.17; - -// import "@openzeppelin-2.5.0/token/ERC20/SafeERC20.sol"; -import "../core/State.sol"; -import "../mixins/VaultController.sol"; -import "./FeesHelper.sol"; - - -contract InterestUser is State, VaultController, FeesHelper { - using SafeERC20 for IERC20; - - function _payInterest( - address lender, - address interestToken) - internal - { - LenderInterest storage lenderInterestLocal = lenderInterest[lender][interestToken]; - - uint256 interestOwedNow = 0; - if (lenderInterestLocal.owedPerDay != 0 && lenderInterestLocal.updatedTimestamp != 0) { - interestOwedNow = block.timestamp - .sub(lenderInterestLocal.updatedTimestamp) - .mul(lenderInterestLocal.owedPerDay) - .div(1 days); - - lenderInterestLocal.updatedTimestamp = block.timestamp; - - if (interestOwedNow > lenderInterestLocal.owedTotal) - interestOwedNow = lenderInterestLocal.owedTotal; - - if (interestOwedNow != 0) { - lenderInterestLocal.paidTotal = lenderInterestLocal.paidTotal - .add(interestOwedNow); - lenderInterestLocal.owedTotal = lenderInterestLocal.owedTotal - .sub(interestOwedNow); - - _payInterestTransfer( - lender, - interestToken, - interestOwedNow - ); - } - } else { - lenderInterestLocal.updatedTimestamp = block.timestamp; - } - } - - function _payInterestTransfer( - address lender, - address interestToken, - uint256 interestOwedNow) - internal - { - uint256 lendingFee = interestOwedNow - .mul(lendingFeePercent) - .divCeil(WEI_PERCENT_PRECISION); - - _payLendingFee( - lender, - interestToken, - lendingFee - ); - - // transfers the interest to the lender, less the interest fee - vaultWithdraw( - interestToken, - lender, - interestOwedNow - .sub(lendingFee) - ); - } -} diff --git a/contracts/modules/LoanClosings/LoanClosings.sol b/contracts/modules/LoanClosings/LoanClosings.sol index 05d4da92..3340ef58 100644 --- a/contracts/modules/LoanClosings/LoanClosings.sol +++ b/contracts/modules/LoanClosings/LoanClosings.sol @@ -66,7 +66,7 @@ contract LoanClosings is LoanClosingsBase { address receiver, uint256 swapAmount, // denominated in collateralToken bool returnTokenIsCollateral, // true: withdraws collateralToken, false: withdraws loanToken - bytes memory loanDataBytes) // for future use + bytes memory loanDataBytes) public nonReentrant returns ( diff --git a/contracts/modules/LoanMaintenance/LoanCleanup.sol b/contracts/modules/LoanMaintenance/LoanCleanup.sol deleted file mode 100644 index 0a60ece1..00000000 --- a/contracts/modules/LoanMaintenance/LoanCleanup.sol +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Copyright 2017-2021, bZxDao. All Rights Reserved. - * Licensed under the Apache License, Version 2.0. - */ - -pragma solidity 0.5.17; -pragma experimental ABIEncoderV2; - -import "../../core/State.sol"; -import "../../mixins/VaultController.sol"; -import "../../mixins/InterestUser.sol"; - - -contract LoanCleanup is State, VaultController, InterestUser { - - function initialize( - address target) - external - onlyOwner - { - _setTarget(this.cleanupLoans.selector, target); - } - - function cleanupLoans( - address loanToken, - bytes32[] calldata loanIds) - external - payable - onlyOwner - returns (uint256 totalPrincipalIn) - { - for (uint256 i = 0; i < loanIds.length; i++) { - Loan memory loanLocal = loans[loanIds[i]]; - - uint256 payoffNeeded = loanLocal.principal; - - if (!loanLocal.active || payoffNeeded == 0) - continue; - - require(loanToken == loanParams[loanLocal.loanParamsId].loanToken, "wrong token"); - - // pay outstanding interest to lender - _payInterest( - loanLocal.lender, - loanToken - ); - - LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id]; - LenderInterest storage lenderInterestLocal = lenderInterest[loanLocal.lender][loanToken]; - - lenderInterestLocal.principalTotal = lenderInterestLocal.principalTotal - .sub(payoffNeeded); - - uint256 owedPerDayRefund = loanInterestLocal.owedPerDay; - loanInterestLocal.owedPerDay = 0; - - lenderInterestLocal.owedPerDay = lenderInterestLocal.owedPerDay - .sub(owedPerDayRefund); - - uint256 interestAvailable; - if (block.timestamp < loanLocal.endTimestamp) { - interestAvailable = loanLocal.endTimestamp - .sub(block.timestamp); - interestAvailable = interestAvailable - .mul(owedPerDayRefund); - interestAvailable = interestAvailable - .div(24 hours); - } - - if (interestAvailable >= payoffNeeded) { - vaultWithdraw( - loanToken, - loanLocal.lender, - payoffNeeded - ); - vaultWithdraw( - loanToken, - loanLocal.borrower, - interestAvailable-payoffNeeded - ); - } else { - if (interestAvailable != 0) { - vaultWithdraw( - loanToken, - loanLocal.lender, - interestAvailable - ); - payoffNeeded -= interestAvailable; - } - vaultTransfer( - loanToken, - msg.sender, - loanLocal.lender, - payoffNeeded - ); - totalPrincipalIn += payoffNeeded; - } - loanInterestLocal.depositTotal = 0; - loanInterestLocal.updatedTimestamp = block.timestamp; - - uint256 collateral = loanLocal.collateral; - if (collateral != 0) { - vaultWithdraw( - loanParams[loanLocal.loanParamsId].collateralToken, - msg.sender, - collateral - ); - loanLocal.collateral = 0; - } - - // loanLocal.collateral = 0; // should already be 0 - loanLocal.principal = 0; - loanLocal.active = false; - loanLocal.endTimestamp = block.timestamp; - // loanLocal.pendingTradesId = 0; // should already be 0 - activeLoansSet.removeBytes32(loanLocal.id); - lenderLoanSets[loanLocal.lender].removeBytes32(loanLocal.id); - borrowerLoanSets[loanLocal.borrower].removeBytes32(loanLocal.id); - - loans[loanLocal.id] = loanLocal; - } - } -} \ No newline at end of file diff --git a/contracts/modules/LoanMaintenance/LoanMaintenance.sol b/contracts/modules/LoanMaintenance/LoanMaintenance.sol index a46b9067..bad7158b 100644 --- a/contracts/modules/LoanMaintenance/LoanMaintenance.sol +++ b/contracts/modules/LoanMaintenance/LoanMaintenance.sol @@ -9,13 +9,14 @@ pragma experimental ABIEncoderV2; import "../../core/State.sol"; import "../../events/LoanMaintenanceEvents.sol"; import "../../mixins/VaultController.sol"; -import "../../mixins/InterestUser.sol"; import "../../mixins/LiquidationHelper.sol"; import "../../swaps/SwapsUser.sol"; import "../../governance/PausableGuardian.sol"; +import "../../mixins/InterestHandler.sol"; +// TODO: support new loan format -contract LoanMaintenance is State, LoanMaintenanceEvents, VaultController, InterestUser, SwapsUser, LiquidationHelper, PausableGuardian { +contract LoanMaintenance is State, LoanMaintenanceEvents, VaultController, SwapsUser, LiquidationHelper, PausableGuardian, InterestHandler { function initialize( address target) @@ -24,14 +25,14 @@ contract LoanMaintenance is State, LoanMaintenanceEvents, VaultController, Inter { _setTarget(this.depositCollateral.selector, target); _setTarget(this.withdrawCollateral.selector, target); - _setTarget(this.withdrawAccruedInterest.selector, target); - _setTarget(this.extendLoanDuration.selector, target); - _setTarget(this.reduceLoanDuration.selector, target); + //_setTarget(this.withdrawAccruedInterest.selector, target); <-- remove target + //_setTarget(this.extendLoanDuration.selector, target); <-- remove target + //_setTarget(this.reduceLoanDuration.selector, target); <-- remove target _setTarget(this.setDepositAmount.selector, target); _setTarget(this.claimRewards.selector, target); _setTarget(this.rewardsBalanceOf.selector, target); - _setTarget(this.getLenderInterestData.selector, target); - _setTarget(this.getLoanInterestData.selector, target); + //_setTarget(this.getLenderInterestData.selector, target); + //_setTarget(this.getLoanInterestData.selector, target); _setTarget(this.getUserLoans.selector, target); _setTarget(this.getUserLoansCount.selector, target); _setTarget(this.getLoan.selector, target); @@ -180,223 +181,6 @@ contract LoanMaintenance is State, LoanMaintenanceEvents, VaultController, Inter ); } - function withdrawAccruedInterest( - address loanToken) - external - pausable - { - // pay outstanding interest to lender - _payInterest( - msg.sender, // lender - loanToken - ); - } - - function extendLoanDuration( - bytes32 loanId, - uint256 depositAmount, - bool useCollateral, - bytes calldata /*loanDataBytes*/) // for future use - external - payable - nonReentrant - pausable - returns (uint256 secondsExtended) - { - require(depositAmount != 0, "depositAmount is 0"); - Loan storage loanLocal = loans[loanId]; - LoanParams storage loanParamsLocal = loanParams[loanLocal.loanParamsId]; - - require(loanLocal.active, "loan is closed"); - require( - !useCollateral || - msg.sender == loanLocal.borrower || - delegatedManagers[loanLocal.id][msg.sender], - "unauthorized" - ); - - require(msg.value == 0 || (!useCollateral && loanParamsLocal.loanToken == address(wethToken)), "wrong asset sent"); - - // pay outstanding interest to lender - _payInterest( - loanLocal.lender, - loanParamsLocal.loanToken - ); - - LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id]; - - _settleFeeRewardForInterestExpense( - loanInterestLocal, - loanLocal.id, - loanParamsLocal.loanToken, - loanLocal.borrower, - block.timestamp - ); - - // Handle back interest: calculates interest owned since the loan endtime passed but the loan remained open - uint256 backInterestOwed; - if (block.timestamp > loanLocal.endTimestamp) { - backInterestOwed = block.timestamp - .sub(loanLocal.endTimestamp); - backInterestOwed = backInterestOwed - .mul(loanInterestLocal.owedPerDay); - backInterestOwed = backInterestOwed - .div(1 days); - - require(depositAmount > backInterestOwed, "deposit cannot cover back interest"); - } - - // deposit interest - uint256 collateralUsed; - if (useCollateral) { - collateralUsed = _doSwapWithCollateral( - loanLocal, - loanParamsLocal, - depositAmount - ); - } else { - if (msg.value == 0) { - vaultDeposit( - loanParamsLocal.loanToken, - msg.sender, - depositAmount - ); - } else { - require(msg.value == depositAmount, "ether deposit mismatch"); - vaultEtherDeposit( - msg.sender, - msg.value - ); - } - } - - if (backInterestOwed != 0) { - depositAmount = depositAmount - .sub(backInterestOwed); - - // pay out backInterestOwed - _payInterestTransfer( - loanLocal.lender, - loanParamsLocal.loanToken, - backInterestOwed - ); - } - - secondsExtended = depositAmount - .mul(1 days) - .div(loanInterestLocal.owedPerDay); - - loanLocal.endTimestamp = loanLocal.endTimestamp - .add(secondsExtended); - - require(loanLocal.endTimestamp > block.timestamp && - (loanLocal.endTimestamp - block.timestamp) > 1 hours, - "loan too short" - ); - - loanInterestLocal.depositTotal = loanInterestLocal.depositTotal - .add(depositAmount); - - lenderInterest[loanLocal.lender][loanParamsLocal.loanToken].owedTotal = lenderInterest[loanLocal.lender][loanParamsLocal.loanToken].owedTotal - .add(depositAmount); - - emit ExtendLoanDuration( - loanLocal.borrower, - loanParamsLocal.loanToken, - loanId, - depositAmount, - collateralUsed, - loanLocal.endTimestamp - ); - } - - function reduceLoanDuration( - bytes32 loanId, - address receiver, - uint256 withdrawAmount) - external - nonReentrant - pausable - returns (uint256 secondsReduced) - { - require(withdrawAmount != 0, "withdrawAmount is 0"); - Loan storage loanLocal = loans[loanId]; - LoanParams storage loanParamsLocal = loanParams[loanLocal.loanParamsId]; - - require(loanLocal.active, "loan is closed"); - require( - msg.sender == loanLocal.borrower || - delegatedManagers[loanLocal.id][msg.sender], - "unauthorized" - ); - - require(loanLocal.endTimestamp > block.timestamp, "loan term has ended"); - - // pay outstanding interest to lender - _payInterest( - loanLocal.lender, - loanParamsLocal.loanToken - ); - - LoanInterest storage loanInterestLocal = loanInterest[loanLocal.id]; - - _settleFeeRewardForInterestExpense( - loanInterestLocal, - loanLocal.id, - loanParamsLocal.loanToken, - loanLocal.borrower, - block.timestamp - ); - - uint256 interestDepositRemaining = loanLocal.endTimestamp - .sub(block.timestamp) - .mul(loanInterestLocal.owedPerDay) - .div(1 days); - require(withdrawAmount < interestDepositRemaining, "withdraw amount too high"); - - // withdraw interest - if (loanParamsLocal.loanToken == address(wethToken)) { - vaultEtherWithdraw( - receiver, - withdrawAmount - ); - } else { - vaultWithdraw( - loanParamsLocal.loanToken, - receiver, - withdrawAmount - ); - } - - secondsReduced = withdrawAmount - .mul(1 days) - .div(loanInterestLocal.owedPerDay); - - require (loanLocal.endTimestamp > secondsReduced, "loan too short"); - - loanLocal.endTimestamp = loanLocal.endTimestamp - .sub(secondsReduced); - - require(loanLocal.endTimestamp > block.timestamp && - (loanLocal.endTimestamp - block.timestamp) > 1 hours, - "loan too short" - ); - - loanInterestLocal.depositTotal = loanInterestLocal.depositTotal - .sub(withdrawAmount); - - lenderInterest[loanLocal.lender][loanParamsLocal.loanToken].owedTotal = lenderInterest[loanLocal.lender][loanParamsLocal.loanToken].owedTotal - .sub(withdrawAmount); - - emit ReduceLoanDuration( - loanLocal.borrower, - loanParamsLocal.loanToken, - loanId, - withdrawAmount, - loanLocal.endTimestamp - ); - } - function setDepositAmount( bytes32 loanId, uint256 depositValueAsLoanToken, @@ -459,7 +243,7 @@ contract LoanMaintenance is State, LoanMaintenanceEvents, VaultController, Inter } } - /// @dev Gets current lender interest data totals for all loans with a specific oracle and interest token + /*/// @dev Gets current lender interest data totals for all loans with a specific oracle and interest token /// @param lender The lender address /// @param loanToken The loan token address /// @return interestPaid The total amount of interest that has been paid to a lender so far @@ -527,7 +311,7 @@ contract LoanMaintenance is State, LoanMaintenanceEvents, VaultController, Inter .mul(interestOwedPerDay) .div(1 days) : 0; - } + }*/ // Only returns data for loans that are active // All(0): all loans @@ -764,7 +548,7 @@ contract LoanMaintenance is State, LoanMaintenanceEvents, VaultController, Inter collateralToken: loanParamsLocal.collateralToken, principal: loanLocal.principal, collateral: loanLocal.collateral, - interestOwedPerDay: loanInterestLocal.owedPerDay, + interestOwedPerDay: loanType == LoanType.NonMargin ? loanInterestLocal.owedPerDay : 0, interestDepositRemaining: value, startRate: loanLocal.startRate, startMargin: loanLocal.startMargin, diff --git a/contracts/modules/LoanOpenings/LoanOpenings.sol b/contracts/modules/LoanOpenings/LoanOpenings.sol index 6bac8106..614d5e92 100644 --- a/contracts/modules/LoanOpenings/LoanOpenings.sol +++ b/contracts/modules/LoanOpenings/LoanOpenings.sol @@ -112,15 +112,16 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestHan uint256 loanTokenSent, uint256 collateralTokenSent, uint256 /*interestRate*/, - uint256 /*newPrincipal*/) + uint256 /*newPrincipal*/, + bytes calldata payload) external - view returns (uint256 value) { value = _swapsExpectedReturn( loanToken, collateralToken, - loanTokenSent + loanTokenSent, + payload ); if (value != 0) { return collateralTokenSent From fd45c17fac2fc37d0e83229071ade019eee23fb6 Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Wed, 29 Dec 2021 15:27:00 -0800 Subject: [PATCH 13/83] Update IBZx.sol --- interfaces/IBZx.sol | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/interfaces/IBZx.sol b/interfaces/IBZx.sol index 7190f143..72f56410 100644 --- a/interfaces/IBZx.sol +++ b/interfaces/IBZx.sol @@ -396,7 +396,7 @@ interface IBZx { /// @param receiver collateral token reciever address /// @param swapAmount amount of loan token to swap /// @param returnTokenIsCollateral boolean whether to return tokens is collateral - /// @param loanDataBytes reserved for future use + /// @param loanDataBytes custom payload for specifying swap implementation and data to pass /// @return loanCloseAmount loan close amount /// @return withdrawAmount loan token withdraw amount /// @return withdrawToken loan token address @@ -405,27 +405,7 @@ interface IBZx { address receiver, uint256 swapAmount, // denominated in collateralToken bool returnTokenIsCollateral, // true: withdraws collateralToken, false: withdraws loanToken - bytes calldata loanDataBytes - /** Format: - Uniswap v3: encode (uint256,bytes) - uint256 is the ID for uniswap v3 implementation on dex selector contract - bytes is payload for the routes for swapping on uniswap v3 - Format: encode ExactInputParams[] or ExactOutputParams[] - Allows for multiple swap routes and arbitrarily long swap route as long as start is the source asset and end is destination asset - amountIn/amountOut and amountOutMinimum/amountInMaximum are the percent value to allocate balance for each route IE 100% alloc would be 100 and 100 for the inputs - excess swap amount not accounted for due to incorrectly inputting percentages results in adding the swap amount to the first route - Sushiswap: enter nothing OR encode (uint256,bytes) - uint256 is 1 (sushiswap is always ID 1) - bytes is blank - Curve: encode (uint256,bytes) - uint256 is the ID for curve implementation on dex selector contract - bytes is payload for curve swap route and tokens - Format: encode (bytes4,address,uint128,uint128) - bytes4 is signature for style of execution (depends on Curve pool type) - address: curve pool address - uint128: source asset ID for curve pool (is validated to ensure the source asset matches the ID specified) - uint128: dest asset ID for curve pool (is validated to ensure the source asset matches the ID specified) - */ + bytes calldata loanDataBytes ) external returns ( From ee5bb2dac30cb50b85c9254da88de158c3f3f9a5 Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Wed, 29 Dec 2021 15:28:04 -0800 Subject: [PATCH 14/83] Update IToken.sol --- interfaces/IToken.sol | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/interfaces/IToken.sol b/interfaces/IToken.sol index 0c2bd974..4fb07b2a 100644 --- a/interfaces/IToken.sol +++ b/interfaces/IToken.sol @@ -72,26 +72,6 @@ interface IToken { address collateralTokenAddress, address trader, bytes memory loanDataBytes // arbitrary order data - /* Format: - Uniswap v3: encode (uint256,bytes) - uint256 is the ID for uniswap v3 implementation on dex selector contract - bytes is payload for the routes for swapping on uniswap v3 - Format: encode ExactInputParams[] or ExactOutputParams[] - Allows for multiple swap routes and arbitrarily long swap route as long as start is the source asset and end is destination asset - amountIn/amountOut and amountOutMinimum/amountInMaximum are the percent value to allocate balance for each route IE 100% alloc would be 100 and 100 for the inputs - excess swap amount not accounted for due to incorrectly inputting percentages results in adding the swap amount to the first route - Sushiswap: enter nothing OR encode (uint256,bytes) - uint256 is 1 (sushiswap is always ID 1) - bytes is blank - Curve: encode (uint256,bytes) - uint256 is the ID for curve implementation on dex selector contract - bytes is payload for curve swap route and tokens - Format: encode (bytes4,address,uint128,uint128) - bytes4 is signature for style of execution (depends on Curve pool type) - address: curve pool address - uint128: source asset ID for curve pool (is validated to ensure the source asset matches the ID specified) - uint128: dest asset ID for curve pool (is validated to ensure the source asset matches the ID specified) - */ ) external payable returns (LoanOpenData memory); function marginTradeWithGasToken( From ce896869f6d49c94a4d9939f27f13157830cbbff Mon Sep 17 00:00:00 2001 From: Drypto | OOKI <51417606+Drypto13@users.noreply.github.com> Date: Wed, 29 Dec 2021 17:38:16 -0800 Subject: [PATCH 15/83] Update LoanTokenLogicStandard.sol --- contracts/connectors/loantoken/LoanTokenLogicStandard.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/connectors/loantoken/LoanTokenLogicStandard.sol b/contracts/connectors/loantoken/LoanTokenLogicStandard.sol index 7890bebc..cea26415 100644 --- a/contracts/connectors/loantoken/LoanTokenLogicStandard.sol +++ b/contracts/connectors/loantoken/LoanTokenLogicStandard.sol @@ -152,7 +152,7 @@ contract LoanTokenLogicStandard is AdvancedToken { address collateralTokenAddress, // if address(0), this means ETH and ETH must be sent with the call or loanId must be provided address borrower, address receiver, - bytes memory /*loanDataBytes*/) // arbitrary order data (for future use) + bytes memory loanDataBytes) // arbitrary order data public payable nonReentrant @@ -662,7 +662,7 @@ contract LoanTokenLogicStandard is AdvancedToken { address collateralTokenAddress, // if address(0), this means ETH and ETH must be sent with the call or loanId must be provided address borrower, address receiver, - bytes memory /*loanDataBytes*/) // arbitrary order data (for future use) + bytes memory loanDataBytes) // arbitrary order data internal pausable returns (IBZx.LoanOpenData memory) From 9e765fb83f5d942a291999b0d0c3e301dcf5996d Mon Sep 17 00:00:00 2001 From: Drypto | OOKI <51417606+Drypto13@users.noreply.github.com> Date: Wed, 29 Dec 2021 17:38:50 -0800 Subject: [PATCH 16/83] Update IToken.sol --- interfaces/IToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interfaces/IToken.sol b/interfaces/IToken.sol index d3eb0fc8..d955f99f 100644 --- a/interfaces/IToken.sol +++ b/interfaces/IToken.sol @@ -49,7 +49,7 @@ interface IToken { address collateralTokenAddress, // if address(0), this means ETH and ETH must be sent with the call or loanId must be provided address borrower, address receiver, - bytes calldata /*loanDataBytes*/ // arbitrary order data (for future use) + bytes calldata loanDataBytes // arbitrary order data ) external payable returns (LoanOpenData memory); function borrowWithGasToken( From 717cb79e2f68787c49a28ccfed5d420f50defcd9 Mon Sep 17 00:00:00 2001 From: Drypto | OOKI <51417606+Drypto13@users.noreply.github.com> Date: Thu, 30 Dec 2021 08:23:07 -0800 Subject: [PATCH 17/83] Update SwapsExternal.sol --- contracts/modules/SwapsExternal/SwapsExternal.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/modules/SwapsExternal/SwapsExternal.sol b/contracts/modules/SwapsExternal/SwapsExternal.sol index 44e72895..5a6e4678 100644 --- a/contracts/modules/SwapsExternal/SwapsExternal.sol +++ b/contracts/modules/SwapsExternal/SwapsExternal.sol @@ -116,7 +116,7 @@ contract SwapsExternal is State, VaultController, SwapsUser, PausableGuardian { address sourceToken, address destToken, uint256 sourceTokenAmount, - bytes calldata payload) + bytes calldata payload) external returns (uint256) { @@ -124,7 +124,7 @@ contract SwapsExternal is State, VaultController, SwapsUser, PausableGuardian { sourceToken, destToken, sourceTokenAmount, - payload + payload ); } -} \ No newline at end of file +} From 0551ad26f224f353741e6e72004329b5618ba208 Mon Sep 17 00:00:00 2001 From: Drypto | OOKI <51417606+Drypto13@users.noreply.github.com> Date: Thu, 30 Dec 2021 08:26:09 -0800 Subject: [PATCH 18/83] Update LoanTokenLogicStandard.sol --- contracts/connectors/loantoken/LoanTokenLogicStandard.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/connectors/loantoken/LoanTokenLogicStandard.sol b/contracts/connectors/loantoken/LoanTokenLogicStandard.sol index cea26415..ceb1f2ff 100644 --- a/contracts/connectors/loantoken/LoanTokenLogicStandard.sol +++ b/contracts/connectors/loantoken/LoanTokenLogicStandard.sol @@ -152,7 +152,7 @@ contract LoanTokenLogicStandard is AdvancedToken { address collateralTokenAddress, // if address(0), this means ETH and ETH must be sent with the call or loanId must be provided address borrower, address receiver, - bytes memory loanDataBytes) // arbitrary order data + bytes memory /*loanDataBytes*/) // arbitrary order data public payable nonReentrant @@ -662,7 +662,7 @@ contract LoanTokenLogicStandard is AdvancedToken { address collateralTokenAddress, // if address(0), this means ETH and ETH must be sent with the call or loanId must be provided address borrower, address receiver, - bytes memory loanDataBytes) // arbitrary order data + bytes memory /*loanDataBytes*/) // arbitrary order data internal pausable returns (IBZx.LoanOpenData memory) From 835f00dfd79642d57181ec990e387d9464eedbd9 Mon Sep 17 00:00:00 2001 From: Drypto | OOKI <51417606+Drypto13@users.noreply.github.com> Date: Thu, 30 Dec 2021 08:27:09 -0800 Subject: [PATCH 19/83] Update IToken.sol --- interfaces/IToken.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interfaces/IToken.sol b/interfaces/IToken.sol index d955f99f..a7d5a0cc 100644 --- a/interfaces/IToken.sol +++ b/interfaces/IToken.sol @@ -49,7 +49,7 @@ interface IToken { address collateralTokenAddress, // if address(0), this means ETH and ETH must be sent with the call or loanId must be provided address borrower, address receiver, - bytes calldata loanDataBytes // arbitrary order data + bytes calldata /*loanDataBytes*/ // arbitrary order data ) external payable returns (LoanOpenData memory); function borrowWithGasToken( From fcd9ccf4acda30ba3064b3f0bcd53e75f403a608 Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Sun, 9 Jan 2022 00:07:47 -0800 Subject: [PATCH 20/83] Flags --- contracts/mixins/Flags.sol | 5 ++++ contracts/swaps/SwapsUser.sol | 48 ++++++++++++++++++++++------------- tests/test_selector.py | 5 +++- 3 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 contracts/mixins/Flags.sol diff --git a/contracts/mixins/Flags.sol b/contracts/mixins/Flags.sol new file mode 100644 index 00000000..58de56c6 --- /dev/null +++ b/contracts/mixins/Flags.sol @@ -0,0 +1,5 @@ +pragma solidity 0.5.17; + +contract Flags{ + uint128 public constant DEX_SELECTOR_FLAG = 2; // base-2: 10 +} \ No newline at end of file diff --git a/contracts/swaps/SwapsUser.sol b/contracts/swaps/SwapsUser.sol index 512d3280..5737499a 100644 --- a/contracts/swaps/SwapsUser.sol +++ b/contracts/swaps/SwapsUser.sol @@ -12,8 +12,9 @@ import "../events/SwapsEvents.sol"; import "../mixins/FeesHelper.sol"; import "./ISwapsImpl.sol"; import "../interfaces/IDexRecords.sol"; +import "../mixins/Flags.sol"; -contract SwapsUser is State, SwapsEvents, FeesHelper { +contract SwapsUser is State, SwapsEvents, FeesHelper, Flags { function _loanSwap( bytes32 loanId, address sourceToken, @@ -32,7 +33,6 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { uint256 sourceToDestSwapRate ) { - (destTokenAmountReceived, sourceTokenAmountUsed) = _swapsCall( [ sourceToken, @@ -50,7 +50,7 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { bypassFee, loanDataBytes ); - + // will revert if swap size too large _checkSwapSize(sourceToken, sourceTokenAmountUsed); @@ -127,13 +127,28 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { } else { require(vals[0] <= vals[1], "min greater than max"); } - - (destTokenAmountReceived, sourceTokenAmountUsed) = _swapsCall_internal( - addrs, - vals, - loanDataBytes - ); - + if (loanDataBytes.length == 0) { + ( + destTokenAmountReceived, + sourceTokenAmountUsed + ) = _swapsCall_internal(addrs, vals, ""); + } else { + (uint128 flags, bytes[] memory payload) = abi.decode( + loanDataBytes, + (uint128, bytes[]) + ); + if (flags & DEX_SELECTOR_FLAG != 0) { + ( + destTokenAmountReceived, + sourceTokenAmountUsed + ) = _swapsCall_internal(addrs, vals, payload[0]); + } else { + ( + destTokenAmountReceived, + sourceTokenAmountUsed + ) = _swapsCall_internal(addrs, vals, ""); + } + } if (vals[2] == 0) { // there's no minimum destTokenAmount, but all of vals[0] (minSourceTokenAmount) must be spent, and amount spent can't exceed vals[0] require( @@ -177,7 +192,7 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { { bytes memory data; address swapImplAddress; - + if (loanDataBytes.length == 0) { swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress(1); //if nothing specified, default to first dex option available. ensure it does not require any input data or else this will break data = abi.encodeWithSelector( @@ -189,18 +204,18 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { vals[0], // minSourceTokenAmount vals[1], // maxSourceTokenAmount vals[2], // requiredDestTokenAmount - loanDataBytes + "" ); } else { (uint256 DexNumber, bytes memory SwapData) = abi.decode( loanDataBytes, (uint256, bytes) ); - + swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress( DexNumber ); - + data = abi.encodeWithSelector( ISwapsImpl(swapImplAddress).dexSwap.selector, addrs[0], // sourceToken @@ -212,12 +227,11 @@ contract SwapsUser is State, SwapsEvents, FeesHelper { vals[2], // requiredDestTokenAmount SwapData ); - } - + bool success; (success, data) = swapImplAddress.delegatecall(data); - + if (!success) { assembly { let ptr := mload(0x40) diff --git a/tests/test_selector.py b/tests/test_selector.py index ff486eee..8d179643 100644 --- a/tests/test_selector.py +++ b/tests/test_selector.py @@ -48,10 +48,12 @@ def trade_curve(mainState,dexSelector): sendOut = encode_abi(['uint256','bytes'],[2,sendOut]) iToken = Contract.from_abi('i','0x32E4c68B3A4a813b710595AebA7f6B7604Ab9c15',LoanTokenLogicStandard.abi) interface.IERC20('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48').approve(iToken.address,1000000000000e6,{'from':trader,'gas_price':Gas}) + sendOut = encode_abi(['uint128','bytes[]'],[2,[sendOut]]) #flag value of Base-2: 10 tradeReturn = iToken.marginTrade(0,2e18,100000e6,0,"0xdac17f958d2ee523a2206206994597c13d831ec7",trader,sendOut.hex(),{'from':trader,'gas_price':Gas}).return_value print(""+str(tradeReturn[1])+" "+str(tradeReturn[2])) #prints principal and collateral sendOut = encode_abi(['bytes4','address','uint128','uint128'],[HexBytes(swapImpl.ExchangeSig.call()),"0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7",2,1]) sendOut = encode_abi(['uint256','bytes'],[2,sendOut]) + sendOut = encode_abi(['uint128','bytes[]'],[2,[sendOut]]) #flag value of Base-2: 10 mainState.closeWithSwap(tradeReturn[0],trader,10e6,True,sendOut.hex(),{'from':trader,'gas_price':Gas}) @@ -60,10 +62,10 @@ def trade_univ3(mainState,dexSelector): mainState.setTargets(["setSwapApprovals(address[])"],[dexSelector.dexes.call(3)],{'from':timelock,'gas_price':Gas}) swaps1 = Contract.from_abi('Impl',mainState.address,SwapsImplUniswapV3_ETH.abi) swaps1.setSwapApprovals(['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48','0xdac17f958d2ee523a2206206994597c13d831ec7'],{'from':timelock,'gas_price':Gas}) - route = encode_abi_packed(['address','uint24','address'],["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",500,"0xdAC17F958D2ee523a2206206994597C13D831ec7"]) totalPassage = encode_abi(['(bytes,address,uint256,uint256,uint256)[]'],[[(route,mainState.address,1643224769,100,100)]]) sendOut = encode_abi(['uint256','bytes'],[3,totalPassage]) + sendOut = encode_abi(['uint128','bytes[]'],[2,[sendOut]]) #flag value of Base-2: 10 iToken = Contract.from_abi('i','0x32E4c68B3A4a813b710595AebA7f6B7604Ab9c15',LoanTokenLogicStandard.abi) tradeReturn = iToken.marginTrade(0,2e18,100000e6,0,"0xdAC17F958D2ee523a2206206994597C13D831ec7",trader,sendOut.hex(),{'from':trader,'gas_price':Gas}).return_value @@ -71,6 +73,7 @@ def trade_univ3(mainState,dexSelector): route = encode_abi_packed(['address','uint24','address'],["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",500,"0xdAC17F958D2ee523a2206206994597C13D831ec7"]) totalPassage = encode_abi(['(bytes,address,uint256,uint256,uint256)[]'],[[(route,mainState.address,1643224769,100,100)]]) sendOut = encode_abi(['uint256','bytes'],[3,totalPassage]) + sendOut = encode_abi(['uint128','bytes[]'],[2,[sendOut]]) #flag value of Base-2: 10 mainState.closeWithSwap(tradeReturn[0],trader,10e6,True,sendOut.hex(),{'from':trader,'gas_price':Gas}) def trade_univ2(mainState,dexSelector): From 204237e7cd94382e9295916efee02f3c0a24cb7b Mon Sep 17 00:00:00 2001 From: Drypto | OOKI <51417606+Drypto13@users.noreply.github.com> Date: Sun, 9 Jan 2022 00:31:07 -0800 Subject: [PATCH 21/83] Update Flags.sol --- contracts/mixins/Flags.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/mixins/Flags.sol b/contracts/mixins/Flags.sol index 58de56c6..2c104e3c 100644 --- a/contracts/mixins/Flags.sol +++ b/contracts/mixins/Flags.sol @@ -1,5 +1,5 @@ pragma solidity 0.5.17; contract Flags{ - uint128 public constant DEX_SELECTOR_FLAG = 2; // base-2: 10 -} \ No newline at end of file + uint128 public constant DEX_SELECTOR_FLAG = 2; // base-2: 10 +} From f8a0d443000af71dbb03db8810918956df057b82 Mon Sep 17 00:00:00 2001 From: Roman Hiden Date: Sun, 9 Jan 2022 14:46:49 +0200 Subject: [PATCH 22/83] formated Flags --- contracts/mixins/Flags.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/mixins/Flags.sol b/contracts/mixins/Flags.sol index 2c104e3c..1711a8f1 100644 --- a/contracts/mixins/Flags.sol +++ b/contracts/mixins/Flags.sol @@ -1,5 +1,5 @@ pragma solidity 0.5.17; -contract Flags{ +contract Flags { uint128 public constant DEX_SELECTOR_FLAG = 2; // base-2: 10 } From 34f7decca90d57e1e305bb5d411275c45f0524ed Mon Sep 17 00:00:00 2001 From: Roman Hiden Date: Sun, 9 Jan 2022 14:48:24 +0200 Subject: [PATCH 23/83] formated loan opening l115 --- contracts/modules/LoanOpenings/LoanOpenings.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/modules/LoanOpenings/LoanOpenings.sol b/contracts/modules/LoanOpenings/LoanOpenings.sol index ea5a9004..ffa3bcbf 100644 --- a/contracts/modules/LoanOpenings/LoanOpenings.sol +++ b/contracts/modules/LoanOpenings/LoanOpenings.sol @@ -113,7 +113,7 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestHan uint256 collateralTokenSent, uint256 /*interestRate*/, uint256 /*newPrincipal*/, - bytes calldata payload) + bytes calldata payload) external returns (uint256 value) { From 78478fe2d2fcbe8975398eddbab9f00490a80587 Mon Sep 17 00:00:00 2001 From: Roman Hiden Date: Sun, 9 Jan 2022 14:48:48 +0200 Subject: [PATCH 24/83] formated loan opening l124 --- contracts/modules/LoanOpenings/LoanOpenings.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/modules/LoanOpenings/LoanOpenings.sol b/contracts/modules/LoanOpenings/LoanOpenings.sol index ffa3bcbf..614d5e92 100644 --- a/contracts/modules/LoanOpenings/LoanOpenings.sol +++ b/contracts/modules/LoanOpenings/LoanOpenings.sol @@ -121,7 +121,7 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestHan loanToken, collateralToken, loanTokenSent, - payload + payload ); if (value != 0) { return collateralTokenSent From 436fc66ade541b61f1faafded63796709fea23a7 Mon Sep 17 00:00:00 2001 From: Roman Hiden Date: Sun, 9 Jan 2022 14:49:36 +0200 Subject: [PATCH 25/83] formated swap external --- contracts/modules/SwapsExternal/SwapsExternal.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/modules/SwapsExternal/SwapsExternal.sol b/contracts/modules/SwapsExternal/SwapsExternal.sol index 5a6e4678..f4559d1f 100644 --- a/contracts/modules/SwapsExternal/SwapsExternal.sol +++ b/contracts/modules/SwapsExternal/SwapsExternal.sol @@ -116,7 +116,7 @@ contract SwapsExternal is State, VaultController, SwapsUser, PausableGuardian { address sourceToken, address destToken, uint256 sourceTokenAmount, - bytes calldata payload) + bytes calldata payload) external returns (uint256) { @@ -124,7 +124,7 @@ contract SwapsExternal is State, VaultController, SwapsUser, PausableGuardian { sourceToken, destToken, sourceTokenAmount, - payload + payload ); } } From 1c8bf9def7cdcfb0fc77cdc66c597e1067b375ac Mon Sep 17 00:00:00 2001 From: Roman Hiden Date: Sun, 9 Jan 2022 15:11:54 +0200 Subject: [PATCH 26/83] minor var rename --- contracts/swaps/SwapsUser.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/swaps/SwapsUser.sol b/contracts/swaps/SwapsUser.sol index 5737499a..94ef5f05 100644 --- a/contracts/swaps/SwapsUser.sol +++ b/contracts/swaps/SwapsUser.sol @@ -207,13 +207,13 @@ contract SwapsUser is State, SwapsEvents, FeesHelper, Flags { "" ); } else { - (uint256 DexNumber, bytes memory SwapData) = abi.decode( + (uint256 dexNumber, bytes memory swapData) = abi.decode( loanDataBytes, (uint256, bytes) ); swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress( - DexNumber + dexNumber ); data = abi.encodeWithSelector( @@ -225,7 +225,7 @@ contract SwapsUser is State, SwapsEvents, FeesHelper, Flags { vals[0], // minSourceTokenAmount vals[1], // maxSourceTokenAmount vals[2], // requiredDestTokenAmount - SwapData + swapData ); } From a8d2a7de162b3cee713bc99bd728df5e3850bbaa Mon Sep 17 00:00:00 2001 From: Roman Hiden Date: Sun, 9 Jan 2022 15:22:03 +0200 Subject: [PATCH 27/83] optimized deployment cost --- contracts/swaps/SwapsUser.sol | 39 ++++++++++++++--------------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/contracts/swaps/SwapsUser.sol b/contracts/swaps/SwapsUser.sol index 94ef5f05..d95358ba 100644 --- a/contracts/swaps/SwapsUser.sol +++ b/contracts/swaps/SwapsUser.sol @@ -192,22 +192,13 @@ contract SwapsUser is State, SwapsEvents, FeesHelper, Flags { { bytes memory data; address swapImplAddress; + bytes memory swapData; if (loanDataBytes.length == 0) { swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress(1); //if nothing specified, default to first dex option available. ensure it does not require any input data or else this will break - data = abi.encodeWithSelector( - ISwapsImpl(swapImplAddress).dexSwap.selector, - addrs[0], // sourceToken - addrs[1], // destToken - addrs[2], // receiverAddress - addrs[3], // returnToSenderAddress - vals[0], // minSourceTokenAmount - vals[1], // maxSourceTokenAmount - vals[2], // requiredDestTokenAmount - "" - ); } else { - (uint256 dexNumber, bytes memory swapData) = abi.decode( + uint256 dexNumber; + (dexNumber, swapData) = abi.decode( loanDataBytes, (uint256, bytes) ); @@ -215,20 +206,20 @@ contract SwapsUser is State, SwapsEvents, FeesHelper, Flags { swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress( dexNumber ); - - data = abi.encodeWithSelector( - ISwapsImpl(swapImplAddress).dexSwap.selector, - addrs[0], // sourceToken - addrs[1], // destToken - addrs[2], // receiverAddress - addrs[3], // returnToSenderAddress - vals[0], // minSourceTokenAmount - vals[1], // maxSourceTokenAmount - vals[2], // requiredDestTokenAmount - swapData - ); } + data = abi.encodeWithSelector( + ISwapsImpl(swapImplAddress).dexSwap.selector, + addrs[0], // sourceToken + addrs[1], // destToken + addrs[2], // receiverAddress + addrs[3], // returnToSenderAddress + vals[0], // minSourceTokenAmount + vals[1], // maxSourceTokenAmount + vals[2], // requiredDestTokenAmount + swapData + ); + bool success; (success, data) = swapImplAddress.delegatecall(data); From 4b9ad1ff3cca3efed7c200b0444ad2013cd8531f Mon Sep 17 00:00:00 2001 From: Roman Hiden Date: Sun, 9 Jan 2022 15:34:22 +0200 Subject: [PATCH 28/83] moar optimizations --- contracts/swaps/SwapsUser.sol | 45 ++++++++++++++++------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/contracts/swaps/SwapsUser.sol b/contracts/swaps/SwapsUser.sol index d95358ba..d24b627d 100644 --- a/contracts/swaps/SwapsUser.sol +++ b/contracts/swaps/SwapsUser.sol @@ -193,21 +193,18 @@ contract SwapsUser is State, SwapsEvents, FeesHelper, Flags { bytes memory data; address swapImplAddress; bytes memory swapData; - - if (loanDataBytes.length == 0) { - swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress(1); //if nothing specified, default to first dex option available. ensure it does not require any input data or else this will break - } else { - uint256 dexNumber; + uint256 dexNumber = 1; + if (loanDataBytes.length != 0) { (dexNumber, swapData) = abi.decode( loanDataBytes, (uint256, bytes) ); - - swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress( - dexNumber - ); } + swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress( + dexNumber + ); + data = abi.encodeWithSelector( ISwapsImpl(swapImplAddress).dexSwap.selector, addrs[0], // sourceToken @@ -247,28 +244,26 @@ contract SwapsUser is State, SwapsEvents, FeesHelper, Flags { if (tradingFee != 0) { sourceTokenAmount = sourceTokenAmount.sub(tradingFee); } + address swapImplAddress; + bytes memory dataToSend; + uint256 dexNumber = 1; if (payload.length == 0) { - address swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress( - 1 - ); //default dex address - bytes memory dataToSend = abi.encode(sourceToken, destToken); - (expectedReturn, ) = ISwapsImpl(swapImplAddress).dexAmountOut( - dataToSend, - sourceTokenAmount - ); + dataToSend = abi.encode(sourceToken, destToken); } else { - (uint256 DexNumber, bytes memory dataToSend) = abi.decode( + (dexNumber, dataToSend) = abi.decode( payload, (uint256, bytes) ); - address swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress( - DexNumber - ); - (expectedReturn, ) = ISwapsImpl(swapImplAddress).dexAmountOut( - dataToSend, - sourceTokenAmount - ); } + + swapImplAddress = IDexRecords(swapsImpl).retrieveDexAddress( + dexNumber + ); + + (expectedReturn, ) = ISwapsImpl(swapImplAddress).dexAmountOut( + dataToSend, + sourceTokenAmount + ); } function _checkSwapSize(address tokenAddress, uint256 amount) From e1e27b78b0cd9c02aa69350787bb5b30edd89028 Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Sun, 9 Jan 2022 15:13:14 -0800 Subject: [PATCH 29/83] revert changes --- contracts/swaps/connectors/SwapsImplKyber.sol | 102 ++++++++--------- .../connectors/SwapsImplUniswapV2_BSC.sol | 106 +++++++++--------- .../connectors/SwapsImplUniswapV2_POLYGON.sol | 105 +++++++++-------- 3 files changed, 153 insertions(+), 160 deletions(-) diff --git a/contracts/swaps/connectors/SwapsImplKyber.sol b/contracts/swaps/connectors/SwapsImplKyber.sol index ecf2afb2..9a0edf8e 100644 --- a/contracts/swaps/connectors/SwapsImplKyber.sol +++ b/contracts/swaps/connectors/SwapsImplKyber.sol @@ -1,5 +1,5 @@ /** - * Copyright 2017-2021, bZxDao. All Rights Reserved. + * Copyright 2017-2021, bZeroX, LLC . All Rights Reserved. * Licensed under the Apache License, Version 2.0. */ @@ -10,18 +10,17 @@ import "../../../interfaces/IPriceFeeds.sol"; import "@openzeppelin-2.5.0/token/ERC20/SafeERC20.sol"; import "../ISwapsImpl.sol"; -contract SwapsImplKyber is State { - using SafeERC20 for IERC20; - address internal constant feeWallet = - 0x13ddAC8d492E463073934E2a101e419481970299; +contract SwapsImplKyber is State, ISwapsImpl { + using SafeERC20 for IERC20; - address public constant kyberContract = - 0x818E6FECD516Ecc3849DAf6845e3EC868087B755; // mainnet + address internal constant feeWallet = 0x13ddAC8d492E463073934E2a101e419481970299; + address public constant kyberContract = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755; // mainnet //address public constant kyberContract = 0x692f391bCc85cefCe8C237C01e1f636BbD70EA4D; // kovan //address public constant kyberContract = 0x818E6FECD516Ecc3849DAf6845e3EC868087B755; // ropsten + function dexSwap( address sourceTokenAddress, address destTokenAddress, @@ -29,17 +28,12 @@ contract SwapsImplKyber is State { address returnToSenderAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount - ) + uint256 requiredDestTokenAmount) public returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) { require(sourceTokenAddress != destTokenAddress, "source == dest"); - require( - supportedTokens[sourceTokenAddress] && - supportedTokens[destTokenAddress], - "invalid tokens" - ); + require(supportedTokens[sourceTokenAddress] && supportedTokens[destTokenAddress], "invalid tokens"); bytes memory txnData = _getSwapTxnData( sourceTokenAddress, @@ -63,18 +57,13 @@ contract SwapsImplKyber is State { assembly { destTokenAmountReceived := mload(add(returnData, 32)) } - sourceTokenAmountUsed = sourceBalanceBefore.sub( - sourceToken.balanceOf(_thisAddress) - ); + sourceTokenAmountUsed = sourceBalanceBefore.sub(sourceToken.balanceOf(_thisAddress)); - if ( - returnToSenderAddress != _thisAddress && - sourceTokenAmountUsed < maxSourceTokenAmount - ) { + if (returnToSenderAddress != _thisAddress && sourceTokenAmountUsed < maxSourceTokenAmount) { // send unused source token back sourceToken.safeTransfer( returnToSenderAddress, - maxSourceTokenAmount - sourceTokenAmountUsed + maxSourceTokenAmount-sourceTokenAmountUsed ); } } @@ -82,8 +71,11 @@ contract SwapsImplKyber is State { function dexExpectedRate( address sourceTokenAddress, address destTokenAddress, - uint256 sourceTokenAmount - ) public view returns (uint256 expectedRate) { + uint256 sourceTokenAmount) + public + view + returns (uint256 expectedRate) + { if (sourceTokenAddress == destTokenAddress) { expectedRate = WEI_PRECISION; } else if (sourceTokenAmount != 0) { @@ -105,7 +97,10 @@ contract SwapsImplKyber is State { return expectedRate; } - function setSwapApprovals(address[] memory tokens) public { + function setSwapApprovals( + address[] memory tokens) + public + { for (uint256 i = 0; i < tokens.length; i++) { IERC20(tokens[i]).safeApprove(kyberContract, 0); IERC20(tokens[i]).safeApprove(kyberContract, uint256(-1)); @@ -118,29 +113,31 @@ contract SwapsImplKyber is State { address receiverAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount - ) internal view returns (bytes memory) { + uint256 requiredDestTokenAmount) + internal + view + returns (bytes memory) + { uint256 estimatedSourceAmount; if (requiredDestTokenAmount != 0) { - uint256 sourceToDestPrecision = IPriceFeeds(priceFeeds) - .queryPrecision(sourceTokenAddress, destTokenAddress); + uint256 sourceToDestPrecision = IPriceFeeds(priceFeeds).queryPrecision( + sourceTokenAddress, + destTokenAddress + ); if (sourceToDestPrecision == 0) { return ""; } - uint256 bufferMultiplier = sourceBufferPercent.add( - WEI_PERCENT_PRECISION - ); + uint256 bufferMultiplier = sourceBufferPercent + .add(WEI_PERCENT_PRECISION); estimatedSourceAmount = requiredDestTokenAmount .mul(sourceToDestPrecision) - .div( - dexExpectedRate( - sourceTokenAddress, - destTokenAddress, - minSourceTokenAmount - ) - ); + .div(dexExpectedRate( + sourceTokenAddress, + destTokenAddress, + minSourceTokenAmount + )); if (estimatedSourceAmount == 0) { return ""; } @@ -156,19 +153,18 @@ contract SwapsImplKyber is State { estimatedSourceAmount = minSourceTokenAmount; } - return - abi.encodeWithSelector( - 0x29589f61, // keccak("tradeWithHint(address,uint256,address,address,uint256,uint256,address,bytes)") - sourceTokenAddress, - estimatedSourceAmount, - destTokenAddress, - receiverAddress, - requiredDestTokenAmount == 0 || requiredDestTokenAmount > 10**28 // maxDestAmount - ? 10**28 - : requiredDestTokenAmount, - 0, // minConversionRate - feeWallet, - "" // hint - ); + return abi.encodeWithSelector( + 0x29589f61, // keccak("tradeWithHint(address,uint256,address,address,uint256,uint256,address,bytes)") + sourceTokenAddress, + estimatedSourceAmount, + destTokenAddress, + receiverAddress, + requiredDestTokenAmount == 0 || requiredDestTokenAmount > 10**28 ? // maxDestAmount + 10**28 : + requiredDestTokenAmount, + 0, // minConversionRate + feeWallet, + "" // hint + ); } } diff --git a/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol b/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol index e758e400..19da0ca7 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV2_BSC.sol @@ -10,13 +10,13 @@ import "../../interfaces/IUniswapV2Router.sol"; import "@openzeppelin-2.5.0/token/ERC20/SafeERC20.sol"; import "../ISwapsImpl.sol"; + contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { using SafeERC20 for IERC20; // bsc (PancakeSwap) //address public constant uniswapRouter = 0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F; // PancakeSwap v1 - address public constant uniswapRouter = - 0x10ED43C718714eb63d5aA57B78B54704E256024E; // PancakeSwap v2 + address public constant uniswapRouter = 0x10ED43C718714eb63d5aA57B78B54704E256024E; // PancakeSwap v2 address public constant busd = 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56; address public constant usdt = 0x55d398326f99059fF775485246999027B3197955; @@ -28,18 +28,12 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { address returnToSenderAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount, - bytes memory payload - ) + uint256 requiredDestTokenAmount) public returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) { require(sourceTokenAddress != destTokenAddress, "source == dest"); - require( - supportedTokens[sourceTokenAddress] && - supportedTokens[destTokenAddress], - "invalid tokens" - ); + require(supportedTokens[sourceTokenAddress] && supportedTokens[destTokenAddress], "invalid tokens"); IERC20 sourceToken = IERC20(sourceTokenAddress); address _thisAddress = address(this); @@ -53,14 +47,11 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { requiredDestTokenAmount ); - if ( - returnToSenderAddress != _thisAddress && - sourceTokenAmountUsed < maxSourceTokenAmount - ) { + if (returnToSenderAddress != _thisAddress && sourceTokenAmountUsed < maxSourceTokenAmount) { // send unused source token back sourceToken.safeTransfer( returnToSenderAddress, - maxSourceTokenAmount - sourceTokenAmountUsed + maxSourceTokenAmount-sourceTokenAmountUsed ); } } @@ -68,16 +59,22 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { function dexExpectedRate( address sourceTokenAddress, address destTokenAddress, - uint256 sourceTokenAmount - ) public view returns (uint256 expectedRate) { + uint256 sourceTokenAmount) + public + view + returns (uint256 expectedRate) + { revert("unsupported"); } function dexAmountOut( - bytes memory payload, - uint256 amountIn - ) public returns (uint256 amountOut, address midToken) { - (address sourceTokenAddress, address destTokenAddress) = abi.decode(payload,(address,address)); + address sourceTokenAddress, + address destTokenAddress, + uint256 amountIn) + public + view + returns (uint256 amountOut, address midToken) + { if (sourceTokenAddress == destTokenAddress) { amountOut = amountIn; } else if (amountIn != 0) { @@ -91,11 +88,8 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { path = new address[](3); path[0] = sourceTokenAddress; path[2] = destTokenAddress; - - if ( - sourceTokenAddress != address(wethToken) && - destTokenAddress != address(wethToken) - ) { + + if (sourceTokenAddress != address(wethToken) && destTokenAddress != address(wethToken)) { path[1] = address(wethToken); tmpValue = _getAmountOut(amountIn, path); if (tmpValue > amountOut) { @@ -125,10 +119,13 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { } function dexAmountIn( - bytes memory payload, - uint256 amountOut - ) public returns (uint256 amountIn, address midToken) { - (address sourceTokenAddress, address destTokenAddress) = abi.decode(payload,(address,address)); + address sourceTokenAddress, + address destTokenAddress, + uint256 amountOut) + public + view + returns (uint256 amountIn, address midToken) + { if (sourceTokenAddress == destTokenAddress) { amountIn = amountOut; } else if (amountOut != 0) { @@ -142,11 +139,8 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { path = new address[](3); path[0] = sourceTokenAddress; path[2] = destTokenAddress; - - if ( - sourceTokenAddress != address(wethToken) && - destTokenAddress != address(wethToken) - ) { + + if (sourceTokenAddress != address(wethToken) && destTokenAddress != address(wethToken)) { path[1] = address(wethToken); tmpValue = _getAmountIn(amountOut, path); if (tmpValue < amountIn) { @@ -179,7 +173,9 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { } } - function _getAmountOut(uint256 amountIn, address[] memory path) + function _getAmountOut( + uint256 amountIn, + address[] memory path) public view returns (uint256 amountOut) @@ -199,7 +195,9 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { } } - function _getAmountIn(uint256 amountOut, address[] memory path) + function _getAmountIn( + uint256 amountOut, + address[] memory path) public view returns (uint256 amountIn) @@ -222,7 +220,10 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { } } - function setSwapApprovals(address[] memory tokens) public { + function setSwapApprovals( + address[] memory tokens) + public + { for (uint256 i = 0; i < tokens.length; i++) { IERC20(tokens[i]).safeApprove(uniswapRouter, 0); IERC20(tokens[i]).safeApprove(uniswapRouter, uint256(-1)); @@ -235,28 +236,26 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { address receiverAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount - ) + uint256 requiredDestTokenAmount) internal returns (uint256 sourceTokenAmountUsed, uint256 destTokenAmountReceived) { address midToken; if (requiredDestTokenAmount != 0) { (sourceTokenAmountUsed, midToken) = dexAmountIn( - abi.encode(sourceTokenAddress, destTokenAddress), + sourceTokenAddress, + destTokenAddress, requiredDestTokenAmount ); if (sourceTokenAmountUsed == 0) { return (0, 0); } - require( - sourceTokenAmountUsed <= maxSourceTokenAmount, - "source amount too high" - ); + require(sourceTokenAmountUsed <= maxSourceTokenAmount, "source amount too high"); } else { sourceTokenAmountUsed = minSourceTokenAmount; (destTokenAmountReceived, midToken) = dexAmountOut( - abi.encode(sourceTokenAddress, destTokenAddress), + sourceTokenAddress, + destTokenAddress, sourceTokenAmountUsed ); if (destTokenAmountReceived == 0) { @@ -276,14 +275,13 @@ contract SwapsImplUniswapV2_BSC is State, ISwapsImpl { path[1] = destTokenAddress; } - uint256[] memory amounts = IUniswapV2Router(uniswapRouter) - .swapExactTokensForTokens( - sourceTokenAmountUsed, - 1, // amountOutMin - path, - receiverAddress, - block.timestamp - ); + uint256[] memory amounts = IUniswapV2Router(uniswapRouter).swapExactTokensForTokens( + sourceTokenAmountUsed, + 1, // amountOutMin + path, + receiverAddress, + block.timestamp + ); destTokenAmountReceived = amounts[amounts.length - 1]; } diff --git a/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol b/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol index d4c6aa7f..29b7e218 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol @@ -10,11 +10,11 @@ import "../../interfaces/IUniswapV2Router.sol"; import "@openzeppelin-2.5.0/token/ERC20/SafeERC20.sol"; import "../ISwapsImpl.sol"; + contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { using SafeERC20 for IERC20; - address public constant uniswapRouter = - 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506; // Sushiswap + address public constant uniswapRouter = 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506; // Sushiswap //address public constant uniswapRouter = 0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff; // QuickSwap address public constant eth = 0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619; @@ -29,17 +29,12 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { address returnToSenderAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount - ) + uint256 requiredDestTokenAmount) public returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) { require(sourceTokenAddress != destTokenAddress, "source == dest"); - require( - supportedTokens[sourceTokenAddress] && - supportedTokens[destTokenAddress], - "invalid tokens" - ); + require(supportedTokens[sourceTokenAddress] && supportedTokens[destTokenAddress], "invalid tokens"); IERC20 sourceToken = IERC20(sourceTokenAddress); address _thisAddress = address(this); @@ -53,14 +48,11 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { requiredDestTokenAmount ); - if ( - returnToSenderAddress != _thisAddress && - sourceTokenAmountUsed < maxSourceTokenAmount - ) { + if (returnToSenderAddress != _thisAddress && sourceTokenAmountUsed < maxSourceTokenAmount) { // send unused source token back sourceToken.safeTransfer( returnToSenderAddress, - maxSourceTokenAmount - sourceTokenAmountUsed + maxSourceTokenAmount-sourceTokenAmountUsed ); } } @@ -68,16 +60,22 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { function dexExpectedRate( address sourceTokenAddress, address destTokenAddress, - uint256 sourceTokenAmount - ) public view returns (uint256 expectedRate) { + uint256 sourceTokenAmount) + public + view + returns (uint256 expectedRate) + { revert("unsupported"); } function dexAmountOut( - bytes memory payload, - uint256 amountIn - ) public returns (uint256 amountOut, address midToken) { - (address sourceTokenAddress, address destTokenAddress) = abi.decode(payload,(address,address)); + address sourceTokenAddress, + address destTokenAddress, + uint256 amountIn) + public + view + returns (uint256 amountOut, address midToken) + { if (sourceTokenAddress == destTokenAddress) { amountOut = amountIn; } else if (amountIn != 0) { @@ -91,11 +89,8 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { path = new address[](3); path[0] = sourceTokenAddress; path[2] = destTokenAddress; - - if ( - sourceTokenAddress != address(wethToken) && - destTokenAddress != address(wethToken) - ) { + + if (sourceTokenAddress != address(wethToken) && destTokenAddress != address(wethToken)) { path[1] = address(wethToken); tmpValue = _getAmountOut(amountIn, path); if (tmpValue > amountOut) { @@ -143,10 +138,13 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } function dexAmountIn( - bytes memory payload, - uint256 amountOut - ) public returns (uint256 amountIn, address midToken) { - (address sourceTokenAddress, address destTokenAddress) = abi.decode(payload,(address,address)); + address sourceTokenAddress, + address destTokenAddress, + uint256 amountOut) + public + view + returns (uint256 amountIn, address midToken) + { if (sourceTokenAddress == destTokenAddress) { amountIn = amountOut; } else if (amountOut != 0) { @@ -160,11 +158,8 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { path = new address[](3); path[0] = sourceTokenAddress; path[2] = destTokenAddress; - - if ( - sourceTokenAddress != address(wethToken) && - destTokenAddress != address(wethToken) - ) { + + if (sourceTokenAddress != address(wethToken) && destTokenAddress != address(wethToken)) { path[1] = address(wethToken); tmpValue = _getAmountIn(amountOut, path); if (tmpValue < amountIn) { @@ -215,7 +210,9 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } } - function _getAmountOut(uint256 amountIn, address[] memory path) + function _getAmountOut( + uint256 amountIn, + address[] memory path) public view returns (uint256 amountOut) @@ -235,7 +232,9 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } } - function _getAmountIn(uint256 amountOut, address[] memory path) + function _getAmountIn( + uint256 amountOut, + address[] memory path) public view returns (uint256 amountIn) @@ -258,7 +257,10 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } } - function setSwapApprovals(address[] memory tokens) public { + function setSwapApprovals( + address[] memory tokens) + public + { for (uint256 i = 0; i < tokens.length; i++) { IERC20(tokens[i]).safeApprove(uniswapRouter, 0); IERC20(tokens[i]).safeApprove(uniswapRouter, uint256(-1)); @@ -271,28 +273,26 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { address receiverAddress, uint256 minSourceTokenAmount, uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount - ) + uint256 requiredDestTokenAmount) internal returns (uint256 sourceTokenAmountUsed, uint256 destTokenAmountReceived) { address midToken; if (requiredDestTokenAmount != 0) { (sourceTokenAmountUsed, midToken) = dexAmountIn( - abi.encode(sourceTokenAddress, destTokenAddress), + sourceTokenAddress, + destTokenAddress, requiredDestTokenAmount ); if (sourceTokenAmountUsed == 0) { return (0, 0); } - require( - sourceTokenAmountUsed <= maxSourceTokenAmount, - "source amount too high" - ); + require(sourceTokenAmountUsed <= maxSourceTokenAmount, "source amount too high"); } else { sourceTokenAmountUsed = minSourceTokenAmount; (destTokenAmountReceived, midToken) = dexAmountOut( - abi.encode(sourceTokenAddress, destTokenAddress), + sourceTokenAddress, + destTokenAddress, sourceTokenAmountUsed ); if (destTokenAmountReceived == 0) { @@ -312,14 +312,13 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { path[1] = destTokenAddress; } - uint256[] memory amounts = IUniswapV2Router(uniswapRouter) - .swapExactTokensForTokens( - sourceTokenAmountUsed, - 1, // amountOutMin - path, - receiverAddress, - block.timestamp - ); + uint256[] memory amounts = IUniswapV2Router(uniswapRouter).swapExactTokensForTokens( + sourceTokenAmountUsed, + 1, // amountOutMin + path, + receiverAddress, + block.timestamp + ); destTokenAmountReceived = amounts[amounts.length - 1]; } From 0424099938e21d3fb89e9a67725cc8f1103fb0d1 Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Sun, 9 Jan 2022 17:38:01 -0800 Subject: [PATCH 30/83] name changes --- .../connectors/SwapsImplUniswapV3_ETH.sol | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol index 48cb0e17..ee131ed8 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol @@ -152,32 +152,32 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { bytes[] memory encodedTXs = new bytes[](exactParams.length); uint256 totalAmountsOut = 0; uint256 totalAmountsInMax = 0; - for (uint256 x = 0; x < exactParams.length; x++) { - require(receiverAddress == exactParams[x].recipient); - address tokenIn = exactParams[x].path.toAddress(0); + for (uint256 uniqueOutputParam = 0; uniqueOutputParam < exactParams.length; uniqueOutputParam++) { + require(receiverAddress == exactParams[uniqueOutputParam].recipient); + address tokenIn = exactParams[uniqueOutputParam].path.toAddress(0); require(tokenIn == destTokenAddress, "improper destination"); require( - exactParams[x].path.toAddress( - exactParams[x].path.length - 20 + exactParams[uniqueOutputParam].path.toAddress( + exactParams[uniqueOutputParam].path.length - 20 ) == sourceTokenAddress, "improper source" ); - exactParams[x].amountOut = requiredDestTokenAmount - .mul(exactParams[x].amountOut) + exactParams[uniqueOutputParam].amountOut = requiredDestTokenAmount + .mul(exactParams[uniqueOutputParam].amountOut) .div(100); - exactParams[x].amountInMaximum = maxSourceTokenAmount - .mul(exactParams[x].amountInMaximum) + exactParams[uniqueOutputParam].amountInMaximum = maxSourceTokenAmount + .mul(exactParams[uniqueOutputParam].amountInMaximum) .div(100); - totalAmountsOut = totalAmountsOut.add(exactParams[x].amountOut); + totalAmountsOut = totalAmountsOut.add(exactParams[uniqueOutputParam].amountOut); totalAmountsInMax = totalAmountsInMax.add( - exactParams[x].amountInMaximum + exactParams[uniqueOutputParam].amountInMaximum ); - encodedTXs[x] = abi.encodeWithSelector( + encodedTXs[uniqueOutputParam] = abi.encodeWithSelector( IUniswapV3SwapRouter(uniswapSwapRouter) .exactOutput .selector, - exactParams[x] + exactParams[uniqueOutputParam] ); } if(totalAmountsOut Date: Tue, 11 Jan 2022 18:15:03 -0800 Subject: [PATCH 31/83] Major refactor and partial re-design --- contracts/orderbook/Enumerates/EnumLimits.sol | 79 -- contracts/orderbook/Enumerates/EnumOrders.sol | 79 -- .../orderbook/Enumerates/EnumTraders.sol | 79 -- .../{IWalletFactor.sol => IOrderBook.sol} | 15 +- .../orderbook/Keepers/IUniswapV2Router.sol | 2 +- .../orderbook/Keepers/OrderBookInterface.sol | 28 +- contracts/orderbook/Keepers/OrderKeeper.sol | 17 +- .../orderbook/Keepers/OrderKeeperClear.sol | 17 +- contracts/orderbook/Logic/OrderBook.sol | 717 ++++++++---------- contracts/orderbook/Logic/OrderBookData.sol | 96 ++- .../orderbook/Logic/OrderBookMarketOrders.sol | 68 +- .../orderbook/Logic/OrderBookOrderPlace.sol | 291 ++++--- .../orderbook/Logic/UniswapInterfaces.sol | 2 +- contracts/orderbook/Logic/dexSwaps.sol | 22 +- contracts/orderbook/OrderBookProxy.sol | 64 +- contracts/orderbook/OrderVault/Deposits.sol | 76 +- contracts/orderbook/OrderVault/IDeposits.sol | 18 +- .../orderbook/Storage/OrderBookEvents.sol | 49 +- .../orderbook/Storage/OrderBookStorage.sol | 31 +- contracts/orderbook/WrappedToken.sol | 2 +- contracts/orderbook/bZxInterfaces/IBZX.sol | 83 -- .../orderbook/bZxInterfaces/ILoanToken.sol | 20 - .../orderbook/bZxInterfaces/IPriceFeeds.sol | 75 -- 23 files changed, 736 insertions(+), 1194 deletions(-) delete mode 100644 contracts/orderbook/Enumerates/EnumLimits.sol delete mode 100644 contracts/orderbook/Enumerates/EnumOrders.sol delete mode 100644 contracts/orderbook/Enumerates/EnumTraders.sol rename contracts/orderbook/{IWalletFactor.sol => IOrderBook.sol} (79%) delete mode 100644 contracts/orderbook/bZxInterfaces/IBZX.sol delete mode 100644 contracts/orderbook/bZxInterfaces/ILoanToken.sol delete mode 100644 contracts/orderbook/bZxInterfaces/IPriceFeeds.sol diff --git a/contracts/orderbook/Enumerates/EnumLimits.sol b/contracts/orderbook/Enumerates/EnumLimits.sol deleted file mode 100644 index 3ee2c0dd..00000000 --- a/contracts/orderbook/Enumerates/EnumLimits.sol +++ /dev/null @@ -1,79 +0,0 @@ -pragma solidity ^0.8.4; - -library OrderRecords { - struct orderSet { - mapping(uint256 => uint256) idx; - uint256[] vals; - } - - function addOrderNum(orderSet storage hist, uint256 val) - internal - returns (bool) - { - if (inVals(hist, val) == false) { - hist.vals.push(val); - hist.idx[val] = length(hist); - return true; - } else { - return false; - } - } - - function removeOrderNum(orderSet storage hist, uint256 val) - internal - returns (bool) - { - if (inVals(hist, val)) { - uint256 delIdx = hist.idx[val] - 1; - uint256 lastIdx = hist.vals.length - 1; - if (delIdx != lastIdx) { - uint256 tempVal = hist.vals[lastIdx]; - hist.vals[delIdx] = tempVal; - hist.idx[tempVal] = delIdx + 1; - } - delete hist.idx[val]; - hist.vals.pop(); - return true; - } else { - return false; - } - } - - function inVals(orderSet storage hist, uint256 val) - internal - view - returns (bool) - { - return hist.idx[val] != 0; - } - - function length(orderSet storage hist) internal view returns (uint256) { - return hist.vals.length; - } - - function get(orderSet storage hist, uint256 idx) - internal - view - returns (uint256) - { - return hist.vals[idx]; - } - - function enums( - orderSet storage hist, - uint256 start, - uint256 len - ) internal view returns (uint256[] memory out) { - uint256 stop = start + len; - require(stop >= start); - stop = hist.vals.length < stop ? hist.vals.length : stop; - if (start >= stop || stop == 0) { - return out; - } - out = new uint256[](stop - start); - for (uint256 i = start; i < stop; i++) { - out[i - start] = hist.vals[i]; - } - return out; - } -} diff --git a/contracts/orderbook/Enumerates/EnumOrders.sol b/contracts/orderbook/Enumerates/EnumOrders.sol deleted file mode 100644 index 1c5064f0..00000000 --- a/contracts/orderbook/Enumerates/EnumOrders.sol +++ /dev/null @@ -1,79 +0,0 @@ -pragma solidity ^0.8.4; - -library OrderEntry { - struct orderSet { - mapping(bytes32 => uint256) idx; - bytes32[] vals; - } - - function addTrade(orderSet storage hist, bytes32 val) - internal - returns (bool) - { - if (inVals(hist, val) == false) { - hist.vals.push(val); - hist.idx[val] = length(hist); - return true; - } else { - return false; - } - } - - function removeTrade(orderSet storage hist, bytes32 val) - internal - returns (bool) - { - if (inVals(hist, val)) { - uint256 delIdx = hist.idx[val] - 1; - uint256 lastIdx = hist.vals.length - 1; - if (delIdx != lastIdx) { - bytes32 tempVal = hist.vals[lastIdx]; - hist.vals[delIdx] = tempVal; - hist.idx[tempVal] = delIdx + 1; - } - delete hist.idx[val]; - hist.vals.pop(); - return true; - } else { - return false; - } - } - - function inVals(orderSet storage hist, bytes32 val) - internal - view - returns (bool) - { - return hist.idx[val] != 0; - } - - function length(orderSet storage hist) internal view returns (uint256) { - return hist.vals.length; - } - - function get(orderSet storage hist, uint256 idx) - internal - view - returns (bytes32) - { - return hist.vals[idx]; - } - - function enums( - orderSet storage hist, - uint256 start, - uint256 len - ) internal view returns (bytes32[] memory out) { - uint256 stop = start + len; - require(stop >= start); - stop = hist.vals.length < stop ? hist.vals.length : stop; - if (start >= stop || stop == 0) { - return out; - } - out = new bytes32[](stop - start); - for (uint256 i = start; i < stop; i++) { - out[i - start] = hist.vals[i]; - } - return out; - } -} diff --git a/contracts/orderbook/Enumerates/EnumTraders.sol b/contracts/orderbook/Enumerates/EnumTraders.sol deleted file mode 100644 index 97478ee2..00000000 --- a/contracts/orderbook/Enumerates/EnumTraders.sol +++ /dev/null @@ -1,79 +0,0 @@ -pragma solidity ^0.8.4; - -library ActiveTraders { - struct orderSet { - mapping(address => uint256) idx; - address[] vals; - } - - function addTrader(orderSet storage hist, address val) - internal - returns (bool) - { - if (inVals(hist, val) == false) { - hist.vals.push(val); - hist.idx[val] = length(hist); - return true; - } else { - return false; - } - } - - function removeTrader(orderSet storage hist, address val) - internal - returns (bool) - { - if (inVals(hist, val)) { - uint256 delIdx = hist.idx[val] - 1; - uint256 lastIdx = hist.vals.length - 1; - if (delIdx != lastIdx) { - address tempVal = hist.vals[lastIdx]; - hist.vals[delIdx] = tempVal; - hist.idx[tempVal] = delIdx + 1; - } - delete hist.idx[val]; - hist.vals.pop(); - return true; - } else { - return false; - } - } - - function inVals(orderSet storage hist, address val) - internal - view - returns (bool) - { - return hist.idx[val] != 0; - } - - function length(orderSet storage hist) internal view returns (uint256) { - return hist.vals.length; - } - - function get(orderSet storage hist, uint256 idx) - internal - view - returns (address) - { - return hist.vals[idx]; - } - - function enums( - orderSet storage hist, - uint256 start, - uint256 len - ) internal view returns (address[] memory out) { - uint256 stop = start + len; - require(stop >= start); - stop = hist.vals.length < stop ? hist.vals.length : stop; - if (start >= stop || stop == 0) { - return out; - } - out = new address[](stop - start); - for (uint256 i = start; i < stop; i++) { - out[i - start] = hist.vals[i]; - } - return out; - } -} diff --git a/contracts/orderbook/IWalletFactor.sol b/contracts/orderbook/IOrderBook.sol similarity index 79% rename from contracts/orderbook/IWalletFactor.sol rename to contracts/orderbook/IOrderBook.sol index 39878f22..ddd0c2ee 100644 --- a/contracts/orderbook/IWalletFactor.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -1,6 +1,6 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; -interface IWalletFactory { +interface IOrderBook { enum OrderType { LIMIT_OPEN, LIMIT_CLOSE, @@ -15,17 +15,12 @@ interface IWalletFactory { uint256 leverage; uint256 loanTokenAmount; uint256 collateralTokenAmount; - bool isActive; + bool isCancelled; address base; OrderType orderType; bool isCollateral; - uint256 orderID; - bytes loanData; - } - - struct OrderQueue { - address trader; - uint256 orderID; + bytes32 orderID; + bytes loanDataBytes; } function getRouter() external view returns (address); diff --git a/contracts/orderbook/Keepers/IUniswapV2Router.sol b/contracts/orderbook/Keepers/IUniswapV2Router.sol index 72632b99..d528519b 100644 --- a/contracts/orderbook/Keepers/IUniswapV2Router.sol +++ b/contracts/orderbook/Keepers/IUniswapV2Router.sol @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. */ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; interface uniswapV2Router { // 0x38ed1739 diff --git a/contracts/orderbook/Keepers/OrderBookInterface.sol b/contracts/orderbook/Keepers/OrderBookInterface.sol index 38c56c74..389096c1 100644 --- a/contracts/orderbook/Keepers/OrderBookInterface.sol +++ b/contracts/orderbook/Keepers/OrderBookInterface.sol @@ -1,27 +1,27 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; interface IOrderBook { - function getOrders(uint256 start, uint256 count) + function getOrders() external view returns (OpenOrder[] memory); - function prelimCheck(address trader, uint256 orderID) + function prelimCheck(bytes32 orderID) external view returns (bool); function executeOrder( address payable keeper, - address trader, - uint256 orderID + bytes32 orderID ) external; - function cancelOrderProtocol(address trader, uint256 orderID) external; - function clearOrder(address trader, uint256 orderID) - external - view - returns (bool); - function getTotalActiveOrders() external view returns (uint256); + + function cancelOrderProtocol(bytes32 orderID) external; + + function clearOrder(bytes32 orderID) + external + view + returns (bool); struct OpenOrder { address trader; @@ -36,11 +36,7 @@ interface IOrderBook { address base; uint256 orderType; bool isCollateral; - uint256 orderID; + bytes32 orderID; bytes loanData; } - struct OrderQueue { - address trader; - uint256 orderID; - } } diff --git a/contracts/orderbook/Keepers/OrderKeeper.sol b/contracts/orderbook/Keepers/OrderKeeper.sol index db74f86a..412da492 100644 --- a/contracts/orderbook/Keepers/OrderKeeper.sol +++ b/contracts/orderbook/Keepers/OrderKeeper.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; import "./OrderBookInterface.sol"; import "./IUniswapV2Router.sol"; import "../WrappedToken.sol"; @@ -20,17 +20,15 @@ contract OrderKeeper { returns (bool upkeepNeeded, bytes memory performData) { IOrderBook.OpenOrder[] memory listOfMainOrders = IOrderBook(factory) - .getOrders(0, IOrderBook(factory).getTotalActiveOrders()); + .getOrders(); for (uint256 x = 0; x < listOfMainOrders.length; x++) { if ( IOrderBook(factory).prelimCheck( - listOfMainOrders[x].trader, listOfMainOrders[x].orderID - ) == true + ) ) { upkeepNeeded = true; performData = abi.encode( - listOfMainOrders[x].trader, listOfMainOrders[x].orderID ); return (upkeepNeeded, performData); @@ -40,19 +38,18 @@ contract OrderKeeper { } function performUpkeep(bytes calldata performData) public { - (address trader, uint256 orderId) = abi.decode( + (bytes32 orderId) = abi.decode( performData, - (address, uint256) + (bytes32) ); //emit OrderExecuted(trader,orderId); IOrderBook(factory).executeOrder( payable(address(this)), - trader, orderId ); } - function handleFees(address[] memory tokenAddress) public { + /*function handleFees(address[] memory tokenAddress) public { address[] memory path; path = new address[](3); path[1] = WETH; @@ -85,5 +82,5 @@ contract OrderKeeper { function handleETHFees() public { WrappedToken(WETH).deposit{value: address(this).balance}(); - } + }*/ } diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol index 5e90cf7f..cd2a6997 100644 --- a/contracts/orderbook/Keepers/OrderKeeperClear.sol +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; import "./OrderBookInterface.sol"; import "./IUniswapV2Router.sol"; import "../WrappedToken.sol"; @@ -17,17 +17,15 @@ contract OrderKeeperClear { returns (bool upkeepNeeded, bytes memory performData) { IOrderBook.OpenOrder[] memory listOfMainOrders = IOrderBook(factory) - .getOrders(0, IOrderBook(factory).getTotalActiveOrders()); + .getOrders(); for (uint256 x = 0; x < listOfMainOrders.length; x++) { if ( IOrderBook(factory).clearOrder( - listOfMainOrders[x].trader, listOfMainOrders[x].orderID - ) == true + ) ) { upkeepNeeded = true; performData = abi.encode( - listOfMainOrders[x].trader, listOfMainOrders[x].orderID ); return (upkeepNeeded, performData); @@ -37,14 +35,11 @@ contract OrderKeeperClear { } function performUpkeep(bytes calldata performData) public { - (address trader, uint256 orderId) = abi.decode( + (bytes32 orderId) = abi.decode( performData, - (address, uint256) + (bytes32) ); //emit OrderExecuted(trader,orderId); - IOrderBook(factory).cancelOrderProtocol( - trader, - orderId - ); + IOrderBook(factory).cancelOrderProtocol(orderId); } } diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index b60e8db1..5f5d1299 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -1,105 +1,85 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; import "../Storage/OrderBookEvents.sol"; import "../Storage/OrderBookStorage.sol"; import "./dexSwaps.sol"; import "./UniswapInterfaces.sol"; import "../OrderVault/IDeposits.sol"; import "../../utils/ExponentMath.sol"; +import "../../interfaces/IDexRecords.sol"; contract OrderBook is OrderBookEvents, OrderBookStorage { - using ExponentMath for uint256; - function executeTradeOpen( - address trader, - uint256 orderID, - address keeper, - address usedToken - ) internal returns (uint256 success) { - IWalletFactory.OpenOrder memory internalOrder = HistoricalOrders[ - trader - ][orderID]; + using ExponentMath for uint256; + using EnumerableSet for EnumerableSet.Bytes32Set; + + function initialize( + address target) + public + onlyOwner + { + _setTarget(this.getSwapAddress.selector, target); + _setTarget(this.currentSwapRate.selector, target); + _setTarget(this.getFeed.selector, target); + _setTarget(this.dexSwapRate.selector, target); + _setTarget(this.clearOrder.selector, target); + _setTarget(this.prelimCheck.selector, target); + _setTarget(this.currentDexRate.selector, target); + _setTarget(this.priceCheck.selector, target); + _setTarget(this.executeOrder.selector, target); + _setTarget(this.setVaultAddress.selector, target); + } + + + function _executeTradeOpen(address trader, bytes32 orderID) internal { + IOrderBook.OpenOrder memory internalOrder = _allOrders[orderID]; IDeposits(vault).withdraw(trader, orderID); - SafeERC20.safeTransferFrom( - IERC20(usedToken), - trader, - address(this), - IERC20Metadata(usedToken).balanceOf(trader) + (bool result, bytes memory data) = _allOrders[orderID].iToken.call( + abi.encodeWithSelector( + IToken(internalOrder.iToken).marginTrade.selector, + internalOrder.loanID, + internalOrder.leverage, + internalOrder.loanTokenAmount, + internalOrder.collateralTokenAmount, + internalOrder.base, + address(this), + internalOrder.loanDataBytes + ) ); - (bool result, bytes memory data) = HistoricalOrders[trader][orderID] - .iToken - .call( - abi.encodeWithSelector( - LoanTokenI(internalOrder.iToken).marginTrade.selector, - internalOrder.loanID, - internalOrder.leverage, - internalOrder.loanTokenAmount, - internalOrder.collateralTokenAmount, - internalOrder.base, - address(this), - internalOrder.loanData - ) - ); - if (result == true) { + if (result) { (bytes32 loanID, , ) = abi.decode( data, (bytes32, uint256, uint256) ); - if (OrderEntry.inVals(ActiveTrades[trader], loanID) == false) { - OrderEntry.addTrade(ActiveTrades[trader], loanID); + if (!_activeTrades[trader].contains(loanID)) { + _activeTrades[trader].add(loanID); } } - success = gasleft(); } - function executeTradeClose( + function _executeTradeClose( address trader, - address payable keeper, bytes32 loanID, uint256 amount, bool iscollateral, - address loanTokenAddress, address collateralAddress, - uint256 startGas, - bytes memory arbData - ) internal returns (bool success) { - address usedToken; - usedToken = iscollateral ? collateralAddress : loanTokenAddress; - uint256 traderB = IERC20Metadata(usedToken).balanceOf(trader); - SafeERC20.safeTransferFrom( - IERC20(usedToken), - trader, - address(this), - traderB - ); - if (IBZX(bZxRouterAddress).getLoan(loanID).collateral == amount) { - OrderEntry.removeTrade(ActiveTrades[trader], loanID); + bytes memory loanDataBytes + ) internal { + if (IBZx(protocol).getLoan(loanID).collateral == amount) { + _activeTrades[trader].remove(loanID); } - bZxRouterAddress.call( + protocol.call( abi.encodeWithSelector( - IBZX(bZxRouterAddress).closeWithSwap.selector, + IBZx(protocol).closeWithSwap.selector, loanID, address(this), amount, iscollateral, - arbData + loanDataBytes ) ); - if (usedToken == wrapToken) { - WrappedToken(wrapToken).deposit{value: address(this).balance}(); - } - uint256 gasUsed = ((startGas - gasleft()) * gasPrice(usedToken)) / - (10**36); - SafeERC20.safeTransfer(IERC20(usedToken), keeper, gasUsed); - SafeERC20.safeTransfer( - IERC20(usedToken), - trader, - IERC20Metadata(usedToken).balanceOf(address(this)) - ); - - success = true; } function getSwapAddress() public view returns (address) { - return StateI(bZxRouterAddress).swapsImpl(); + return IBZx(protocol).swapsImpl(); } function currentSwapRate(address start, address end) @@ -111,29 +91,35 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { } function getFeed() public view returns (address) { - return StateI(bZxRouterAddress).priceFeeds(); + return IBZx(protocol).priceFeeds(); } function isActiveLoan(bytes32 ID) internal view returns (bool) { - (, , , , , , , , , , , bool active) = IBZX(bZxRouterAddress).loans(ID); - return active; + return IBZx(protocol).loans(ID).active; } - function dexSwapRate(IWalletFactory.OpenOrder memory order) + function dexSwapRate(IOrderBook.OpenOrder memory order) public view returns (uint256) { uint256 tradeSize; - if (order.orderType == IWalletFactory.OrderType.LIMIT_OPEN) { + (uint256 dexID, bytes memory payload) = abi.decode( + order.loanDataBytes, + (uint256, bytes) + ); + if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { if (order.loanTokenAmount > 0) { tradeSize = (order.loanTokenAmount * order.leverage) / 1 ether; } else { - (tradeSize, ) = dexSwaps(getSwapAddress()).dexAmountOut( - order.base, - order.loanTokenAddress, - order.collateralTokenAmount - ); + (tradeSize, ) = dexSwaps( + IDexRecords(getSwapAddress()).retrieveDexAddress(dexID) + ).dexAmountOut( + dexID != 1 + ? payload + : abi.encode(order.base, order.loanTokenAddress), + order.collateralTokenAmount + ); if (tradeSize == 0) { return 0; } @@ -141,207 +127,122 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { } } (uint256 fSwapRate, ) = order.orderType == - IWalletFactory.OrderType.LIMIT_OPEN - ? dexSwaps(getSwapAddress()).dexAmountOut( - order.loanTokenAddress, - order.base, - tradeSize - ) - : dexSwaps(getSwapAddress()).dexAmountOut( - order.base, - order.loanTokenAddress, - order.collateralTokenAmount - ); + IOrderBook.OrderType.LIMIT_OPEN + ? dexSwaps(IDexRecords(getSwapAddress()).retrieveDexAddress(dexID)) + .dexAmountOut( + dexID != 1 + ? payload + : abi.encode(order.loanTokenAddress, order.base), + tradeSize + ) + : dexSwaps(IDexRecords(getSwapAddress()).retrieveDexAddress(dexID)) + .dexAmountOut( + dexID != 1 + ? payload + : abi.encode(order.base, order.loanTokenAddress), + order.collateralTokenAmount + ); if (fSwapRate == 0) { return 0; } return - order.orderType == IWalletFactory.OrderType.LIMIT_OPEN - ? (tradeSize.TenExp(18 - int8(IERC20Metadata(order.loanTokenAddress).decimals())) * 1 ether) / - (fSwapRate.TenExp(18 - int8(IERC20Metadata(order.base).decimals()))) - : (1 ether *(fSwapRate.TenExp(18 - int8(IERC20Metadata(order.loanTokenAddress).decimals())))) / - (order.collateralTokenAmount.TenExp(18 - int8(IERC20Metadata(order.base).decimals()))); + order.orderType == IOrderBook.OrderType.LIMIT_OPEN + ? (tradeSize.TenExp( + 18 - int8(IERC20Metadata(order.loanTokenAddress).decimals()) + ) * 1 ether) / + ( + fSwapRate.TenExp( + 18 - int8(IERC20Metadata(order.base).decimals()) + ) + ) + : (1 ether * + ( + fSwapRate.TenExp( + 18 - + int8( + IERC20Metadata(order.loanTokenAddress) + .decimals() + ) + ) + )) / + ( + order.collateralTokenAmount.TenExp( + 18 - int8(IERC20Metadata(order.base).decimals()) + ) + ); } - function dexSwapCheck( - uint256 collateralTokenAmount, - uint256 loanTokenAmount, - address loanTokenAddress, - address base, - uint256 leverage, - IWalletFactory.OrderType orderType - ) public view returns (uint256) { - uint256 tradeSize; - if (orderType == IWalletFactory.OrderType.LIMIT_OPEN) { - if (loanTokenAmount > 0) { - tradeSize = (loanTokenAmount * leverage) / 1 ether; - } else { - (tradeSize, ) = dexSwaps(getSwapAddress()).dexAmountOut( - base, - loanTokenAddress, - collateralTokenAmount - ); - if (tradeSize == 0) { - return 0; - } - tradeSize = (tradeSize * leverage) / 1 ether; - } + function clearOrder(bytes32 orderID) public view returns (bool) { + if (_orderExpiration[orderID] < block.timestamp) { + return true; } - (uint256 fSwapRate, ) = orderType == IWalletFactory.OrderType.LIMIT_OPEN - ? dexSwaps(getSwapAddress()).dexAmountOut( - loanTokenAddress, - base, - tradeSize + uint256 swapRate = currentSwapRate( + _allOrders[orderID].loanTokenAddress, + _allOrders[orderID].base + ); + if ( + ( + _allOrders[orderID].price > swapRate + ? (_allOrders[orderID].price - swapRate) > + (_allOrders[orderID].price * 25) / 100 + : (swapRate - _allOrders[orderID].price) > + (swapRate * 25) / 100 ) - : dexSwaps(getSwapAddress()).dexAmountOut( - base, - loanTokenAddress, - collateralTokenAmount - ); - if (fSwapRate == 0) { - return 0; + ) { + return true; } - return - orderType == IWalletFactory.OrderType.LIMIT_OPEN - ? (tradeSize.TenExp(18 - int8(IERC20Metadata(loanTokenAddress).decimals())) * - 1 ether) / - (fSwapRate.TenExp(18 - int8(IERC20Metadata(base).decimals()))) - : (1 ether * - (fSwapRate.TenExp(18 - - int8(IERC20Metadata(loanTokenAddress).decimals())))) / - (collateralTokenAmount.TenExp(18 - int8(IERC20Metadata(base).decimals()))); - } - - function gasPrice(address payToken) public view returns (uint256) { - return IPriceFeeds(getFeed()).getFastGasPrice(payToken) * 2; + return false; } - function clearOrder(address trader, uint256 orderID) - public - view - returns (bool) - { - if(orderExpiration[trader][orderID] < block.timestamp){ - return true; - } - uint256 swapRate = currentSwapRate(HistoricalOrders[trader][orderID].loanTokenAddress, HistoricalOrders[trader][orderID].base); - if(!(HistoricalOrders[trader][orderID].price > swapRate - ? (HistoricalOrders[trader][orderID].price - swapRate) < (HistoricalOrders[trader][orderID].price * 4) / 100 - : (swapRate - HistoricalOrders[trader][orderID].price) < (swapRate * 4) / 100)){ - return true; - } - return false; - } - - function prelimCheck(address trader, uint256 orderID) - public - view - returns (bool) - { - if (orderExpiration[trader][orderID] < block.timestamp) { + function prelimCheck(bytes32 orderID) public view returns (bool) { + IOrderBook.OpenOrder memory order = _allOrders[orderID]; + address trader = order.trader; + if (_orderExpiration[orderID] < block.timestamp) { return false; } if ( - HistoricalOrders[trader][orderID].orderType == - IWalletFactory.OrderType.LIMIT_OPEN + order.orderType == IOrderBook.OrderType.LIMIT_OPEN ) { if ( - HistoricalOrders[trader][orderID].loanID == 0 || - isActiveLoan(HistoricalOrders[trader][orderID].loanID) - ) {} else { - return false; - } - uint256 tAmount = HistoricalOrders[trader][orderID] - .collateralTokenAmount > 0 - ? HistoricalOrders[trader][orderID].collateralTokenAmount + - (gasPrice(HistoricalOrders[trader][orderID].base) * - 2300000) / - 10**36 - : HistoricalOrders[trader][orderID].loanTokenAmount + - (gasPrice( - HistoricalOrders[trader][orderID].loanTokenAddress - ) * 2300000) / - 10**36; - address tokenUsed = HistoricalOrders[trader][orderID] - .collateralTokenAmount > 0 - ? HistoricalOrders[trader][orderID].base - : HistoricalOrders[trader][orderID].loanTokenAddress; - if ( - tAmount > - IERC20Metadata(tokenUsed).balanceOf(trader) + - IDeposits(vault).getDeposit(trader, orderID) + !(order.loanID == 0) && + !isActiveLoan(order.loanID) ) { return false; } - uint256 dSwapValue = dexSwapCheck( - HistoricalOrders[trader][orderID].collateralTokenAmount, - HistoricalOrders[trader][orderID].loanTokenAmount, - HistoricalOrders[trader][orderID].loanTokenAddress, - HistoricalOrders[trader][orderID].base, - HistoricalOrders[trader][orderID].leverage, - IWalletFactory.OrderType.LIMIT_OPEN - ); + uint256 dSwapValue = dexSwapRate(order); - if ( - HistoricalOrders[trader][orderID].price >= dSwapValue && - dSwapValue > 0 - ) { + if (order.price >= dSwapValue && dSwapValue > 0) { return true; } } else if ( - HistoricalOrders[trader][orderID].orderType == - IWalletFactory.OrderType.LIMIT_CLOSE + order.orderType == + IOrderBook.OrderType.LIMIT_CLOSE ) { - if (!isActiveLoan(HistoricalOrders[trader][orderID].loanID)) { - return false; - } - uint256 tAmount = HistoricalOrders[trader][orderID].isCollateral - ? (gasPrice(HistoricalOrders[trader][orderID].base) * 1600000) / - 10**36 - : (gasPrice( - HistoricalOrders[trader][orderID].loanTokenAddress - ) * 1600000) / 10**36; - address tokenUsed = HistoricalOrders[trader][orderID].isCollateral - ? HistoricalOrders[trader][orderID].base - : HistoricalOrders[trader][orderID].loanTokenAddress; - if (tAmount > IERC20Metadata(tokenUsed).balanceOf(trader)) { + if (!isActiveLoan(order.loanID)) { return false; } if ( - HistoricalOrders[trader][orderID].price <= - dexSwapCheck( - HistoricalOrders[trader][orderID].collateralTokenAmount, - HistoricalOrders[trader][orderID].loanTokenAmount, - HistoricalOrders[trader][orderID].loanTokenAddress, - HistoricalOrders[trader][orderID].base, - HistoricalOrders[trader][orderID].leverage, - IWalletFactory.OrderType.LIMIT_CLOSE - ) + order.price <= + dexSwapRate(order) ) { return true; } } else { - if (!isActiveLoan(HistoricalOrders[trader][orderID].loanID)) { - return false; - } - uint256 tAmount = HistoricalOrders[trader][orderID].isCollateral - ? (gasPrice(HistoricalOrders[trader][orderID].base) * 1600000) / - 10**36 - : (gasPrice( - HistoricalOrders[trader][orderID].loanTokenAddress - ) * 1600000) / 10**36; - address tokenUsed = HistoricalOrders[trader][orderID].isCollateral - ? HistoricalOrders[trader][orderID].base - : HistoricalOrders[trader][orderID].loanTokenAddress; - if (tAmount > IERC20Metadata(tokenUsed).balanceOf(trader)) { + if (!isActiveLoan(order.loanID)) { return false; } if ( - HistoricalOrders[trader][orderID].price >= - currentSwapRate( - HistoricalOrders[trader][orderID].base, - HistoricalOrders[trader][orderID].loanTokenAddress - ) + _useOracle[trader] + ? order.price >= + currentSwapRate( + order.base, + order.loanTokenAddress + ) + : order.price >= + currentDexRate( + order.base, + order.loanTokenAddress + ) ) { return true; } @@ -355,8 +256,8 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { returns (uint256) { uint256 dexRate; - if (src == wrapToken || dest == wrapToken) { - address pairAddress = UniswapFactory(UniFactoryContract).getPair( + if (src == WRAPPED_TOKEN || dest == WRAPPED_TOKEN) { + address pairAddress = UniswapFactory(UNI_FACTORY).getPair( src, dest ); @@ -365,70 +266,151 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { uint256 res0 = uint256(reserve0); uint256 res1 = uint256(reserve1); dexRate = UniswapPair(pairAddress).token0() == src - ? (res0.TenExp(18 - - int8(IERC20Metadata(UniswapPair(pairAddress).token0()) - .decimals()) + - 18)) / - (res1.TenExp(18 - - int8(IERC20Metadata( + ? ( + res0.TenExp( + 18 - + int8( + IERC20Metadata( + UniswapPair(pairAddress).token0() + ).decimals() + ) + + 18 + ) + ) / + ( + res1.TenExp( + 18 - + int8( + IERC20Metadata( + UniswapPair(pairAddress).token1() + ).decimals() + ) + ) + ) + : (( + res1.TenExp( + 18 - + int8( + IERC20Metadata( UniswapPair(pairAddress).token1() - ).decimals()))) - : ((res1.TenExp(18 - - int8(IERC20Metadata(UniswapPair(pairAddress).token1()) - .decimals()) + - 18)) / res0).TenExp(18 - - int8(IERC20Metadata(UniswapPair(pairAddress).token0()) - .decimals())); + ).decimals() + ) + + 18 + ) + ) / res0).TenExp( + 18 - + int8( + IERC20Metadata( + UniswapPair(pairAddress).token0() + ).decimals() + ) + ); } else { - address pairAddress0 = UniswapFactory(UniFactoryContract).getPair( + address pairAddress0 = UniswapFactory(UNI_FACTORY).getPair( src, - wrapToken + WRAPPED_TOKEN ); (uint112 reserve0, uint112 reserve1, ) = UniswapPair(pairAddress0) .getReserves(); uint256 res0 = uint256(reserve0); uint256 res1 = uint256(reserve1); uint256 midSwapRate = UniswapPair(pairAddress0).token0() == - wrapToken - ? (res1.TenExp(18 - - int8(IERC20Metadata(UniswapPair(pairAddress0).token1()) - .decimals()) + - 18)) / - (res0.TenExp(18 - - int8(IERC20Metadata( + WRAPPED_TOKEN + ? ( + res1.TenExp( + 18 - + int8( + IERC20Metadata( + UniswapPair(pairAddress0).token1() + ).decimals() + ) + + 18 + ) + ) / + ( + res0.TenExp( + 18 - + int8( + IERC20Metadata( + UniswapPair(pairAddress0).token0() + ).decimals() + ) + ) + ) + : ( + res0.TenExp( + 18 - + int8( + IERC20Metadata( UniswapPair(pairAddress0).token0() - ).decimals()))) - : (res0.TenExp(18 - int8(IERC20Metadata(UniswapPair(pairAddress0).token0()).decimals()) + 18)) / - (res1.TenExp(18 - int8(IERC20Metadata(UniswapPair(pairAddress0).token0()).decimals()))); - address pairAddress1 = UniswapFactory(UniFactoryContract).getPair( + ).decimals() + ) + + 18 + ) + ) / + ( + res1.TenExp( + 18 - + int8( + IERC20Metadata( + UniswapPair(pairAddress0).token0() + ).decimals() + ) + ) + ); + address pairAddress1 = UniswapFactory(UNI_FACTORY).getPair( dest, - wrapToken + WRAPPED_TOKEN ); (uint112 reserve2, uint112 reserve3, ) = UniswapPair(pairAddress1) .getReserves(); uint256 res2 = uint256(reserve2); uint256 res3 = uint256(reserve3); - dexRate = UniswapPair(pairAddress1).token0() == wrapToken + dexRate = UniswapPair(pairAddress1).token0() == WRAPPED_TOKEN ? ((10**36 / - ((res3.TenExp(18 - - int8(IERC20Metadata( - UniswapPair(pairAddress1).token1() - ).decimals()) + - 18)) / - (res2.TenExp(18 - - int8(IERC20Metadata( - UniswapPair(pairAddress1).token0() - ).decimals()))))) * midSwapRate) / 10**18 - : ((10**36 / - ((res2.TenExp(18 - - int8(IERC20Metadata( - UniswapPair(pairAddress1).token0() - ).decimals()) + - 18)) / - (res3.TenExp(18 - - int8(IERC20Metadata( + (( + res3.TenExp( + 18 - + int8( + IERC20Metadata( UniswapPair(pairAddress1).token1() - ).decimals()))))) * midSwapRate) / 10**18; + ).decimals() + ) + + 18 + ) + ) / + ( + res2.TenExp( + 18 - + int8( + IERC20Metadata( + UniswapPair(pairAddress1).token0() + ).decimals() + ) + ) + ))) * midSwapRate) / 10**18 + : ((10**36 / + (( + res2.TenExp( + 18 - + int8( + IERC20Metadata( + UniswapPair(pairAddress1).token0() + ).decimals() + ) + + 18 + ) + ) / + ( + res3.TenExp( + 18 - + int8( + IERC20Metadata( + UniswapPair(pairAddress1).token1() + ).decimals() + ) + ) + ))) * midSwapRate) / 10**18; } return dexRate; } @@ -448,126 +430,89 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { : false; } - function executeOrder( - address payable keeper, - address trader, - uint256 orderID - ) public { - uint256 startGas = gasleft(); - require(HistoricalOrders[trader][orderID].isActive, "non active"); - //HistoricalOrders[trader][orderID].collateralTokenAmount > 0 ? checkCollateralAllowance(HistoricalOrders[trader][orderID]) : checkLoanTokenAllowance(HistoricalOrders[trader][orderID]); + function executeOrder(address payable keeper, bytes32 orderID) public { + require(!_allOrders[orderID].isCancelled, "non active"); + IOrderBook.OpenOrder memory order = _allOrders[orderID]; + address trader = _allOrders[orderID].trader; if ( - HistoricalOrders[trader][orderID].orderType == - IWalletFactory.OrderType.LIMIT_OPEN + _allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN ) { require( - HistoricalOrders[trader][orderID].price >= - dexSwapRate(HistoricalOrders[trader][orderID]), + _allOrders[orderID].price >= dexSwapRate(_allOrders[orderID]), "invalid swap rate" ); - address usedToken = HistoricalOrders[trader][orderID] - .collateralTokenAmount > - HistoricalOrders[trader][orderID].loanTokenAmount - ? HistoricalOrders[trader][orderID].base - : HistoricalOrders[trader][orderID].loanTokenAddress; - - SafeERC20.safeTransfer( - IERC20(usedToken), - keeper, - ((startGas - - executeTradeOpen(trader, orderID, keeper, usedToken)) * - gasPrice(usedToken)) / (10**36) - ); - SafeERC20.safeTransfer( - IERC20(usedToken), - trader, - IERC20Metadata(usedToken).balanceOf(address(this)) - ); - HistoricalOrders[trader][orderID].isActive = false; - OrderRecords.removeOrderNum( - AllOrderIDs, - matchingID[trader][orderID] - ); - OrderRecords.removeOrderNum(HistOrders[trader], orderID); - if (OrderRecords.length(HistOrders[trader]) == 0) { - ActiveTraders.removeTrader(activeTraders, trader); - } + _executeTradeOpen(trader, orderID); + _allOrders[orderID].isCancelled = true; + _allOrderIDs.remove(orderID); + _histOrders[trader].remove(orderID); emit OrderExecuted(trader, orderID); return; } if ( - HistoricalOrders[trader][orderID].orderType == - IWalletFactory.OrderType.LIMIT_CLOSE + order.orderType == + IOrderBook.OrderType.LIMIT_CLOSE ) { require( - HistoricalOrders[trader][orderID].price <= - dexSwapRate(HistoricalOrders[trader][orderID]), + order.price <= dexSwapRate(_allOrders[orderID]), "invalid swap rate" ); - executeTradeClose( + _executeTradeClose( trader, - keeper, - HistoricalOrders[trader][orderID].loanID, - HistoricalOrders[trader][orderID].collateralTokenAmount, - HistoricalOrders[trader][orderID].isCollateral, - HistoricalOrders[trader][orderID].loanTokenAddress, - HistoricalOrders[trader][orderID].base, - startGas, - HistoricalOrders[trader][orderID].loanData - ); - HistoricalOrders[trader][orderID].isActive = false; - OrderRecords.removeOrderNum( - AllOrderIDs, - matchingID[trader][orderID] + order.loanID, + order.collateralTokenAmount, + order.isCollateral, + order.base, + order.loanDataBytes ); - OrderRecords.removeOrderNum(HistOrders[trader], orderID); - if (OrderRecords.length(HistOrders[trader]) == 0) { - ActiveTraders.removeTrader(activeTraders, trader); - } + _allOrders[orderID].isCancelled = true; + _allOrderIDs.remove(orderID); + _histOrders[trader].remove(orderID); emit OrderExecuted(trader, orderID); return; } if ( - HistoricalOrders[trader][orderID].orderType == - IWalletFactory.OrderType.MARKET_STOP + order.orderType == + IOrderBook.OrderType.MARKET_STOP ) { require( - HistoricalOrders[trader][orderID].price >= - currentSwapRate( - HistoricalOrders[trader][orderID].base, - HistoricalOrders[trader][orderID].loanTokenAddress - ) && - priceCheck( - HistoricalOrders[trader][orderID].loanTokenAddress, - HistoricalOrders[trader][orderID].base - ), + _useOracle[trader] + ? order.price >= + currentDexRate( + order.base, + order.loanTokenAddress + ) && + priceCheck( + order.loanTokenAddress, + order.base + ) + : order.price >= + currentDexRate( + order.base, + order.loanTokenAddress + ) && + priceCheck( + order.loanTokenAddress, + order.base + ), "invalid swap rate" ); - executeTradeClose( + _executeTradeClose( trader, - keeper, - HistoricalOrders[trader][orderID].loanID, - HistoricalOrders[trader][orderID].collateralTokenAmount, - HistoricalOrders[trader][orderID].isCollateral, - HistoricalOrders[trader][orderID].loanTokenAddress, - HistoricalOrders[trader][orderID].base, - startGas, - HistoricalOrders[trader][orderID].loanData - ); - HistoricalOrders[trader][orderID].isActive = false; - OrderRecords.removeOrderNum( - AllOrderIDs, - matchingID[trader][orderID] + order.loanID, + order.collateralTokenAmount, + order.isCollateral, + order.base, + order.loanDataBytes ); - OrderRecords.removeOrderNum(HistOrders[trader], orderID); - if (OrderRecords.length(HistOrders[trader]) == 0) { - ActiveTraders.removeTrader(activeTraders, trader); - } + _allOrders[orderID].isCancelled = true; + _allOrderIDs.remove(orderID); + _histOrders[trader].remove(orderID); emit OrderExecuted(trader, orderID); return; } } - function setVaultAddress(address nVault) onlyOwner() public{ - vault = nVault; - } + + function setVaultAddress(address nVault) public onlyOwner { + vault = nVault; + } } diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index 0c6e5b38..2dc1529d 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -1,88 +1,84 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; import "../Storage/OrderBookEvents.sol"; import "../Storage/OrderBookStorage.sol"; contract OrderBookData is OrderBookEvents, OrderBookStorage { - function getRouter() public view returns (address) { - return bZxRouterAddress; + using EnumerableSet for EnumerableSet.Bytes32Set; + + function initialize( + address target) + public + onlyOwner + { + _setTarget(this.getProtocolAddress.selector, target); + _setTarget(this.adjustAllowance.selector, target); + _setTarget(this.getActiveOrders.selector, target); + _setTarget(this.getOrderByOrderID.selector, target); + _setTarget(this.getActiveOrderIDs.selector, target); + _setTarget(this.getTotalOrders.selector, target); + _setTarget(this.getTotalActiveOrders.selector, target); + _setTarget(this.getOrders.selector, target); + _setTarget(this.getActiveTrades.selector, target); + } + + function getProtocolAddress() public view returns (address) { + return protocol; } function adjustAllowance(address spender, address token) public { require( - IBZX(bZxRouterAddress).isLoanPool(spender) || - bZxRouterAddress == spender || vault == spender, + IBZx(protocol).isLoanPool(spender) || + protocol == spender || + vault == spender, "invalid spender" ); IERC20Metadata(token).approve(spender, type(uint256).max); } function getActiveOrders( - address smartWallet, - uint256 start, - uint256 count - ) public view returns (IWalletFactory.OpenOrder[] memory fullList) { - uint256[] memory idSet = OrderRecords.enums( - HistOrders[smartWallet], - start, - count - ); + address trader + ) public view returns (IOrderBook.OpenOrder[] memory fullList) { + bytes32[] memory idSet = _histOrders[trader].values(); - fullList = new IWalletFactory.OpenOrder[](idSet.length); + fullList = new IOrderBook.OpenOrder[](idSet.length); for (uint256 i = 0; i < idSet.length; i++) { - fullList[i] = HistoricalOrders[smartWallet][idSet[i]]; + fullList[i] = _allOrders[idSet[i]]; } return fullList; } - function getOrderByOrderID(address smartWallet, uint256 orderID) + function getOrderByOrderID(bytes32 orderID) public view - returns (IWalletFactory.OpenOrder memory) + returns (IOrderBook.OpenOrder memory) { - return HistoricalOrders[smartWallet][orderID]; + return _allOrders[orderID]; } function getActiveOrderIDs( - address smartWallet, - uint256 start, - uint256 count - ) public view returns (uint256[] memory) { - return OrderRecords.enums(HistOrders[smartWallet], start, count); - } - - function getTotalOrders(address smartWallet) public view returns (uint256) { - return OrderRecords.length(HistOrders[smartWallet]); - } - - function getTradersWithOrders(uint256 start, uint256 count) - public - view - returns (address[] memory) - { - return ActiveTraders.enums(activeTraders, start, count); + address trader + ) public view returns (bytes32[] memory) { + return _histOrders[trader].values(); } - function getTotalTradersWithOrders() public view returns (uint256) { - return ActiveTraders.length(activeTraders); + function getTotalOrders(address trader) public view returns (uint256) { + return _histOrders[trader].length(); } function getTotalActiveOrders() public view returns (uint256) { - return OrderRecords.length(AllOrderIDs); + return _allOrderIDs.length(); } - function getOrders(uint256 start, uint256 count) + function getOrders() public view - returns (IWalletFactory.OpenOrder[] memory fullList) + returns (IOrderBook.OpenOrder[] memory fullList) { - uint256[] memory idSet = OrderRecords.enums(AllOrderIDs, start, count); + bytes32[] memory idSet = _allOrderIDs.values(); - fullList = new IWalletFactory.OpenOrder[](idSet.length); + fullList = new IOrderBook.OpenOrder[](idSet.length); for (uint256 i = 0; i < idSet.length; i++) { - fullList[i] = getOrderByOrderID( - AllOrders[idSet[i]].trader, - AllOrders[idSet[i]].orderID - ); + fullList[i] = getOrderByOrderID(idSet[i]); } return fullList; } @@ -93,10 +89,6 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { returns (bytes32[] memory) { return - OrderEntry.enums( - ActiveTrades[trader], - 0, - OrderEntry.length(ActiveTrades[trader]) - ); + _activeTrades[trader].values(); } } diff --git a/contracts/orderbook/Logic/OrderBookMarketOrders.sol b/contracts/orderbook/Logic/OrderBookMarketOrders.sol index 1c29ae50..9f054f05 100644 --- a/contracts/orderbook/Logic/OrderBookMarketOrders.sol +++ b/contracts/orderbook/Logic/OrderBookMarketOrders.sol @@ -1,8 +1,19 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; import "../Storage/OrderBookEvents.sol"; import "../Storage/OrderBookStorage.sol"; contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { + using EnumerableSet for EnumerableSet.Bytes32Set; + + function initialize( + address target) + public + onlyOwner + { + _setTarget(this.marketOpen.selector, target); + _setTarget(this.marketClose.selector, target); + } + function marketOpen( bytes32 loanID, uint256 leverage, @@ -10,9 +21,9 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { uint256 collateralTokenAmount, address iToken, address base, - bytes memory loanData + bytes memory loanDataBytes ) public { - executeMarketOpen( + _executeMarketOpen( msg.sender, loanID, leverage, @@ -20,7 +31,7 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { collateralTokenAmount, iToken, base, - loanData + loanDataBytes ); } @@ -30,20 +41,20 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { bool iscollateral, address loanTokenAddress, address collateralAddress, - bytes memory arbData + bytes memory loanDataBytes ) public { - executeMarketClose( + _executeMarketClose( msg.sender, loanID, amount, iscollateral, loanTokenAddress, collateralAddress, - arbData + loanDataBytes ); } - function executeMarketOpen( + function _executeMarketOpen( address trader, bytes32 lID, uint256 leverage, @@ -51,11 +62,12 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { uint256 collateralTokenAmount, address iToken, address base, - bytes memory loanData + bytes memory loanDataBytes ) internal { + require(IBZx(protocol).isLoanPool(iToken)); address usedToken = collateralTokenAmount > loanTokenAmount ? base - : LoanTokenI(iToken).loanTokenAddress(); + : IToken(iToken).loanTokenAddress(); uint256 transferAmount = collateralTokenAmount > loanTokenAmount ? collateralTokenAmount : loanTokenAmount; @@ -65,8 +77,8 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { address(this), transferAmount ); - loanData = ""; - bytes32 loanID = LoanTokenI(iToken) + loanDataBytes = ""; + bytes32 loanID = IToken(iToken) .marginTrade( lID, leverage, @@ -74,28 +86,28 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { collateralTokenAmount, base, address(this), - loanData + loanDataBytes ) - .LoanId; - if (OrderEntry.inVals(ActiveTrades[trader], loanID) == false) { - OrderEntry.addTrade(ActiveTrades[trader], loanID); + .loanId; + if (!_activeTrades[trader].contains(loanID)) { + _activeTrades[trader].add(loanID); } } - function executeMarketClose( + function _executeMarketClose( address trader, bytes32 loanID, uint256 amount, bool iscollateral, address loanTokenAddress, address collateralAddress, - bytes memory arbData + bytes memory loanDataBytes ) internal { address usedToken; - arbData = ""; + loanDataBytes = ""; if ( - (iscollateral == true && collateralAddress != wrapToken) || - (iscollateral == false && loanTokenAddress != wrapToken) + (iscollateral && collateralAddress != WRAPPED_TOKEN) || + (!iscollateral && loanTokenAddress != WRAPPED_TOKEN) ) { usedToken = iscollateral ? collateralAddress : loanTokenAddress; uint256 traderB = IERC20Metadata(usedToken).balanceOf(trader); @@ -108,15 +120,15 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { } else { usedToken = address(0); } - if (IBZX(bZxRouterAddress).getLoan(loanID).collateral == amount) { - OrderEntry.removeTrade(ActiveTrades[trader], loanID); + if (IBZx(protocol).getLoan(loanID).collateral == amount) { + _activeTrades[trader].remove(loanID); } - IBZX(bZxRouterAddress).closeWithSwap( + IBZx(protocol).closeWithSwap( loanID, address(this), amount, iscollateral, - arbData + loanDataBytes ); if (usedToken != address(0)) { SafeERC20.safeTransfer( @@ -125,11 +137,11 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { IERC20Metadata(usedToken).balanceOf(address(this)) ); } else { - WrappedToken(wrapToken).deposit{value: address(this).balance}(); + WrappedToken(WRAPPED_TOKEN).deposit{value: address(this).balance}(); SafeERC20.safeTransfer( - IERC20(wrapToken), + IERC20(WRAPPED_TOKEN), trader, - IERC20Metadata(wrapToken).balanceOf(address(this)) + IERC20Metadata(WRAPPED_TOKEN).balanceOf(address(this)) ); } } diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 457cdcf4..de11de12 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -1,99 +1,108 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; import "../Storage/OrderBookEvents.sol"; import "../Storage/OrderBookStorage.sol"; import "../OrderVault/IDeposits.sol"; + contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { - + using EnumerableSet for EnumerableSet.Bytes32Set; + + function initialize( + address target) + public + onlyOwner + { + _setTarget(this.placeOrder.selector, target); + _setTarget(this.currentSwapRate.selector, target); + _setTarget(this.amendOrder.selector, target); + _setTarget(this.cancelOrder.selector, target); + _setTarget(this.cancelOrderProtocol.selector, target); + _setTarget(this.changeStopType.selector, target); + _setTarget(this.minimumAmount.selector, target); + } + function currentSwapRate(address start, address end) public view returns (uint256 executionPrice) { - (executionPrice, ) = IPriceFeeds(StateI(bZxRouterAddress).priceFeeds()) + (executionPrice, ) = IPriceFeeds(IBZx(protocol).priceFeeds()) .queryRate(end, start); } - function collateralTokenMatch(IWalletFactory.OpenOrder memory checkOrder) + function _collateralTokenMatch(IOrderBook.OpenOrder memory checkOrder) internal view returns (bool) { return - IBZX(bZxRouterAddress).getLoan(checkOrder.loanID).collateralToken == + IBZx(protocol).getLoan(checkOrder.loanID).collateralToken == checkOrder.base; } - function loanTokenMatch(IWalletFactory.OpenOrder memory checkOrder) + function _loanTokenMatch(IOrderBook.OpenOrder memory checkOrder) internal view returns (bool) { return - IBZX(bZxRouterAddress).getLoan(checkOrder.loanID).loanToken == + IBZx(protocol).getLoan(checkOrder.loanID).loanToken == checkOrder.loanTokenAddress; } - function placeOrder(IWalletFactory.OpenOrder memory Order) public { + function _abs(int256 x) private pure returns (int256) { + return x >= 0 ? x : -x; + } + + function placeOrder(IOrderBook.OpenOrder memory Order) public { require( - (Order.loanTokenAmount == 0 && Order.collateralTokenAmount > 0) || - (Order.loanTokenAmount > 0 && Order.collateralTokenAmount == 0), + _abs( + int256(Order.loanTokenAmount) - + int256(Order.collateralTokenAmount) + ) == int256(Order.loanTokenAmount + Order.collateralTokenAmount), "only one token can be used" ); - //uint256 swapRate = currentSwapRate(Order.loanTokenAddress, Order.base); - require(IBZX(bZxRouterAddress).supportedTokens(Order.loanTokenAddress) && IBZX(bZxRouterAddress).supportedTokens(Order.base), "invalid pair"); - /*require( - Order.price > swapRate - ? (Order.price - swapRate) < (Order.price * 4) / 100 - : (swapRate - Order.price) < (swapRate * 4) / 100, - "price too far away" - );*/ + require( + IBZx(protocol).supportedTokens(Order.loanTokenAddress) && + IBZx(protocol).supportedTokens(Order.base), + "invalid pair" + ); require( Order.loanID != 0 - ? collateralTokenMatch(Order) && loanTokenMatch(Order) + ? _collateralTokenMatch(Order) && _loanTokenMatch(Order) : true, "incorrect collateral and/or loan token specified" ); require( - Order.orderType == IWalletFactory.OrderType.LIMIT_OPEN - ? Order.loanID == 0 || OrderEntry.inVals(ActiveTrades[msg.sender], Order.loanID) - : OrderEntry.inVals(ActiveTrades[msg.sender], Order.loanID), + Order.orderType == IOrderBook.OrderType.LIMIT_OPEN + ? IBZx(protocol).isLoanPool(Order.iToken) + : true + ); + require( + Order.orderType == IOrderBook.OrderType.LIMIT_OPEN + ? Order.loanID == 0 || + _activeTrades[msg.sender].contains(Order.loanID) + : _activeTrades[msg.sender].contains(Order.loanID), "inactive loan" ); - uint256 amountUsed = Order.loanTokenAmount + - Order.collateralTokenAmount; //one is always 0 so correct amount and no overflow issues - address usedToken = Order.loanTokenAmount > - Order.collateralTokenAmount - ? Order.loanTokenAddress - : Order.base; - require(currentSwapRate(usedToken,USDC)*amountUsed/10**(IERC20Metadata(usedToken).decimals()) > MIN_AMOUNT_IN_USDC); - HistoricalOrderIDs[msg.sender]++; - mainOBID++; - Order.orderID = HistoricalOrderIDs[msg.sender]; - Order.trader = msg.sender; - Order.isActive = true; - Order.loanData = ""; - orderExpiration[msg.sender][Order.orderID] = block.timestamp + DAYS_14; - HistoricalOrders[msg.sender][HistoricalOrderIDs[msg.sender]] = Order; - AllOrders[mainOBID].trader = msg.sender; - AllOrders[mainOBID].orderID = Order.orderID; - OrderRecords.addOrderNum( - HistOrders[msg.sender], - HistoricalOrderIDs[msg.sender] + uint256 amountUsed = Order.loanTokenAmount + + Order.collateralTokenAmount; //one is always 0 so correct amount and no overflow issues + address usedToken = Order.loanTokenAmount > Order.collateralTokenAmount + ? Order.loanTokenAddress + : Order.base; + require( + (currentSwapRate(usedToken, USDC) * amountUsed) / + 10**(IERC20Metadata(usedToken).decimals()) > + MIN_AMOUNT_IN_USDC ); - OrderRecords.addOrderNum(AllOrderIDs, mainOBID); - matchingID[msg.sender][HistoricalOrderIDs[msg.sender]] = mainOBID; - if (ActiveTraders.inVals(activeTraders, msg.sender) == false) { - ActiveTraders.addTrader(activeTraders, msg.sender); - } - if (Order.orderType == IWalletFactory.OrderType.LIMIT_OPEN) { - - - SafeERC20.safeTransferFrom( - IERC20(usedToken), - msg.sender, - address(this), - amountUsed - ); + require(Order.trader == msg.sender); + require(!Order.isCancelled); + mainOBID++; + Order.orderID = keccak256(abi.encode(msg.sender, mainOBID)); + _orderExpiration[Order.orderID] = block.timestamp + DAYS_14; + _allOrders[Order.orderID] = Order; + _histOrders[msg.sender].add(Order.orderID); + _allOrderIDs.add(Order.orderID); + if (Order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { IDeposits(vault).deposit( Order.orderID, amountUsed, @@ -105,46 +114,57 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { msg.sender, Order.orderType, Order.price, - HistoricalOrderIDs[msg.sender], + Order.orderID, Order.base, Order.loanTokenAddress ); } - function amendOrder(IWalletFactory.OpenOrder memory Order, uint256 orderID) - public - { + function amendOrder(IOrderBook.OpenOrder memory Order) public { require( - (Order.loanTokenAmount == 0 && Order.collateralTokenAmount > 0) || - (Order.loanTokenAmount > 0 && Order.collateralTokenAmount == 0), + _abs( + int256(Order.loanTokenAmount) - + int256(Order.collateralTokenAmount) + ) == int256(Order.loanTokenAmount + Order.collateralTokenAmount), "only one token can be used" ); //uint256 swapRate = currentSwapRate(Order.loanTokenAddress, Order.base); - require(Order.base==HistoricalOrders[msg.sender][orderID].base&&Order.loanTokenAddress==HistoricalOrders[msg.sender][orderID].loanTokenAddress, "invalid tokens"); + require( + Order.base == _allOrders[Order.orderID].base && + Order.loanTokenAddress == + _allOrders[Order.orderID].loanTokenAddress, + "invalid tokens" + ); /*require( Order.price > swapRate - ? (Order.price - swapRate) < (Order.price * 4) / 100 - : (swapRate - Order.price) < (swapRate * 4) / 100, + ? (Order.price - swapRate) < (Order.price * 25) / 100 + : (swapRate - Order.price) < (swapRate * 25) / 100, "price too far away" );*/ - require(Order.trader == msg.sender, "trader of order != sender"); require( - Order.orderID == HistoricalOrders[msg.sender][orderID].orderID, + Order.orderType == IOrderBook.OrderType.LIMIT_OPEN + ? IBZx(protocol).isLoanPool(Order.iToken) + : true + ); + require( + Order.orderID == _allOrders[Order.orderID].orderID, "improper ID" ); require( - HistoricalOrders[msg.sender][orderID].isActive == true, + !_allOrders[Order.orderID].isCancelled, "inactive order specified" ); require( - Order.orderType == IWalletFactory.OrderType.LIMIT_OPEN - ? Order.loanID == 0 || OrderEntry.inVals(ActiveTrades[msg.sender], Order.loanID) - : OrderEntry.inVals(ActiveTrades[msg.sender], Order.loanID), + Order.orderType == IOrderBook.OrderType.LIMIT_OPEN + ? Order.loanID == 0 || + _activeTrades[msg.sender].contains(Order.loanID) + : _activeTrades[msg.sender].contains(Order.loanID), "inactive loan" ); - orderExpiration[msg.sender][Order.orderID] = block.timestamp + DAYS_14; - Order.loanData = ""; - if (Order.orderType == IWalletFactory.OrderType.LIMIT_OPEN) { + require(Order.trader == msg.sender); + require(!_allOrders[Order.orderID].isCancelled); + _orderExpiration[Order.orderID] = block.timestamp + DAYS_14; + if (Order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { uint256 amountUsed = Order.loanTokenAmount + Order.collateralTokenAmount; //one is always 0 so correct amount and no overflow issues address usedToken = Order.loanTokenAmount > @@ -159,119 +179,86 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { usedToken == IDeposits(vault).getTokenUsed(msg.sender, Order.orderID) ); - IDeposits(vault).withdraw(msg.sender, Order.orderID); - if (storedAmount > amountUsed) { - IDeposits(vault).deposit( - Order.orderID, - amountUsed, + uint256 amountUsedOld = _allOrders[Order.orderID].loanTokenAmount + + _allOrders[Order.orderID].collateralTokenAmount; + if (amountUsedOld > amountUsed) { + IDeposits(vault).partialWithdraw( msg.sender, - usedToken + Order.orderID, + amountUsedOld - amountUsed ); } else { - SafeERC20.safeTransferFrom( - IERC20(usedToken), - msg.sender, - address(this), - amountUsed - storedAmount - ); IDeposits(vault).deposit( Order.orderID, - amountUsed, + amountUsed - amountUsedOld, msg.sender, usedToken ); } - SafeERC20.safeTransfer( - IERC20(usedToken), - msg.sender, - IERC20Metadata(usedToken).balanceOf(address(this)) - ); } - HistoricalOrders[msg.sender][orderID] = Order; + _allOrders[Order.orderID] = Order; emit OrderAmended( msg.sender, Order.orderType, Order.price, - orderID, + Order.orderID, Order.base, Order.loanTokenAddress ); } - function cancelOrder(uint256 orderID) public { - require( - HistoricalOrders[msg.sender][orderID].isActive == true, - "inactive order" - ); - HistoricalOrders[msg.sender][orderID].isActive = false; - OrderRecords.removeOrderNum(HistOrders[msg.sender], orderID); - OrderRecords.removeOrderNum( - AllOrderIDs, - matchingID[msg.sender][orderID] - ); - if (OrderRecords.length(HistOrders[msg.sender]) == 0) { - ActiveTraders.removeTrader(activeTraders, msg.sender); - } + function cancelOrder(bytes32 orderID) public { + require(!_allOrders[orderID].isCancelled, "inactive order"); + _allOrders[orderID].isCancelled = true; + _histOrders[msg.sender].remove(orderID); + _allOrderIDs.remove(orderID); if ( - HistoricalOrders[msg.sender][orderID].orderType == - IWalletFactory.OrderType.LIMIT_OPEN + _allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN ) { address usedToken = IDeposits(vault).getTokenUsed( msg.sender, orderID ); - IDeposits(vault).withdraw(msg.sender, orderID); - SafeERC20.safeTransfer( - IERC20(usedToken), - msg.sender, - IERC20Metadata(usedToken).balanceOf(address(this)) - ); + IDeposits(vault).withdrawToTrader(msg.sender, orderID); } emit OrderCancelled(msg.sender, orderID); } - - function cancelOrderProtocol(address trader, uint256 orderID) public { - require( - HistoricalOrders[trader][orderID].isActive == true, - "inactive order" + + function cancelOrderProtocol(bytes32 orderID) public { + address trader = _allOrders[orderID].trader; + require(!_allOrders[orderID].isCancelled, "inactive order"); + uint256 swapRate = currentSwapRate( + _allOrders[orderID].loanTokenAddress, + _allOrders[orderID].base ); - require(orderExpiration[trader][orderID] < block.timestamp); - uint256 swapRate = currentSwapRate(HistoricalOrders[trader][orderID].loanTokenAddress, HistoricalOrders[trader][orderID].base); require( - HistoricalOrders[trader][orderID].price > swapRate - ? (HistoricalOrders[trader][orderID].price - swapRate) < (HistoricalOrders[trader][orderID].price * 4) / 100 - : (swapRate - HistoricalOrders[trader][orderID].price) < (swapRate * 4) / 100, - "price too far away" - ); - HistoricalOrders[trader][orderID].isActive = false; - OrderRecords.removeOrderNum(HistOrders[trader], orderID); - OrderRecords.removeOrderNum( - AllOrderIDs, - matchingID[trader][orderID] + ( + _allOrders[orderID].price > swapRate + ? (_allOrders[orderID].price - swapRate) > + (_allOrders[orderID].price * 25) / 100 + : (swapRate - _allOrders[orderID].price) > + (swapRate * 25) / 100 + ) || _orderExpiration[orderID] < block.timestamp, + "no conditions met" ); - if (OrderRecords.length(HistOrders[trader]) == 0) { - ActiveTraders.removeTrader(activeTraders, trader); - } + _allOrders[orderID].isCancelled = true; + _histOrders[trader].remove(orderID); + _allOrderIDs.remove(orderID); if ( - HistoricalOrders[trader][orderID].orderType == - IWalletFactory.OrderType.LIMIT_OPEN + _allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN ) { - address usedToken = IDeposits(vault).getTokenUsed( - trader, - orderID - ); - IDeposits(vault).withdraw(trader, orderID); - SafeERC20.safeTransfer( - IERC20(usedToken), - trader, - IERC20Metadata(usedToken).balanceOf(address(this)) - ); + address usedToken = IDeposits(vault).getTokenUsed(trader, orderID); + IDeposits(vault).withdrawToTrader(trader, orderID); } emit OrderCancelled(trader, orderID); } - - function minimumAmount() public view returns(uint256){ - return MIN_AMOUNT_IN_USDC; - } + + function changeStopType(bool stop) public { + _useOracle[msg.sender] = stop; + } + + function minimumAmount() public view returns (uint256) { + return MIN_AMOUNT_IN_USDC; + } } diff --git a/contracts/orderbook/Logic/UniswapInterfaces.sol b/contracts/orderbook/Logic/UniswapInterfaces.sol index 6391f3bd..4ed2c82f 100644 --- a/contracts/orderbook/Logic/UniswapInterfaces.sol +++ b/contracts/orderbook/Logic/UniswapInterfaces.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; interface UniswapFactory { function getPair(address tokenA, address tokenB) diff --git a/contracts/orderbook/Logic/dexSwaps.sol b/contracts/orderbook/Logic/dexSwaps.sol index c4e99935..2da52877 100644 --- a/contracts/orderbook/Logic/dexSwaps.sol +++ b/contracts/orderbook/Logic/dexSwaps.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; interface dexSwaps { function dexExpectedRate( @@ -7,15 +7,15 @@ interface dexSwaps { uint256 sourceTokenAmount ) external view virtual returns (uint256); - function dexAmountOut( - address sourceTokenAddress, - address destTokenAddress, - uint256 amountIn - ) external view virtual returns (uint256 amountOut, address midToken); + function dexAmountOut(bytes memory payload, uint256 amountIn) + external + view + virtual + returns (uint256 amountOut, address midToken); - function dexAmountIn( - address sourceTokenAddress, - address destTokenAddress, - uint256 amountOut - ) external view virtual returns (uint256 amountIn, address midToken); + function dexAmountIn(bytes memory payload, uint256 amountOut) + external + view + virtual + returns (uint256 amountIn, address midToken); } diff --git a/contracts/orderbook/OrderBookProxy.sol b/contracts/orderbook/OrderBookProxy.sol index fbd4c303..6bcf912d 100644 --- a/contracts/orderbook/OrderBookProxy.sol +++ b/contracts/orderbook/OrderBookProxy.sol @@ -1,55 +1,63 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; import "./Storage/OrderBookStorage.sol"; import "./Storage/OrderBookEvents.sol"; contract OrderBookProxy is OrderBookEvents, OrderBookStorage { - mapping(bytes4 => address) internal implMatch; - - constructor(address bzx) { - bZxRouterAddress = bzx; + constructor(address _contract) { + protocol = _contract; } - fallback() external payable { + fallback() + external + payable + { if (gasleft() <= 2300) { return; } - address impl = implMatch[msg.sig]; + address target = logicTargets[msg.sig]; + require(target != address(0), "target not active"); bytes memory data = msg.data; assembly { - let result := delegatecall( - gas(), - impl, - add(data, 0x20), - mload(data), - 0, - 0 - ) + let result := delegatecall(gas(), target, add(data, 0x20), mload(data), 0, 0) let size := returndatasize() let ptr := mload(0x40) returndatacopy(ptr, 0, size) switch result - case 0 { - revert(ptr, size) - } - default { - return(ptr, size) - } + case 0 { revert(ptr, size) } + default { return(ptr, size) } } } - function setTargets(bytes4[] calldata sigs, address[] calldata targets) - public + function replaceContract( + address target) + external + onlyOwner + { + (bool success,) = target.delegatecall(abi.encodeWithSignature("initialize(address)", target)); + require(success, "setup failed"); + } + + function setTargets( + string[] calldata sigsArr, + address[] calldata targetsArr) + external onlyOwner { - require(sigs.length == targets.length); - for (uint256 i = 0; i < targets.length; i++) { - implMatch[sigs[i]] = targets[i]; + require(sigsArr.length == targetsArr.length, "count mismatch"); + + for (uint256 i = 0; i < sigsArr.length; i++) { + _setTarget(bytes4(keccak256(abi.encodePacked(sigsArr[i]))), targetsArr[i]); } } - function getTarget(bytes4 sig) public view returns (address) { - return implMatch[sig]; + function getTarget( + string calldata sig) + external + view + returns (address) + { + return logicTargets[bytes4(keccak256(abi.encodePacked(sig)))]; } } diff --git a/contracts/orderbook/OrderVault/Deposits.sol b/contracts/orderbook/OrderVault/Deposits.sol index b20c69b1..befb7748 100644 --- a/contracts/orderbook/OrderVault/Deposits.sol +++ b/contracts/orderbook/OrderVault/Deposits.sol @@ -1,54 +1,84 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; import "@openzeppelin-4.3.2/access/Ownable.sol"; import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; contract Deposits is Ownable { - mapping(address => mapping(uint256 => uint256)) storedBalance; - mapping(address => mapping(uint256 => address)) correctTokenAddress; - address public OrderBook = address(0); + struct DepositInfo { + address depositToken; + uint256 depositAmount; + } + mapping(bytes32 => DepositInfo) internal _depositInfo; + address public OrderBook = address(0); + function deposit( - uint256 orderID, - uint256 TokenAmount, + bytes32 orderID, + uint256 tokenAmount, address trader, address token ) public { - require(msg.sender==OrderBook,"unauthorized"); - storedBalance[trader][orderID] += TokenAmount; - correctTokenAddress[trader][orderID] = token; + require(msg.sender == OrderBook, "unauthorized"); + _depositInfo[orderID].depositToken = token; + _depositInfo[orderID].depositAmount = tokenAmount; SafeERC20.safeTransferFrom( IERC20(token), - msg.sender, + trader, address(this), - TokenAmount + tokenAmount ); } - function SetOrderBook(address n) public onlyOwner{ - OrderBook=n; - } - function withdraw(address trader, uint256 orderID) public { - require(msg.sender==OrderBook,"unauthorized"); + + function SetOrderBook(address n) public onlyOwner { + OrderBook = n; + } + + function withdraw(address trader, bytes32 orderID) public { + require(msg.sender == OrderBook, "unauthorized"); SafeERC20.safeTransfer( - IERC20(correctTokenAddress[trader][orderID]), + IERC20(_depositInfo[orderID].depositToken), msg.sender, - storedBalance[trader][orderID] + _depositInfo[orderID].depositAmount + ); + _depositInfo[orderID].depositAmount = 0; + } + + function withdrawToTrader(address trader, bytes32 orderID) public { + require(msg.sender == OrderBook, "unauthorized"); + SafeERC20.safeTransfer( + IERC20(_depositInfo[orderID].depositToken), + msg.sender, + _depositInfo[orderID].depositAmount + ); + _depositInfo[orderID].depositAmount = 0; + } + + function partialWithdraw( + address trader, + bytes32 orderID, + uint256 amount + ) public { + require(msg.sender == OrderBook, "unauthorized"); + SafeERC20.safeTransfer( + IERC20(_depositInfo[orderID].depositToken), + trader, + amount ); - storedBalance[trader][orderID] = 0; + _depositInfo[orderID].depositAmount -= amount; } - function getDeposit(address trader, uint256 orderID) + function getDeposit(address trader, bytes32 orderID) public view returns (uint256) { - return storedBalance[trader][orderID]; + return _depositInfo[orderID].depositAmount; } - function getTokenUsed(address trader, uint256 orderID) + function getTokenUsed(address trader, bytes32 orderID) public view returns (address) { - return correctTokenAddress[trader][orderID]; + return _depositInfo[orderID].depositToken; } } diff --git a/contracts/orderbook/OrderVault/IDeposits.sol b/contracts/orderbook/OrderVault/IDeposits.sol index f7a2f225..f9f5bdae 100644 --- a/contracts/orderbook/OrderVault/IDeposits.sol +++ b/contracts/orderbook/OrderVault/IDeposits.sol @@ -1,21 +1,29 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; interface IDeposits { function deposit( - uint256 orderID, + bytes32 orderID, uint256 TokenAmount, address trader, address token ) external; - function withdraw(address trader, uint256 orderID) external; + function withdraw(address trader, bytes32 orderID) external; - function getDeposit(address trader, uint256 orderID) + function withdrawToTrader(address trader, bytes32 orderID) external; + + function partialWithdraw( + address trader, + bytes32 orderID, + uint256 amount + ) external; + + function getDeposit(address trader, bytes32 orderID) external view returns (uint256); - function getTokenUsed(address trader, uint256 orderID) + function getTokenUsed(address trader, bytes32 orderID) external view returns (address); diff --git a/contracts/orderbook/Storage/OrderBookEvents.sol b/contracts/orderbook/Storage/OrderBookEvents.sol index c3b0d285..e800d679 100644 --- a/contracts/orderbook/Storage/OrderBookEvents.sol +++ b/contracts/orderbook/Storage/OrderBookEvents.sol @@ -1,42 +1,31 @@ -pragma solidity ^0.8.4; -import "../Enumerates/EnumLimits.sol"; -import "../Enumerates/EnumTraders.sol"; -import "../Enumerates/EnumOrders.sol"; -import "../IWalletFactor.sol"; +pragma solidity ^0.8.0; +import "../IOrderBook.sol"; import "@openzeppelin-4.3.2/access/Ownable.sol"; - +import "@openzeppelin-4.3.2/utils/structs/EnumerableSet.sol"; contract OrderBookEvents is Ownable { - using OrderRecords for OrderRecords.orderSet; - mapping(address => bool) internal hasSmartWallet; - mapping(address => address) internal smartWalletOwnership; - mapping(address => bool) internal isSmartWallet; - mapping(address => mapping(uint256 => IWalletFactory.OpenOrder)) - internal HistoricalOrders; - mapping(uint256 => IWalletFactory.OrderQueue) AllOrders; - mapping(address => mapping(uint256 => uint256)) internal orderExpiration; - mapping(address => OrderRecords.orderSet) internal HistOrders; - mapping(address => OrderEntry.orderSet) internal ActiveTrades; - mapping(address => uint256) internal HistoricalOrderIDs; - mapping(address => mapping(uint256 => uint256)) internal matchingID; - mapping(bytes32 => address) internal loanIDOwnership; - mapping(address => mapping(address => uint256)) internal allocatedBalances; - OrderRecords.orderSet internal AllOrderIDs; - ActiveTraders.orderSet internal activeTraders; - event OrderCancelled(address indexed smartWallet, uint256 nonce); + using EnumerableSet for EnumerableSet.Bytes32Set; + mapping(bytes32 => IOrderBook.OpenOrder) internal _allOrders; + mapping(bytes32 => uint256) internal _orderExpiration; + mapping(address => EnumerableSet.Bytes32Set) internal _histOrders; + mapping(address => EnumerableSet.Bytes32Set) internal _activeTrades; + mapping(address => bool) internal _useOracle; + EnumerableSet.Bytes32Set internal _allOrderIDs; + + event OrderCancelled(address indexed trader, bytes32 orderID); event OrderPlaced( - address indexed smartWallet, - IWalletFactory.OrderType indexed OrderType, + address indexed trader, + IOrderBook.OrderType indexed OrderType, uint256 indexed execPrice, - uint256 orderID, + bytes32 orderID, address collateralTokenAddress, address loanTokenAddress ); - event OrderExecuted(address indexed smartWallet, uint256 nonce); + event OrderExecuted(address indexed trader, bytes32 orderID); event OrderAmended( - address indexed smartWallet, - IWalletFactory.OrderType indexed OrderType, + address indexed trader, + IOrderBook.OrderType indexed OrderType, uint256 indexed execPrice, - uint256 orderID, + bytes32 orderID, address collateralTokenAddress, address loanTokenAddress ); diff --git a/contracts/orderbook/Storage/OrderBookStorage.sol b/contracts/orderbook/Storage/OrderBookStorage.sol index 66c47640..cbdd2b53 100644 --- a/contracts/orderbook/Storage/OrderBookStorage.sol +++ b/contracts/orderbook/Storage/OrderBookStorage.sol @@ -1,23 +1,26 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; import "../WrappedToken.sol"; -import "../bZxInterfaces/IPriceFeeds.sol"; -import "../bZxInterfaces/ILoanToken.sol"; -import "../bZxInterfaces/IBZX.sol"; +import "../../../interfaces/IPriceFeeds.sol"; +import "../../../interfaces/IToken.sol"; +import "../../../interfaces/IBZX.sol"; import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; contract OrderBookStorage { - address internal vault = address(0); - address internal bZxRouterAddress = address(0); - address internal walletGen; - address internal constant wrapToken = + mapping(bytes4 => address) public logicTargets; + address public vault = address(0); + address public protocol = address(0); + address public constant WRAPPED_TOKEN = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; - address internal smartWalletLogic; - address internal constant UniFactoryContract = + address public constant UNI_FACTORY = 0xc35DADB65012eC5796536bD9864eD8773aBc74C4; - uint256 internal mainOBID = 0; - uint256 internal DAYS_14 = 86400 * 14; - uint256 internal MIN_AMOUNT_IN_USDC = 1*10**15; - address internal USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; + uint256 public mainOBID = 0; + uint256 public DAYS_14 = 86400 * 14; + uint256 public MIN_AMOUNT_IN_USDC = 1 * 10**15; + address public constant USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; + + function _setTarget(bytes4 sig, address target) internal { + logicTargets[sig] = target; + } } diff --git a/contracts/orderbook/WrappedToken.sol b/contracts/orderbook/WrappedToken.sol index 41fb03ef..22e4d4c1 100644 --- a/contracts/orderbook/WrappedToken.sol +++ b/contracts/orderbook/WrappedToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; abstract contract WrappedToken { function deposit() public payable virtual; diff --git a/contracts/orderbook/bZxInterfaces/IBZX.sol b/contracts/orderbook/bZxInterfaces/IBZX.sol deleted file mode 100644 index 727c430d..00000000 --- a/contracts/orderbook/bZxInterfaces/IBZX.sol +++ /dev/null @@ -1,83 +0,0 @@ -pragma solidity ^0.8.4; - -abstract contract IBZX { - enum LoanType { - All, - Margin, - NonMargin - } - struct Loan { - bytes32 id; // id of the loan - bytes32 loanParamsId; // the linked loan params id - bytes32 pendingTradesId; // the linked pending trades id - uint256 principal; // total borrowed amount outstanding - uint256 collateral; // total collateral escrowed for the loan - uint256 startTimestamp; // loan start time - uint256 endTimestamp; // for active loans, this is the expected loan end time, for in-active loans, is the actual (past) end time - uint256 startMargin; // initial margin when the loan opened - uint256 startRate; // reference rate when the loan opened for converting collateralToken to loanToken - address borrower; // borrower of this loan - address lender; // lender of this loan - bool active; // if false, the loan has been fully closed - } - mapping(bytes32 => Loan) public loans; - struct LoanReturnData { - bytes32 loanId; // id of the loan - uint96 endTimestamp; // loan end timestamp - address loanToken; // loan token address - address collateralToken; // collateral token address - uint256 principal; // principal amount of the loan - uint256 collateral; // collateral amount of the loan - uint256 interestOwedPerDay; // interest owned per day - uint256 interestDepositRemaining; // remaining unspent interest - uint256 startRate; // collateralToLoanRate - uint256 startMargin; // margin with which loan was open - uint256 maintenanceMargin; // maintenance margin - uint256 currentMargin; /// current margin - uint256 maxLoanTerm; // maximum term of the loan - uint256 maxLiquidatable; // current max liquidatable - uint256 maxSeizable; // current max seizable - uint256 depositValueAsLoanToken; // net value of deposit denominated as loanToken - uint256 depositValueAsCollateralToken; // net value of deposit denominated as collateralToken - } - - function closeWithSwap( - bytes32 loanId, - address receiver, - uint256 swapAmount, // denominated in collateralToken - bool returnTokenIsCollateral, // true: withdraws collateralToken, false: withdraws loanToken - bytes calldata loanDataBytes - ) - external - virtual - returns ( - uint256 loanCloseAmount, - uint256 withdrawAmount, - address withdrawToken - ); - - function getUserLoans( - address user, - uint256 start, - uint256 count, - LoanType loanType, - bool isLender, - bool unsafeOnly - ) external view virtual returns (LoanReturnData[] memory loansData); - - function getUserLoansCount(address user, bool isLender) - external - view - virtual - returns (uint256); - - function getLoan(bytes32 loanId) - external - view - virtual - returns (LoanReturnData memory loanData); - - function isLoanPool(address loanPool) external view virtual returns (bool); - - function supportedTokens(address Token) external virtual view returns (bool); -} diff --git a/contracts/orderbook/bZxInterfaces/ILoanToken.sol b/contracts/orderbook/bZxInterfaces/ILoanToken.sol deleted file mode 100644 index 42e2e981..00000000 --- a/contracts/orderbook/bZxInterfaces/ILoanToken.sol +++ /dev/null @@ -1,20 +0,0 @@ -pragma solidity ^0.8.4; - -abstract contract LoanTokenI { - struct LoanOpenData { - bytes32 LoanId; - uint256 principal; - uint256 collateral; - } - address public loanTokenAddress; - - function marginTrade( - bytes32 loanId, - uint256 leverageAmount, - uint256 loanTokenSent, - uint256 collateralTokenSent, - address collateralTokenAddress, - address trader, - bytes memory loanDataBytes - ) external payable virtual returns (LoanOpenData memory); -} diff --git a/contracts/orderbook/bZxInterfaces/IPriceFeeds.sol b/contracts/orderbook/bZxInterfaces/IPriceFeeds.sol deleted file mode 100644 index 5484b845..00000000 --- a/contracts/orderbook/bZxInterfaces/IPriceFeeds.sol +++ /dev/null @@ -1,75 +0,0 @@ -pragma solidity ^0.8.4; - -interface IPriceFeeds { - function queryRate(address sourceToken, address destToken) - external - view - returns (uint256 rate, uint256 precision); - - function queryPrecision(address sourceToken, address destToken) - external - view - returns (uint256 precision); - - function queryReturn( - address sourceToken, - address destToken, - uint256 sourceAmount - ) external view returns (uint256 destAmount); - - function checkPriceDisagreement( - address sourceToken, - address destToken, - uint256 sourceAmount, - uint256 destAmount, - uint256 maxSlippage - ) external view returns (uint256 sourceToDestSwapRate); - - function amountInEth(address Token, uint256 amount) - external - view - returns (uint256 ethAmount); - - function getMaxDrawdown( - address loanToken, - address collateralToken, - uint256 loanAmount, - uint256 collateralAmount, - uint256 maintenanceMargin - ) external view returns (uint256); - - function getCurrentMarginAndCollateralSize( - address loanToken, - address collateralToken, - uint256 loanAmount, - uint256 collateralAmount - ) - external - view - returns (uint256 currentMargin, uint256 collateralInEthAmount); - - function getCurrentMargin( - address loanToken, - address collateralToken, - uint256 loanAmount, - uint256 collateralAmount - ) - external - view - returns (uint256 currentMargin, uint256 collateralToLoanRate); - - function shouldLiquidate( - address loanToken, - address collateralToken, - uint256 loanAmount, - uint256 collateralAmount, - uint256 maintenanceMargin - ) external view returns (bool); - - function getFastGasPrice(address payToken) external view returns (uint256); -} - -abstract contract StateI { - address public priceFeeds; - address public swapsImpl; -} From 1e97bf397c6f1c2a512f511d4b8dbac56b846dec Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Tue, 11 Jan 2022 18:17:16 -0800 Subject: [PATCH 32/83] interface version changes --- interfaces/IBZx.sol | 2 +- interfaces/IPriceFeeds.sol | 2 +- interfaces/IToken.sol | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interfaces/IBZx.sol b/interfaces/IBZx.sol index c45e868d..52f6b951 100644 --- a/interfaces/IBZx.sol +++ b/interfaces/IBZx.sol @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. */ // SPDX-License-Identifier: Apache License, Version 2.0. -pragma solidity >=0.5.0 <=0.8.4; +pragma solidity >=0.5.0 <=0.8.11; pragma experimental ABIEncoderV2; /// @title A proxy interface for The Protocol diff --git a/interfaces/IPriceFeeds.sol b/interfaces/IPriceFeeds.sol index c11a1e04..bd65d4ea 100644 --- a/interfaces/IPriceFeeds.sol +++ b/interfaces/IPriceFeeds.sol @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. */ -pragma solidity >=0.5.0 <=0.8.4; +pragma solidity >=0.5.0 <=0.8.11; interface IPriceFeeds { diff --git a/interfaces/IToken.sol b/interfaces/IToken.sol index 4fb07b2a..4aca6470 100644 --- a/interfaces/IToken.sol +++ b/interfaces/IToken.sol @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. */ -pragma solidity >=0.5.0 <=0.8.4; +pragma solidity >=0.5.0 <=0.8.11; pragma experimental ABIEncoderV2; // import "@openzeppelin-3.4.0/token/ERC20/IERC20.sol"; From 321b42230bc869bf411e52906e005e6a462b6607 Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Wed, 12 Jan 2022 19:46:37 -0800 Subject: [PATCH 33/83] Format Compliant Calls --- contracts/swaps/DexRecords.sol | 4 + contracts/swaps/ISwapsImpl.sol | 11 +- .../swaps/connectors/SwapsImplCurve_ETH.sol | 26 +++ .../connectors/SwapsImplUniswapV2_ETH.sol | 18 ++ .../connectors/SwapsImplUniswapV2_POLYGON.sol | 36 +++- .../connectors/SwapsImplUniswapV3_ETH.sol | 194 ++++++++++++++---- 6 files changed, 231 insertions(+), 58 deletions(-) diff --git a/contracts/swaps/DexRecords.sol b/contracts/swaps/DexRecords.sol index 637bdf9f..024a5852 100644 --- a/contracts/swaps/DexRecords.sol +++ b/contracts/swaps/DexRecords.sol @@ -14,4 +14,8 @@ contract DexRecords is Ownable { dexCount++; dexes[dexCount] = dex; } + + function setDexID(uint256 ID, address dex) public onlyOwner { + dexes[dexCount] = dex; + } } diff --git a/contracts/swaps/ISwapsImpl.sol b/contracts/swaps/ISwapsImpl.sol index 6823081c..f2ebb09f 100644 --- a/contracts/swaps/ISwapsImpl.sol +++ b/contracts/swaps/ISwapsImpl.sol @@ -32,10 +32,17 @@ interface ISwapsImpl { external returns (uint256 amountOut, address midToken); + function dexAmountOutFormatted(bytes calldata route, uint256 amountOut) + external + returns (uint256 amountIn, address midToken); + function dexAmountIn(bytes calldata route, uint256 amountOut) external returns (uint256 amountIn, address midToken); - function setSwapApprovals(address[] calldata tokens) - external; + function dexAmountInFormatted(bytes calldata route, uint256 amountOut) + external + returns (uint256 amountIn, address midToken); + + function setSwapApprovals(address[] calldata tokens) external; } diff --git a/contracts/swaps/connectors/SwapsImplCurve_ETH.sol b/contracts/swaps/connectors/SwapsImplCurve_ETH.sol index 40bb6aa4..46dce91e 100644 --- a/contracts/swaps/connectors/SwapsImplCurve_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplCurve_ETH.sol @@ -92,6 +92,17 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { } } + function dexAmountOutFormatted(bytes memory route, uint256 amountIn) + public + returns (uint256 amountOut, address midToken) + { + if (amountIn == 0) { + amountOut = 0; + } else if (amountIn != 0) { + amountOut = _getAmountOut(amountIn, route); + } + } + function dexAmountIn(bytes memory route, uint256 amountOut) public returns (uint256 amountIn, address midToken) @@ -107,6 +118,21 @@ contract SwapsImplCurve_ETH is State, ISwapsImpl { } } + function dexAmountInFormatted(bytes memory route, uint256 amountOut) + public + returns (uint256 amountIn, address midToken) + { + if (amountOut != 0) { + amountIn = _getAmountIn(amountOut, route); + + if (amountIn == uint256(-1)) { + amountIn = 0; + } + } else { + amountIn = 0; + } + } + function _getAmountOut(uint256 amountIn, bytes memory path) public returns (uint256) diff --git a/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol b/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol index d1f81a9c..52ea0d97 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV2_ETH.sol @@ -133,6 +133,15 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { } } + function dexAmountOutFormatted( + bytes memory payload, + uint256 amountIn) + public + returns (uint256 amountOut, address midToken) + { + return dexAmountOut(payload, amountIn); + } + function dexAmountIn( bytes memory payload, uint256 amountOut @@ -197,6 +206,15 @@ contract SwapsImplUniswapV2_ETH is State, ISwapsImpl { } } + function dexAmountInFormatted( + bytes memory payload, + uint256 amountOut) + public + returns (uint256 amountIn, address midToken) + { + return dexAmountIn(payload, amountOut); + } + function _getAmountOut(uint256 amountIn, address[] memory path) public view diff --git a/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol b/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol index 29b7e218..c252cc49 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV2_POLYGON.sol @@ -69,13 +69,12 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } function dexAmountOut( - address sourceTokenAddress, - address destTokenAddress, + bytes memory payload, uint256 amountIn) public - view returns (uint256 amountOut, address midToken) { + (address sourceTokenAddress, address destTokenAddress) = abi.decode(payload, (address, address)); if (sourceTokenAddress == destTokenAddress) { amountOut = amountIn; } else if (amountIn != 0) { @@ -137,15 +136,23 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } } + function dexAmountOutFormatted( + bytes memory payload, + uint256 amountIn) + public + returns (uint256 amountOut, address midToken) + { + return dexAmountOut(payload, amountIn); + } + function dexAmountIn( - address sourceTokenAddress, - address destTokenAddress, + bytes memory payload, uint256 amountOut) public - view returns (uint256 amountIn, address midToken) { - if (sourceTokenAddress == destTokenAddress) { + (address sourceTokenAddress, address destTokenAddress) = abi.decode(payload, (address, address)); + if (sourceTokenAddress == destTokenAddress) { amountIn = amountOut; } else if (amountOut != 0) { uint256 tmpValue; @@ -210,6 +217,15 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } } + function dexAmountInFormatted( + bytes memory payload, + uint256 amountOut) + public + returns (uint256 amountIn, address midToken) + { + return dexAmountIn(payload, amountOut); + } + function _getAmountOut( uint256 amountIn, address[] memory path) @@ -280,8 +296,7 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { address midToken; if (requiredDestTokenAmount != 0) { (sourceTokenAmountUsed, midToken) = dexAmountIn( - sourceTokenAddress, - destTokenAddress, + abi.encode(sourceTokenAddress, destTokenAddress), requiredDestTokenAmount ); if (sourceTokenAmountUsed == 0) { @@ -291,8 +306,7 @@ contract SwapsImplUniswapV2_POLYGON is State, ISwapsImpl { } else { sourceTokenAmountUsed = minSourceTokenAmount; (destTokenAmountReceived, midToken) = dexAmountOut( - sourceTokenAddress, - destTokenAddress, + abi.encode(sourceTokenAddress, destTokenAddress), sourceTokenAmountUsed ); if (destTokenAmountReceived == 0) { diff --git a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol index ee131ed8..70037d64 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol @@ -84,6 +84,42 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { } } + function dexAmountOutFormatted(bytes memory payload, uint256 amountIn) + public + returns (uint256 amountOut, address midToken) + { + IUniswapV3SwapRouter.ExactInputParams[] memory exactParams = abi.decode( + payload, + (IUniswapV3SwapRouter.ExactInputParams[]) + ); + uint256 totalAmounts = 0; + for ( + uint256 uniqueInputParam = 0; + uniqueInputParam < exactParams.length; + uniqueInputParam++ + ) { + exactParams[uniqueInputParam].amountIn = exactParams[ + uniqueInputParam + ].amountIn.mul(amountIn).div(100); //amountIn on data is % of funds to use per route. Total should add to source token amount or else it fails. take into consideration rounding + totalAmounts = totalAmounts.add( + exactParams[uniqueInputParam].amountIn + ); + } + if (totalAmounts < amountIn) { + exactParams[0].amountIn = exactParams[0].amountIn.add( + amountIn.sub(totalAmounts) + ); //adds displacement to first swap set + } + uint256 tempAmountOut; + for (uint256 i = 0; i < exactParams.length; i++) { + (tempAmountOut, ) = dexAmountOut( + exactParams[i].path, + exactParams[i].amountIn + ); + amountOut = amountOut.add(tempAmountOut); + } + } + function dexAmountIn(bytes memory route, uint256 amountOut) public returns (uint256 amountIn, address midToken) @@ -99,14 +135,46 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { } } + function dexAmountInFormatted(bytes memory payload, uint256 amountOut) + public + returns (uint256 amountIn, address midToken) + { + IUniswapV3SwapRouter.ExactOutputParams[] memory exactParams = abi + .decode(payload, (IUniswapV3SwapRouter.ExactOutputParams[])); + uint256 totalAmounts = 0; + for ( + uint256 uniqueOutputParam = 0; + uniqueOutputParam < exactParams.length; + uniqueOutputParam++ + ) { + exactParams[uniqueOutputParam].amountOut = exactParams[ + uniqueOutputParam + ].amountOut.mul(amountOut).div(100); //amountOut on data is % of funds to use per route. Total should add to source token amount or else it fails. take into consideration rounding + totalAmounts = totalAmounts.add( + exactParams[uniqueOutputParam].amountOut + ); + } + if (totalAmounts < amountOut) { + exactParams[0].amountOut = exactParams[0].amountOut.add( + amountOut.sub(totalAmounts) + ); //adds displacement to first swap set + } + uint256 tempAmountIn; + for (uint256 i = 0; i < exactParams.length; i++) { + (tempAmountIn, ) = dexAmountIn( + exactParams[i].path, + exactParams[i].amountOut + ); + amountOut.add(tempAmountIn); + } + } + function _getAmountOut(uint256 amountIn, bytes memory path) public returns (uint256) { - uint256 amountOut = IUniswapQuoter(uniswapQuoteContract).quoteExactInput( - path, - amountIn - ); + uint256 amountOut = IUniswapQuoter(uniswapQuoteContract) + .quoteExactInput(path, amountIn); if (amountOut == 0) { amountOut = uint256(-1); } @@ -117,10 +185,8 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { public returns (uint256) { - uint256 amountIn = IUniswapQuoter(uniswapQuoteContract).quoteExactOutput( - path, - amountOut - ); + uint256 amountIn = IUniswapQuoter(uniswapQuoteContract) + .quoteExactOutput(path, amountOut); if (amountIn == 0) { amountIn = uint256(-1); } @@ -152,9 +218,17 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { bytes[] memory encodedTXs = new bytes[](exactParams.length); uint256 totalAmountsOut = 0; uint256 totalAmountsInMax = 0; - for (uint256 uniqueOutputParam = 0; uniqueOutputParam < exactParams.length; uniqueOutputParam++) { - require(receiverAddress == exactParams[uniqueOutputParam].recipient); - address tokenIn = exactParams[uniqueOutputParam].path.toAddress(0); + for ( + uint256 uniqueOutputParam = 0; + uniqueOutputParam < exactParams.length; + uniqueOutputParam++ + ) { + require( + receiverAddress == exactParams[uniqueOutputParam].recipient + ); + address tokenIn = exactParams[uniqueOutputParam].path.toAddress( + 0 + ); require(tokenIn == destTokenAddress, "improper destination"); require( exactParams[uniqueOutputParam].path.toAddress( @@ -162,13 +236,17 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { ) == sourceTokenAddress, "improper source" ); - exactParams[uniqueOutputParam].amountOut = requiredDestTokenAmount + exactParams[uniqueOutputParam] + .amountOut = requiredDestTokenAmount .mul(exactParams[uniqueOutputParam].amountOut) .div(100); - exactParams[uniqueOutputParam].amountInMaximum = maxSourceTokenAmount + exactParams[uniqueOutputParam] + .amountInMaximum = maxSourceTokenAmount .mul(exactParams[uniqueOutputParam].amountInMaximum) .div(100); - totalAmountsOut = totalAmountsOut.add(exactParams[uniqueOutputParam].amountOut); + totalAmountsOut = totalAmountsOut.add( + exactParams[uniqueOutputParam].amountOut + ); totalAmountsInMax = totalAmountsInMax.add( exactParams[uniqueOutputParam].amountInMaximum ); @@ -180,22 +258,26 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { exactParams[uniqueOutputParam] ); } - if(totalAmountsOut Date: Wed, 12 Jan 2022 19:49:15 -0800 Subject: [PATCH 34/83] Update DexRecords.sol --- contracts/swaps/DexRecords.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/swaps/DexRecords.sol b/contracts/swaps/DexRecords.sol index 024a5852..7db8a1fb 100644 --- a/contracts/swaps/DexRecords.sol +++ b/contracts/swaps/DexRecords.sol @@ -16,6 +16,6 @@ contract DexRecords is Ownable { } function setDexID(uint256 ID, address dex) public onlyOwner { - dexes[dexCount] = dex; + dexes[ID] = dex; } } From 8f60b2c69bd50a9e1c90968c605cc7c31a0f0ac6 Mon Sep 17 00:00:00 2001 From: Drypto | OOKI <51417606+Drypto13@users.noreply.github.com> Date: Wed, 12 Jan 2022 23:58:21 -0800 Subject: [PATCH 35/83] Update SwapsExternal.sol --- contracts/modules/SwapsExternal/SwapsExternal.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/modules/SwapsExternal/SwapsExternal.sol b/contracts/modules/SwapsExternal/SwapsExternal.sol index f4559d1f..5a6e4678 100644 --- a/contracts/modules/SwapsExternal/SwapsExternal.sol +++ b/contracts/modules/SwapsExternal/SwapsExternal.sol @@ -116,7 +116,7 @@ contract SwapsExternal is State, VaultController, SwapsUser, PausableGuardian { address sourceToken, address destToken, uint256 sourceTokenAmount, - bytes calldata payload) + bytes calldata payload) external returns (uint256) { @@ -124,7 +124,7 @@ contract SwapsExternal is State, VaultController, SwapsUser, PausableGuardian { sourceToken, destToken, sourceTokenAmount, - payload + payload ); } } From 929999e1534b06dc6e18187925507651b5b6a9a3 Mon Sep 17 00:00:00 2001 From: Drypto | OOKI <51417606+Drypto13@users.noreply.github.com> Date: Wed, 12 Jan 2022 23:58:58 -0800 Subject: [PATCH 36/83] Update SwapsUser.sol --- contracts/swaps/SwapsUser.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/swaps/SwapsUser.sol b/contracts/swaps/SwapsUser.sol index d24b627d..cb9a7aac 100644 --- a/contracts/swaps/SwapsUser.sol +++ b/contracts/swaps/SwapsUser.sol @@ -260,7 +260,7 @@ contract SwapsUser is State, SwapsEvents, FeesHelper, Flags { dexNumber ); - (expectedReturn, ) = ISwapsImpl(swapImplAddress).dexAmountOut( + (expectedReturn, ) = ISwapsImpl(swapImplAddress).dexAmountOutFormatted( dataToSend, sourceTokenAmount ); From c6dc85ec4ce4ae8c34978d1368957bc6e128009a Mon Sep 17 00:00:00 2001 From: Drypto | OOKI <51417606+Drypto13@users.noreply.github.com> Date: Thu, 13 Jan 2022 00:00:45 -0800 Subject: [PATCH 37/83] Formatting --- contracts/modules/LoanOpenings/LoanOpenings.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/modules/LoanOpenings/LoanOpenings.sol b/contracts/modules/LoanOpenings/LoanOpenings.sol index 614d5e92..97d0fb62 100644 --- a/contracts/modules/LoanOpenings/LoanOpenings.sol +++ b/contracts/modules/LoanOpenings/LoanOpenings.sol @@ -113,7 +113,7 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestHan uint256 collateralTokenSent, uint256 /*interestRate*/, uint256 /*newPrincipal*/, - bytes calldata payload) + bytes calldata payload) external returns (uint256 value) { @@ -121,7 +121,7 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestHan loanToken, collateralToken, loanTokenSent, - payload + payload ); if (value != 0) { return collateralTokenSent @@ -590,4 +590,4 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestHan .add(collateralTokenAmount); } } -} \ No newline at end of file +} From dfff2b663041d162e9a63f539a015694ff59f1e5 Mon Sep 17 00:00:00 2001 From: Drypto/ICantThinkofAName Date: Sun, 23 Jan 2022 23:02:55 -0800 Subject: [PATCH 38/83] Re-design on order storage --- contracts/orderbook/IOrderBook.sol | 8 +- .../orderbook/Keepers/OrderBookInterface.sol | 20 +- contracts/orderbook/Keepers/OrderKeeper.sol | 20 +- .../orderbook/Keepers/OrderKeeperClear.sol | 15 +- contracts/orderbook/Logic/OrderBook.sol | 623 ++++++++---------- contracts/orderbook/Logic/OrderBookData.sol | 57 +- .../orderbook/Logic/OrderBookMarketOrders.sol | 14 +- .../orderbook/Logic/OrderBookOrderPlace.sol | 108 +-- .../orderbook/Logic/UniswapInterfaces.sol | 23 - contracts/orderbook/Logic/dexSwaps.sol | 21 - contracts/orderbook/OrderBookProxy.sol | 50 +- .../orderbook/Storage/OrderBookEvents.sol | 3 +- .../orderbook/Storage/OrderBookStorage.sol | 10 +- 13 files changed, 407 insertions(+), 565 deletions(-) delete mode 100644 contracts/orderbook/Logic/UniswapInterfaces.sol delete mode 100644 contracts/orderbook/Logic/dexSwaps.sol diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index ddd0c2ee..770e052e 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -6,12 +6,12 @@ interface IOrderBook { LIMIT_CLOSE, MARKET_STOP } - struct OpenOrder { + struct Order { address trader; bytes32 loanID; address iToken; address loanTokenAddress; - uint256 price; + uint256 amountReceived; uint256 leverage; uint256 loanTokenAmount; uint256 collateralTokenAmount; @@ -25,9 +25,9 @@ interface IOrderBook { function getRouter() external view returns (address); - function placeOrder(OpenOrder calldata Order) external; + function placeOrder(Order calldata order) external; - function amendOrder(OpenOrder calldata Order, uint256 orderID) external; + function amendOrder(Order calldata order, uint256 orderID) external; function cancelOrder(uint256 orderID) external; diff --git a/contracts/orderbook/Keepers/OrderBookInterface.sol b/contracts/orderbook/Keepers/OrderBookInterface.sol index 389096c1..bbea7bb3 100644 --- a/contracts/orderbook/Keepers/OrderBookInterface.sol +++ b/contracts/orderbook/Keepers/OrderBookInterface.sol @@ -1,27 +1,15 @@ pragma solidity ^0.8.0; interface IOrderBook { - function getOrders() - external - view - returns (OpenOrder[] memory); + function getOrders() external view returns (OpenOrder[] memory); - function prelimCheck(bytes32 orderID) - external - view - returns (bool); + function prelimCheck(bytes32 orderID) external view returns (bool); - function executeOrder( - address payable keeper, - bytes32 orderID - ) external; + function executeOrder(address payable keeper, bytes32 orderID) external; function cancelOrderProtocol(bytes32 orderID) external; - function clearOrder(bytes32 orderID) - external - view - returns (bool); + function clearOrder(bytes32 orderID) external view returns (bool); struct OpenOrder { address trader; diff --git a/contracts/orderbook/Keepers/OrderKeeper.sol b/contracts/orderbook/Keepers/OrderKeeper.sol index 412da492..7e68fdc4 100644 --- a/contracts/orderbook/Keepers/OrderKeeper.sol +++ b/contracts/orderbook/Keepers/OrderKeeper.sol @@ -22,15 +22,9 @@ contract OrderKeeper { IOrderBook.OpenOrder[] memory listOfMainOrders = IOrderBook(factory) .getOrders(); for (uint256 x = 0; x < listOfMainOrders.length; x++) { - if ( - IOrderBook(factory).prelimCheck( - listOfMainOrders[x].orderID - ) - ) { + if (IOrderBook(factory).prelimCheck(listOfMainOrders[x].orderID)) { upkeepNeeded = true; - performData = abi.encode( - listOfMainOrders[x].orderID - ); + performData = abi.encode(listOfMainOrders[x].orderID); return (upkeepNeeded, performData); } } @@ -38,15 +32,9 @@ contract OrderKeeper { } function performUpkeep(bytes calldata performData) public { - (bytes32 orderId) = abi.decode( - performData, - (bytes32) - ); + bytes32 orderId = abi.decode(performData, (bytes32)); //emit OrderExecuted(trader,orderId); - IOrderBook(factory).executeOrder( - payable(address(this)), - orderId - ); + IOrderBook(factory).executeOrder(payable(address(this)), orderId); } /*function handleFees(address[] memory tokenAddress) public { diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol index cd2a6997..3302576d 100644 --- a/contracts/orderbook/Keepers/OrderKeeperClear.sol +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -19,15 +19,9 @@ contract OrderKeeperClear { IOrderBook.OpenOrder[] memory listOfMainOrders = IOrderBook(factory) .getOrders(); for (uint256 x = 0; x < listOfMainOrders.length; x++) { - if ( - IOrderBook(factory).clearOrder( - listOfMainOrders[x].orderID - ) - ) { + if (IOrderBook(factory).clearOrder(listOfMainOrders[x].orderID)) { upkeepNeeded = true; - performData = abi.encode( - listOfMainOrders[x].orderID - ); + performData = abi.encode(listOfMainOrders[x].orderID); return (upkeepNeeded, performData); } } @@ -35,10 +29,7 @@ contract OrderKeeperClear { } function performUpkeep(bytes calldata performData) public { - (bytes32 orderId) = abi.decode( - performData, - (bytes32) - ); + bytes32 orderId = abi.decode(performData, (bytes32)); //emit OrderExecuted(trader,orderId); IOrderBook(factory).cancelOrderProtocol(orderId); } diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index 5f5d1299..a7fe62c4 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -1,38 +1,32 @@ pragma solidity ^0.8.0; import "../Storage/OrderBookEvents.sol"; import "../Storage/OrderBookStorage.sol"; -import "./dexSwaps.sol"; -import "./UniswapInterfaces.sol"; +import "../../swaps/ISwapsImpl.sol"; import "../OrderVault/IDeposits.sol"; -import "../../utils/ExponentMath.sol"; import "../../interfaces/IDexRecords.sol"; contract OrderBook is OrderBookEvents, OrderBookStorage { - using ExponentMath for uint256; - using EnumerableSet for EnumerableSet.Bytes32Set; - - function initialize( - address target) - public - onlyOwner - { - _setTarget(this.getSwapAddress.selector, target); - _setTarget(this.currentSwapRate.selector, target); - _setTarget(this.getFeed.selector, target); - _setTarget(this.dexSwapRate.selector, target); - _setTarget(this.clearOrder.selector, target); - _setTarget(this.prelimCheck.selector, target); - _setTarget(this.currentDexRate.selector, target); - _setTarget(this.priceCheck.selector, target); - _setTarget(this.executeOrder.selector, target); - _setTarget(this.setVaultAddress.selector, target); - } - - - function _executeTradeOpen(address trader, bytes32 orderID) internal { - IOrderBook.OpenOrder memory internalOrder = _allOrders[orderID]; - IDeposits(vault).withdraw(trader, orderID); - (bool result, bytes memory data) = _allOrders[orderID].iToken.call( + using EnumerableSet for EnumerableSet.Bytes32Set; + + function initialize(address target) public onlyOwner { + _setTarget(this.getSwapAddress.selector, target); + _setTarget(this.currentSwapRate.selector, target); + _setTarget(this.getFeed.selector, target); + _setTarget(this.getDexRate.selector, target); + _setTarget(this.clearOrder.selector, target); + _setTarget(this.prelimCheck.selector, target); + _setTarget(this.queryRateReturn.selector, target); + _setTarget(this.priceCheck.selector, target); + _setTarget(this.executeOrder.selector, target); + _setTarget(this.setVaultAddress.selector, target); + } + + function _executeTradeOpen( + address trader, + IOrderBook.Order memory internalOrder + ) internal { + IDeposits(vault).withdraw(trader, internalOrder.orderID); + (bool result, bytes memory data) = internalOrder.iToken.call( abi.encodeWithSelector( IToken(internalOrder.iToken).marginTrade.selector, internalOrder.loanID, @@ -98,94 +92,32 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { return IBZx(protocol).loans(ID).active; } - function dexSwapRate(IOrderBook.OpenOrder memory order) - public - view - returns (uint256) - { - uint256 tradeSize; - (uint256 dexID, bytes memory payload) = abi.decode( - order.loanDataBytes, - (uint256, bytes) - ); - if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { - if (order.loanTokenAmount > 0) { - tradeSize = (order.loanTokenAmount * order.leverage) / 1 ether; - } else { - (tradeSize, ) = dexSwaps( - IDexRecords(getSwapAddress()).retrieveDexAddress(dexID) - ).dexAmountOut( - dexID != 1 - ? payload - : abi.encode(order.base, order.loanTokenAddress), - order.collateralTokenAmount - ); - if (tradeSize == 0) { - return 0; - } - tradeSize = (tradeSize * order.leverage) / 1 ether; - } - } - (uint256 fSwapRate, ) = order.orderType == - IOrderBook.OrderType.LIMIT_OPEN - ? dexSwaps(IDexRecords(getSwapAddress()).retrieveDexAddress(dexID)) - .dexAmountOut( - dexID != 1 - ? payload - : abi.encode(order.loanTokenAddress, order.base), - tradeSize - ) - : dexSwaps(IDexRecords(getSwapAddress()).retrieveDexAddress(dexID)) - .dexAmountOut( - dexID != 1 - ? payload - : abi.encode(order.base, order.loanTokenAddress), - order.collateralTokenAmount - ); - if (fSwapRate == 0) { - return 0; - } - return - order.orderType == IOrderBook.OrderType.LIMIT_OPEN - ? (tradeSize.TenExp( - 18 - int8(IERC20Metadata(order.loanTokenAddress).decimals()) - ) * 1 ether) / - ( - fSwapRate.TenExp( - 18 - int8(IERC20Metadata(order.base).decimals()) - ) - ) - : (1 ether * - ( - fSwapRate.TenExp( - 18 - - int8( - IERC20Metadata(order.loanTokenAddress) - .decimals() - ) - ) - )) / - ( - order.collateralTokenAmount.TenExp( - 18 - int8(IERC20Metadata(order.base).decimals()) - ) - ); - } - function clearOrder(bytes32 orderID) public view returns (bool) { if (_orderExpiration[orderID] < block.timestamp) { return true; } - uint256 swapRate = currentSwapRate( - _allOrders[orderID].loanTokenAddress, - _allOrders[orderID].base - ); + uint256 amountUsed = _allOrders[orderID].collateralTokenAmount + + _allOrders[orderID].loanTokenAmount; + uint256 swapRate; + if (_allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN) { + swapRate = queryRateReturn( + _allOrders[orderID].loanTokenAddress, + _allOrders[orderID].base, + amountUsed + ); + } else { + swapRate = queryRateReturn( + _allOrders[orderID].base, + _allOrders[orderID].loanTokenAddress, + amountUsed + ); + } if ( ( - _allOrders[orderID].price > swapRate - ? (_allOrders[orderID].price - swapRate) > - (_allOrders[orderID].price * 25) / 100 - : (swapRate - _allOrders[orderID].price) > + _allOrders[orderID].amountReceived > swapRate + ? (_allOrders[orderID].amountReceived - swapRate) > + (_allOrders[orderID].amountReceived * 25) / 100 + : (swapRate - _allOrders[orderID].amountReceived) > (swapRate * 25) / 100 ) ) { @@ -194,268 +126,238 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { return false; } - function prelimCheck(bytes32 orderID) public view returns (bool) { - IOrderBook.OpenOrder memory order = _allOrders[orderID]; + function queryRateReturn( + address start, + address end, + uint256 amount + ) public view returns (uint256) { + (uint256 executionPrice, uint256 precision) = IPriceFeeds(getFeed()) + .queryRate(start, end); + return (executionPrice * amount) / precision; + } + + function _prepDexAndPayload(bytes memory input) + internal + pure + returns (uint256 dexID, bytes memory payload) + { + if (input.length != 0) { + (dexID, payload) = abi.decode(input, (uint256, bytes)); + } else { + dexID = 1; + } + } + + function prelimCheck(bytes32 orderID) public returns (bool) { + IOrderBook.Order memory order = _allOrders[orderID]; address trader = order.trader; + uint256 amountUsed; + address srcToken; + (uint256 dexID, bytes memory payload) = _prepDexAndPayload( + order.loanDataBytes + ); + if (dexID == 1) { + if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { + payload = abi.encode(order.loanTokenAddress, order.base); + } else { + payload = abi.encode(order.base, order.loanTokenAddress); + } + } + ISwapsImpl swapImpl = ISwapsImpl( + IDexRecords(getSwapAddress()).retrieveDexAddress(dexID) + ); + if (order.collateralTokenAmount > order.loanTokenAmount) { + srcToken = order.base; + } else { + srcToken = order.loanTokenAddress; + } if (_orderExpiration[orderID] < block.timestamp) { return false; } - if ( - order.orderType == IOrderBook.OrderType.LIMIT_OPEN - ) { - if ( - !(order.loanID == 0) && - !isActiveLoan(order.loanID) - ) { + if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { + if (order.loanID != 0 && !isActiveLoan(order.loanID)) { return false; } - uint256 dSwapValue = dexSwapRate(order); + if (srcToken == order.loanTokenAddress) { + amountUsed = (order.loanTokenAmount * order.leverage) / 10**18; //adjusts leverage + } else { + amountUsed = queryRateReturn( + order.base, + order.loanTokenAddress, + order.collateralTokenAmount + ); + amountUsed = (amountUsed * order.leverage) / 10**18; + } + (uint256 dSwapValue, ) = swapImpl.dexAmountOutFormatted( + payload, + amountUsed + ); - if (order.price >= dSwapValue && dSwapValue > 0) { + if (order.amountReceived <= dSwapValue && dSwapValue > 0) { return true; } - } else if ( - order.orderType == - IOrderBook.OrderType.LIMIT_CLOSE - ) { + } else if (order.orderType == IOrderBook.OrderType.LIMIT_CLOSE) { if (!isActiveLoan(order.loanID)) { return false; } - if ( - order.price <= - dexSwapRate(order) - ) { + uint256 dSwapValue; + if (order.isCollateral) { + (dSwapValue, ) = swapImpl.dexAmountOutFormatted( + payload, + order.collateralTokenAmount + ); + } else { + (dSwapValue, ) = swapImpl.dexAmountInFormatted( + payload, + order.collateralTokenAmount + ); + } + if (order.amountReceived <= dSwapValue) { return true; } } else { if (!isActiveLoan(order.loanID)) { return false; } - if ( - _useOracle[trader] - ? order.price >= - currentSwapRate( - order.base, - order.loanTokenAddress - ) - : order.price >= - currentDexRate( - order.base, - order.loanTokenAddress - ) - ) { + bool operand; + if (_useOracle[trader]) { + operand = + order.amountReceived >= + queryRateReturn( + order.base, + order.loanTokenAddress, + order.collateralTokenAmount + ); //TODO: Adjust for precision + } else { + operand = + order.amountReceived >= + getDexRate( + swapImpl, + order.base, + order.loanTokenAddress, + payload, + order.collateralTokenAmount + ); + } + if (operand) { return true; } } return false; } - function currentDexRate(address dest, address src) - public - view - returns (uint256) - { - uint256 dexRate; - if (src == WRAPPED_TOKEN || dest == WRAPPED_TOKEN) { - address pairAddress = UniswapFactory(UNI_FACTORY).getPair( - src, - dest - ); - (uint112 reserve0, uint112 reserve1, ) = UniswapPair(pairAddress) - .getReserves(); - uint256 res0 = uint256(reserve0); - uint256 res1 = uint256(reserve1); - dexRate = UniswapPair(pairAddress).token0() == src - ? ( - res0.TenExp( - 18 - - int8( - IERC20Metadata( - UniswapPair(pairAddress).token0() - ).decimals() - ) + - 18 - ) - ) / - ( - res1.TenExp( - 18 - - int8( - IERC20Metadata( - UniswapPair(pairAddress).token1() - ).decimals() - ) - ) - ) - : (( - res1.TenExp( - 18 - - int8( - IERC20Metadata( - UniswapPair(pairAddress).token1() - ).decimals() - ) + - 18 - ) - ) / res0).TenExp( - 18 - - int8( - IERC20Metadata( - UniswapPair(pairAddress).token0() - ).decimals() - ) - ); - } else { - address pairAddress0 = UniswapFactory(UNI_FACTORY).getPair( - src, - WRAPPED_TOKEN - ); - (uint112 reserve0, uint112 reserve1, ) = UniswapPair(pairAddress0) - .getReserves(); - uint256 res0 = uint256(reserve0); - uint256 res1 = uint256(reserve1); - uint256 midSwapRate = UniswapPair(pairAddress0).token0() == - WRAPPED_TOKEN - ? ( - res1.TenExp( - 18 - - int8( - IERC20Metadata( - UniswapPair(pairAddress0).token1() - ).decimals() - ) + - 18 - ) - ) / - ( - res0.TenExp( - 18 - - int8( - IERC20Metadata( - UniswapPair(pairAddress0).token0() - ).decimals() - ) - ) - ) - : ( - res0.TenExp( - 18 - - int8( - IERC20Metadata( - UniswapPair(pairAddress0).token0() - ).decimals() - ) + - 18 - ) - ) / - ( - res1.TenExp( - 18 - - int8( - IERC20Metadata( - UniswapPair(pairAddress0).token0() - ).decimals() - ) - ) - ); - address pairAddress1 = UniswapFactory(UNI_FACTORY).getPair( - dest, - WRAPPED_TOKEN - ); - (uint112 reserve2, uint112 reserve3, ) = UniswapPair(pairAddress1) - .getReserves(); - uint256 res2 = uint256(reserve2); - uint256 res3 = uint256(reserve3); - dexRate = UniswapPair(pairAddress1).token0() == WRAPPED_TOKEN - ? ((10**36 / - (( - res3.TenExp( - 18 - - int8( - IERC20Metadata( - UniswapPair(pairAddress1).token1() - ).decimals() - ) + - 18 - ) - ) / - ( - res2.TenExp( - 18 - - int8( - IERC20Metadata( - UniswapPair(pairAddress1).token0() - ).decimals() - ) - ) - ))) * midSwapRate) / 10**18 - : ((10**36 / - (( - res2.TenExp( - 18 - - int8( - IERC20Metadata( - UniswapPair(pairAddress1).token0() - ).decimals() - ) + - 18 - ) - ) / - ( - res3.TenExp( - 18 - - int8( - IERC20Metadata( - UniswapPair(pairAddress1).token1() - ).decimals() - ) - ) - ))) * midSwapRate) / 10**18; - } - return dexRate; + function getDexRate( + ISwapsImpl swapImpl, + address base, + address loanTokenAddress, + bytes memory payload, + uint256 amountIn + ) public returns (uint256 rate) { + (rate, ) = swapImpl.dexAmountOutFormatted( + payload, + 10**IERC20Metadata(base).decimals() + ); + rate = (rate * amountIn) / 10**IERC20Metadata(base).decimals(); } - function priceCheck(address loanTokenAddress, address base) - public - view - returns (bool) - { - uint256 dexRate = currentDexRate(base, loanTokenAddress); - uint256 indexRate = currentSwapRate(base, loanTokenAddress); - return - dexRate >= indexRate - ? ((dexRate - indexRate) * 1000) / dexRate <= 5 ? true : false - : ((indexRate - dexRate) * 1000) / indexRate <= 5 - ? true - : false; + function priceCheck( + address loanTokenAddress, + address base, + ISwapsImpl swapImpl, + bytes memory payload + ) public returns (bool) { + uint256 dexRate = getDexRate( + swapImpl, + base, + loanTokenAddress, + payload, + 10**IERC20Metadata(base).decimals() + ); + uint256 indexRate = queryRateReturn( + base, + loanTokenAddress, + 10**IERC20Metadata(base).decimals() + ); + if (dexRate >= indexRate) { + if (((dexRate - indexRate) * 1000) / dexRate <= 5) { + return true; + } else { + return false; + } + } else { + if (((indexRate - dexRate) * 1000) / indexRate <= 5) { + return true; + } else { + return false; + } + } } - function executeOrder(address payable keeper, bytes32 orderID) public { + function executeOrder(bytes32 orderID) public { require(!_allOrders[orderID].isCancelled, "non active"); - IOrderBook.OpenOrder memory order = _allOrders[orderID]; - address trader = _allOrders[orderID].trader; - if ( - _allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN - ) { + IOrderBook.Order memory order = _allOrders[orderID]; + address trader = order.trader; + address srcToken; + uint256 amountUsed; + (uint256 dexID, bytes memory payload) = _prepDexAndPayload( + order.loanDataBytes + ); + if (dexID == 1) { + if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { + payload = abi.encode(order.loanTokenAddress, order.base); + } else { + payload = abi.encode(order.base, order.loanTokenAddress); + } + } + ISwapsImpl swapImpl = ISwapsImpl( + IDexRecords(getSwapAddress()).retrieveDexAddress(dexID) + ); + if (order.collateralTokenAmount > order.loanTokenAmount) { + srcToken = order.base; + } else { + srcToken = order.loanTokenAddress; + } + if (_allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN) { + if (srcToken == order.loanTokenAddress) { + amountUsed = (order.loanTokenAmount * order.leverage) / 10**18; //adjusts leverage + } else { + amountUsed = queryRateReturn( + order.base, + order.loanTokenAddress, + order.collateralTokenAmount + ); + amountUsed = (amountUsed * order.leverage) / 10**18; + } + (uint256 dSwapValue, ) = swapImpl.dexAmountOutFormatted( + payload, + amountUsed + ); + require( - _allOrders[orderID].price >= dexSwapRate(_allOrders[orderID]), - "invalid swap rate" + order.amountReceived <= dSwapValue && dSwapValue > 0, + "amountOut too low" ); - _executeTradeOpen(trader, orderID); + _executeTradeOpen(trader, order); _allOrders[orderID].isCancelled = true; _allOrderIDs.remove(orderID); _histOrders[trader].remove(orderID); emit OrderExecuted(trader, orderID); return; } - if ( - order.orderType == - IOrderBook.OrderType.LIMIT_CLOSE - ) { - require( - order.price <= dexSwapRate(_allOrders[orderID]), - "invalid swap rate" - ); + if (order.orderType == IOrderBook.OrderType.LIMIT_CLOSE) { + uint256 dSwapValue; + if (order.isCollateral) { + (dSwapValue, ) = swapImpl.dexAmountOutFormatted( + payload, + order.collateralTokenAmount + ); + } else { + (dSwapValue, ) = swapImpl.dexAmountInFormatted( + payload, + order.collateralTokenAmount + ); + } + require(order.amountReceived <= dSwapValue, "amountOut too low"); _executeTradeClose( trader, order.loanID, @@ -470,30 +372,35 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { emit OrderExecuted(trader, orderID); return; } - if ( - order.orderType == - IOrderBook.OrderType.MARKET_STOP - ) { + if (order.orderType == IOrderBook.OrderType.MARKET_STOP) { + bool operand; + if (_useOracle[trader]) { + operand = + order.amountReceived >= + queryRateReturn( + order.base, + order.loanTokenAddress, + order.collateralTokenAmount + ); //TODO: Adjust for precision + } else { + operand = + order.amountReceived >= + getDexRate( + swapImpl, + order.base, + order.loanTokenAddress, + payload, + order.collateralTokenAmount + ); + } require( - _useOracle[trader] - ? order.price >= - currentDexRate( - order.base, - order.loanTokenAddress - ) && - priceCheck( - order.loanTokenAddress, - order.base - ) - : order.price >= - currentDexRate( - order.base, - order.loanTokenAddress - ) && - priceCheck( - order.loanTokenAddress, - order.base - ), + operand && + priceCheck( + order.loanTokenAddress, + order.base, + swapImpl, + payload + ), "invalid swap rate" ); _executeTradeClose( diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index 2dc1529d..f83aa6dc 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -5,23 +5,19 @@ import "../Storage/OrderBookStorage.sol"; contract OrderBookData is OrderBookEvents, OrderBookStorage { using EnumerableSet for EnumerableSet.Bytes32Set; - function initialize( - address target) - public - onlyOwner - { - _setTarget(this.getProtocolAddress.selector, target); - _setTarget(this.adjustAllowance.selector, target); - _setTarget(this.getActiveOrders.selector, target); - _setTarget(this.getOrderByOrderID.selector, target); - _setTarget(this.getActiveOrderIDs.selector, target); - _setTarget(this.getTotalOrders.selector, target); - _setTarget(this.getTotalActiveOrders.selector, target); - _setTarget(this.getOrders.selector, target); - _setTarget(this.getActiveTrades.selector, target); - } - - function getProtocolAddress() public view returns (address) { + function initialize(address target) public onlyOwner { + _setTarget(this.getProtocolAddress.selector, target); + _setTarget(this.adjustAllowance.selector, target); + _setTarget(this.getActiveOrders.selector, target); + _setTarget(this.getOrderByOrderID.selector, target); + _setTarget(this.getActiveOrderIDs.selector, target); + _setTarget(this.getTotalOrders.selector, target); + _setTarget(this.getTotalActiveOrders.selector, target); + _setTarget(this.getOrders.selector, target); + _setTarget(this.getActiveTrades.selector, target); + } + + function getProtocolAddress() public view returns (address) { return protocol; } @@ -35,12 +31,14 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { IERC20Metadata(token).approve(spender, type(uint256).max); } - function getActiveOrders( - address trader - ) public view returns (IOrderBook.OpenOrder[] memory fullList) { + function getActiveOrders(address trader) + public + view + returns (IOrderBook.Order[] memory fullList) + { bytes32[] memory idSet = _histOrders[trader].values(); - fullList = new IOrderBook.OpenOrder[](idSet.length); + fullList = new IOrderBook.Order[](idSet.length); for (uint256 i = 0; i < idSet.length; i++) { fullList[i] = _allOrders[idSet[i]]; } @@ -50,14 +48,16 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { function getOrderByOrderID(bytes32 orderID) public view - returns (IOrderBook.OpenOrder memory) + returns (IOrderBook.Order memory) { return _allOrders[orderID]; } - function getActiveOrderIDs( - address trader - ) public view returns (bytes32[] memory) { + function getActiveOrderIDs(address trader) + public + view + returns (bytes32[] memory) + { return _histOrders[trader].values(); } @@ -72,11 +72,11 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { function getOrders() public view - returns (IOrderBook.OpenOrder[] memory fullList) + returns (IOrderBook.Order[] memory fullList) { bytes32[] memory idSet = _allOrderIDs.values(); - fullList = new IOrderBook.OpenOrder[](idSet.length); + fullList = new IOrderBook.Order[](idSet.length); for (uint256 i = 0; i < idSet.length; i++) { fullList[i] = getOrderByOrderID(idSet[i]); } @@ -88,7 +88,6 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { view returns (bytes32[] memory) { - return - _activeTrades[trader].values(); + return _activeTrades[trader].values(); } } diff --git a/contracts/orderbook/Logic/OrderBookMarketOrders.sol b/contracts/orderbook/Logic/OrderBookMarketOrders.sol index 9f054f05..7bc57fb0 100644 --- a/contracts/orderbook/Logic/OrderBookMarketOrders.sol +++ b/contracts/orderbook/Logic/OrderBookMarketOrders.sol @@ -3,16 +3,12 @@ import "../Storage/OrderBookEvents.sol"; import "../Storage/OrderBookStorage.sol"; contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { - using EnumerableSet for EnumerableSet.Bytes32Set; + using EnumerableSet for EnumerableSet.Bytes32Set; - function initialize( - address target) - public - onlyOwner - { - _setTarget(this.marketOpen.selector, target); - _setTarget(this.marketClose.selector, target); - } + function initialize(address target) public onlyOwner { + _setTarget(this.marketOpen.selector, target); + _setTarget(this.marketClose.selector, target); + } function marketOpen( bytes32 loanID, diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index de11de12..7f7a4c6e 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -4,32 +4,41 @@ import "../Storage/OrderBookStorage.sol"; import "../OrderVault/IDeposits.sol"; contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { - using EnumerableSet for EnumerableSet.Bytes32Set; + using EnumerableSet for EnumerableSet.Bytes32Set; - function initialize( - address target) - public - onlyOwner - { - _setTarget(this.placeOrder.selector, target); - _setTarget(this.currentSwapRate.selector, target); - _setTarget(this.amendOrder.selector, target); - _setTarget(this.cancelOrder.selector, target); - _setTarget(this.cancelOrderProtocol.selector, target); - _setTarget(this.changeStopType.selector, target); - _setTarget(this.minimumAmount.selector, target); - } + function initialize(address target) public onlyOwner { + _setTarget(this.placeOrder.selector, target); + _setTarget(this.currentSwapRate.selector, target); + _setTarget(this.amendOrder.selector, target); + _setTarget(this.cancelOrder.selector, target); + _setTarget(this.cancelOrderProtocol.selector, target); + _setTarget(this.changeStopType.selector, target); + _setTarget(this.minimumAmount.selector, target); + } function currentSwapRate(address start, address end) public view returns (uint256 executionPrice) { - (executionPrice, ) = IPriceFeeds(IBZx(protocol).priceFeeds()) - .queryRate(end, start); + (executionPrice, ) = IPriceFeeds(IBZx(protocol).priceFeeds()).queryRate( + end, + start + ); + } + + function queryRateReturn( + address start, + address end, + uint256 amount + ) public view returns (uint256) { + (uint256 executionPrice, uint256 precision) = IPriceFeeds( + IBZx(protocol).priceFeeds() + ).queryRate(start, end); + return (executionPrice * amount) / precision; } - function _collateralTokenMatch(IOrderBook.OpenOrder memory checkOrder) + function _collateralTokenMatch(IOrderBook.Order memory checkOrder) internal view returns (bool) @@ -39,7 +48,7 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { checkOrder.base; } - function _loanTokenMatch(IOrderBook.OpenOrder memory checkOrder) + function _loanTokenMatch(IOrderBook.Order memory checkOrder) internal view returns (bool) @@ -53,7 +62,7 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { return x >= 0 ? x : -x; } - function placeOrder(IOrderBook.OpenOrder memory Order) public { + function placeOrder(IOrderBook.Order calldata Order) external { require( _abs( int256(Order.loanTokenAmount) - @@ -97,30 +106,26 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { require(Order.trader == msg.sender); require(!Order.isCancelled); mainOBID++; - Order.orderID = keccak256(abi.encode(msg.sender, mainOBID)); - _orderExpiration[Order.orderID] = block.timestamp + DAYS_14; - _allOrders[Order.orderID] = Order; + bytes32 ID = keccak256(abi.encode(msg.sender, mainOBID)); + _orderExpiration[ID] = block.timestamp + DAYS_14; + _allOrders[ID] = Order; + _allOrders[ID].orderID = ID; _histOrders[msg.sender].add(Order.orderID); - _allOrderIDs.add(Order.orderID); + _allOrderIDs.add(ID); if (Order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { - IDeposits(vault).deposit( - Order.orderID, - amountUsed, - msg.sender, - usedToken - ); + IDeposits(vault).deposit(ID, amountUsed, msg.sender, usedToken); } emit OrderPlaced( msg.sender, Order.orderType, - Order.price, - Order.orderID, + Order.amountReceived, + ID, Order.base, Order.loanTokenAddress ); } - function amendOrder(IOrderBook.OpenOrder memory Order) public { + function amendOrder(IOrderBook.Order memory Order) public { require( _abs( int256(Order.loanTokenAmount) - @@ -201,7 +206,7 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { emit OrderAmended( msg.sender, Order.orderType, - Order.price, + Order.amountReceived, Order.orderID, Order.base, Order.loanTokenAddress @@ -213,9 +218,7 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { _allOrders[orderID].isCancelled = true; _histOrders[msg.sender].remove(orderID); _allOrderIDs.remove(orderID); - if ( - _allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN - ) { + if (_allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN) { address usedToken = IDeposits(vault).getTokenUsed( msg.sender, orderID @@ -228,26 +231,37 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { function cancelOrderProtocol(bytes32 orderID) public { address trader = _allOrders[orderID].trader; require(!_allOrders[orderID].isCancelled, "inactive order"); - uint256 swapRate = currentSwapRate( - _allOrders[orderID].loanTokenAddress, - _allOrders[orderID].base - ); + uint256 amountUsed = _allOrders[orderID].collateralTokenAmount + + _allOrders[orderID].loanTokenAmount; + uint256 swapRate; + if (_allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN) { + swapRate = queryRateReturn( + _allOrders[orderID].loanTokenAddress, + _allOrders[orderID].base, + amountUsed + ); + } else { + swapRate = queryRateReturn( + _allOrders[orderID].base, + _allOrders[orderID].loanTokenAddress, + amountUsed + ); + } require( ( - _allOrders[orderID].price > swapRate - ? (_allOrders[orderID].price - swapRate) > - (_allOrders[orderID].price * 25) / 100 - : (swapRate - _allOrders[orderID].price) > + _allOrders[orderID].amountReceived > swapRate + ? (_allOrders[orderID].amountReceived - swapRate) > + (_allOrders[orderID].amountReceived * 25) / 100 + : (swapRate - _allOrders[orderID].amountReceived) > (swapRate * 25) / 100 ) || _orderExpiration[orderID] < block.timestamp, "no conditions met" ); + _allOrders[orderID].isCancelled = true; _histOrders[trader].remove(orderID); _allOrderIDs.remove(orderID); - if ( - _allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN - ) { + if (_allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN) { address usedToken = IDeposits(vault).getTokenUsed(trader, orderID); IDeposits(vault).withdrawToTrader(trader, orderID); } diff --git a/contracts/orderbook/Logic/UniswapInterfaces.sol b/contracts/orderbook/Logic/UniswapInterfaces.sol deleted file mode 100644 index 4ed2c82f..00000000 --- a/contracts/orderbook/Logic/UniswapInterfaces.sol +++ /dev/null @@ -1,23 +0,0 @@ -pragma solidity ^0.8.0; - -interface UniswapFactory { - function getPair(address tokenA, address tokenB) - external - view - returns (address pair); -} - -interface UniswapPair { - function token0() external view returns (address); - - function token1() external view returns (address); - - function getReserves() - external - view - returns ( - uint112 reserve0, - uint112 reserve1, - uint32 blockTimestampLast - ); -} diff --git a/contracts/orderbook/Logic/dexSwaps.sol b/contracts/orderbook/Logic/dexSwaps.sol deleted file mode 100644 index 2da52877..00000000 --- a/contracts/orderbook/Logic/dexSwaps.sol +++ /dev/null @@ -1,21 +0,0 @@ -pragma solidity ^0.8.0; - -interface dexSwaps { - function dexExpectedRate( - address sourceTokenAddress, - address destTokenAddress, - uint256 sourceTokenAmount - ) external view virtual returns (uint256); - - function dexAmountOut(bytes memory payload, uint256 amountIn) - external - view - virtual - returns (uint256 amountOut, address midToken); - - function dexAmountIn(bytes memory payload, uint256 amountOut) - external - view - virtual - returns (uint256 amountIn, address midToken); -} diff --git a/contracts/orderbook/OrderBookProxy.sol b/contracts/orderbook/OrderBookProxy.sol index 6bcf912d..d8e722a0 100644 --- a/contracts/orderbook/OrderBookProxy.sol +++ b/contracts/orderbook/OrderBookProxy.sol @@ -7,10 +7,7 @@ contract OrderBookProxy is OrderBookEvents, OrderBookStorage { protocol = _contract; } - fallback() - external - payable - { + fallback() external payable { if (gasleft() <= 2300) { return; } @@ -20,44 +17,49 @@ contract OrderBookProxy is OrderBookEvents, OrderBookStorage { bytes memory data = msg.data; assembly { - let result := delegatecall(gas(), target, add(data, 0x20), mload(data), 0, 0) + let result := delegatecall( + gas(), + target, + add(data, 0x20), + mload(data), + 0, + 0 + ) let size := returndatasize() let ptr := mload(0x40) returndatacopy(ptr, 0, size) switch result - case 0 { revert(ptr, size) } - default { return(ptr, size) } + case 0 { + revert(ptr, size) + } + default { + return(ptr, size) + } } } - function replaceContract( - address target) - external - onlyOwner - { - (bool success,) = target.delegatecall(abi.encodeWithSignature("initialize(address)", target)); + function replaceContract(address target) external onlyOwner { + (bool success, ) = target.delegatecall( + abi.encodeWithSignature("initialize(address)", target) + ); require(success, "setup failed"); } function setTargets( string[] calldata sigsArr, - address[] calldata targetsArr) - external - onlyOwner - { + address[] calldata targetsArr + ) external onlyOwner { require(sigsArr.length == targetsArr.length, "count mismatch"); for (uint256 i = 0; i < sigsArr.length; i++) { - _setTarget(bytes4(keccak256(abi.encodePacked(sigsArr[i]))), targetsArr[i]); + _setTarget( + bytes4(keccak256(abi.encodePacked(sigsArr[i]))), + targetsArr[i] + ); } } - function getTarget( - string calldata sig) - external - view - returns (address) - { + function getTarget(string calldata sig) external view returns (address) { return logicTargets[bytes4(keccak256(abi.encodePacked(sig)))]; } } diff --git a/contracts/orderbook/Storage/OrderBookEvents.sol b/contracts/orderbook/Storage/OrderBookEvents.sol index e800d679..757ef5ec 100644 --- a/contracts/orderbook/Storage/OrderBookEvents.sol +++ b/contracts/orderbook/Storage/OrderBookEvents.sol @@ -2,9 +2,10 @@ pragma solidity ^0.8.0; import "../IOrderBook.sol"; import "@openzeppelin-4.3.2/access/Ownable.sol"; import "@openzeppelin-4.3.2/utils/structs/EnumerableSet.sol"; + contract OrderBookEvents is Ownable { using EnumerableSet for EnumerableSet.Bytes32Set; - mapping(bytes32 => IOrderBook.OpenOrder) internal _allOrders; + mapping(bytes32 => IOrderBook.Order) internal _allOrders; mapping(bytes32 => uint256) internal _orderExpiration; mapping(address => EnumerableSet.Bytes32Set) internal _histOrders; mapping(address => EnumerableSet.Bytes32Set) internal _activeTrades; diff --git a/contracts/orderbook/Storage/OrderBookStorage.sol b/contracts/orderbook/Storage/OrderBookStorage.sol index cbdd2b53..d488ea4b 100644 --- a/contracts/orderbook/Storage/OrderBookStorage.sol +++ b/contracts/orderbook/Storage/OrderBookStorage.sol @@ -8,7 +8,7 @@ import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; contract OrderBookStorage { - mapping(bytes4 => address) public logicTargets; + mapping(bytes4 => address) public logicTargets; address public vault = address(0); address public protocol = address(0); address public constant WRAPPED_TOKEN = @@ -19,8 +19,8 @@ contract OrderBookStorage { uint256 public DAYS_14 = 86400 * 14; uint256 public MIN_AMOUNT_IN_USDC = 1 * 10**15; address public constant USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; - - function _setTarget(bytes4 sig, address target) internal { - logicTargets[sig] = target; - } + + function _setTarget(bytes4 sig, address target) internal { + logicTargets[sig] = target; + } } From ee6af34b614de1806b732e11567802b2c3b56129 Mon Sep 17 00:00:00 2001 From: Drypto | OOKI <51417606+Drypto13@users.noreply.github.com> Date: Thu, 10 Mar 2022 20:59:15 -0800 Subject: [PATCH 39/83] Delete BytesLib.sol --- contracts/mixins/BytesLib.sol | 121 ---------------------------------- 1 file changed, 121 deletions(-) delete mode 100644 contracts/mixins/BytesLib.sol diff --git a/contracts/mixins/BytesLib.sol b/contracts/mixins/BytesLib.sol deleted file mode 100644 index f99dc064..00000000 --- a/contracts/mixins/BytesLib.sol +++ /dev/null @@ -1,121 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * @title Solidity Bytes Arrays Utils - * @author Gonçalo Sá - * - * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. - * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. - */ -pragma solidity >=0.5.17; - -library BytesLib { - function slice( - bytes memory _bytes, - uint256 _start, - uint256 _length - ) internal pure returns (bytes memory) { - require(_length + 31 >= _length, "slice_overflow"); - require(_start + _length >= _start, "slice_overflow"); - require(_bytes.length >= _start + _length, "slice_outOfBounds"); - - bytes memory tempBytes; - - assembly { - switch iszero(_length) - case 0 { - // Get a location of some free memory and store it in tempBytes as - // Solidity does for memory variables. - tempBytes := mload(0x40) - - // The first word of the slice result is potentially a partial - // word read from the original array. To read it, we calculate - // the length of that partial word and start copying that many - // bytes into the array. The first word we copy will start with - // data we don't care about, but the last `lengthmod` bytes will - // land at the beginning of the contents of the new array. When - // we're done copying, we overwrite the full first word with - // the actual length of the slice. - let lengthmod := and(_length, 31) - - // The multiplication in the next line is necessary - // because when slicing multiples of 32 bytes (lengthmod == 0) - // the following copy loop was copying the origin's length - // and then ending prematurely not copying everything it should. - let mc := add( - add(tempBytes, lengthmod), - mul(0x20, iszero(lengthmod)) - ) - let end := add(mc, _length) - - for { - // The multiplication in the next line has the same exact purpose - // as the one above. - let cc := add( - add( - add(_bytes, lengthmod), - mul(0x20, iszero(lengthmod)) - ), - _start - ) - } lt(mc, end) { - mc := add(mc, 0x20) - cc := add(cc, 0x20) - } { - mstore(mc, mload(cc)) - } - - mstore(tempBytes, _length) - - //update free-memory pointer - //allocating the array padded to 32 bytes like the compiler does now - mstore(0x40, and(add(mc, 31), not(31))) - } - //if we want a zero-length slice let's just return a zero-length array - default { - tempBytes := mload(0x40) - //zero out the 32 bytes slice we are about to return - //we need to do it because Solidity does not garbage collect - mstore(tempBytes, 0) - - mstore(0x40, add(tempBytes, 0x20)) - } - } - - return tempBytes; - } - - function toAddress(bytes memory _bytes, uint256 _start) - internal - pure - returns (address) - { - require(_start + 20 >= _start, "toAddress_overflow"); - require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); - address tempAddress; - - assembly { - tempAddress := div( - mload(add(add(_bytes, 0x20), _start)), - 0x1000000000000000000000000 - ) - } - - return tempAddress; - } - - function toUint24(bytes memory _bytes, uint256 _start) - internal - pure - returns (uint24) - { - require(_start + 3 >= _start, "toUint24_overflow"); - require(_bytes.length >= _start + 3, "toUint24_outOfBounds"); - uint24 tempUint; - - assembly { - tempUint := mload(add(add(_bytes, 0x3), _start)) - } - - return tempUint; - } -} From 709025a1b1052a6a4831a27fe85999f5c111373c Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Thu, 10 Mar 2022 21:02:56 -0800 Subject: [PATCH 40/83] cleanup --- contracts/interfaces/ICurve.sol | 33 -- .../interfaces/ICurvePoolRegistration.sol | 15 - contracts/mixins/Path.sol | 74 ---- .../CurvePoolRegistration.sol | 25 -- .../swaps/connectors/SwapsImplCurve_ETH.sol | 318 ------------------ contracts/swaps/connectors/SwapsImplKyber.sol | 1 + .../connectors/SwapsImplUniswapV3_ETH.sol | 1 - 7 files changed, 1 insertion(+), 466 deletions(-) delete mode 100644 contracts/interfaces/ICurve.sol delete mode 100644 contracts/interfaces/ICurvePoolRegistration.sol delete mode 100644 contracts/mixins/Path.sol delete mode 100644 contracts/swaps/CurvePoolRegistration/CurvePoolRegistration.sol delete mode 100644 contracts/swaps/connectors/SwapsImplCurve_ETH.sol diff --git a/contracts/interfaces/ICurve.sol b/contracts/interfaces/ICurve.sol deleted file mode 100644 index 15685c98..00000000 --- a/contracts/interfaces/ICurve.sol +++ /dev/null @@ -1,33 +0,0 @@ -pragma solidity >=0.5.17; - -interface ICurve { - function exchange( - int128 i, - int128 j, - uint256 dx, - uint256 min_dy - ) external; - - function exchange_underlying( - int128 i, - int128 j, - uint256 dx, - uint256 min_dy - ) external; - - function get_dy( - int128 i, - int128 j, - uint256 dx - ) external returns (uint256); - - function get_dy_underlying( - int128 i, - int128 j, - uint256 dx - ) external returns (uint256); - - function underlying_coins(uint256) external returns (address); - - function coins(uint256) external returns (address); -} diff --git a/contracts/interfaces/ICurvePoolRegistration.sol b/contracts/interfaces/ICurvePoolRegistration.sol deleted file mode 100644 index d2c38ee5..00000000 --- a/contracts/interfaces/ICurvePoolRegistration.sol +++ /dev/null @@ -1,15 +0,0 @@ -pragma solidity >=0.5.17; - -interface ICurvePoolRegistration { - function addPool( - address tokenPool, - address[] calldata tokensInPool, - uint128[] calldata tokenIDs - ) external; - - function disablePool(address tokenPool) external; - - function CheckPoolValidity(address pool) external view returns (bool); - - function getPoolType(address tokenPool) external view returns (uint256); -} diff --git a/contracts/mixins/Path.sol b/contracts/mixins/Path.sol deleted file mode 100644 index 650742b3..00000000 --- a/contracts/mixins/Path.sol +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.17; - -import "./BytesLib.sol"; - -/// @title Functions for manipulating path data for multihop swaps -library Path { - using BytesLib for bytes; - - /// @dev The length of the bytes encoded address - uint256 private constant ADDR_SIZE = 20; - /// @dev The length of the bytes encoded fee - uint256 private constant FEE_SIZE = 3; - - /// @dev The offset of a single token address and pool fee - uint256 private constant NEXT_OFFSET = ADDR_SIZE + FEE_SIZE; - /// @dev The offset of an encoded pool key - uint256 private constant POP_OFFSET = NEXT_OFFSET + ADDR_SIZE; - /// @dev The minimum length of an encoding that contains 2 or more pools - uint256 private constant MULTIPLE_POOLS_MIN_LENGTH = - POP_OFFSET + NEXT_OFFSET; - - /// @notice Returns true iff the path contains two or more pools - /// @param path The encoded swap path - /// @return True if path contains two or more pools, otherwise false - function hasMultiplePools(bytes memory path) internal pure returns (bool) { - return path.length >= MULTIPLE_POOLS_MIN_LENGTH; - } - - /// @notice Returns the number of pools in the path - /// @param path The encoded swap path - /// @return The number of pools in the path - function numPools(bytes memory path) internal pure returns (uint256) { - // Ignore the first token address. From then on every fee and token offset indicates a pool. - return ((path.length - ADDR_SIZE) / NEXT_OFFSET); - } - - /// @notice Decodes the first pool in path - /// @param path The bytes encoded swap path - /// @return tokenA The first token of the given pool - /// @return tokenB The second token of the given pool - /// @return fee The fee level of the pool - function decodeFirstPool(bytes memory path) - internal - pure - returns ( - address tokenA, - address tokenB, - uint24 fee - ) - { - tokenA = path.toAddress(0); - fee = path.toUint24(ADDR_SIZE); - tokenB = path.toAddress(NEXT_OFFSET); - } - - /// @notice Gets the segment corresponding to the first pool in the path - /// @param path The bytes encoded swap path - /// @return The segment containing all data necessary to target the first pool in the path - function getFirstPool(bytes memory path) - internal - pure - returns (bytes memory) - { - return path.slice(0, POP_OFFSET); - } - - /// @notice Skips a token + fee element from the buffer and returns the remainder - /// @param path The swap path - /// @return The remaining token + fee elements in the path - function skipToken(bytes memory path) internal pure returns (bytes memory) { - return path.slice(NEXT_OFFSET, path.length - NEXT_OFFSET); - } -} diff --git a/contracts/swaps/CurvePoolRegistration/CurvePoolRegistration.sol b/contracts/swaps/CurvePoolRegistration/CurvePoolRegistration.sol deleted file mode 100644 index c46a89e4..00000000 --- a/contracts/swaps/CurvePoolRegistration/CurvePoolRegistration.sol +++ /dev/null @@ -1,25 +0,0 @@ -pragma solidity 0.5.17; - -import "@openzeppelin-2.5.0/ownership/Ownable.sol"; - -contract CurvePoolRegistration is Ownable { - mapping(address => bool) public validPool; - mapping(address => uint256) public poolType; - - function addPool(address tokenPool, uint256 PoolT) public { - validPool[tokenPool] = true; - poolType[tokenPool] = PoolT; - } - - function disablePool(address tokenPool) public { - validPool[tokenPool] = false; - } - - function CheckPoolValidity(address pool) public view returns (bool) { - return validPool[pool]; - } - - function getPoolType(address tokenPool) public view returns (uint256) { - return poolType[tokenPool]; - } -} diff --git a/contracts/swaps/connectors/SwapsImplCurve_ETH.sol b/contracts/swaps/connectors/SwapsImplCurve_ETH.sol deleted file mode 100644 index 46dce91e..00000000 --- a/contracts/swaps/connectors/SwapsImplCurve_ETH.sol +++ /dev/null @@ -1,318 +0,0 @@ -/** - * Copyright 2017-2021, bZeroX, LLC. All Rights Reserved. - * Licensed under the Apache License, Version 2.0. - */ - -pragma solidity 0.5.17; -pragma experimental ABIEncoderV2; -import "../../core/State.sol"; -import "@openzeppelin-2.5.0/token/ERC20/SafeERC20.sol"; -import "../ISwapsImpl.sol"; -import "../../interfaces/ICurve.sol"; -import "../../mixins/Path.sol"; -import "../../interfaces/ICurvePoolRegistration.sol"; - -contract SwapsImplCurve_ETH is State, ISwapsImpl { - using SafeERC20 for IERC20; - using Path for bytes; - using BytesLib for bytes; - address public constant PoolRegistry = - 0x18E317A7D70d8fBf8e6E893616b52390EbBdb629; //set to address for monitoring Curve pools - bytes4 public constant ExchangeUnderlyingSig = - bytes4( - keccak256("exchange_underlying(uint256,uint256,uint256,uint256)") - ); - bytes4 public constant ExchangeSig = - bytes4(keccak256("exchange(uint256,uint256,uint256,uint256)")); - bytes4 public constant GetDySig = - bytes4(keccak256("get_dy(uint256,uint256,uint256)")); - bytes4 public constant GetDyUnderlyingSig = - bytes4(keccak256("get_dy_underlying(uint256,uint256,uint256)")); - - function dexSwap( - address sourceTokenAddress, - address destTokenAddress, - address receiverAddress, - address returnToSenderAddress, - uint256 minSourceTokenAmount, - uint256 maxSourceTokenAmount, - uint256 requiredDestTokenAmount, - bytes memory payload - ) - public - returns (uint256 destTokenAmountReceived, uint256 sourceTokenAmountUsed) - { - require(sourceTokenAddress != destTokenAddress, "source == dest"); - require( - supportedTokens[sourceTokenAddress] && - supportedTokens[destTokenAddress], - "invalid tokens" - ); - - IERC20 sourceToken = IERC20(sourceTokenAddress); - address _thisAddress = address(this); - (sourceTokenAmountUsed, destTokenAmountReceived) = _swapWithCurve( - sourceTokenAddress, - destTokenAddress, - receiverAddress, - minSourceTokenAmount, - maxSourceTokenAmount, - requiredDestTokenAmount, - payload - ); - - if ( - returnToSenderAddress != _thisAddress && - sourceTokenAmountUsed < maxSourceTokenAmount - ) { - // send unused source token back - sourceToken.safeTransfer( - returnToSenderAddress, - maxSourceTokenAmount - sourceTokenAmountUsed - ); - } - } - - function dexExpectedRate( - address sourceTokenAddress, - address destTokenAddress, - uint256 sourceTokenAmount - ) public view returns (uint256 expectedRate) { - revert("unsupported"); - } - - function dexAmountOut(bytes memory route, uint256 amountIn) - public - returns (uint256 amountOut, address midToken) - { - if (amountIn == 0) { - amountOut = 0; - } else if (amountIn != 0) { - amountOut = _getAmountOut(amountIn, route); - } - } - - function dexAmountOutFormatted(bytes memory route, uint256 amountIn) - public - returns (uint256 amountOut, address midToken) - { - if (amountIn == 0) { - amountOut = 0; - } else if (amountIn != 0) { - amountOut = _getAmountOut(amountIn, route); - } - } - - function dexAmountIn(bytes memory route, uint256 amountOut) - public - returns (uint256 amountIn, address midToken) - { - if (amountOut != 0) { - amountIn = _getAmountIn(amountOut, route); - - if (amountIn == uint256(-1)) { - amountIn = 0; - } - } else { - amountIn = 0; - } - } - - function dexAmountInFormatted(bytes memory route, uint256 amountOut) - public - returns (uint256 amountIn, address midToken) - { - if (amountOut != 0) { - amountIn = _getAmountIn(amountOut, route); - - if (amountIn == uint256(-1)) { - amountIn = 0; - } - } else { - amountIn = 0; - } - } - - function _getAmountOut(uint256 amountIn, bytes memory path) - public - returns (uint256) - { - (bytes4 sig, address curvePool, uint128 tokenIn, uint128 tokenOut) = abi - .decode(path, (bytes4, address, uint128, uint128)); - uint256 amountOut; - if (sig == GetDySig || sig == ExchangeSig) { - amountOut = ICurve(curvePool).get_dy( - int128(tokenOut), - int128(tokenIn), - amountIn - ); - } else if (sig == GetDyUnderlyingSig || sig == ExchangeUnderlyingSig) { - amountOut = ICurve(curvePool).get_dy_underlying( - int128(tokenOut), - int128(tokenIn), - amountIn - ); - } else { - revert("Unsupported Signature"); - } - if (amountOut == 0) { - amountOut = uint256(-1); - } - return amountOut; - } - - function _getAmountIn(uint256 amountOut, bytes memory path) - public - returns (uint256) - { - (bytes4 sig, address curvePool, uint128 tokenIn, uint128 tokenOut) = abi - .decode(path, (bytes4, address, uint128, uint128)); - uint256 amountIn; - if (sig == GetDySig || sig == ExchangeSig) { - amountIn = ICurve(curvePool).get_dy( - int128(tokenOut), - int128(tokenIn), - amountOut - ); - } else if (sig == GetDyUnderlyingSig || sig == ExchangeUnderlyingSig) { - amountIn = ICurve(curvePool).get_dy_underlying( - int128(tokenOut), - int128(tokenIn), - amountOut - ); - } else { - revert("Unsupported Signature"); - } - if (amountIn == 0) { - amountIn = uint256(-1); - } - return amountIn; - } - - function setSwapApprovals(address[] memory tokens) public { - require( - ICurvePoolRegistration(PoolRegistry).CheckPoolValidity(tokens[0]) - ); - for (uint256 i = 1; i < tokens.length; i++) { - IERC20(tokens[i]).safeApprove(tokens[0], 0); - IERC20(tokens[i]).safeApprove(tokens[0], uint256(-1)); - } - } - - function _getDexNumber(address pool, uint256 tokenID) - internal - returns (address) - { - address token = address(0); - if (ICurvePoolRegistration(PoolRegistry).getPoolType(pool) == 0) { - token = ICurve(pool).underlying_coins(tokenID); - } else { - token = ICurve(pool).coins(tokenID); - } - return token; - } - - function _swapWithCurve( - address sourceTokenAddress, - address destTokenAddress, - address receiverAddress, - uint256 minSourceTokenAmount, - uint256 sourceTokenAmount, - uint256 requiredDestTokenAmount, - bytes memory payload - ) - internal - returns (uint256 sourceTokenAmountUsed, uint256 destTokenAmountReceived) - { - if (requiredDestTokenAmount != 0) { - ( - bytes4 sig, - address curvePool, - uint128 tokenIn, - uint128 tokenOut - ) = abi.decode(payload, (bytes4, address, uint128, uint128)); - require( - ICurvePoolRegistration(PoolRegistry).CheckPoolValidity( - curvePool - ) - ); - require(sourceTokenAddress == _getDexNumber(curvePool, tokenIn)); - require(destTokenAddress == _getDexNumber(curvePool, tokenOut)); - (uint256 amountIn, ) = dexAmountIn( - payload, - requiredDestTokenAmount - ); - require(amountIn <= sourceTokenAmount, "too much"); - if (sig == ExchangeUnderlyingSig) { - ICurve(curvePool).exchange_underlying( - int128(tokenIn), - int128(tokenOut), - amountIn, - 1 - ); - } else if (sig == ExchangeSig) { - ICurve(curvePool).exchange( - int128(tokenIn), - int128(tokenOut), - amountIn, - 1 - ); - } else { - revert("Unsupported Signature"); - } - if (receiverAddress != address(this)) { - IERC20(destTokenAddress).safeTransfer( - receiverAddress, - requiredDestTokenAmount - ); - } - sourceTokenAmountUsed = amountIn; - destTokenAmountReceived = requiredDestTokenAmount; - } else { - ( - bytes4 sig, - address curvePool, - uint128 tokenIn, - uint128 tokenOut - ) = abi.decode(payload, (bytes4, address, uint128, uint128)); - require( - ICurvePoolRegistration(PoolRegistry).CheckPoolValidity( - curvePool - ) - ); - - require( - sourceTokenAddress == _getDexNumber(curvePool, tokenIn), - "source token number off" - ); - require( - destTokenAddress == _getDexNumber(curvePool, tokenOut), - "dest token number off" - ); - - (uint256 recv, ) = dexAmountOut(payload, minSourceTokenAmount); - if (sig == ExchangeUnderlyingSig) { - ICurve(curvePool).exchange_underlying( - int128(tokenIn), - int128(tokenOut), - minSourceTokenAmount, - 1 - ); - } else if (sig == ExchangeSig) { - ICurve(curvePool).exchange( - int128(tokenIn), - int128(tokenOut), - minSourceTokenAmount, - 1 - ); - } else { - revert("Unsupported Signature"); - } - if (receiverAddress != address(this)) { - IERC20(destTokenAddress).safeTransfer(receiverAddress, recv); - } - sourceTokenAmountUsed = minSourceTokenAmount; - destTokenAmountReceived = recv; - } - } -} diff --git a/contracts/swaps/connectors/SwapsImplKyber.sol b/contracts/swaps/connectors/SwapsImplKyber.sol index f787e8b2..862c4cce 100644 --- a/contracts/swaps/connectors/SwapsImplKyber.sol +++ b/contracts/swaps/connectors/SwapsImplKyber.sol @@ -1,3 +1,4 @@ +/** * Copyright 2017-2022, OokiDao. All Rights Reserved. * Licensed under the Apache License, Version 2.0. */ diff --git a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol index d4c4e884..5b11b8f6 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol @@ -18,7 +18,6 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { IUniswapQuoter public constant uniswapQuoteContract = IUniswapQuoter(0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6); //mainnet - function dexSwap( address sourceTokenAddress, address destTokenAddress, From 11c618f3ad5fb02883dc3776cfe4bc80edf5ca69 Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Sat, 12 Mar 2022 20:57:23 -0800 Subject: [PATCH 41/83] optimizations --- contracts/orderbook/IOrderBook.sol | 31 +++- .../orderbook/Keepers/IUniswapV2Router.sol | 38 ---- .../orderbook/Keepers/OrderBookInterface.sol | 30 ---- contracts/orderbook/Keepers/OrderKeeper.sol | 69 ++------ .../orderbook/Keepers/OrderKeeperClear.sol | 28 +-- contracts/orderbook/Logic/OrderBook.sol | 165 +++++++++--------- contracts/orderbook/Logic/OrderBookData.sol | 55 ++++-- .../orderbook/Logic/OrderBookMarketOrders.sol | 61 ++----- .../orderbook/Logic/OrderBookOrderPlace.sol | 137 ++++++--------- contracts/orderbook/OrderBookProxy.sol | 2 +- contracts/orderbook/OrderVault/Deposits.sol | 32 ++-- contracts/orderbook/OrderVault/IDeposits.sol | 6 +- .../orderbook/Storage/OrderBookStorage.sol | 17 +- contracts/orderbook/WrappedToken.sol | 5 - 14 files changed, 278 insertions(+), 398 deletions(-) delete mode 100644 contracts/orderbook/Keepers/IUniswapV2Router.sol delete mode 100644 contracts/orderbook/Keepers/OrderBookInterface.sol delete mode 100644 contracts/orderbook/WrappedToken.sol diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index 770e052e..9cf5a2f7 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -7,19 +7,20 @@ interface IOrderBook { MARKET_STOP } struct Order { - address trader; bytes32 loanID; - address iToken; - address loanTokenAddress; + bytes32 orderID; uint256 amountReceived; uint256 leverage; uint256 loanTokenAmount; uint256 collateralTokenAmount; - bool isCancelled; + uint64 timeTillExpiration; + address trader; + address iToken; + address loanTokenAddress; address base; OrderType orderType; + bool isCancelled; bool isCollateral; - bytes32 orderID; bytes loanDataBytes; } @@ -29,9 +30,27 @@ interface IOrderBook { function amendOrder(Order calldata order, uint256 orderID) external; - function cancelOrder(uint256 orderID) external; + function cancelOrder(bytes32 orderID) external; function getSwapAddress() external view returns (address); function getFeed() external view returns (address); + + function getOrdersLimited(uint256 start, uint256 end) external view returns(Order[] memory); + + function getOrders(uint256 start, uint256 end) external view returns(Order[] memory); + + function getActiveOrders(address trader, uint256 start, uint256 end) external view returns(Order[] memory); + + function getActiveOrdersLimited(address trader, uint256 start, uint256 end) external view returns(Order[] memory); + + function executeOrder(bytes32 orderID) external; + + function cancelOrderProtocol(bytes32 orderID) external; + + function clearOrder(bytes32 orderID) external view returns (bool); + + function prelimCheck(bytes32 orderID) external returns (bool); + + function getTotalActiveOrders() external view returns (uint256); } diff --git a/contracts/orderbook/Keepers/IUniswapV2Router.sol b/contracts/orderbook/Keepers/IUniswapV2Router.sol deleted file mode 100644 index d528519b..00000000 --- a/contracts/orderbook/Keepers/IUniswapV2Router.sol +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2017-2020, bZeroX, LLC. All Rights Reserved. - * Licensed under the Apache License, Version 2.0. - */ - -pragma solidity ^0.8.0; - -interface uniswapV2Router { - // 0x38ed1739 - function swapExactTokensForTokens( - uint256 amountIn, - uint256 amountOutMin, - address[] calldata path, - address to, - uint256 deadline - ) external returns (uint256[] memory amounts); - - // 0x8803dbee - function swapTokensForExactTokens( - uint256 amountOut, - uint256 amountInMax, - address[] calldata path, - address to, - uint256 deadline - ) external returns (uint256[] memory amounts); - - // 0x1f00ca74 - function getAmountsIn(uint256 amountOut, address[] calldata path) - external - view - returns (uint256[] memory amounts); - - // 0xd06ca61f - function getAmountsOut(uint256 amountIn, address[] calldata path) - external - view - returns (uint256[] memory amounts); -} diff --git a/contracts/orderbook/Keepers/OrderBookInterface.sol b/contracts/orderbook/Keepers/OrderBookInterface.sol deleted file mode 100644 index bbea7bb3..00000000 --- a/contracts/orderbook/Keepers/OrderBookInterface.sol +++ /dev/null @@ -1,30 +0,0 @@ -pragma solidity ^0.8.0; - -interface IOrderBook { - function getOrders() external view returns (OpenOrder[] memory); - - function prelimCheck(bytes32 orderID) external view returns (bool); - - function executeOrder(address payable keeper, bytes32 orderID) external; - - function cancelOrderProtocol(bytes32 orderID) external; - - function clearOrder(bytes32 orderID) external view returns (bool); - - struct OpenOrder { - address trader; - bytes32 loanID; - address iToken; - address loanTokenAddress; - uint256 price; - uint256 leverage; - uint256 loanTokenAmount; - uint256 collateralTokenAmount; - bool isActive; - address base; - uint256 orderType; - bool isCollateral; - bytes32 orderID; - bytes loanData; - } -} diff --git a/contracts/orderbook/Keepers/OrderKeeper.sol b/contracts/orderbook/Keepers/OrderKeeper.sol index 7e68fdc4..56189146 100644 --- a/contracts/orderbook/Keepers/OrderKeeper.sol +++ b/contracts/orderbook/Keepers/OrderKeeper.sol @@ -1,32 +1,36 @@ pragma solidity ^0.8.0; -import "./OrderBookInterface.sol"; -import "./IUniswapV2Router.sol"; -import "../WrappedToken.sol"; +import "../IOrderBook.sol"; import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; contract OrderKeeper { - address factory; - address constant LINK = 0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39; - address constant WETH = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; - address swapAddress = 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506; + IOrderBook public factory; - constructor(address factoryAddress) { + constructor(IOrderBook factoryAddress) { factory = factoryAddress; } function checkUpkeep(bytes calldata checkData) - public - view + external returns (bool upkeepNeeded, bytes memory performData) { - IOrderBook.OpenOrder[] memory listOfMainOrders = IOrderBook(factory) - .getOrders(); - for (uint256 x = 0; x < listOfMainOrders.length; x++) { - if (IOrderBook(factory).prelimCheck(listOfMainOrders[x].orderID)) { + (uint256 start, uint256 end) = abi.decode(checkData, (uint256, uint256)); + uint256 orderIDLength = factory.getTotalActiveOrders(); + if(end < orderIDLength) { + if (start > orderIDLength) { + end = orderIDLength; + } else { + return (upkeepNeeded, performData); + } + } + IOrderBook.Order[] memory listOfMainOrders = factory + .getOrdersLimited(start, end); + for (uint256 x = 0; x < listOfMainOrders.length;) { + if (factory.prelimCheck(listOfMainOrders[x].orderID)) { upkeepNeeded = true; performData = abi.encode(listOfMainOrders[x].orderID); return (upkeepNeeded, performData); } + unchecked { ++x; } } return (upkeepNeeded, performData); } @@ -34,41 +38,6 @@ contract OrderKeeper { function performUpkeep(bytes calldata performData) public { bytes32 orderId = abi.decode(performData, (bytes32)); //emit OrderExecuted(trader,orderId); - IOrderBook(factory).executeOrder(payable(address(this)), orderId); - } - - /*function handleFees(address[] memory tokenAddress) public { - address[] memory path; - path = new address[](3); - path[1] = WETH; - path[2] = LINK; - for (uint256 x = 0; x < tokenAddress.length; x++) { - if (tokenAddress[x] != WETH) { - path[0] = tokenAddress[x]; - uniswapV2Router(swapAddress).swapExactTokensForTokens( - IERC20Metadata(tokenAddress[x]).balanceOf(address(this)), - 1, - path, - address(this), - block.timestamp - ); - } else { - address[] memory pathWETH; - pathWETH = new address[](2); - pathWETH[0] = WETH; - pathWETH[1] = LINK; - uniswapV2Router(swapAddress).swapExactTokensForTokens( - IERC20Metadata(tokenAddress[x]).balanceOf(address(this)), - 1, - pathWETH, - address(this), - block.timestamp - ); - } - } + factory.executeOrder(orderId); } - - function handleETHFees() public { - WrappedToken(WETH).deposit{value: address(this).balance}(); - }*/ } diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol index 3302576d..46d3d2f3 100644 --- a/contracts/orderbook/Keepers/OrderKeeperClear.sol +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -1,13 +1,11 @@ pragma solidity ^0.8.0; -import "./OrderBookInterface.sol"; -import "./IUniswapV2Router.sol"; -import "../WrappedToken.sol"; +import "../IOrderBook.sol"; import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; contract OrderKeeperClear { - address factory; + IOrderBook public factory; - constructor(address factoryAddress) { + constructor(IOrderBook factoryAddress) { factory = factoryAddress; } @@ -16,14 +14,24 @@ contract OrderKeeperClear { view returns (bool upkeepNeeded, bytes memory performData) { - IOrderBook.OpenOrder[] memory listOfMainOrders = IOrderBook(factory) - .getOrders(); - for (uint256 x = 0; x < listOfMainOrders.length; x++) { - if (IOrderBook(factory).clearOrder(listOfMainOrders[x].orderID)) { + (uint256 start, uint256 end) = abi.decode(checkData, (uint256, uint256)); + uint256 orderIDLength = factory.getTotalActiveOrders(); + if(end < orderIDLength) { + if (start > orderIDLength) { + end = orderIDLength; + } else { + return (upkeepNeeded, performData); + } + } + IOrderBook.Order[] memory listOfMainOrders = factory + .getOrdersLimited(start, end); + for (uint256 x = 0; x < listOfMainOrders.length;) { + if (factory.clearOrder(listOfMainOrders[x].orderID)) { upkeepNeeded = true; performData = abi.encode(listOfMainOrders[x].orderID); return (upkeepNeeded, performData); } + unchecked { ++x; } } return (upkeepNeeded, performData); } @@ -31,6 +39,6 @@ contract OrderKeeperClear { function performUpkeep(bytes calldata performData) public { bytes32 orderId = abi.decode(performData, (bytes32)); //emit OrderExecuted(trader,orderId); - IOrderBook(factory).cancelOrderProtocol(orderId); + factory.cancelOrderProtocol(orderId); } } diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index a7fe62c4..d568d10c 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -4,13 +4,12 @@ import "../Storage/OrderBookStorage.sol"; import "../../swaps/ISwapsImpl.sol"; import "../OrderVault/IDeposits.sol"; import "../../interfaces/IDexRecords.sol"; - -contract OrderBook is OrderBookEvents, OrderBookStorage { +import "../../mixins/Flags.sol"; +contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { using EnumerableSet for EnumerableSet.Bytes32Set; function initialize(address target) public onlyOwner { _setTarget(this.getSwapAddress.selector, target); - _setTarget(this.currentSwapRate.selector, target); _setTarget(this.getFeed.selector, target); _setTarget(this.getDexRate.selector, target); _setTarget(this.clearOrder.selector, target); @@ -22,10 +21,9 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { } function _executeTradeOpen( - address trader, IOrderBook.Order memory internalOrder ) internal { - IDeposits(vault).withdraw(trader, internalOrder.orderID); + IDeposits(vault).withdraw(internalOrder.orderID); (bool result, bytes memory data) = internalOrder.iToken.call( abi.encodeWithSelector( IToken(internalOrder.iToken).marginTrade.selector, @@ -38,14 +36,12 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { internalOrder.loanDataBytes ) ); - if (result) { - (bytes32 loanID, , ) = abi.decode( + if (result && internalOrder.loanID==0) { + (bytes32 loanID) = abi.decode( data, - (bytes32, uint256, uint256) + (bytes32) ); - if (!_activeTrades[trader].contains(loanID)) { - _activeTrades[trader].add(loanID); - } + _activeTrades[internalOrder.trader].add(loanID); } } @@ -57,12 +53,12 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { address collateralAddress, bytes memory loanDataBytes ) internal { - if (IBZx(protocol).getLoan(loanID).collateral == amount) { + if (protocol.getLoan(loanID).collateral == amount) { _activeTrades[trader].remove(loanID); } - protocol.call( + address(protocol).call( abi.encodeWithSelector( - IBZx(protocol).closeWithSwap.selector, + protocol.closeWithSwap.selector, loanID, address(this), amount, @@ -73,27 +69,19 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { } function getSwapAddress() public view returns (address) { - return IBZx(protocol).swapsImpl(); - } - - function currentSwapRate(address start, address end) - public - view - returns (uint256 executionPrice) - { - (executionPrice, ) = IPriceFeeds(getFeed()).queryRate(end, start); + return protocol.swapsImpl(); } function getFeed() public view returns (address) { - return IBZx(protocol).priceFeeds(); + return protocol.priceFeeds(); } - function isActiveLoan(bytes32 ID) internal view returns (bool) { - return IBZx(protocol).loans(ID).active; + function _isActiveLoan(bytes32 ID) internal view returns (bool) { + return protocol.loans(ID).active; } function clearOrder(bytes32 orderID) public view returns (bool) { - if (_orderExpiration[orderID] < block.timestamp) { + if (_allOrders[orderID].timeTillExpiration < block.timestamp) { return true; } uint256 amountUsed = _allOrders[orderID].collateralTokenAmount + @@ -142,21 +130,22 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { returns (uint256 dexID, bytes memory payload) { if (input.length != 0) { - (dexID, payload) = abi.decode(input, (uint256, bytes)); - } else { - dexID = 1; + (uint128 flag, bytes[] memory payloads) = abi.decode( + input, + (uint128, bytes[]) + ); + if(flag & DEX_SELECTOR_FLAG != 0){ + (dexID, payload) = abi.decode(payloads[0], (uint256, bytes)); + } } } function prelimCheck(bytes32 orderID) public returns (bool) { IOrderBook.Order memory order = _allOrders[orderID]; - address trader = order.trader; uint256 amountUsed; address srcToken; - (uint256 dexID, bytes memory payload) = _prepDexAndPayload( - order.loanDataBytes - ); - if (dexID == 1) { + (uint256 dexID, bytes memory payload) = _prepDexAndPayload(order.loanDataBytes); + if (payload.length == 0) { if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { payload = abi.encode(order.loanTokenAddress, order.base); } else { @@ -166,20 +155,21 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { ISwapsImpl swapImpl = ISwapsImpl( IDexRecords(getSwapAddress()).retrieveDexAddress(dexID) ); - if (order.collateralTokenAmount > order.loanTokenAmount) { - srcToken = order.base; - } else { - srcToken = order.loanTokenAddress; - } - if (_orderExpiration[orderID] < block.timestamp) { + srcToken = order.collateralTokenAmount > order.loanTokenAmount ? order.base : order.loanTokenAddress; + if (order.timeTillExpiration < block.timestamp) { return false; } if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { - if (order.loanID != 0 && !isActiveLoan(order.loanID)) { + if (order.loanID != 0 && !_isActiveLoan(order.loanID)) { return false; } + uint256 dSwapValue; if (srcToken == order.loanTokenAddress) { - amountUsed = (order.loanTokenAmount * order.leverage) / 10**18; //adjusts leverage + amountUsed = order.loanTokenAmount + (order.loanTokenAmount * order.leverage) / 10**18; //adjusts leverage + (dSwapValue, ) = swapImpl.dexAmountOutFormatted( + payload, + amountUsed + ); } else { amountUsed = queryRateReturn( order.base, @@ -187,27 +177,29 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { order.collateralTokenAmount ); amountUsed = (amountUsed * order.leverage) / 10**18; + (dSwapValue, ) = swapImpl.dexAmountOutFormatted( + payload, + amountUsed + ); + dSwapValue += order.collateralTokenAmount; } - (uint256 dSwapValue, ) = swapImpl.dexAmountOutFormatted( - payload, - amountUsed - ); - if (order.amountReceived <= dSwapValue && dSwapValue > 0) { + + if (order.amountReceived <= dSwapValue) { return true; } } else if (order.orderType == IOrderBook.OrderType.LIMIT_CLOSE) { - if (!isActiveLoan(order.loanID)) { + if (!_isActiveLoan(order.loanID)) { return false; } uint256 dSwapValue; if (order.isCollateral) { - (dSwapValue, ) = swapImpl.dexAmountOutFormatted( + (dSwapValue, ) = swapImpl.dexAmountInFormatted( payload, order.collateralTokenAmount ); } else { - (dSwapValue, ) = swapImpl.dexAmountInFormatted( + (dSwapValue, ) = swapImpl.dexAmountOutFormatted( payload, order.collateralTokenAmount ); @@ -216,18 +208,18 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { return true; } } else { - if (!isActiveLoan(order.loanID)) { + if (!_isActiveLoan(order.loanID)) { return false; } bool operand; - if (_useOracle[trader]) { + if (_useOracle[order.trader]) { operand = order.amountReceived >= queryRateReturn( order.base, order.loanTokenAddress, order.collateralTokenAmount - ); //TODO: Adjust for precision + ); } else { operand = order.amountReceived >= @@ -294,15 +286,12 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { } function executeOrder(bytes32 orderID) public { - require(!_allOrders[orderID].isCancelled, "non active"); + require(!_allOrders[orderID].isCancelled, "OrderBook: non active"); IOrderBook.Order memory order = _allOrders[orderID]; - address trader = order.trader; address srcToken; uint256 amountUsed; - (uint256 dexID, bytes memory payload) = _prepDexAndPayload( - order.loanDataBytes - ); - if (dexID == 1) { + (uint256 dexID, bytes memory payload) = _prepDexAndPayload(order.loanDataBytes); + if (payload.length == 0) { if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { payload = abi.encode(order.loanTokenAddress, order.base); } else { @@ -312,14 +301,16 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { ISwapsImpl swapImpl = ISwapsImpl( IDexRecords(getSwapAddress()).retrieveDexAddress(dexID) ); - if (order.collateralTokenAmount > order.loanTokenAmount) { - srcToken = order.base; - } else { - srcToken = order.loanTokenAddress; - } + srcToken = order.collateralTokenAmount > order.loanTokenAmount ? order.base : order.loanTokenAddress; + require(order.timeTillExpiration < block.timestamp, "OrderBook: Order Expired"); if (_allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN) { + uint256 dSwapValue; if (srcToken == order.loanTokenAddress) { - amountUsed = (order.loanTokenAmount * order.leverage) / 10**18; //adjusts leverage + amountUsed = order.loanTokenAmount + (order.loanTokenAmount * order.leverage) / 10**18; //adjusts leverage + (dSwapValue, ) = swapImpl.dexAmountOutFormatted( + payload, + amountUsed + ); } else { amountUsed = queryRateReturn( order.base, @@ -327,39 +318,41 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { order.collateralTokenAmount ); amountUsed = (amountUsed * order.leverage) / 10**18; + (dSwapValue, ) = swapImpl.dexAmountOutFormatted( + payload, + amountUsed + ); + dSwapValue += order.collateralTokenAmount; } - (uint256 dSwapValue, ) = swapImpl.dexAmountOutFormatted( - payload, - amountUsed - ); require( - order.amountReceived <= dSwapValue && dSwapValue > 0, - "amountOut too low" + order.amountReceived <= dSwapValue, + "OrderBook: amountOut too low" ); - _executeTradeOpen(trader, order); + _executeTradeOpen(order); _allOrders[orderID].isCancelled = true; _allOrderIDs.remove(orderID); - _histOrders[trader].remove(orderID); - emit OrderExecuted(trader, orderID); + _histOrders[order.trader].remove(orderID); + emit OrderExecuted(order.trader, orderID); return; } if (order.orderType == IOrderBook.OrderType.LIMIT_CLOSE) { uint256 dSwapValue; if (order.isCollateral) { - (dSwapValue, ) = swapImpl.dexAmountOutFormatted( + (dSwapValue, ) = swapImpl.dexAmountInFormatted( payload, order.collateralTokenAmount ); + require(order.amountReceived <= dSwapValue, "OrderBook: amountIn too low"); } else { - (dSwapValue, ) = swapImpl.dexAmountInFormatted( + (dSwapValue, ) = swapImpl.dexAmountOutFormatted( payload, order.collateralTokenAmount ); + require(order.amountReceived <= dSwapValue, "OrderBook: amountOut too low"); } - require(order.amountReceived <= dSwapValue, "amountOut too low"); _executeTradeClose( - trader, + order.trader, order.loanID, order.collateralTokenAmount, order.isCollateral, @@ -368,13 +361,13 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { ); _allOrders[orderID].isCancelled = true; _allOrderIDs.remove(orderID); - _histOrders[trader].remove(orderID); - emit OrderExecuted(trader, orderID); + _histOrders[order.trader].remove(orderID); + emit OrderExecuted(order.trader, orderID); return; } if (order.orderType == IOrderBook.OrderType.MARKET_STOP) { bool operand; - if (_useOracle[trader]) { + if (_useOracle[order.trader]) { operand = order.amountReceived >= queryRateReturn( @@ -401,10 +394,10 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { swapImpl, payload ), - "invalid swap rate" + "OrderBook: invalid swap rate" ); _executeTradeClose( - trader, + order.trader, order.loanID, order.collateralTokenAmount, order.isCollateral, @@ -413,8 +406,8 @@ contract OrderBook is OrderBookEvents, OrderBookStorage { ); _allOrders[orderID].isCancelled = true; _allOrderIDs.remove(orderID); - _histOrders[trader].remove(orderID); - emit OrderExecuted(trader, orderID); + _histOrders[order.trader].remove(orderID); + emit OrderExecuted(order.trader, orderID); return; } } diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index f83aa6dc..ffc03f79 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -2,33 +2,36 @@ pragma solidity ^0.8.0; import "../Storage/OrderBookEvents.sol"; import "../Storage/OrderBookStorage.sol"; +interface IERC { + function approve(address spender, uint amount) external; //for USDT +} + contract OrderBookData is OrderBookEvents, OrderBookStorage { using EnumerableSet for EnumerableSet.Bytes32Set; function initialize(address target) public onlyOwner { - _setTarget(this.getProtocolAddress.selector, target); _setTarget(this.adjustAllowance.selector, target); _setTarget(this.getActiveOrders.selector, target); + _setTarget(this.getActiveOrdersLimited.selector, target); _setTarget(this.getOrderByOrderID.selector, target); _setTarget(this.getActiveOrderIDs.selector, target); _setTarget(this.getTotalOrders.selector, target); + _setTarget(this.getActiveOrders.selector, target); _setTarget(this.getTotalActiveOrders.selector, target); _setTarget(this.getOrders.selector, target); + _setTarget(this.getOrdersLimited.selector, target); _setTarget(this.getActiveTrades.selector, target); } - function getProtocolAddress() public view returns (address) { - return protocol; - } - function adjustAllowance(address spender, address token) public { require( - IBZx(protocol).isLoanPool(spender) || - protocol == spender || + protocol.isLoanPool(spender) || + address(protocol) == spender || vault == spender, - "invalid spender" + "OrderBook: invalid spender" ); - IERC20Metadata(token).approve(spender, type(uint256).max); + IERC(token).approve(spender, 0); //needs to be zeroed out because of different iterations of ERC-20 standard + IERC(token).approve(spender, type(uint256).max); } function getActiveOrders(address trader) @@ -39,8 +42,23 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { bytes32[] memory idSet = _histOrders[trader].values(); fullList = new IOrderBook.Order[](idSet.length); - for (uint256 i = 0; i < idSet.length; i++) { + for (uint256 i = 0; i < idSet.length;) { fullList[i] = _allOrders[idSet[i]]; + unchecked { ++i; } + } + return fullList; + } + + function getActiveOrdersLimited(address trader, uint start, uint end) + public + view + returns (IOrderBook.Order[] memory fullList) + { + require(end<=_histOrders[trader].length(), "OrderBook: end is past max orders"); + fullList = new IOrderBook.Order[](end-start); + for (uint256 i = start; i < end;) { + fullList[i] = _allOrders[_histOrders[trader].at(i)]; + unchecked { ++i; } } return fullList; } @@ -77,8 +95,23 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { bytes32[] memory idSet = _allOrderIDs.values(); fullList = new IOrderBook.Order[](idSet.length); - for (uint256 i = 0; i < idSet.length; i++) { + for (uint256 i = 0; i < idSet.length;) { fullList[i] = getOrderByOrderID(idSet[i]); + unchecked { ++i; } + } + return fullList; + } + + function getOrdersLimited(uint start, uint end) + public + view + returns (IOrderBook.Order[] memory fullList) + { + require(end<=_allOrderIDs.length(), "OrderBook: end is past max orders"); + fullList = new IOrderBook.Order[](end-start); + for (uint256 i = start; i < end;) { + fullList[i] = _allOrders[_allOrderIDs.at(i)]; + unchecked { ++i; } } return fullList; } diff --git a/contracts/orderbook/Logic/OrderBookMarketOrders.sol b/contracts/orderbook/Logic/OrderBookMarketOrders.sol index 7bc57fb0..8ba593c0 100644 --- a/contracts/orderbook/Logic/OrderBookMarketOrders.sol +++ b/contracts/orderbook/Logic/OrderBookMarketOrders.sol @@ -35,8 +35,6 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { bytes32 loanID, uint256 amount, bool iscollateral, - address loanTokenAddress, - address collateralAddress, bytes memory loanDataBytes ) public { _executeMarketClose( @@ -44,8 +42,6 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { loanID, amount, iscollateral, - loanTokenAddress, - collateralAddress, loanDataBytes ); } @@ -60,20 +56,16 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { address base, bytes memory loanDataBytes ) internal { - require(IBZx(protocol).isLoanPool(iToken)); - address usedToken = collateralTokenAmount > loanTokenAmount - ? base - : IToken(iToken).loanTokenAddress(); - uint256 transferAmount = collateralTokenAmount > loanTokenAmount - ? collateralTokenAmount - : loanTokenAmount; + require(protocol.isLoanPool(iToken), "OrderBook: Not an iToken Contract"); + (address usedToken, uint256 transferAmount) = collateralTokenAmount > loanTokenAmount + ? (base, collateralTokenAmount) + : (IToken(iToken).loanTokenAddress(), loanTokenAmount); SafeERC20.safeTransferFrom( IERC20(usedToken), trader, address(this), transferAmount ); - loanDataBytes = ""; bytes32 loanID = IToken(iToken) .marginTrade( lID, @@ -90,55 +82,26 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { } } + function _isActiveLoan(bytes32 ID) internal view returns (bool) { + return protocol.loans(ID).active; + } + function _executeMarketClose( address trader, bytes32 loanID, uint256 amount, bool iscollateral, - address loanTokenAddress, - address collateralAddress, bytes memory loanDataBytes ) internal { - address usedToken; - loanDataBytes = ""; - if ( - (iscollateral && collateralAddress != WRAPPED_TOKEN) || - (!iscollateral && loanTokenAddress != WRAPPED_TOKEN) - ) { - usedToken = iscollateral ? collateralAddress : loanTokenAddress; - uint256 traderB = IERC20Metadata(usedToken).balanceOf(trader); - SafeERC20.safeTransferFrom( - IERC20(usedToken), - trader, - address(this), - traderB - ); - } else { - usedToken = address(0); - } - if (IBZx(protocol).getLoan(loanID).collateral == amount) { - _activeTrades[trader].remove(loanID); - } - IBZx(protocol).closeWithSwap( + protocol.closeWithSwap( loanID, - address(this), + trader, amount, iscollateral, loanDataBytes ); - if (usedToken != address(0)) { - SafeERC20.safeTransfer( - IERC20(usedToken), - trader, - IERC20Metadata(usedToken).balanceOf(address(this)) - ); - } else { - WrappedToken(WRAPPED_TOKEN).deposit{value: address(this).balance}(); - SafeERC20.safeTransfer( - IERC20(WRAPPED_TOKEN), - trader, - IERC20Metadata(WRAPPED_TOKEN).balanceOf(address(this)) - ); + if (!_isActiveLoan(loanID)) { + _activeTrades[trader].remove(loanID); } } } diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 7f7a4c6e..7f67445f 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -8,23 +8,10 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { function initialize(address target) public onlyOwner { _setTarget(this.placeOrder.selector, target); - _setTarget(this.currentSwapRate.selector, target); _setTarget(this.amendOrder.selector, target); _setTarget(this.cancelOrder.selector, target); _setTarget(this.cancelOrderProtocol.selector, target); _setTarget(this.changeStopType.selector, target); - _setTarget(this.minimumAmount.selector, target); - } - - function currentSwapRate(address start, address end) - public - view - returns (uint256 executionPrice) - { - (executionPrice, ) = IPriceFeeds(IBZx(protocol).priceFeeds()).queryRate( - end, - start - ); } function queryRateReturn( @@ -33,7 +20,7 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { uint256 amount ) public view returns (uint256) { (uint256 executionPrice, uint256 precision) = IPriceFeeds( - IBZx(protocol).priceFeeds() + protocol.priceFeeds() ).queryRate(start, end); return (executionPrice * amount) / precision; } @@ -44,7 +31,7 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { returns (bool) { return - IBZx(protocol).getLoan(checkOrder.loanID).collateralToken == + protocol.getLoan(checkOrder.loanID).collateralToken == checkOrder.base; } @@ -54,7 +41,7 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { returns (bool) { return - IBZx(protocol).getLoan(checkOrder.loanID).loanToken == + protocol.getLoan(checkOrder.loanID).loanToken == checkOrder.loanTokenAddress; } @@ -68,49 +55,48 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { int256(Order.loanTokenAmount) - int256(Order.collateralTokenAmount) ) == int256(Order.loanTokenAmount + Order.collateralTokenAmount), - "only one token can be used" + "OrderBook: only one token can be used" ); require( - IBZx(protocol).supportedTokens(Order.loanTokenAddress) && - IBZx(protocol).supportedTokens(Order.base), - "invalid pair" + protocol.supportedTokens(Order.loanTokenAddress) && + protocol.supportedTokens(Order.base), + "OrderBook: invalid pair" ); require( Order.loanID != 0 ? _collateralTokenMatch(Order) && _loanTokenMatch(Order) : true, - "incorrect collateral and/or loan token specified" + "OrderBook: incorrect collateral and/or loan token specified" ); require( Order.orderType == IOrderBook.OrderType.LIMIT_OPEN - ? IBZx(protocol).isLoanPool(Order.iToken) - : true + ? protocol.isLoanPool(Order.iToken) + : true, + "OrderBook: invalid iToken specified" ); require( Order.orderType == IOrderBook.OrderType.LIMIT_OPEN ? Order.loanID == 0 || _activeTrades[msg.sender].contains(Order.loanID) : _activeTrades[msg.sender].contains(Order.loanID), - "inactive loan" + "OrderBook: inactive loan" ); - uint256 amountUsed = Order.loanTokenAmount + - Order.collateralTokenAmount; //one is always 0 so correct amount and no overflow issues - address usedToken = Order.loanTokenAmount > Order.collateralTokenAmount - ? Order.loanTokenAddress - : Order.base; + (uint256 amountUsed, address usedToken) = Order.loanTokenAmount > Order.collateralTokenAmount + ? (Order.loanTokenAmount, Order.loanTokenAddress) + : (Order.collateralTokenAmount, Order.base); require( - (currentSwapRate(usedToken, USDC) * amountUsed) / - 10**(IERC20Metadata(usedToken).decimals()) > - MIN_AMOUNT_IN_USDC + queryRateReturn(usedToken, USDC, amountUsed) > + MIN_AMOUNT_IN_USDC, + "OrderBook: Order too small" ); - require(Order.trader == msg.sender); - require(!Order.isCancelled); + require(Order.trader == msg.sender, "OrderBook: invalid trader"); + require(!Order.isCancelled, "OrderBook: invalid order state"); mainOBID++; bytes32 ID = keccak256(abi.encode(msg.sender, mainOBID)); - _orderExpiration[ID] = block.timestamp + DAYS_14; + require(IDeposits(vault).getTokenUsed(ID) == address(0), "Orderbook: collision"); //in the very unlikely chance of collision on ID error is thrown _allOrders[ID] = Order; _allOrders[ID].orderID = ID; - _histOrders[msg.sender].add(Order.orderID); + _histOrders[msg.sender].add(ID); _allOrderIDs.add(ID); if (Order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { IDeposits(vault).deposit(ID, amountUsed, msg.sender, usedToken); @@ -131,14 +117,13 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { int256(Order.loanTokenAmount) - int256(Order.collateralTokenAmount) ) == int256(Order.loanTokenAmount + Order.collateralTokenAmount), - "only one token can be used" + "OrderBook: only one token can be used" ); - //uint256 swapRate = currentSwapRate(Order.loanTokenAddress, Order.base); require( Order.base == _allOrders[Order.orderID].base && Order.loanTokenAddress == _allOrders[Order.orderID].loanTokenAddress, - "invalid tokens" + "OrderBook: invalid tokens" ); /*require( Order.price > swapRate @@ -148,41 +133,32 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { );*/ require( Order.orderType == IOrderBook.OrderType.LIMIT_OPEN - ? IBZx(protocol).isLoanPool(Order.iToken) + ? protocol.isLoanPool(Order.iToken) : true ); - require( - Order.orderID == _allOrders[Order.orderID].orderID, - "improper ID" - ); require( !_allOrders[Order.orderID].isCancelled, - "inactive order specified" + "OrderBook: inactive order specified" ); require( Order.orderType == IOrderBook.OrderType.LIMIT_OPEN ? Order.loanID == 0 || _activeTrades[msg.sender].contains(Order.loanID) : _activeTrades[msg.sender].contains(Order.loanID), - "inactive loan" + "OrderBook: inactive loan" ); - require(Order.trader == msg.sender); - require(!_allOrders[Order.orderID].isCancelled); - _orderExpiration[Order.orderID] = block.timestamp + DAYS_14; + require(Order.trader == msg.sender, "OrderBook: invalid trader"); if (Order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { - uint256 amountUsed = Order.loanTokenAmount + - Order.collateralTokenAmount; //one is always 0 so correct amount and no overflow issues - address usedToken = Order.loanTokenAmount > - Order.collateralTokenAmount - ? Order.loanTokenAddress - : Order.base; + (uint256 amountUsed, address usedToken) = Order.loanTokenAmount > Order.collateralTokenAmount + ? (Order.loanTokenAmount, Order.loanTokenAddress) + : (Order.collateralTokenAmount, Order.base); uint256 storedAmount = IDeposits(vault).getDeposit( - msg.sender, Order.orderID ); require( usedToken == - IDeposits(vault).getTokenUsed(msg.sender, Order.orderID) + IDeposits(vault).getTokenUsed(Order.orderID), + "OrderBook: invalid used token" ); uint256 amountUsedOld = _allOrders[Order.orderID].loanTokenAmount + _allOrders[Order.orderID].collateralTokenAmount; @@ -202,7 +178,6 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { } } _allOrders[Order.orderID] = Order; - emit OrderAmended( msg.sender, Order.orderType, @@ -214,13 +189,12 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { } function cancelOrder(bytes32 orderID) public { - require(!_allOrders[orderID].isCancelled, "inactive order"); + require(!_allOrders[orderID].isCancelled, "OrderBook: inactive order"); _allOrders[orderID].isCancelled = true; - _histOrders[msg.sender].remove(orderID); + require(_histOrders[msg.sender].remove(orderID), "OrderBook: not owner of order"); _allOrderIDs.remove(orderID); if (_allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN) { address usedToken = IDeposits(vault).getTokenUsed( - msg.sender, orderID ); IDeposits(vault).withdrawToTrader(msg.sender, orderID); @@ -229,40 +203,41 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { } function cancelOrderProtocol(bytes32 orderID) public { - address trader = _allOrders[orderID].trader; - require(!_allOrders[orderID].isCancelled, "inactive order"); - uint256 amountUsed = _allOrders[orderID].collateralTokenAmount + - _allOrders[orderID].loanTokenAmount; + IOrderBook.Order memory order = _allOrders[orderID]; + address trader = order.trader; + require(!order.isCancelled, "OrderBook: inactive order"); + uint256 amountUsed = order.collateralTokenAmount + + order.loanTokenAmount; uint256 swapRate; - if (_allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN) { + if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { swapRate = queryRateReturn( - _allOrders[orderID].loanTokenAddress, - _allOrders[orderID].base, + order.loanTokenAddress, + order.base, amountUsed ); } else { swapRate = queryRateReturn( - _allOrders[orderID].base, - _allOrders[orderID].loanTokenAddress, + order.base, + order.loanTokenAddress, amountUsed ); } require( ( - _allOrders[orderID].amountReceived > swapRate - ? (_allOrders[orderID].amountReceived - swapRate) > - (_allOrders[orderID].amountReceived * 25) / 100 - : (swapRate - _allOrders[orderID].amountReceived) > + order.amountReceived > swapRate + ? (order.amountReceived - swapRate) > + (order.amountReceived * 25) / 100 + : (swapRate - order.amountReceived) > (swapRate * 25) / 100 - ) || _orderExpiration[orderID] < block.timestamp, - "no conditions met" + ) || order.timeTillExpiration < block.timestamp, + "OrderBook: no conditions met" ); - _allOrders[orderID].isCancelled = true; + order.isCancelled = true; _histOrders[trader].remove(orderID); _allOrderIDs.remove(orderID); - if (_allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN) { - address usedToken = IDeposits(vault).getTokenUsed(trader, orderID); + if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { + address usedToken = IDeposits(vault).getTokenUsed(orderID); IDeposits(vault).withdrawToTrader(trader, orderID); } emit OrderCancelled(trader, orderID); @@ -271,8 +246,4 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { function changeStopType(bool stop) public { _useOracle[msg.sender] = stop; } - - function minimumAmount() public view returns (uint256) { - return MIN_AMOUNT_IN_USDC; - } } diff --git a/contracts/orderbook/OrderBookProxy.sol b/contracts/orderbook/OrderBookProxy.sol index d8e722a0..7a708c8f 100644 --- a/contracts/orderbook/OrderBookProxy.sol +++ b/contracts/orderbook/OrderBookProxy.sol @@ -3,7 +3,7 @@ import "./Storage/OrderBookStorage.sol"; import "./Storage/OrderBookEvents.sol"; contract OrderBookProxy is OrderBookEvents, OrderBookStorage { - constructor(address _contract) { + constructor(IBZx _contract) { protocol = _contract; } diff --git a/contracts/orderbook/OrderVault/Deposits.sol b/contracts/orderbook/OrderVault/Deposits.sol index befb7748..3bca9c33 100644 --- a/contracts/orderbook/OrderVault/Deposits.sol +++ b/contracts/orderbook/OrderVault/Deposits.sol @@ -9,15 +9,15 @@ contract Deposits is Ownable { uint256 depositAmount; } mapping(bytes32 => DepositInfo) internal _depositInfo; - address public OrderBook = address(0); + address public orderBook = address(0); function deposit( bytes32 orderID, uint256 tokenAmount, address trader, address token - ) public { - require(msg.sender == OrderBook, "unauthorized"); + ) external { + require(msg.sender == orderBook, "unauthorized"); _depositInfo[orderID].depositToken = token; _depositInfo[orderID].depositAmount = tokenAmount; SafeERC20.safeTransferFrom( @@ -28,12 +28,12 @@ contract Deposits is Ownable { ); } - function SetOrderBook(address n) public onlyOwner { - OrderBook = n; + function setOrderBook(address n) external onlyOwner { + orderBook = n; } - function withdraw(address trader, bytes32 orderID) public { - require(msg.sender == OrderBook, "unauthorized"); + function withdraw(bytes32 orderID) external { + require(msg.sender == orderBook, "unauthorized"); SafeERC20.safeTransfer( IERC20(_depositInfo[orderID].depositToken), msg.sender, @@ -42,11 +42,11 @@ contract Deposits is Ownable { _depositInfo[orderID].depositAmount = 0; } - function withdrawToTrader(address trader, bytes32 orderID) public { - require(msg.sender == OrderBook, "unauthorized"); + function withdrawToTrader(address trader, bytes32 orderID) external { + require(msg.sender == orderBook, "unauthorized"); SafeERC20.safeTransfer( IERC20(_depositInfo[orderID].depositToken), - msg.sender, + trader, _depositInfo[orderID].depositAmount ); _depositInfo[orderID].depositAmount = 0; @@ -56,8 +56,8 @@ contract Deposits is Ownable { address trader, bytes32 orderID, uint256 amount - ) public { - require(msg.sender == OrderBook, "unauthorized"); + ) external { + require(msg.sender == orderBook, "unauthorized"); SafeERC20.safeTransfer( IERC20(_depositInfo[orderID].depositToken), trader, @@ -66,16 +66,16 @@ contract Deposits is Ownable { _depositInfo[orderID].depositAmount -= amount; } - function getDeposit(address trader, bytes32 orderID) - public + function getDeposit(bytes32 orderID) + external view returns (uint256) { return _depositInfo[orderID].depositAmount; } - function getTokenUsed(address trader, bytes32 orderID) - public + function getTokenUsed(bytes32 orderID) + external view returns (address) { diff --git a/contracts/orderbook/OrderVault/IDeposits.sol b/contracts/orderbook/OrderVault/IDeposits.sol index f9f5bdae..2e429f3c 100644 --- a/contracts/orderbook/OrderVault/IDeposits.sol +++ b/contracts/orderbook/OrderVault/IDeposits.sol @@ -8,7 +8,7 @@ interface IDeposits { address token ) external; - function withdraw(address trader, bytes32 orderID) external; + function withdraw(bytes32 orderID) external; function withdrawToTrader(address trader, bytes32 orderID) external; @@ -18,12 +18,12 @@ interface IDeposits { uint256 amount ) external; - function getDeposit(address trader, bytes32 orderID) + function getDeposit(bytes32 orderID) external view returns (uint256); - function getTokenUsed(address trader, bytes32 orderID) + function getTokenUsed(bytes32 orderID) external view returns (address); diff --git a/contracts/orderbook/Storage/OrderBookStorage.sol b/contracts/orderbook/Storage/OrderBookStorage.sol index d488ea4b..e3a24652 100644 --- a/contracts/orderbook/Storage/OrderBookStorage.sol +++ b/contracts/orderbook/Storage/OrderBookStorage.sol @@ -1,25 +1,22 @@ pragma solidity ^0.8.0; -import "../WrappedToken.sol"; import "../../../interfaces/IPriceFeeds.sol"; import "../../../interfaces/IToken.sol"; -import "../../../interfaces/IBZX.sol"; +import "../../../interfaces/IBZx.sol"; import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; contract OrderBookStorage { - mapping(bytes4 => address) public logicTargets; - address public vault = address(0); - address public protocol = address(0); address public constant WRAPPED_TOKEN = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; - address public constant UNI_FACTORY = - 0xc35DADB65012eC5796536bD9864eD8773aBc74C4; - uint256 public mainOBID = 0; - uint256 public DAYS_14 = 86400 * 14; - uint256 public MIN_AMOUNT_IN_USDC = 1 * 10**15; + uint256 public constant MIN_AMOUNT_IN_USDC = 1e15; address public constant USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; + mapping(bytes4 => address) public logicTargets; + address public vault; + IBZx public protocol; + uint256 public mainOBID; + function _setTarget(bytes4 sig, address target) internal { logicTargets[sig] = target; } diff --git a/contracts/orderbook/WrappedToken.sol b/contracts/orderbook/WrappedToken.sol deleted file mode 100644 index 22e4d4c1..00000000 --- a/contracts/orderbook/WrappedToken.sol +++ /dev/null @@ -1,5 +0,0 @@ -pragma solidity ^0.8.0; - -abstract contract WrappedToken { - function deposit() public payable virtual; -} From 9e28dfa4e18269738049c069bbd8dd8bb2f0ad7f Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Sun, 13 Mar 2022 16:44:06 -0700 Subject: [PATCH 42/83] minor fixes and limited test case --- contracts/orderbook/IOrderBook.sol | 10 +-- contracts/orderbook/Keepers/OrderKeeper.sol | 16 +++-- .../orderbook/Keepers/OrderKeeperClear.sol | 4 +- contracts/orderbook/Logic/OrderBook.sol | 4 +- .../orderbook/Logic/OrderBookOrderPlace.sol | 11 ++-- .../orderbook/Storage/OrderBookStorage.sol | 6 +- .../connectors/SwapsImplUniswapV3_ETH.sol | 4 ++ testsarbitrum/limit-orders/test_Orders.py | 63 +++++++++++++++++++ 8 files changed, 97 insertions(+), 21 deletions(-) create mode 100644 testsarbitrum/limit-orders/test_Orders.py diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index 9cf5a2f7..9f62ee74 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -24,11 +24,9 @@ interface IOrderBook { bytes loanDataBytes; } - function getRouter() external view returns (address); - function placeOrder(Order calldata order) external; - function amendOrder(Order calldata order, uint256 orderID) external; + function amendOrder(Order calldata order) external; function cancelOrder(bytes32 orderID) external; @@ -38,9 +36,9 @@ interface IOrderBook { function getOrdersLimited(uint256 start, uint256 end) external view returns(Order[] memory); - function getOrders(uint256 start, uint256 end) external view returns(Order[] memory); + function getOrders() external view returns(Order[] memory); - function getActiveOrders(address trader, uint256 start, uint256 end) external view returns(Order[] memory); + function getActiveOrders(address trader) external view returns(Order[] memory); function getActiveOrdersLimited(address trader, uint256 start, uint256 end) external view returns(Order[] memory); @@ -53,4 +51,6 @@ interface IOrderBook { function prelimCheck(bytes32 orderID) external returns (bool); function getTotalActiveOrders() external view returns (uint256); + + function getActiveTrades(address trader) external view returns(bytes32[] memory); } diff --git a/contracts/orderbook/Keepers/OrderKeeper.sol b/contracts/orderbook/Keepers/OrderKeeper.sol index 56189146..63b29d6d 100644 --- a/contracts/orderbook/Keepers/OrderKeeper.sol +++ b/contracts/orderbook/Keepers/OrderKeeper.sol @@ -9,7 +9,7 @@ contract OrderKeeper { factory = factoryAddress; } - function checkUpkeep(bytes calldata checkData) + function checkUpKeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData) { @@ -25,17 +25,21 @@ contract OrderKeeper { IOrderBook.Order[] memory listOfMainOrders = factory .getOrdersLimited(start, end); for (uint256 x = 0; x < listOfMainOrders.length;) { - if (factory.prelimCheck(listOfMainOrders[x].orderID)) { - upkeepNeeded = true; - performData = abi.encode(listOfMainOrders[x].orderID); - return (upkeepNeeded, performData); + try factory.prelimCheck(listOfMainOrders[x].orderID) returns (bool isExecutable) { + if(isExecutable) { + upkeepNeeded = true; + performData = abi.encode(listOfMainOrders[x].orderID); + return (upkeepNeeded, performData); + } + } catch Error (string memory) { + } unchecked { ++x; } } return (upkeepNeeded, performData); } - function performUpkeep(bytes calldata performData) public { + function performUpKeep(bytes calldata performData) public { bytes32 orderId = abi.decode(performData, (bytes32)); //emit OrderExecuted(trader,orderId); factory.executeOrder(orderId); diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol index 46d3d2f3..2cbe2113 100644 --- a/contracts/orderbook/Keepers/OrderKeeperClear.sol +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -9,7 +9,7 @@ contract OrderKeeperClear { factory = factoryAddress; } - function checkUpkeep(bytes calldata checkData) + function checkUpKeep(bytes calldata checkData) public view returns (bool upkeepNeeded, bytes memory performData) @@ -36,7 +36,7 @@ contract OrderKeeperClear { return (upkeepNeeded, performData); } - function performUpkeep(bytes calldata performData) public { + function performUpKeep(bytes calldata performData) public { bytes32 orderId = abi.decode(performData, (bytes32)); //emit OrderExecuted(trader,orderId); factory.cancelOrderProtocol(orderId); diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index d568d10c..acb17e14 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -151,6 +151,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } else { payload = abi.encode(order.base, order.loanTokenAddress); } + dexID = 1; } ISwapsImpl swapImpl = ISwapsImpl( IDexRecords(getSwapAddress()).retrieveDexAddress(dexID) @@ -297,12 +298,13 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } else { payload = abi.encode(order.base, order.loanTokenAddress); } + dexID = 1; } ISwapsImpl swapImpl = ISwapsImpl( IDexRecords(getSwapAddress()).retrieveDexAddress(dexID) ); srcToken = order.collateralTokenAmount > order.loanTokenAmount ? order.base : order.loanTokenAddress; - require(order.timeTillExpiration < block.timestamp, "OrderBook: Order Expired"); + require(order.timeTillExpiration > block.timestamp, "OrderBook: Order Expired"); if (_allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN) { uint256 dSwapValue; if (srcToken == order.loanTokenAddress) { diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 7f67445f..57fd2cb7 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -3,7 +3,7 @@ import "../Storage/OrderBookEvents.sol"; import "../Storage/OrderBookStorage.sol"; import "../OrderVault/IDeposits.sol"; -contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { +contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { using EnumerableSet for EnumerableSet.Bytes32Set; function initialize(address target) public onlyOwner { @@ -50,6 +50,7 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { } function placeOrder(IOrderBook.Order calldata Order) external { + require(Order.loanDataBytes.length < 3500, "OrderBook: loanDataBytes too complex"); require( _abs( int256(Order.loanTokenAmount) - @@ -85,7 +86,7 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { ? (Order.loanTokenAmount, Order.loanTokenAddress) : (Order.collateralTokenAmount, Order.base); require( - queryRateReturn(usedToken, USDC, amountUsed) > + queryRateReturn(usedToken, USDC, amountUsed) >= MIN_AMOUNT_IN_USDC, "OrderBook: Order too small" ); @@ -111,7 +112,8 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { ); } - function amendOrder(IOrderBook.Order memory Order) public { + function amendOrder(IOrderBook.Order calldata Order) external { + require(Order.loanDataBytes.length < 3500, "OrderBook: loanDataBytes too complex"); require( _abs( int256(Order.loanTokenAmount) - @@ -134,7 +136,8 @@ contract OrderBookOrderPlacement is OrderBookEvents, OrderBookStorage { require( Order.orderType == IOrderBook.OrderType.LIMIT_OPEN ? protocol.isLoanPool(Order.iToken) - : true + : true, + "OrderBook: invalid iToken specified" ); require( !_allOrders[Order.orderID].isCancelled, diff --git a/contracts/orderbook/Storage/OrderBookStorage.sol b/contracts/orderbook/Storage/OrderBookStorage.sol index e3a24652..e80f0969 100644 --- a/contracts/orderbook/Storage/OrderBookStorage.sol +++ b/contracts/orderbook/Storage/OrderBookStorage.sol @@ -8,9 +8,9 @@ import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; contract OrderBookStorage { address public constant WRAPPED_TOKEN = - 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; - uint256 public constant MIN_AMOUNT_IN_USDC = 1e15; - address public constant USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; + 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1; + uint256 public constant MIN_AMOUNT_IN_USDC = 1e6; + address public constant USDC = 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8; mapping(bytes4 => address) public logicTargets; address public vault; diff --git a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol index 5b11b8f6..e4e4d6b8 100644 --- a/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol +++ b/contracts/swaps/connectors/SwapsImplUniswapV3_ETH.sol @@ -101,6 +101,8 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { exactParams[0].amountIn = exactParams[0].amountIn.add( amountIn.sub(totalAmounts) ); //adds displacement to first swap set + } else { + return dexAmountOut(exactParams[0].path, amountIn); //this else intentionally ignores the other swap impls. It is specifically designed to avoid edge cases } uint256 tempAmountOut; for (uint256 i = 0; i < exactParams.length; i++) { @@ -141,6 +143,8 @@ contract SwapsImplUniswapV3_ETH is State, ISwapsImpl { exactParams[0].amountOut = exactParams[0].amountOut.add( amountOut.sub(totalAmounts) ); //adds displacement to first swap set + } else { + return dexAmountIn(exactParams[0].path, amountOut); //this else intentionally ignores the other swap impls. It is specifically designed to avoid edge cases } uint256 tempAmountIn; for (uint256 i = 0; i < exactParams.length; i++) { diff --git a/testsarbitrum/limit-orders/test_Orders.py b/testsarbitrum/limit-orders/test_Orders.py new file mode 100644 index 00000000..2e5b5645 --- /dev/null +++ b/testsarbitrum/limit-orders/test_Orders.py @@ -0,0 +1,63 @@ +from brownie import * +import pytest +from eth_abi import encode_abi +accounts = ['0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266'] + +def test_main(): + orderbook, keeper = deploy_contracts() + set_perms(orderbook) + orderbook = interface.IOrderBook(orderbook.address) + place_order_open(orderbook) + print(orderbook.prelimCheck.call(orderbook.getActiveOrders(accounts[0])[0][1])) + orderbook.executeOrder(orderbook.getActiveOrders(accounts[0])[0][1], {'from':accounts[0]}) + trades = orderbook.getActiveTrades(accounts[0]) + place_order_close(orderbook, trades[0], int(7.4e14)) + print(orderbook.getOrders()) + isExec, data = keeper.checkUpKeep.call(encode_abi(['uint256','uint256'],[0,orderbook.getTotalActiveOrders()])) + keeper.checkUpKeep(encode_abi(['uint256','uint256'],[0,orderbook.getTotalActiveOrders()]), {'from':accounts[0]}) + if(isExec): + keeper.performUpKeep(data) + print(orderbook.getActiveOrders(accounts[0])) + assert(False) + +def deploy_contracts(): + orderbook = OrderBookProxy.deploy('0x37407F3178ffE07a6cF5C847F8f680FEcf319FAB', {'from':accounts[0]}) + orderbookLogic = OrderBook.deploy({'from':accounts[0]}) + orderBookData = OrderBookData.deploy({'from':accounts[0]}) + orderBookMarketOrders = OrderBookMarketOrders.deploy({'from':accounts[0]}) + orderBookOrderPlace = OrderBookOrderPlace.deploy({'from':accounts[0]}) + orderbook.replaceContract(orderbookLogic.address, {'from':orderbook.owner()}) + orderbook.replaceContract(orderBookData.address, {'from':orderbook.owner()}) + orderbook.replaceContract(orderBookMarketOrders.address, {'from':orderbook.owner()}) + orderbook.replaceContract(orderBookOrderPlace.address, {'from':orderbook.owner()}) + d = Deposits.deploy({'from':accounts[0]}) + d.setOrderBook(orderbook.address, {'from':accounts[0]}) + Contract.from_abi('',orderbook.address,OrderBook.abi).setVaultAddress(d.address, {'from':orderbook.owner()}) + keeper = OrderKeeper.deploy(orderbook.address, {'from':accounts[0]}) + return orderbook, keeper + +def set_perms(orderbook): + Contract.from_abi('',orderbook.address,OrderBookData.abi).adjustAllowance('0xEDa7f294844808B7C93EE524F990cA7792AC2aBd', + '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', {'from':orderbook.owner()}) + interface.IERC20('0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8').transfer(accounts[0], 200e6, {'from':'0x489ee077994b6658eafa855c308275ead8097c4a'}) + interface.IERC20('0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8').approve(orderbook.vault.call(), 2000e6, {'from':accounts[0]}) +def place_order_open(orderbook_contract): + orderdata = [0,0,3e14,2e18,1e6,0,(1647152473+60*60*24*30),accounts[0], + '0xEDa7f294844808B7C93EE524F990cA7792AC2aBd','0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8','0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', + 0,False,False,b''] + orderbook_contract.placeOrder(orderdata, {'from':accounts[0]}) + +def place_order_close(orderbook_contract, position, size): + orderdata = [position,0,10,0,0,size,(1647152473+60*60*24*30),accounts[0], + '0xEDa7f294844808B7C93EE524F990cA7792AC2aBd','0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8','0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', + 1,False,False,b''] + orderbook_contract.placeOrder(orderdata, {'from':accounts[0]}) + +def amend_order(): + print() + +def cancel_order(): + print() + +def get_order_specs(orderID): + return \ No newline at end of file From 4355ece5aaa02e176aefa1699eb48b31cad3657e Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Sun, 13 Mar 2022 19:30:47 -0700 Subject: [PATCH 43/83] edge case handling and minor improvements --- contracts/orderbook/Keepers/OrderKeeper.sol | 2 +- .../orderbook/Keepers/OrderKeeperClear.sol | 4 ++-- contracts/orderbook/Logic/OrderBook.sol | 8 +++++--- contracts/orderbook/Logic/OrderBookData.sol | 18 +++++++++--------- .../orderbook/Logic/OrderBookMarketOrders.sol | 4 ++-- .../orderbook/Logic/OrderBookOrderPlace.sol | 15 ++++++++++++--- contracts/orderbook/OrderVault/Deposits.sol | 15 ++++++++++++++- contracts/orderbook/OrderVault/IDeposits.sol | 2 ++ 8 files changed, 47 insertions(+), 21 deletions(-) diff --git a/contracts/orderbook/Keepers/OrderKeeper.sol b/contracts/orderbook/Keepers/OrderKeeper.sol index 63b29d6d..5cb7a70f 100644 --- a/contracts/orderbook/Keepers/OrderKeeper.sol +++ b/contracts/orderbook/Keepers/OrderKeeper.sol @@ -39,7 +39,7 @@ contract OrderKeeper { return (upkeepNeeded, performData); } - function performUpKeep(bytes calldata performData) public { + function performUpKeep(bytes calldata performData) external { bytes32 orderId = abi.decode(performData, (bytes32)); //emit OrderExecuted(trader,orderId); factory.executeOrder(orderId); diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol index 2cbe2113..6c80ca63 100644 --- a/contracts/orderbook/Keepers/OrderKeeperClear.sol +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -10,7 +10,7 @@ contract OrderKeeperClear { } function checkUpKeep(bytes calldata checkData) - public + external view returns (bool upkeepNeeded, bytes memory performData) { @@ -36,7 +36,7 @@ contract OrderKeeperClear { return (upkeepNeeded, performData); } - function performUpKeep(bytes calldata performData) public { + function performUpKeep(bytes calldata performData) external { bytes32 orderId = abi.decode(performData, (bytes32)); //emit OrderExecuted(trader,orderId); factory.cancelOrderProtocol(orderId); diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index acb17e14..b94c9753 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -42,6 +42,8 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { (bytes32) ); _activeTrades[internalOrder.trader].add(loanID); + } else if (!result) { + IDeposits(vault).refund(internalOrder.orderID, (internalOrder.loanTokenAmount + internalOrder.collateralTokenAmount)); //unlikely to be needed } } @@ -140,7 +142,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } } - function prelimCheck(bytes32 orderID) public returns (bool) { + function prelimCheck(bytes32 orderID) external returns (bool) { IOrderBook.Order memory order = _allOrders[orderID]; uint256 amountUsed; address srcToken; @@ -286,7 +288,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } } - function executeOrder(bytes32 orderID) public { + function executeOrder(bytes32 orderID) external { require(!_allOrders[orderID].isCancelled, "OrderBook: non active"); IOrderBook.Order memory order = _allOrders[orderID]; address srcToken; @@ -414,7 +416,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } } - function setVaultAddress(address nVault) public onlyOwner { + function setVaultAddress(address nVault) external onlyOwner { vault = nVault; } } diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index ffc03f79..d289ce84 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -23,7 +23,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { _setTarget(this.getActiveTrades.selector, target); } - function adjustAllowance(address spender, address token) public { + function adjustAllowance(address spender, address token) external { require( protocol.isLoanPool(spender) || address(protocol) == spender || @@ -35,7 +35,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { } function getActiveOrders(address trader) - public + external view returns (IOrderBook.Order[] memory fullList) { @@ -50,7 +50,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { } function getActiveOrdersLimited(address trader, uint start, uint end) - public + external view returns (IOrderBook.Order[] memory fullList) { @@ -72,23 +72,23 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { } function getActiveOrderIDs(address trader) - public + external view returns (bytes32[] memory) { return _histOrders[trader].values(); } - function getTotalOrders(address trader) public view returns (uint256) { + function getTotalOrders(address trader) external view returns (uint256) { return _histOrders[trader].length(); } - function getTotalActiveOrders() public view returns (uint256) { + function getTotalActiveOrders() external view returns (uint256) { return _allOrderIDs.length(); } function getOrders() - public + external view returns (IOrderBook.Order[] memory fullList) { @@ -103,7 +103,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { } function getOrdersLimited(uint start, uint end) - public + external view returns (IOrderBook.Order[] memory fullList) { @@ -117,7 +117,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { } function getActiveTrades(address trader) - public + external view returns (bytes32[] memory) { diff --git a/contracts/orderbook/Logic/OrderBookMarketOrders.sol b/contracts/orderbook/Logic/OrderBookMarketOrders.sol index 8ba593c0..1b2f078c 100644 --- a/contracts/orderbook/Logic/OrderBookMarketOrders.sol +++ b/contracts/orderbook/Logic/OrderBookMarketOrders.sol @@ -18,7 +18,7 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { address iToken, address base, bytes memory loanDataBytes - ) public { + ) external { _executeMarketOpen( msg.sender, loanID, @@ -36,7 +36,7 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { uint256 amount, bool iscollateral, bytes memory loanDataBytes - ) public { + ) external { _executeMarketClose( msg.sender, loanID, diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 57fd2cb7..75de06e4 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -12,6 +12,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { _setTarget(this.cancelOrder.selector, target); _setTarget(this.cancelOrderProtocol.selector, target); _setTarget(this.changeStopType.selector, target); + _setTarget(this.recoverFundsFromFailedOrder.selector, target); } function queryRateReturn( @@ -191,7 +192,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { ); } - function cancelOrder(bytes32 orderID) public { + function cancelOrder(bytes32 orderID) external { require(!_allOrders[orderID].isCancelled, "OrderBook: inactive order"); _allOrders[orderID].isCancelled = true; require(_histOrders[msg.sender].remove(orderID), "OrderBook: not owner of order"); @@ -205,7 +206,15 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { emit OrderCancelled(msg.sender, orderID); } - function cancelOrderProtocol(bytes32 orderID) public { + function recoverFundsFromFailedOrder(bytes32 orderID) external { + IOrderBook.Order memory order = _allOrders[orderID]; + require(msg.sender == order.trader, "OrderBook: Not trade owner"); + require(order.isCancelled, "OrderBook: Order not executed"); + require(!_allOrderIDs.contains(orderID), "OrderBook: Order still in records"); + IDeposits(vault).withdrawToTrader(msg.sender, orderID); + } + + function cancelOrderProtocol(bytes32 orderID) external { IOrderBook.Order memory order = _allOrders[orderID]; address trader = order.trader; require(!order.isCancelled, "OrderBook: inactive order"); @@ -246,7 +255,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { emit OrderCancelled(trader, orderID); } - function changeStopType(bool stop) public { + function changeStopType(bool stop) external { _useOracle[msg.sender] = stop; } } diff --git a/contracts/orderbook/OrderVault/Deposits.sol b/contracts/orderbook/OrderVault/Deposits.sol index 3bca9c33..c0e4864d 100644 --- a/contracts/orderbook/OrderVault/Deposits.sol +++ b/contracts/orderbook/OrderVault/Deposits.sol @@ -18,8 +18,10 @@ contract Deposits is Ownable { address token ) external { require(msg.sender == orderBook, "unauthorized"); + require(_depositInfo[orderID].depositToken == address(0)|| _depositInfo[orderID].depositToken == token, + "Deposits: invalid token specified"); _depositInfo[orderID].depositToken = token; - _depositInfo[orderID].depositAmount = tokenAmount; + _depositInfo[orderID].depositAmount += tokenAmount; SafeERC20.safeTransferFrom( IERC20(token), trader, @@ -52,6 +54,17 @@ contract Deposits is Ownable { _depositInfo[orderID].depositAmount = 0; } + function refund(bytes32 orderID, uint256 amount) external { + require(msg.sender == orderBook, "unauthorized"); + SafeERC20.safeTransferFrom( + IERC20(_depositInfo[orderID].depositToken), + orderBook, + address(this), + amount + ); + _depositInfo[orderID].depositAmount += amount; + } + function partialWithdraw( address trader, bytes32 orderID, diff --git a/contracts/orderbook/OrderVault/IDeposits.sol b/contracts/orderbook/OrderVault/IDeposits.sol index 2e429f3c..014c1778 100644 --- a/contracts/orderbook/OrderVault/IDeposits.sol +++ b/contracts/orderbook/OrderVault/IDeposits.sol @@ -12,6 +12,8 @@ interface IDeposits { function withdrawToTrader(address trader, bytes32 orderID) external; + function refund(bytes32 orderID, uint256 amount) external; + function partialWithdraw( address trader, bytes32 orderID, From b00403cfea1f642a20cf3f9643819c310c6630ed Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Sun, 13 Mar 2022 19:38:16 -0700 Subject: [PATCH 44/83] pausable guardian for orderbook contracts --- contracts/governance/PausableGuardian_0_8.sol | 60 +++++++++++++++++++ contracts/orderbook/Keepers/OrderKeeper.sol | 5 +- .../orderbook/Keepers/OrderKeeperClear.sol | 5 +- contracts/orderbook/Logic/OrderBook.sol | 4 +- contracts/orderbook/Logic/OrderBookData.sol | 2 +- .../orderbook/Logic/OrderBookMarketOrders.sol | 4 +- .../orderbook/Logic/OrderBookOrderPlace.sol | 12 ++-- contracts/orderbook/OrderVault/Deposits.sol | 14 ++--- .../orderbook/Storage/OrderBookEvents.sol | 4 +- 9 files changed, 86 insertions(+), 24 deletions(-) create mode 100644 contracts/governance/PausableGuardian_0_8.sol diff --git a/contracts/governance/PausableGuardian_0_8.sol b/contracts/governance/PausableGuardian_0_8.sol new file mode 100644 index 00000000..1a2914ee --- /dev/null +++ b/contracts/governance/PausableGuardian_0_8.sol @@ -0,0 +1,60 @@ +/** + * Copyright 2017-2022, OokiDao. All Rights Reserved. + * Licensed under the Apache License, Version 2.0. + */ + +pragma solidity ^0.8.0; + +import "@openzeppelin-4.3.2/access/Ownable.sol"; + + +contract PausableGuardian_0_8 is Ownable { + + // keccak256("Pausable_FunctionPause") + bytes32 internal constant Pausable_FunctionPause = 0xa7143c84d793a15503da6f19bf9119a2dac94448ca45d77c8bf08f57b2e91047; + + // keccak256("Pausable_GuardianAddress") + bytes32 internal constant Pausable_GuardianAddress = 0x80e6706973d0c59541550537fd6a33b971efad732635e6c3b99fb01006803cdf; + + modifier pausable { + require(!_isPaused(msg.sig), "paused"); + _; + } + + function _isPaused(bytes4 sig) public view returns (bool isPaused) { + bytes32 slot = keccak256(abi.encodePacked(sig, Pausable_FunctionPause)); + assembly { + isPaused := sload(slot) + } + } + + function toggleFunctionPause(bytes4 sig) public { + require(msg.sender == getGuardian() || msg.sender == owner(), "unauthorized"); + bytes32 slot = keccak256(abi.encodePacked(sig, Pausable_FunctionPause)); + assembly { + sstore(slot, 1) + } + } + + function toggleFunctionUnPause(bytes4 sig) public { + // only DAO can unpause, and adding guardian temporarily + require(msg.sender == getGuardian() || msg.sender == owner(), "unauthorized"); + bytes32 slot = keccak256(abi.encodePacked(sig, Pausable_FunctionPause)); + assembly { + sstore(slot, 0) + } + } + + function changeGuardian(address newGuardian) public { + require(msg.sender == getGuardian() || msg.sender == owner(), "unauthorized"); + assembly { + sstore(Pausable_GuardianAddress, newGuardian) + } + } + + function getGuardian() public view returns (address guardian) { + assembly { + guardian := sload(Pausable_GuardianAddress) + } + } +} \ No newline at end of file diff --git a/contracts/orderbook/Keepers/OrderKeeper.sol b/contracts/orderbook/Keepers/OrderKeeper.sol index 5cb7a70f..30eb717f 100644 --- a/contracts/orderbook/Keepers/OrderKeeper.sol +++ b/contracts/orderbook/Keepers/OrderKeeper.sol @@ -1,8 +1,9 @@ pragma solidity ^0.8.0; import "../IOrderBook.sol"; import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; +import "../../governance/PausableGuardian_0_8.sol"; -contract OrderKeeper { +contract OrderKeeper is PausableGuardian_0_8 { IOrderBook public factory; constructor(IOrderBook factoryAddress) { @@ -39,7 +40,7 @@ contract OrderKeeper { return (upkeepNeeded, performData); } - function performUpKeep(bytes calldata performData) external { + function performUpKeep(bytes calldata performData) external pausable { bytes32 orderId = abi.decode(performData, (bytes32)); //emit OrderExecuted(trader,orderId); factory.executeOrder(orderId); diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol index 6c80ca63..7217e296 100644 --- a/contracts/orderbook/Keepers/OrderKeeperClear.sol +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -1,8 +1,9 @@ pragma solidity ^0.8.0; import "../IOrderBook.sol"; import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; +import "../../governance/PausableGuardian_0_8.sol"; -contract OrderKeeperClear { +contract OrderKeeperClear is PausableGuardian_0_8 { IOrderBook public factory; constructor(IOrderBook factoryAddress) { @@ -36,7 +37,7 @@ contract OrderKeeperClear { return (upkeepNeeded, performData); } - function performUpKeep(bytes calldata performData) external { + function performUpKeep(bytes calldata performData) external pausable { bytes32 orderId = abi.decode(performData, (bytes32)); //emit OrderExecuted(trader,orderId); factory.cancelOrderProtocol(orderId); diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index b94c9753..91f6f450 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -82,7 +82,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { return protocol.loans(ID).active; } - function clearOrder(bytes32 orderID) public view returns (bool) { + function clearOrder(bytes32 orderID) public view pausable returns (bool) { if (_allOrders[orderID].timeTillExpiration < block.timestamp) { return true; } @@ -288,7 +288,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } } - function executeOrder(bytes32 orderID) external { + function executeOrder(bytes32 orderID) external pausable { require(!_allOrders[orderID].isCancelled, "OrderBook: non active"); IOrderBook.Order memory order = _allOrders[orderID]; address srcToken; diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index d289ce84..1351debd 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -23,7 +23,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { _setTarget(this.getActiveTrades.selector, target); } - function adjustAllowance(address spender, address token) external { + function adjustAllowance(address spender, address token) external pausable { require( protocol.isLoanPool(spender) || address(protocol) == spender || diff --git a/contracts/orderbook/Logic/OrderBookMarketOrders.sol b/contracts/orderbook/Logic/OrderBookMarketOrders.sol index 1b2f078c..fd60781b 100644 --- a/contracts/orderbook/Logic/OrderBookMarketOrders.sol +++ b/contracts/orderbook/Logic/OrderBookMarketOrders.sol @@ -18,7 +18,7 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { address iToken, address base, bytes memory loanDataBytes - ) external { + ) external pausable { _executeMarketOpen( msg.sender, loanID, @@ -36,7 +36,7 @@ contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { uint256 amount, bool iscollateral, bytes memory loanDataBytes - ) external { + ) external pausable { _executeMarketClose( msg.sender, loanID, diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 75de06e4..462fe12e 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -50,7 +50,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { return x >= 0 ? x : -x; } - function placeOrder(IOrderBook.Order calldata Order) external { + function placeOrder(IOrderBook.Order calldata Order) external pausable { require(Order.loanDataBytes.length < 3500, "OrderBook: loanDataBytes too complex"); require( _abs( @@ -113,7 +113,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { ); } - function amendOrder(IOrderBook.Order calldata Order) external { + function amendOrder(IOrderBook.Order calldata Order) external pausable { require(Order.loanDataBytes.length < 3500, "OrderBook: loanDataBytes too complex"); require( _abs( @@ -192,7 +192,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { ); } - function cancelOrder(bytes32 orderID) external { + function cancelOrder(bytes32 orderID) external pausable { require(!_allOrders[orderID].isCancelled, "OrderBook: inactive order"); _allOrders[orderID].isCancelled = true; require(_histOrders[msg.sender].remove(orderID), "OrderBook: not owner of order"); @@ -206,7 +206,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { emit OrderCancelled(msg.sender, orderID); } - function recoverFundsFromFailedOrder(bytes32 orderID) external { + function recoverFundsFromFailedOrder(bytes32 orderID) external pausable { IOrderBook.Order memory order = _allOrders[orderID]; require(msg.sender == order.trader, "OrderBook: Not trade owner"); require(order.isCancelled, "OrderBook: Order not executed"); @@ -214,7 +214,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { IDeposits(vault).withdrawToTrader(msg.sender, orderID); } - function cancelOrderProtocol(bytes32 orderID) external { + function cancelOrderProtocol(bytes32 orderID) external pausable { IOrderBook.Order memory order = _allOrders[orderID]; address trader = order.trader; require(!order.isCancelled, "OrderBook: inactive order"); @@ -255,7 +255,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { emit OrderCancelled(trader, orderID); } - function changeStopType(bool stop) external { + function changeStopType(bool stop) external pausable { _useOracle[msg.sender] = stop; } } diff --git a/contracts/orderbook/OrderVault/Deposits.sol b/contracts/orderbook/OrderVault/Deposits.sol index c0e4864d..7e2f6c19 100644 --- a/contracts/orderbook/OrderVault/Deposits.sol +++ b/contracts/orderbook/OrderVault/Deposits.sol @@ -1,9 +1,9 @@ pragma solidity ^0.8.0; -import "@openzeppelin-4.3.2/access/Ownable.sol"; +import "../../governance/PausableGuardian_0_8.sol"; import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; -contract Deposits is Ownable { +contract Deposits is PausableGuardian_0_8 { struct DepositInfo { address depositToken; uint256 depositAmount; @@ -16,7 +16,7 @@ contract Deposits is Ownable { uint256 tokenAmount, address trader, address token - ) external { + ) external pausable { require(msg.sender == orderBook, "unauthorized"); require(_depositInfo[orderID].depositToken == address(0)|| _depositInfo[orderID].depositToken == token, "Deposits: invalid token specified"); @@ -34,7 +34,7 @@ contract Deposits is Ownable { orderBook = n; } - function withdraw(bytes32 orderID) external { + function withdraw(bytes32 orderID) external pausable { require(msg.sender == orderBook, "unauthorized"); SafeERC20.safeTransfer( IERC20(_depositInfo[orderID].depositToken), @@ -44,7 +44,7 @@ contract Deposits is Ownable { _depositInfo[orderID].depositAmount = 0; } - function withdrawToTrader(address trader, bytes32 orderID) external { + function withdrawToTrader(address trader, bytes32 orderID) external pausable { require(msg.sender == orderBook, "unauthorized"); SafeERC20.safeTransfer( IERC20(_depositInfo[orderID].depositToken), @@ -54,7 +54,7 @@ contract Deposits is Ownable { _depositInfo[orderID].depositAmount = 0; } - function refund(bytes32 orderID, uint256 amount) external { + function refund(bytes32 orderID, uint256 amount) external pausable { require(msg.sender == orderBook, "unauthorized"); SafeERC20.safeTransferFrom( IERC20(_depositInfo[orderID].depositToken), @@ -69,7 +69,7 @@ contract Deposits is Ownable { address trader, bytes32 orderID, uint256 amount - ) external { + ) external pausable { require(msg.sender == orderBook, "unauthorized"); SafeERC20.safeTransfer( IERC20(_depositInfo[orderID].depositToken), diff --git a/contracts/orderbook/Storage/OrderBookEvents.sol b/contracts/orderbook/Storage/OrderBookEvents.sol index 757ef5ec..2fd8878e 100644 --- a/contracts/orderbook/Storage/OrderBookEvents.sol +++ b/contracts/orderbook/Storage/OrderBookEvents.sol @@ -1,9 +1,9 @@ pragma solidity ^0.8.0; import "../IOrderBook.sol"; -import "@openzeppelin-4.3.2/access/Ownable.sol"; +import "../../governance/PausableGuardian_0_8.sol"; import "@openzeppelin-4.3.2/utils/structs/EnumerableSet.sol"; -contract OrderBookEvents is Ownable { +contract OrderBookEvents is PausableGuardian_0_8 { using EnumerableSet for EnumerableSet.Bytes32Set; mapping(bytes32 => IOrderBook.Order) internal _allOrders; mapping(bytes32 => uint256) internal _orderExpiration; From 57229dbc46c577ff0b39b82a7844211db0a3cf6e Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Mon, 14 Mar 2022 13:47:43 -0700 Subject: [PATCH 45/83] delegated manager upgrade with test cases --- contracts/orderbook/IOrderBook.sol | 2 - contracts/orderbook/Logic/OrderBook.sol | 13 +-- contracts/orderbook/Logic/OrderBookData.sol | 9 -- .../orderbook/Logic/OrderBookMarketOrders.sol | 107 ------------------ .../orderbook/Logic/OrderBookOrderPlace.sol | 33 +++--- .../orderbook/Storage/OrderBookEvents.sol | 1 - testsarbitrum/limit-orders/test_Orders.py | 22 ++-- 7 files changed, 31 insertions(+), 156 deletions(-) delete mode 100644 contracts/orderbook/Logic/OrderBookMarketOrders.sol diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index 9f62ee74..b180afef 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -51,6 +51,4 @@ interface IOrderBook { function prelimCheck(bytes32 orderID) external returns (bool); function getTotalActiveOrders() external view returns (uint256); - - function getActiveTrades(address trader) external view returns(bytes32[] memory); } diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index 91f6f450..65441dbd 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -32,17 +32,11 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { internalOrder.loanTokenAmount, internalOrder.collateralTokenAmount, internalOrder.base, - address(this), + internalOrder.trader, internalOrder.loanDataBytes ) ); - if (result && internalOrder.loanID==0) { - (bytes32 loanID) = abi.decode( - data, - (bytes32) - ); - _activeTrades[internalOrder.trader].add(loanID); - } else if (!result) { + if (!result) { IDeposits(vault).refund(internalOrder.orderID, (internalOrder.loanTokenAmount + internalOrder.collateralTokenAmount)); //unlikely to be needed } } @@ -55,9 +49,6 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { address collateralAddress, bytes memory loanDataBytes ) internal { - if (protocol.getLoan(loanID).collateral == amount) { - _activeTrades[trader].remove(loanID); - } address(protocol).call( abi.encodeWithSelector( protocol.closeWithSwap.selector, diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index 1351debd..b602780b 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -20,7 +20,6 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { _setTarget(this.getTotalActiveOrders.selector, target); _setTarget(this.getOrders.selector, target); _setTarget(this.getOrdersLimited.selector, target); - _setTarget(this.getActiveTrades.selector, target); } function adjustAllowance(address spender, address token) external pausable { @@ -115,12 +114,4 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { } return fullList; } - - function getActiveTrades(address trader) - external - view - returns (bytes32[] memory) - { - return _activeTrades[trader].values(); - } } diff --git a/contracts/orderbook/Logic/OrderBookMarketOrders.sol b/contracts/orderbook/Logic/OrderBookMarketOrders.sol deleted file mode 100644 index fd60781b..00000000 --- a/contracts/orderbook/Logic/OrderBookMarketOrders.sol +++ /dev/null @@ -1,107 +0,0 @@ -pragma solidity ^0.8.0; -import "../Storage/OrderBookEvents.sol"; -import "../Storage/OrderBookStorage.sol"; - -contract OrderBookMarketOrders is OrderBookEvents, OrderBookStorage { - using EnumerableSet for EnumerableSet.Bytes32Set; - - function initialize(address target) public onlyOwner { - _setTarget(this.marketOpen.selector, target); - _setTarget(this.marketClose.selector, target); - } - - function marketOpen( - bytes32 loanID, - uint256 leverage, - uint256 loanTokenAmount, - uint256 collateralTokenAmount, - address iToken, - address base, - bytes memory loanDataBytes - ) external pausable { - _executeMarketOpen( - msg.sender, - loanID, - leverage, - loanTokenAmount, - collateralTokenAmount, - iToken, - base, - loanDataBytes - ); - } - - function marketClose( - bytes32 loanID, - uint256 amount, - bool iscollateral, - bytes memory loanDataBytes - ) external pausable { - _executeMarketClose( - msg.sender, - loanID, - amount, - iscollateral, - loanDataBytes - ); - } - - function _executeMarketOpen( - address trader, - bytes32 lID, - uint256 leverage, - uint256 loanTokenAmount, - uint256 collateralTokenAmount, - address iToken, - address base, - bytes memory loanDataBytes - ) internal { - require(protocol.isLoanPool(iToken), "OrderBook: Not an iToken Contract"); - (address usedToken, uint256 transferAmount) = collateralTokenAmount > loanTokenAmount - ? (base, collateralTokenAmount) - : (IToken(iToken).loanTokenAddress(), loanTokenAmount); - SafeERC20.safeTransferFrom( - IERC20(usedToken), - trader, - address(this), - transferAmount - ); - bytes32 loanID = IToken(iToken) - .marginTrade( - lID, - leverage, - loanTokenAmount, - collateralTokenAmount, - base, - address(this), - loanDataBytes - ) - .loanId; - if (!_activeTrades[trader].contains(loanID)) { - _activeTrades[trader].add(loanID); - } - } - - function _isActiveLoan(bytes32 ID) internal view returns (bool) { - return protocol.loans(ID).active; - } - - function _executeMarketClose( - address trader, - bytes32 loanID, - uint256 amount, - bool iscollateral, - bytes memory loanDataBytes - ) internal { - protocol.closeWithSwap( - loanID, - trader, - amount, - iscollateral, - loanDataBytes - ); - if (!_isActiveLoan(loanID)) { - _activeTrades[trader].remove(loanID); - } - } -} diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 462fe12e..851700b5 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -26,24 +26,21 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { return (executionPrice * amount) / precision; } - function _collateralTokenMatch(IOrderBook.Order memory checkOrder) + function _caseChecks(bytes32 ID, address collateral, address loanToken) internal view returns (bool) { - return - protocol.getLoan(checkOrder.loanID).collateralToken == - checkOrder.base; + return + protocol.getLoan(ID).loanToken == + loanToken && + protocol.getLoan(ID).collateralToken == + collateral && + protocol.delegatedManagers(ID, address(this)); } - function _loanTokenMatch(IOrderBook.Order memory checkOrder) - internal - view - returns (bool) - { - return - protocol.getLoan(checkOrder.loanID).loanToken == - checkOrder.loanTokenAddress; + function _isActiveLoan(bytes32 ID) internal view returns (bool) { + return protocol.loans(ID).active; } function _abs(int256 x) private pure returns (int256) { @@ -66,9 +63,9 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { ); require( Order.loanID != 0 - ? _collateralTokenMatch(Order) && _loanTokenMatch(Order) + ? _caseChecks(Order.loanID, Order.base, Order.loanTokenAddress) : true, - "OrderBook: incorrect collateral and/or loan token specified" + "OrderBook: cases not passed" ); require( Order.orderType == IOrderBook.OrderType.LIMIT_OPEN @@ -79,8 +76,8 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { require( Order.orderType == IOrderBook.OrderType.LIMIT_OPEN ? Order.loanID == 0 || - _activeTrades[msg.sender].contains(Order.loanID) - : _activeTrades[msg.sender].contains(Order.loanID), + _isActiveLoan(Order.loanID) + : _isActiveLoan(Order.loanID), "OrderBook: inactive loan" ); (uint256 amountUsed, address usedToken) = Order.loanTokenAmount > Order.collateralTokenAmount @@ -147,8 +144,8 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { require( Order.orderType == IOrderBook.OrderType.LIMIT_OPEN ? Order.loanID == 0 || - _activeTrades[msg.sender].contains(Order.loanID) - : _activeTrades[msg.sender].contains(Order.loanID), + _isActiveLoan(Order.loanID) + : _isActiveLoan(Order.loanID), "OrderBook: inactive loan" ); require(Order.trader == msg.sender, "OrderBook: invalid trader"); diff --git a/contracts/orderbook/Storage/OrderBookEvents.sol b/contracts/orderbook/Storage/OrderBookEvents.sol index 2fd8878e..01b36225 100644 --- a/contracts/orderbook/Storage/OrderBookEvents.sol +++ b/contracts/orderbook/Storage/OrderBookEvents.sol @@ -8,7 +8,6 @@ contract OrderBookEvents is PausableGuardian_0_8 { mapping(bytes32 => IOrderBook.Order) internal _allOrders; mapping(bytes32 => uint256) internal _orderExpiration; mapping(address => EnumerableSet.Bytes32Set) internal _histOrders; - mapping(address => EnumerableSet.Bytes32Set) internal _activeTrades; mapping(address => bool) internal _useOracle; EnumerableSet.Bytes32Set internal _allOrderIDs; diff --git a/testsarbitrum/limit-orders/test_Orders.py b/testsarbitrum/limit-orders/test_Orders.py index 2e5b5645..d8b54c65 100644 --- a/testsarbitrum/limit-orders/test_Orders.py +++ b/testsarbitrum/limit-orders/test_Orders.py @@ -10,30 +10,35 @@ def test_main(): place_order_open(orderbook) print(orderbook.prelimCheck.call(orderbook.getActiveOrders(accounts[0])[0][1])) orderbook.executeOrder(orderbook.getActiveOrders(accounts[0])[0][1], {'from':accounts[0]}) - trades = orderbook.getActiveTrades(accounts[0]) - place_order_close(orderbook, trades[0], int(7.4e14)) + trades = interface.IBZx('0x37407F3178ffE07a6cF5C847F8f680FEcf319FAB').getUserLoans(accounts[0],0,10,0,False,False) + print(trades) + place_order_close(orderbook, trades[0][0], int(7.4e14)) print(orderbook.getOrders()) isExec, data = keeper.checkUpKeep.call(encode_abi(['uint256','uint256'],[0,orderbook.getTotalActiveOrders()])) keeper.checkUpKeep(encode_abi(['uint256','uint256'],[0,orderbook.getTotalActiveOrders()]), {'from':accounts[0]}) if(isExec): keeper.performUpKeep(data) print(orderbook.getActiveOrders(accounts[0])) + trades = interface.IBZx('0x37407F3178ffE07a6cF5C847F8f680FEcf319FAB').getUserLoans(accounts[0],0,10,0,False,False) + print(trades) assert(False) def deploy_contracts(): orderbook = OrderBookProxy.deploy('0x37407F3178ffE07a6cF5C847F8f680FEcf319FAB', {'from':accounts[0]}) orderbookLogic = OrderBook.deploy({'from':accounts[0]}) orderBookData = OrderBookData.deploy({'from':accounts[0]}) - orderBookMarketOrders = OrderBookMarketOrders.deploy({'from':accounts[0]}) orderBookOrderPlace = OrderBookOrderPlace.deploy({'from':accounts[0]}) orderbook.replaceContract(orderbookLogic.address, {'from':orderbook.owner()}) orderbook.replaceContract(orderBookData.address, {'from':orderbook.owner()}) - orderbook.replaceContract(orderBookMarketOrders.address, {'from':orderbook.owner()}) orderbook.replaceContract(orderBookOrderPlace.address, {'from':orderbook.owner()}) d = Deposits.deploy({'from':accounts[0]}) d.setOrderBook(orderbook.address, {'from':accounts[0]}) Contract.from_abi('',orderbook.address,OrderBook.abi).setVaultAddress(d.address, {'from':orderbook.owner()}) keeper = OrderKeeper.deploy(orderbook.address, {'from':accounts[0]}) + BZX = interface.IBZx('0x37407F3178ffE07a6cF5C847F8f680FEcf319FAB') + bzx = Contract.from_abi('',BZX.address,bZxProtocol.abi) + LoanOpening = LoanOpenings.deploy({'from':bzx.owner()}) + bzx.replaceContract(LoanOpening.address, {'from':bzx.owner()}) return orderbook, keeper def set_perms(orderbook): @@ -42,9 +47,10 @@ def set_perms(orderbook): interface.IERC20('0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8').transfer(accounts[0], 200e6, {'from':'0x489ee077994b6658eafa855c308275ead8097c4a'}) interface.IERC20('0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8').approve(orderbook.vault.call(), 2000e6, {'from':accounts[0]}) def place_order_open(orderbook_contract): + payload = encode_abi(['uint128','bytes[]'],[4,[b'',encode_abi(['address'], [orderbook_contract.address])]]) orderdata = [0,0,3e14,2e18,1e6,0,(1647152473+60*60*24*30),accounts[0], '0xEDa7f294844808B7C93EE524F990cA7792AC2aBd','0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8','0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', - 0,False,False,b''] + 0,False,False,payload] orderbook_contract.placeOrder(orderdata, {'from':accounts[0]}) def place_order_close(orderbook_contract, position, size): @@ -56,8 +62,8 @@ def place_order_close(orderbook_contract, position, size): def amend_order(): print() -def cancel_order(): - print() +def cancel_order(orderbook_contract, position): + orderbook_contract.cancelOrder(position, {'from':accounts[0]}) def get_order_specs(orderID): - return \ No newline at end of file + return From 415ccab6eb9a1dc572e9eb57aaf4d63d9058b186 Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Tue, 15 Mar 2022 12:50:46 -0700 Subject: [PATCH 46/83] minor improvements --- contracts/orderbook/IOrderBook.sol | 13 +++- .../orderbook/Keepers/OrderKeeperClear.sol | 12 +++- contracts/orderbook/Logic/OrderBook.sol | 64 +++++++++---------- contracts/orderbook/Logic/OrderBookData.sol | 10 +++ .../orderbook/Logic/OrderBookOrderPlace.sol | 37 ++++------- contracts/orderbook/OrderBookProxy.sol | 3 - .../orderbook/Storage/OrderBookStorage.sol | 5 +- 7 files changed, 76 insertions(+), 68 deletions(-) diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index b180afef..a65d52ff 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -6,6 +6,13 @@ interface IOrderBook { LIMIT_CLOSE, MARKET_STOP } + + enum OrderStatus { + ACTIVE, + CANCELLED, + EXECUTED + } + struct Order { bytes32 loanID; bytes32 orderID; @@ -13,13 +20,13 @@ interface IOrderBook { uint256 leverage; uint256 loanTokenAmount; uint256 collateralTokenAmount; - uint64 timeTillExpiration; address trader; address iToken; address loanTokenAddress; address base; OrderType orderType; - bool isCancelled; + OrderStatus status; + uint64 timeTillExpiration; bool isCollateral; bytes loanDataBytes; } @@ -51,4 +58,6 @@ interface IOrderBook { function prelimCheck(bytes32 orderID) external returns (bool); function getTotalActiveOrders() external view returns (uint256); + + function getOrderIDsLimited(uint256 start, uint256 end) external view returns(bytes32[] memory); } diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol index 7217e296..e583322d 100644 --- a/contracts/orderbook/Keepers/OrderKeeperClear.sol +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -26,10 +26,13 @@ contract OrderKeeperClear is PausableGuardian_0_8 { } IOrderBook.Order[] memory listOfMainOrders = factory .getOrdersLimited(start, end); + bytes32[] memory clearable = new bytes32[](7); + uint iter = 0; for (uint256 x = 0; x < listOfMainOrders.length;) { if (factory.clearOrder(listOfMainOrders[x].orderID)) { upkeepNeeded = true; - performData = abi.encode(listOfMainOrders[x].orderID); + clearable[iter] = listOfMainOrders[x].orderID; + ++iter; return (upkeepNeeded, performData); } unchecked { ++x; } @@ -38,8 +41,11 @@ contract OrderKeeperClear is PausableGuardian_0_8 { } function performUpKeep(bytes calldata performData) external pausable { - bytes32 orderId = abi.decode(performData, (bytes32)); + bytes32[] memory orderId = abi.decode(performData, (bytes32[])); //emit OrderExecuted(trader,orderId); - factory.cancelOrderProtocol(orderId); + for (uint i;i swapRate - ? (_allOrders[orderID].amountReceived - swapRate) > - (_allOrders[orderID].amountReceived * 25) / 100 - : (swapRate - _allOrders[orderID].amountReceived) > + order.amountReceived > swapRate + ? (order.amountReceived - swapRate) > + (order.amountReceived * 25) / 100 + : (swapRate - order.amountReceived) > (swapRate * 25) / 100 ) ) { @@ -280,8 +280,8 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } function executeOrder(bytes32 orderID) external pausable { - require(!_allOrders[orderID].isCancelled, "OrderBook: non active"); IOrderBook.Order memory order = _allOrders[orderID]; + require(order.status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: non active"); address srcToken; uint256 amountUsed; (uint256 dexID, bytes memory payload) = _prepDexAndPayload(order.loanDataBytes); @@ -298,7 +298,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { ); srcToken = order.collateralTokenAmount > order.loanTokenAmount ? order.base : order.loanTokenAddress; require(order.timeTillExpiration > block.timestamp, "OrderBook: Order Expired"); - if (_allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN) { + if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { uint256 dSwapValue; if (srcToken == order.loanTokenAddress) { amountUsed = order.loanTokenAmount + (order.loanTokenAmount * order.leverage) / 10**18; //adjusts leverage @@ -325,7 +325,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { "OrderBook: amountOut too low" ); _executeTradeOpen(order); - _allOrders[orderID].isCancelled = true; + _allOrders[orderID].status = IOrderBook.OrderStatus.EXECUTED; _allOrderIDs.remove(orderID); _histOrders[order.trader].remove(orderID); emit OrderExecuted(order.trader, orderID); @@ -354,7 +354,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { order.base, order.loanDataBytes ); - _allOrders[orderID].isCancelled = true; + _allOrders[orderID].status = IOrderBook.OrderStatus.EXECUTED; _allOrderIDs.remove(orderID); _histOrders[order.trader].remove(orderID); emit OrderExecuted(order.trader, orderID); @@ -399,15 +399,11 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { order.base, order.loanDataBytes ); - _allOrders[orderID].isCancelled = true; + _allOrders[orderID].status = IOrderBook.OrderStatus.EXECUTED; _allOrderIDs.remove(orderID); _histOrders[order.trader].remove(orderID); emit OrderExecuted(order.trader, orderID); return; } } - - function setVaultAddress(address nVault) external onlyOwner { - vault = nVault; - } } diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index b602780b..06233068 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -86,6 +86,16 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { return _allOrderIDs.length(); } + function getOrderIDsLimited(uint start, uint end) external view returns (bytes32[] memory fullList) { + require(end<=_allOrderIDs.length(), "OrderBook: end is past max orders"); + fullList = new bytes32[](end-start); + for (uint256 i = start; i < end;) { + fullList[i] = _allOrderIDs.at(i); + unchecked { ++i; } + } + return fullList; + } + function getOrders() external view diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 851700b5..b9090ffd 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -31,11 +31,10 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { view returns (bool) { + IBZx.LoanReturnData memory data = protocol.getLoan(ID); return - protocol.getLoan(ID).loanToken == - loanToken && - protocol.getLoan(ID).collateralToken == - collateral && + data.loanToken == loanToken && + data.collateralToken == collateral && protocol.delegatedManagers(ID, address(this)); } @@ -43,17 +42,10 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { return protocol.loans(ID).active; } - function _abs(int256 x) private pure returns (int256) { - return x >= 0 ? x : -x; - } - function placeOrder(IOrderBook.Order calldata Order) external pausable { require(Order.loanDataBytes.length < 3500, "OrderBook: loanDataBytes too complex"); require( - _abs( - int256(Order.loanTokenAmount) - - int256(Order.collateralTokenAmount) - ) == int256(Order.loanTokenAmount + Order.collateralTokenAmount), + !(Order.collateralTokenAmount>0) || !(Order.loanTokenAmount >0), "OrderBook: only one token can be used" ); require( @@ -65,7 +57,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { Order.loanID != 0 ? _caseChecks(Order.loanID, Order.base, Order.loanTokenAddress) : true, - "OrderBook: cases not passed" + "OrderBook: incorrect collateral and/or loan token specified" ); require( Order.orderType == IOrderBook.OrderType.LIMIT_OPEN @@ -89,7 +81,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { "OrderBook: Order too small" ); require(Order.trader == msg.sender, "OrderBook: invalid trader"); - require(!Order.isCancelled, "OrderBook: invalid order state"); + require(Order.status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: invalid order state"); mainOBID++; bytes32 ID = keccak256(abi.encode(msg.sender, mainOBID)); require(IDeposits(vault).getTokenUsed(ID) == address(0), "Orderbook: collision"); //in the very unlikely chance of collision on ID error is thrown @@ -113,10 +105,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { function amendOrder(IOrderBook.Order calldata Order) external pausable { require(Order.loanDataBytes.length < 3500, "OrderBook: loanDataBytes too complex"); require( - _abs( - int256(Order.loanTokenAmount) - - int256(Order.collateralTokenAmount) - ) == int256(Order.loanTokenAmount + Order.collateralTokenAmount), + !(Order.collateralTokenAmount>0) || !(Order.loanTokenAmount >0), "OrderBook: only one token can be used" ); require( @@ -138,7 +127,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { "OrderBook: invalid iToken specified" ); require( - !_allOrders[Order.orderID].isCancelled, + Order.status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: inactive order specified" ); require( @@ -190,8 +179,8 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { } function cancelOrder(bytes32 orderID) external pausable { - require(!_allOrders[orderID].isCancelled, "OrderBook: inactive order"); - _allOrders[orderID].isCancelled = true; + require(_allOrders[orderID].status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: inactive order"); + _allOrders[orderID].status = IOrderBook.OrderStatus.CANCELLED; require(_histOrders[msg.sender].remove(orderID), "OrderBook: not owner of order"); _allOrderIDs.remove(orderID); if (_allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN) { @@ -206,7 +195,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { function recoverFundsFromFailedOrder(bytes32 orderID) external pausable { IOrderBook.Order memory order = _allOrders[orderID]; require(msg.sender == order.trader, "OrderBook: Not trade owner"); - require(order.isCancelled, "OrderBook: Order not executed"); + require(order.status==IOrderBook.OrderStatus.CANCELLED, "OrderBook: Order not executed"); require(!_allOrderIDs.contains(orderID), "OrderBook: Order still in records"); IDeposits(vault).withdrawToTrader(msg.sender, orderID); } @@ -214,7 +203,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { function cancelOrderProtocol(bytes32 orderID) external pausable { IOrderBook.Order memory order = _allOrders[orderID]; address trader = order.trader; - require(!order.isCancelled, "OrderBook: inactive order"); + require(order.status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: inactive order"); uint256 amountUsed = order.collateralTokenAmount + order.loanTokenAmount; uint256 swapRate; @@ -242,7 +231,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { "OrderBook: no conditions met" ); - order.isCancelled = true; + _allOrders[orderID].status = IOrderBook.OrderStatus.CANCELLED; _histOrders[trader].remove(orderID); _allOrderIDs.remove(orderID); if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { diff --git a/contracts/orderbook/OrderBookProxy.sol b/contracts/orderbook/OrderBookProxy.sol index 7a708c8f..a97d6bb9 100644 --- a/contracts/orderbook/OrderBookProxy.sol +++ b/contracts/orderbook/OrderBookProxy.sol @@ -3,9 +3,6 @@ import "./Storage/OrderBookStorage.sol"; import "./Storage/OrderBookEvents.sol"; contract OrderBookProxy is OrderBookEvents, OrderBookStorage { - constructor(IBZx _contract) { - protocol = _contract; - } fallback() external payable { if (gasleft() <= 2300) { diff --git a/contracts/orderbook/Storage/OrderBookStorage.sol b/contracts/orderbook/Storage/OrderBookStorage.sol index e80f0969..823ffc26 100644 --- a/contracts/orderbook/Storage/OrderBookStorage.sol +++ b/contracts/orderbook/Storage/OrderBookStorage.sol @@ -11,10 +11,11 @@ contract OrderBookStorage { 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1; uint256 public constant MIN_AMOUNT_IN_USDC = 1e6; address public constant USDC = 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8; + address public constant vault = address(0); + IBZx public constant protocol = IBZx(address(0)); mapping(bytes4 => address) public logicTargets; - address public vault; - IBZx public protocol; + uint256 public mainOBID; function _setTarget(bytes4 sig, address target) internal { From dd6e3ed59bd56994b7ba3d69139df78b9d711edc Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Tue, 15 Mar 2022 23:02:15 -0700 Subject: [PATCH 47/83] some more optimizations --- contracts/orderbook/IOrderBook.sol | 4 +- contracts/orderbook/Keepers/OrderKeeper.sol | 2 +- .../orderbook/Keepers/OrderKeeperClear.sol | 2 +- contracts/orderbook/Logic/OrderBook.sol | 10 +- contracts/orderbook/Logic/OrderBookData.sol | 10 +- .../orderbook/Logic/OrderBookOrderPlace.sol | 159 +++++++----------- 6 files changed, 77 insertions(+), 110 deletions(-) diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index a65d52ff..ef75060c 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -57,7 +57,9 @@ interface IOrderBook { function prelimCheck(bytes32 orderID) external returns (bool); - function getTotalActiveOrders() external view returns (uint256); + function getOrderIDs() external view returns (bytes32[] memory); + + function getTotalOrderIDs() external view returns (uint256); function getOrderIDsLimited(uint256 start, uint256 end) external view returns(bytes32[] memory); } diff --git a/contracts/orderbook/Keepers/OrderKeeper.sol b/contracts/orderbook/Keepers/OrderKeeper.sol index 30eb717f..65cdbfb6 100644 --- a/contracts/orderbook/Keepers/OrderKeeper.sol +++ b/contracts/orderbook/Keepers/OrderKeeper.sol @@ -15,7 +15,7 @@ contract OrderKeeper is PausableGuardian_0_8 { returns (bool upkeepNeeded, bytes memory performData) { (uint256 start, uint256 end) = abi.decode(checkData, (uint256, uint256)); - uint256 orderIDLength = factory.getTotalActiveOrders(); + uint256 orderIDLength = factory.getTotalOrderIDs(); if(end < orderIDLength) { if (start > orderIDLength) { end = orderIDLength; diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol index e583322d..326e68fc 100644 --- a/contracts/orderbook/Keepers/OrderKeeperClear.sol +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -16,7 +16,7 @@ contract OrderKeeperClear is PausableGuardian_0_8 { returns (bool upkeepNeeded, bytes memory performData) { (uint256 start, uint256 end) = abi.decode(checkData, (uint256, uint256)); - uint256 orderIDLength = factory.getTotalActiveOrders(); + uint256 orderIDLength = factory.getTotalOrderIDs(); if(end < orderIDLength) { if (start > orderIDLength) { end = orderIDLength; diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index 69529a26..66b5f9fd 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -239,11 +239,12 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { bytes memory payload, uint256 amountIn ) public returns (uint256 rate) { + uint tradeSize = 10**IERC20Metadata(base).decimals(); (rate, ) = swapImpl.dexAmountOutFormatted( payload, - 10**IERC20Metadata(base).decimals() + tradeSize ); - rate = (rate * amountIn) / 10**IERC20Metadata(base).decimals(); + rate = (rate * amountIn) / tradeSize; } function priceCheck( @@ -252,17 +253,18 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { ISwapsImpl swapImpl, bytes memory payload ) public returns (bool) { + uint tradeSize = 10**IERC20Metadata(base).decimals(); uint256 dexRate = getDexRate( swapImpl, base, loanTokenAddress, payload, - 10**IERC20Metadata(base).decimals() + tradeSize ); uint256 indexRate = queryRateReturn( base, loanTokenAddress, - 10**IERC20Metadata(base).decimals() + tradeSize ); if (dexRate >= indexRate) { if (((dexRate - indexRate) * 1000) / dexRate <= 5) { diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index 06233068..0ee1b84a 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -17,8 +17,10 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { _setTarget(this.getActiveOrderIDs.selector, target); _setTarget(this.getTotalOrders.selector, target); _setTarget(this.getActiveOrders.selector, target); - _setTarget(this.getTotalActiveOrders.selector, target); + _setTarget(this.getTotalOrderIDs.selector, target); + _setTarget(this.getOrderIDs.selector, target); _setTarget(this.getOrders.selector, target); + _setTarget(this.getOrderIDsLimited.selector, target); _setTarget(this.getOrdersLimited.selector, target); } @@ -82,7 +84,11 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { return _histOrders[trader].length(); } - function getTotalActiveOrders() external view returns (uint256) { + function getOrderIDs() external view returns (bytes32[] memory) { + return _allOrderIDs.values(); + } + + function getTotalOrderIDs() external view returns (uint256) { return _allOrderIDs.length(); } diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index b9090ffd..b595d2c8 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -15,17 +15,6 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { _setTarget(this.recoverFundsFromFailedOrder.selector, target); } - function queryRateReturn( - address start, - address end, - uint256 amount - ) public view returns (uint256) { - (uint256 executionPrice, uint256 precision) = IPriceFeeds( - protocol.priceFeeds() - ).queryRate(start, end); - return (executionPrice * amount) / precision; - } - function _caseChecks(bytes32 ID, address collateral, address loanToken) internal view @@ -42,139 +31,107 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { return protocol.loans(ID).active; } - function placeOrder(IOrderBook.Order calldata Order) external pausable { - require(Order.loanDataBytes.length < 3500, "OrderBook: loanDataBytes too complex"); - require( - !(Order.collateralTokenAmount>0) || !(Order.loanTokenAmount >0), - "OrderBook: only one token can be used" - ); - require( - protocol.supportedTokens(Order.loanTokenAddress) && - protocol.supportedTokens(Order.base), - "OrderBook: invalid pair" - ); - require( - Order.loanID != 0 - ? _caseChecks(Order.loanID, Order.base, Order.loanTokenAddress) - : true, - "OrderBook: incorrect collateral and/or loan token specified" - ); - require( - Order.orderType == IOrderBook.OrderType.LIMIT_OPEN - ? protocol.isLoanPool(Order.iToken) - : true, - "OrderBook: invalid iToken specified" - ); - require( - Order.orderType == IOrderBook.OrderType.LIMIT_OPEN - ? Order.loanID == 0 || - _isActiveLoan(Order.loanID) - : _isActiveLoan(Order.loanID), - "OrderBook: inactive loan" - ); - (uint256 amountUsed, address usedToken) = Order.loanTokenAmount > Order.collateralTokenAmount - ? (Order.loanTokenAmount, Order.loanTokenAddress) - : (Order.collateralTokenAmount, Order.base); + function _commonChecks(IOrderBook.Order calldata order) internal { + require(!(order.collateralTokenAmount>0) || !(order.loanTokenAmount >0), "OrderBook: collateral and loan token cannot be non-zero"); + require(protocol.supportedTokens(order.loanTokenAddress), "OrderBook: Unsupported loan token"); + require(protocol.supportedTokens(order.base), "OrderBook: Unsupported collateral"); + require(order.loanID != 0 + ? _caseChecks(order.loanID, order.base, order.loanTokenAddress) + : true, + "OrderBook: case checks failed"); + require(order.orderType == IOrderBook.OrderType.LIMIT_OPEN + ? protocol.isLoanPool(order.iToken) + : true, + "OrderBook: Not a loan pool"); + require(order.orderType == IOrderBook.OrderType.LIMIT_OPEN + ? order.loanID == 0 || + _isActiveLoan(order.loanID) + : _isActiveLoan(order.loanID), + "OrderBook: non-active loan specified"); + require(order.loanDataBytes.length < 3500, "OrderBook: loanDataBytes too complex"); + require(order.trader == msg.sender, "OrderBook: invalid trader"); + } + + function placeOrder(IOrderBook.Order calldata order) external pausable { + _commonChecks(order); + (uint256 amountUsed, address usedToken) = order.loanTokenAmount > order.collateralTokenAmount + ? (order.loanTokenAmount, order.loanTokenAddress) + : (order.collateralTokenAmount, order.base); require( - queryRateReturn(usedToken, USDC, amountUsed) >= + IPriceFeeds(protocol.priceFeeds()).queryReturn(usedToken, USDC, amountUsed) >= MIN_AMOUNT_IN_USDC, "OrderBook: Order too small" ); - require(Order.trader == msg.sender, "OrderBook: invalid trader"); - require(Order.status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: invalid order state"); + require(order.status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: invalid order state"); mainOBID++; bytes32 ID = keccak256(abi.encode(msg.sender, mainOBID)); require(IDeposits(vault).getTokenUsed(ID) == address(0), "Orderbook: collision"); //in the very unlikely chance of collision on ID error is thrown - _allOrders[ID] = Order; + _allOrders[ID] = order; _allOrders[ID].orderID = ID; _histOrders[msg.sender].add(ID); _allOrderIDs.add(ID); - if (Order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { + if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { IDeposits(vault).deposit(ID, amountUsed, msg.sender, usedToken); } emit OrderPlaced( msg.sender, - Order.orderType, - Order.amountReceived, + order.orderType, + order.amountReceived, ID, - Order.base, - Order.loanTokenAddress + order.base, + order.loanTokenAddress ); } - function amendOrder(IOrderBook.Order calldata Order) external pausable { - require(Order.loanDataBytes.length < 3500, "OrderBook: loanDataBytes too complex"); - require( - !(Order.collateralTokenAmount>0) || !(Order.loanTokenAmount >0), - "OrderBook: only one token can be used" - ); + function amendOrder(IOrderBook.Order calldata order) external pausable { + _commonChecks(order); require( - Order.base == _allOrders[Order.orderID].base && - Order.loanTokenAddress == - _allOrders[Order.orderID].loanTokenAddress, + order.base == _allOrders[order.orderID].base && + order.loanTokenAddress == + _allOrders[order.orderID].loanTokenAddress, "OrderBook: invalid tokens" ); - /*require( - Order.price > swapRate - ? (Order.price - swapRate) < (Order.price * 25) / 100 - : (swapRate - Order.price) < (swapRate * 25) / 100, - "price too far away" - );*/ require( - Order.orderType == IOrderBook.OrderType.LIMIT_OPEN - ? protocol.isLoanPool(Order.iToken) - : true, - "OrderBook: invalid iToken specified" - ); - require( - Order.status==IOrderBook.OrderStatus.ACTIVE, + _allOrders[order.orderID].status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: inactive order specified" ); - require( - Order.orderType == IOrderBook.OrderType.LIMIT_OPEN - ? Order.loanID == 0 || - _isActiveLoan(Order.loanID) - : _isActiveLoan(Order.loanID), - "OrderBook: inactive loan" - ); - require(Order.trader == msg.sender, "OrderBook: invalid trader"); - if (Order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { - (uint256 amountUsed, address usedToken) = Order.loanTokenAmount > Order.collateralTokenAmount - ? (Order.loanTokenAmount, Order.loanTokenAddress) - : (Order.collateralTokenAmount, Order.base); + if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { + (uint256 amountUsed, address usedToken) = order.loanTokenAmount > order.collateralTokenAmount + ? (order.loanTokenAmount, order.loanTokenAddress) + : (order.collateralTokenAmount, order.base); uint256 storedAmount = IDeposits(vault).getDeposit( - Order.orderID + order.orderID ); require( usedToken == - IDeposits(vault).getTokenUsed(Order.orderID), + IDeposits(vault).getTokenUsed(order.orderID), "OrderBook: invalid used token" ); - uint256 amountUsedOld = _allOrders[Order.orderID].loanTokenAmount + - _allOrders[Order.orderID].collateralTokenAmount; + uint256 amountUsedOld = _allOrders[order.orderID].loanTokenAmount + + _allOrders[order.orderID].collateralTokenAmount; if (amountUsedOld > amountUsed) { IDeposits(vault).partialWithdraw( msg.sender, - Order.orderID, + order.orderID, amountUsedOld - amountUsed ); } else { IDeposits(vault).deposit( - Order.orderID, + order.orderID, amountUsed - amountUsedOld, msg.sender, usedToken ); } } - _allOrders[Order.orderID] = Order; + _allOrders[order.orderID] = order; emit OrderAmended( msg.sender, - Order.orderType, - Order.amountReceived, - Order.orderID, - Order.base, - Order.loanTokenAddress + order.orderType, + order.amountReceived, + order.orderID, + order.base, + order.loanTokenAddress ); } @@ -208,13 +165,13 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { order.loanTokenAmount; uint256 swapRate; if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { - swapRate = queryRateReturn( + swapRate = IPriceFeeds(protocol.priceFeeds()).queryReturn( order.loanTokenAddress, order.base, amountUsed ); } else { - swapRate = queryRateReturn( + swapRate = IPriceFeeds(protocol.priceFeeds()).queryReturn( order.base, order.loanTokenAddress, amountUsed From d32e76708d21c59be84ef726c3674bc1e7b603c5 Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Wed, 16 Mar 2022 23:31:36 -0700 Subject: [PATCH 48/83] bug fix --- contracts/orderbook/Logic/OrderBook.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index 66b5f9fd..738a6545 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -45,14 +45,13 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { bytes32 loanID, uint256 amount, bool iscollateral, - address collateralAddress, bytes memory loanDataBytes ) internal { address(protocol).call( abi.encodeWithSelector( protocol.closeWithSwap.selector, loanID, - address(this), + trader, amount, iscollateral, loanDataBytes @@ -353,7 +352,6 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { order.loanID, order.collateralTokenAmount, order.isCollateral, - order.base, order.loanDataBytes ); _allOrders[orderID].status = IOrderBook.OrderStatus.EXECUTED; @@ -398,7 +396,6 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { order.loanID, order.collateralTokenAmount, order.isCollateral, - order.base, order.loanDataBytes ); _allOrders[orderID].status = IOrderBook.OrderStatus.EXECUTED; From 3d174b2d56c58959053f64b643591042638ad478 Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Sun, 27 Mar 2022 02:27:18 -0700 Subject: [PATCH 49/83] polygon test deployment --- .../modules/LoanOpenings/LoanOpenings.sol | 8 +++-- contracts/orderbook/IOrderBook.sol | 20 +++++++++---- contracts/orderbook/Logic/OrderBookData.sol | 29 +++++++++++++------ .../orderbook/Storage/OrderBookStorage.sol | 8 ++--- scripts/env/set-matic.py | 2 +- 5 files changed, 45 insertions(+), 22 deletions(-) diff --git a/contracts/modules/LoanOpenings/LoanOpenings.sol b/contracts/modules/LoanOpenings/LoanOpenings.sol index fb1c98e7..f179ca5d 100644 --- a/contracts/modules/LoanOpenings/LoanOpenings.sol +++ b/contracts/modules/LoanOpenings/LoanOpenings.sol @@ -355,9 +355,11 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestHan sentValues, isTorqueLoan ); - if(abi.decode(loanDataBytes, (uint128)) & DELEGATE_FLAG != 0) { - (, bytes[] memory payloads) = abi.decode(loanDataBytes, (uint128, bytes[])); - _setDelegatedManager(loanId, sentAddresses[1], abi.decode(payloads[1], (address)), true); + if(loanDataBytes.length != 0) { + if(abi.decode(loanDataBytes, (uint128)) & DELEGATE_FLAG != 0) { + (, bytes[] memory payloads) = abi.decode(loanDataBytes, (uint128, bytes[])); + _setDelegatedManager(loanId, sentAddresses[1], abi.decode(payloads[1], (address)), true); + } } return LoanOpenData({ loanId: loanId, diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index ef75060c..76f70f1f 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -41,13 +41,19 @@ interface IOrderBook { function getFeed() external view returns (address); - function getOrdersLimited(uint256 start, uint256 end) external view returns(Order[] memory); + function getOrdersLimited(uint256 start, uint256 end) external view returns (Order[] memory); - function getOrders() external view returns(Order[] memory); + function getOrders() external view returns (Order[] memory); - function getActiveOrders(address trader) external view returns(Order[] memory); + function getOrderByOrderID(bytes32 orderID) external view returns (Order[] memory); - function getActiveOrdersLimited(address trader, uint256 start, uint256 end) external view returns(Order[] memory); + function getActiveOrders(address trader) external view returns (Order[] memory); + + function getActiveOrderIDs(address trader) external view returns (bytes32[] memory); + + function getActiveOrdersLimited(address trader, uint256 start, uint256 end) external view returns (Order[] memory); + + function getTotalOrders(address trader) external view returns (uint256); function executeOrder(bytes32 orderID) external; @@ -59,7 +65,11 @@ interface IOrderBook { function getOrderIDs() external view returns (bytes32[] memory); + function getOrders() external view returns (Order[] memory); + + function getOrdersLimited(uint start, uint end) external view returns (Order[] memory); + function getTotalOrderIDs() external view returns (uint256); - function getOrderIDsLimited(uint256 start, uint256 end) external view returns(bytes32[] memory); + function getOrderIDsLimited(uint256 start, uint256 end) external view returns (bytes32[] memory); } diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index 0ee1b84a..fce66ba7 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -24,15 +24,26 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { _setTarget(this.getOrdersLimited.selector, target); } - function adjustAllowance(address spender, address token) external pausable { - require( - protocol.isLoanPool(spender) || - address(protocol) == spender || - vault == spender, - "OrderBook: invalid spender" - ); - IERC(token).approve(spender, 0); //needs to be zeroed out because of different iterations of ERC-20 standard - IERC(token).approve(spender, type(uint256).max); + function adjustAllowance(address[] memory spenders, address[] memory tokens) external pausable { + address spender; + address token; + for (uint i; i < spenders.length;) { + spender = spenders[i]; + for (uint y; y < tokens.length;) { + token = tokens[i]; + require( + protocol.isLoanPool(spender) || + address(protocol) == spender || + vault == spender, + "OrderBook: invalid spender" + ); + IERC(token).approve(spender, 0); + IERC(token).approve(spender, type(uint256).max); + unchecked { ++y; } + } + unchecked { ++i; } + } + } function getActiveOrders(address trader) diff --git a/contracts/orderbook/Storage/OrderBookStorage.sol b/contracts/orderbook/Storage/OrderBookStorage.sol index 823ffc26..bf6cfcc1 100644 --- a/contracts/orderbook/Storage/OrderBookStorage.sol +++ b/contracts/orderbook/Storage/OrderBookStorage.sol @@ -8,11 +8,11 @@ import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; contract OrderBookStorage { address public constant WRAPPED_TOKEN = - 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1; + 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; uint256 public constant MIN_AMOUNT_IN_USDC = 1e6; - address public constant USDC = 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8; - address public constant vault = address(0); - IBZx public constant protocol = IBZx(address(0)); + address public constant USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; + address public constant vault = 0xFA6485ec4Aa9AF504adb4ed47b567E1875E21e85; + IBZx public constant protocol = IBZx(0x059D60a9CEfBc70b9Ea9FFBb9a041581B1dFA6a8); mapping(bytes4 => address) public logicTargets; diff --git a/scripts/env/set-matic.py b/scripts/env/set-matic.py index 340267ae..e0303b87 100644 --- a/scripts/env/set-matic.py +++ b/scripts/env/set-matic.py @@ -1,6 +1,6 @@ BZX = Contract.from_abi("BZX", "0x059D60a9CEfBc70b9Ea9FFBb9a041581B1dFA6a8", interface.IBZx.abi) TOKEN_REGISTRY = Contract.from_abi("TOKEN_REGISTRY", "0x4B234781Af34E9fD756C27a47675cbba19DC8765", TokenRegistry.abi) - +ORDERBOOK = Contract.from_abi("ORDERBOOK", "0xF2749292ed252890D6979cd4Ed1676b671235a67", interface.IOrderBook.abi) list = TOKEN_REGISTRY.getTokens(0, 100) for l in list: iTokenTemp = Contract.from_abi("iTokenTemp", l[0], LoanTokenLogicStandard.abi) From d66921b87c7d82ea470263acb14ea5328022ea77 Mon Sep 17 00:00:00 2001 From: Drypto Date: Fri, 8 Apr 2022 11:03:31 -0700 Subject: [PATCH 50/83] minor re-design for price execution --- contracts/orderbook/IOrderBook.sol | 11 +- contracts/orderbook/Logic/OrderBook.sol | 190 +++++++----------- contracts/orderbook/Logic/OrderBookData.sol | 2 +- .../orderbook/Storage/OrderBookStorage.sol | 3 +- 4 files changed, 81 insertions(+), 125 deletions(-) diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index 76f70f1f..5d690943 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -27,10 +27,15 @@ interface IOrderBook { OrderType orderType; OrderStatus status; uint64 timeTillExpiration; - bool isCollateral; bytes loanDataBytes; } + function vault() external view returns(address); + + function protocol() external view returns(address); + + function MIN_AMOUNT_IN_USDC() external view returns(uint256); + function placeOrder(Order calldata order) external; function amendOrder(Order calldata order) external; @@ -65,10 +70,6 @@ interface IOrderBook { function getOrderIDs() external view returns (bytes32[] memory); - function getOrders() external view returns (Order[] memory); - - function getOrdersLimited(uint start, uint end) external view returns (Order[] memory); - function getTotalOrderIDs() external view returns (uint256); function getOrderIDsLimited(uint256 start, uint256 end) external view returns (bytes32[] memory); diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index 738a6545..e562cee4 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -3,8 +3,8 @@ import "../Storage/OrderBookEvents.sol"; import "../Storage/OrderBookStorage.sol"; import "../../swaps/ISwapsImpl.sol"; import "../OrderVault/IDeposits.sol"; -import "../../interfaces/IDexRecords.sol"; import "../../mixins/Flags.sol"; + contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { using EnumerableSet for EnumerableSet.Bytes32Set; @@ -19,9 +19,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { _setTarget(this.executeOrder.selector, target); } - function _executeTradeOpen( - IOrderBook.Order memory order - ) internal { + function _executeTradeOpen(IOrderBook.Order memory order) internal { IDeposits(vault).withdraw(order.orderID); (bool result, bytes memory data) = order.iToken.call( abi.encodeWithSelector( @@ -36,7 +34,10 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { ) ); if (!result) { - IDeposits(vault).refund(order.orderID, (order.loanTokenAmount + order.collateralTokenAmount)); //unlikely to be needed + IDeposits(vault).refund( + order.orderID, + (order.loanTokenAmount + order.collateralTokenAmount) + ); //unlikely to be needed } } @@ -44,7 +45,6 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { address trader, bytes32 loanID, uint256 amount, - bool iscollateral, bytes memory loanDataBytes ) internal { address(protocol).call( @@ -53,7 +53,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { loanID, trader, amount, - iscollateral, + false, loanDataBytes ) ); @@ -97,8 +97,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { order.amountReceived > swapRate ? (order.amountReceived - swapRate) > (order.amountReceived * 25) / 100 - : (swapRate - order.amountReceived) > - (swapRate * 25) / 100 + : (swapRate - order.amountReceived) > (swapRate * 25) / 100 ) ) { return true; @@ -116,39 +115,13 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { return (executionPrice * amount) / precision; } - function _prepDexAndPayload(bytes memory input) - internal - pure - returns (uint256 dexID, bytes memory payload) - { - if (input.length != 0) { - (uint128 flag, bytes[] memory payloads) = abi.decode( - input, - (uint128, bytes[]) - ); - if(flag & DEX_SELECTOR_FLAG != 0){ - (dexID, payload) = abi.decode(payloads[0], (uint256, bytes)); - } - } - } - function prelimCheck(bytes32 orderID) external returns (bool) { IOrderBook.Order memory order = _allOrders[orderID]; uint256 amountUsed; address srcToken; - (uint256 dexID, bytes memory payload) = _prepDexAndPayload(order.loanDataBytes); - if (payload.length == 0) { - if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { - payload = abi.encode(order.loanTokenAddress, order.base); - } else { - payload = abi.encode(order.base, order.loanTokenAddress); - } - dexID = 1; - } - ISwapsImpl swapImpl = ISwapsImpl( - IDexRecords(getSwapAddress()).retrieveDexAddress(dexID) - ); - srcToken = order.collateralTokenAmount > order.loanTokenAmount ? order.base : order.loanTokenAddress; + srcToken = order.collateralTokenAmount > order.loanTokenAmount + ? order.base + : order.loanTokenAddress; if (order.timeTillExpiration < block.timestamp) { return false; } @@ -158,11 +131,10 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } uint256 dSwapValue; if (srcToken == order.loanTokenAddress) { - amountUsed = order.loanTokenAmount + (order.loanTokenAmount * order.leverage) / 10**18; //adjusts leverage - (dSwapValue, ) = swapImpl.dexAmountOutFormatted( - payload, - amountUsed - ); + amountUsed = + order.loanTokenAmount + + (order.loanTokenAmount * order.leverage) / + 10**18; //adjusts leverage } else { amountUsed = queryRateReturn( order.base, @@ -170,13 +142,15 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { order.collateralTokenAmount ); amountUsed = (amountUsed * order.leverage) / 10**18; - (dSwapValue, ) = swapImpl.dexAmountOutFormatted( - payload, - amountUsed - ); - dSwapValue += order.collateralTokenAmount; } - + dSwapValue = + order.collateralTokenAmount + + protocol.getSwapExpectedReturn( + order.loanTokenAddress, + order.base, + amountUsed, + order.loanDataBytes + ); if (order.amountReceived <= dSwapValue) { return true; @@ -186,17 +160,12 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { return false; } uint256 dSwapValue; - if (order.isCollateral) { - (dSwapValue, ) = swapImpl.dexAmountInFormatted( - payload, - order.collateralTokenAmount - ); - } else { - (dSwapValue, ) = swapImpl.dexAmountOutFormatted( - payload, - order.collateralTokenAmount - ); - } + dSwapValue = protocol.getSwapExpectedReturn( + order.base, + order.loanTokenAddress, + order.collateralTokenAmount, + order.loanDataBytes + ); if (order.amountReceived <= dSwapValue) { return true; } @@ -217,10 +186,9 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { operand = order.amountReceived >= getDexRate( - swapImpl, order.base, order.loanTokenAddress, - payload, + order.loanDataBytes, order.collateralTokenAmount ); } @@ -232,16 +200,17 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } function getDexRate( - ISwapsImpl swapImpl, address base, address loanTokenAddress, bytes memory payload, uint256 amountIn ) public returns (uint256 rate) { - uint tradeSize = 10**IERC20Metadata(base).decimals(); - (rate, ) = swapImpl.dexAmountOutFormatted( - payload, - tradeSize + uint256 tradeSize = 10**IERC20Metadata(base).decimals(); + rate = protocol.getSwapExpectedReturn( + base, + loanTokenAddress, + amountIn, + payload ); rate = (rate * amountIn) / tradeSize; } @@ -249,22 +218,16 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { function priceCheck( address loanTokenAddress, address base, - ISwapsImpl swapImpl, bytes memory payload ) public returns (bool) { - uint tradeSize = 10**IERC20Metadata(base).decimals(); + uint256 tradeSize = 10**IERC20Metadata(base).decimals(); uint256 dexRate = getDexRate( - swapImpl, base, loanTokenAddress, payload, tradeSize ); - uint256 indexRate = queryRateReturn( - base, - loanTokenAddress, - tradeSize - ); + uint256 indexRate = queryRateReturn(base, loanTokenAddress, tradeSize); if (dexRate >= indexRate) { if (((dexRate - indexRate) * 1000) / dexRate <= 5) { return true; @@ -282,31 +245,26 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { function executeOrder(bytes32 orderID) external pausable { IOrderBook.Order memory order = _allOrders[orderID]; - require(order.status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: non active"); + require( + order.status == IOrderBook.OrderStatus.ACTIVE, + "OrderBook: non active" + ); address srcToken; uint256 amountUsed; - (uint256 dexID, bytes memory payload) = _prepDexAndPayload(order.loanDataBytes); - if (payload.length == 0) { - if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { - payload = abi.encode(order.loanTokenAddress, order.base); - } else { - payload = abi.encode(order.base, order.loanTokenAddress); - } - dexID = 1; - } - ISwapsImpl swapImpl = ISwapsImpl( - IDexRecords(getSwapAddress()).retrieveDexAddress(dexID) + srcToken = order.collateralTokenAmount > order.loanTokenAmount + ? order.base + : order.loanTokenAddress; + require( + order.timeTillExpiration > block.timestamp, + "OrderBook: Order Expired" ); - srcToken = order.collateralTokenAmount > order.loanTokenAmount ? order.base : order.loanTokenAddress; - require(order.timeTillExpiration > block.timestamp, "OrderBook: Order Expired"); if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { uint256 dSwapValue; if (srcToken == order.loanTokenAddress) { - amountUsed = order.loanTokenAmount + (order.loanTokenAmount * order.leverage) / 10**18; //adjusts leverage - (dSwapValue, ) = swapImpl.dexAmountOutFormatted( - payload, - amountUsed - ); + amountUsed = + order.loanTokenAmount + + (order.loanTokenAmount * order.leverage) / + 10**18; //adjusts leverage } else { amountUsed = queryRateReturn( order.base, @@ -314,12 +272,15 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { order.collateralTokenAmount ); amountUsed = (amountUsed * order.leverage) / 10**18; - (dSwapValue, ) = swapImpl.dexAmountOutFormatted( - payload, - amountUsed - ); - dSwapValue += order.collateralTokenAmount; } + dSwapValue = + order.collateralTokenAmount + + protocol.getSwapExpectedReturn( + order.loanTokenAddress, + order.base, + amountUsed, + order.loanDataBytes + ); require( order.amountReceived <= dSwapValue, @@ -334,24 +295,20 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } if (order.orderType == IOrderBook.OrderType.LIMIT_CLOSE) { uint256 dSwapValue; - if (order.isCollateral) { - (dSwapValue, ) = swapImpl.dexAmountInFormatted( - payload, - order.collateralTokenAmount - ); - require(order.amountReceived <= dSwapValue, "OrderBook: amountIn too low"); - } else { - (dSwapValue, ) = swapImpl.dexAmountOutFormatted( - payload, - order.collateralTokenAmount - ); - require(order.amountReceived <= dSwapValue, "OrderBook: amountOut too low"); - } + dSwapValue = protocol.getSwapExpectedReturn( + order.base, + order.loanTokenAddress, + order.collateralTokenAmount, + order.loanDataBytes + ); + require( + order.amountReceived <= dSwapValue, + "OrderBook: amountOut too low" + ); _executeTradeClose( order.trader, order.loanID, order.collateralTokenAmount, - order.isCollateral, order.loanDataBytes ); _allOrders[orderID].status = IOrderBook.OrderStatus.EXECUTED; @@ -374,10 +331,9 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { operand = order.amountReceived >= getDexRate( - swapImpl, order.base, order.loanTokenAddress, - payload, + order.loanDataBytes, order.collateralTokenAmount ); } @@ -386,8 +342,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { priceCheck( order.loanTokenAddress, order.base, - swapImpl, - payload + order.loanDataBytes ), "OrderBook: invalid swap rate" ); @@ -395,7 +350,6 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { order.trader, order.loanID, order.collateralTokenAmount, - order.isCollateral, order.loanDataBytes ); _allOrders[orderID].status = IOrderBook.OrderStatus.EXECUTED; diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index fce66ba7..c930d994 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -30,7 +30,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { for (uint i; i < spenders.length;) { spender = spenders[i]; for (uint y; y < tokens.length;) { - token = tokens[i]; + token = tokens[y]; require( protocol.isLoanPool(spender) || address(protocol) == spender || diff --git a/contracts/orderbook/Storage/OrderBookStorage.sol b/contracts/orderbook/Storage/OrderBookStorage.sol index bf6cfcc1..550598f1 100644 --- a/contracts/orderbook/Storage/OrderBookStorage.sol +++ b/contracts/orderbook/Storage/OrderBookStorage.sol @@ -12,7 +12,8 @@ contract OrderBookStorage { uint256 public constant MIN_AMOUNT_IN_USDC = 1e6; address public constant USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; address public constant vault = 0xFA6485ec4Aa9AF504adb4ed47b567E1875E21e85; - IBZx public constant protocol = IBZx(0x059D60a9CEfBc70b9Ea9FFBb9a041581B1dFA6a8); + IBZx public constant protocol = + IBZx(0x059D60a9CEfBc70b9Ea9FFBb9a041581B1dFA6a8); mapping(bytes4 => address) public logicTargets; From b10e1278b8277591301a68b486f0b825d77fedc6 Mon Sep 17 00:00:00 2001 From: Drypto Date: Fri, 8 Apr 2022 22:57:23 -0700 Subject: [PATCH 51/83] minor fixes and interface update --- contracts/orderbook/IOrderBook.sol | 36 +++++++++++------- contracts/orderbook/Keepers/OrderKeeper.sol | 4 +- contracts/orderbook/Logic/OrderBook.sol | 42 +++++++++------------ contracts/orderbook/Logic/OrderBookData.sol | 1 - 4 files changed, 44 insertions(+), 39 deletions(-) diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index 5d690943..cb56cab3 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -42,35 +42,45 @@ interface IOrderBook { function cancelOrder(bytes32 orderID) external; - function getSwapAddress() external view returns (address); + function cancelOrderProtocol(bytes32 orderID) external; - function getFeed() external view returns (address); + function changeStopType(bool stopType) external; - function getOrdersLimited(uint256 start, uint256 end) external view returns (Order[] memory); + function recoverFundsFromFailedOrder(bytes32 orderID) external; - function getOrders() external view returns (Order[] memory); + function getFeed() external view returns (address); - function getOrderByOrderID(bytes32 orderID) external view returns (Order[] memory); + function getDexRate(address srcToken, address destToken, bytes calldata payload, uint256 amountIn) external returns(uint256); - function getActiveOrders(address trader) external view returns (Order[] memory); + function clearOrder(bytes32 orderID) external view returns (bool); - function getActiveOrderIDs(address trader) external view returns (bytes32[] memory); + function prelimCheck(bytes32 orderID) external returns (bool); - function getActiveOrdersLimited(address trader, uint256 start, uint256 end) external view returns (Order[] memory); + function queryRateReturn(address srcToken, address destToken, uint256 amount) external view returns(uint256); - function getTotalOrders(address trader) external view returns (uint256); + function priceCheck(address srcToken, address destToken, bytes calldata payload) external returns(bool); function executeOrder(bytes32 orderID) external; - function cancelOrderProtocol(bytes32 orderID) external; + function adjustAllowance(address[] calldata spenders, address[] calldata tokens) external; - function clearOrder(bytes32 orderID) external view returns (bool); + function getActiveOrders(address trader) external view returns (Order[] memory); - function prelimCheck(bytes32 orderID) external returns (bool); + function getActiveOrdersLimited(address trader, uint256 start, uint256 end) external view returns (Order[] memory); - function getOrderIDs() external view returns (bytes32[] memory); + function getOrderByOrderID(bytes32 orderID) external view returns (Order[] memory); + + function getActiveOrderIDs(address trader) external view returns (bytes32[] memory); + + function getTotalOrders(address trader) external view returns (uint256); function getTotalOrderIDs() external view returns (uint256); + function getOrderIDs() external view returns (bytes32[] memory); + + function getOrders() external view returns (Order[] memory); + function getOrderIDsLimited(uint256 start, uint256 end) external view returns (bytes32[] memory); + + function getOrdersLimited(uint256 start, uint256 end) external view returns (Order[] memory); } diff --git a/contracts/orderbook/Keepers/OrderKeeper.sol b/contracts/orderbook/Keepers/OrderKeeper.sol index 65cdbfb6..6dcc05fc 100644 --- a/contracts/orderbook/Keepers/OrderKeeper.sol +++ b/contracts/orderbook/Keepers/OrderKeeper.sol @@ -43,6 +43,8 @@ contract OrderKeeper is PausableGuardian_0_8 { function performUpKeep(bytes calldata performData) external pausable { bytes32 orderId = abi.decode(performData, (bytes32)); //emit OrderExecuted(trader,orderId); - factory.executeOrder(orderId); + try factory.executeOrder(orderId) { + + } catch(bytes memory){} catch Error (string memory) {} } } diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index e562cee4..2c4e7039 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -9,7 +9,6 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { using EnumerableSet for EnumerableSet.Bytes32Set; function initialize(address target) public onlyOwner { - _setTarget(this.getSwapAddress.selector, target); _setTarget(this.getFeed.selector, target); _setTarget(this.getDexRate.selector, target); _setTarget(this.clearOrder.selector, target); @@ -59,10 +58,6 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { ); } - function getSwapAddress() public view returns (address) { - return protocol.swapsImpl(); - } - function getFeed() public view returns (address) { return protocol.priceFeeds(); } @@ -110,9 +105,8 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { address end, uint256 amount ) public view returns (uint256) { - (uint256 executionPrice, uint256 precision) = IPriceFeeds(getFeed()) - .queryRate(start, end); - return (executionPrice * amount) / precision; + return IPriceFeeds(getFeed()) + .queryReturn(start, end, amount); } function prelimCheck(bytes32 orderID) external returns (bool) { @@ -200,15 +194,15 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } function getDexRate( - address base, - address loanTokenAddress, + address srcToken, + address destToken, bytes memory payload, uint256 amountIn ) public returns (uint256 rate) { - uint256 tradeSize = 10**IERC20Metadata(base).decimals(); + uint256 tradeSize = 10**IERC20Metadata(srcToken).decimals(); rate = protocol.getSwapExpectedReturn( - base, - loanTokenAddress, + srcToken, + destToken, amountIn, payload ); @@ -216,26 +210,26 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } function priceCheck( - address loanTokenAddress, - address base, + address srcToken, + address destToken, bytes memory payload ) public returns (bool) { - uint256 tradeSize = 10**IERC20Metadata(base).decimals(); + uint256 tradeSize = 10**IERC20Metadata(srcToken).decimals(); uint256 dexRate = getDexRate( - base, - loanTokenAddress, + srcToken, + destToken, payload, tradeSize ); - uint256 indexRate = queryRateReturn(base, loanTokenAddress, tradeSize); + uint256 indexRate = queryRateReturn(srcToken, destToken, tradeSize); if (dexRate >= indexRate) { - if (((dexRate - indexRate) * 1000) / dexRate <= 5) { + if (((dexRate - indexRate) * 1000) / dexRate <= 9) { return true; } else { return false; } } else { - if (((indexRate - dexRate) * 1000) / indexRate <= 5) { + if (((indexRate - dexRate) * 1000) / indexRate <= 9) { return true; } else { return false; @@ -293,7 +287,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { emit OrderExecuted(order.trader, orderID); return; } - if (order.orderType == IOrderBook.OrderType.LIMIT_CLOSE) { + else if (order.orderType == IOrderBook.OrderType.LIMIT_CLOSE) { uint256 dSwapValue; dSwapValue = protocol.getSwapExpectedReturn( order.base, @@ -317,7 +311,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { emit OrderExecuted(order.trader, orderID); return; } - if (order.orderType == IOrderBook.OrderType.MARKET_STOP) { + else if (order.orderType == IOrderBook.OrderType.MARKET_STOP) { bool operand; if (_useOracle[order.trader]) { operand = @@ -340,8 +334,8 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { require( operand && priceCheck( - order.loanTokenAddress, order.base, + order.loanTokenAddress, order.loanDataBytes ), "OrderBook: invalid swap rate" diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index c930d994..093307c9 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -16,7 +16,6 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { _setTarget(this.getOrderByOrderID.selector, target); _setTarget(this.getActiveOrderIDs.selector, target); _setTarget(this.getTotalOrders.selector, target); - _setTarget(this.getActiveOrders.selector, target); _setTarget(this.getTotalOrderIDs.selector, target); _setTarget(this.getOrderIDs.selector, target); _setTarget(this.getOrders.selector, target); From 55d0ad19adc4d8b7f2c9e0a8a550200f8dbc703f Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Fri, 8 Apr 2022 23:22:44 -0700 Subject: [PATCH 52/83] updated env --- contracts/governance/PausableGuardian_0_8.sol | 6 ++---- contracts/orderbook/Logic/OrderBook.sol | 2 +- contracts/orderbook/Logic/OrderBookData.sol | 12 +++++------- scripts/env/set-matic.py | 2 +- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/contracts/governance/PausableGuardian_0_8.sol b/contracts/governance/PausableGuardian_0_8.sol index 1a2914ee..711727a5 100644 --- a/contracts/governance/PausableGuardian_0_8.sol +++ b/contracts/governance/PausableGuardian_0_8.sol @@ -7,17 +7,15 @@ pragma solidity ^0.8.0; import "@openzeppelin-4.3.2/access/Ownable.sol"; - contract PausableGuardian_0_8 is Ownable { - // keccak256("Pausable_FunctionPause") bytes32 internal constant Pausable_FunctionPause = 0xa7143c84d793a15503da6f19bf9119a2dac94448ca45d77c8bf08f57b2e91047; // keccak256("Pausable_GuardianAddress") bytes32 internal constant Pausable_GuardianAddress = 0x80e6706973d0c59541550537fd6a33b971efad732635e6c3b99fb01006803cdf; - modifier pausable { - require(!_isPaused(msg.sig), "paused"); + modifier pausable() { + require(!_isPaused(msg.sig) || msg.sender == getGuardian(), "paused"); _; } diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index 2c4e7039..20c340b1 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -66,7 +66,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { return protocol.loans(ID).active; } - function clearOrder(bytes32 orderID) public view pausable returns (bool) { + function clearOrder(bytes32 orderID) public view returns (bool) { IOrderBook.Order memory order = _allOrders[orderID]; if (order.timeTillExpiration < block.timestamp) { return true; diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index 093307c9..44e36423 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -1,13 +1,12 @@ pragma solidity ^0.8.0; + import "../Storage/OrderBookEvents.sol"; import "../Storage/OrderBookStorage.sol"; - -interface IERC { - function approve(address spender, uint amount) external; //for USDT -} +import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; contract OrderBookData is OrderBookEvents, OrderBookStorage { using EnumerableSet for EnumerableSet.Bytes32Set; + using SafeERC20 for IERC20; function initialize(address target) public onlyOwner { _setTarget(this.adjustAllowance.selector, target); @@ -23,7 +22,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { _setTarget(this.getOrdersLimited.selector, target); } - function adjustAllowance(address[] memory spenders, address[] memory tokens) external pausable { + function adjustAllowance(address[] memory spenders, address[] memory tokens) external onlyOwner { address spender; address token; for (uint i; i < spenders.length;) { @@ -36,8 +35,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { vault == spender, "OrderBook: invalid spender" ); - IERC(token).approve(spender, 0); - IERC(token).approve(spender, type(uint256).max); + IERC20(token).safeApprove(spender, type(uint256).max); unchecked { ++y; } } unchecked { ++i; } diff --git a/scripts/env/set-matic.py b/scripts/env/set-matic.py index e0303b87..5be53dd6 100644 --- a/scripts/env/set-matic.py +++ b/scripts/env/set-matic.py @@ -1,6 +1,6 @@ BZX = Contract.from_abi("BZX", "0x059D60a9CEfBc70b9Ea9FFBb9a041581B1dFA6a8", interface.IBZx.abi) TOKEN_REGISTRY = Contract.from_abi("TOKEN_REGISTRY", "0x4B234781Af34E9fD756C27a47675cbba19DC8765", TokenRegistry.abi) -ORDERBOOK = Contract.from_abi("ORDERBOOK", "0xF2749292ed252890D6979cd4Ed1676b671235a67", interface.IOrderBook.abi) +ORDERBOOK = Contract.from_abi("ORDERBOOK", "0x9A3B9d4379Ec31aA527cB226890412Ef40A3C1c8", interface.IOrderBook.abi) list = TOKEN_REGISTRY.getTokens(0, 100) for l in list: iTokenTemp = Contract.from_abi("iTokenTemp", l[0], LoanTokenLogicStandard.abi) From 39c864713b126ce06bc370fd326c4a7ded3b86cb Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Sat, 9 Apr 2022 17:26:17 -0700 Subject: [PATCH 53/83] partial re-structure for chainlink keeper contracts --- contracts/orderbook/IOrderBook.sol | 32 ++++++++++++-- contracts/orderbook/Keepers/OrderKeeper.sol | 42 +++++++------------ .../orderbook/Keepers/OrderKeeperClear.sol | 39 ++++++----------- contracts/orderbook/Logic/OrderBook.sol | 33 ++++++++++++++- 4 files changed, 88 insertions(+), 58 deletions(-) diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index cb56cab3..3cca467c 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -30,30 +30,54 @@ interface IOrderBook { bytes loanDataBytes; } - function vault() external view returns(address); + /// Returns Deposits contract address + /// @return vault Deposits Contract + function vault() external view returns(address vault); - function protocol() external view returns(address); + /// Returns Protocol contract address + /// @return protocol bZxProtocol Contract + function protocol() external view returns(address protocol); - function MIN_AMOUNT_IN_USDC() external view returns(uint256); + /// Returns minimum trade size in USDC + /// @return size USDC amount + function MIN_AMOUNT_IN_USDC() external view returns(uint256 size); + /// Places new Order + /// @param order Order Struct function placeOrder(Order calldata order) external; + /// Amends Order + /// @param order Order Struct function amendOrder(Order calldata order) external; + /// Cancels Order + /// @param orderID ID of order to be canceled function cancelOrder(bytes32 orderID) external; + /// Cancels Order + /// @param orderID ID of order to be canceled function cancelOrderProtocol(bytes32 orderID) external; + /// Changes stop type between index and dex price + /// @param stopType true = index, false = dex price function changeStopType(bool stopType) external; + /// Withdraws funds from a trade that failed + /// @param orderID order ID for trade that failed to execute function recoverFundsFromFailedOrder(bytes32 orderID) external; - function getFeed() external view returns (address); + /// Return price feed contract address + /// @return priceFeed Price Feed Contract Address + function getFeed() external view returns (address priceFeed); function getDexRate(address srcToken, address destToken, bytes calldata payload, uint256 amountIn) external returns(uint256); function clearOrder(bytes32 orderID) external view returns (bool); + function getClearOrderList(uint start, uint end) external view returns (bool hasOrders, bytes memory payload); + + function getExecuteOrder(uint start, uint end) external returns (bytes32 ID); + function prelimCheck(bytes32 orderID) external returns (bool); function queryRateReturn(address srcToken, address destToken, uint256 amount) external view returns(uint256); diff --git a/contracts/orderbook/Keepers/OrderKeeper.sol b/contracts/orderbook/Keepers/OrderKeeper.sol index 6dcc05fc..437aafb6 100644 --- a/contracts/orderbook/Keepers/OrderKeeper.sol +++ b/contracts/orderbook/Keepers/OrderKeeper.sol @@ -4,47 +4,35 @@ import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; import "../../governance/PausableGuardian_0_8.sol"; contract OrderKeeper is PausableGuardian_0_8 { - IOrderBook public factory; - - constructor(IOrderBook factoryAddress) { - factory = factoryAddress; - } + address public implementation; + IOrderBook public orderBook; function checkUpKeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData) { (uint256 start, uint256 end) = abi.decode(checkData, (uint256, uint256)); - uint256 orderIDLength = factory.getTotalOrderIDs(); - if(end < orderIDLength) { - if (start > orderIDLength) { - end = orderIDLength; - } else { - return (upkeepNeeded, performData); - } + uint256 orderIDLength = orderBook.getTotalOrderIDs(); + if (start > orderIDLength) { + return (upkeepNeeded, performData); } - IOrderBook.Order[] memory listOfMainOrders = factory - .getOrdersLimited(start, end); - for (uint256 x = 0; x < listOfMainOrders.length;) { - try factory.prelimCheck(listOfMainOrders[x].orderID) returns (bool isExecutable) { - if(isExecutable) { - upkeepNeeded = true; - performData = abi.encode(listOfMainOrders[x].orderID); - return (upkeepNeeded, performData); - } - } catch Error (string memory) { - - } - unchecked { ++x; } + if(end > orderIDLength) { + end = orderIDLength; } - return (upkeepNeeded, performData); + bytes32 orderIDForExec = orderBook + .getExecuteOrder(start, end); + return (orderIDForExec != 0, abi.encode(orderIDForExec)); } function performUpKeep(bytes calldata performData) external pausable { bytes32 orderId = abi.decode(performData, (bytes32)); //emit OrderExecuted(trader,orderId); - try factory.executeOrder(orderId) { + try orderBook.executeOrder(orderId) { } catch(bytes memory){} catch Error (string memory) {} } + + function setOrderBook(IOrderBook contractAddress) external onlyOwner { + orderBook = contractAddress; + } } diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol index 326e68fc..7f3f47cd 100644 --- a/contracts/orderbook/Keepers/OrderKeeperClear.sol +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -4,11 +4,8 @@ import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; import "../../governance/PausableGuardian_0_8.sol"; contract OrderKeeperClear is PausableGuardian_0_8 { - IOrderBook public factory; - - constructor(IOrderBook factoryAddress) { - factory = factoryAddress; - } + address public implementation; + IOrderBook public orderBook; function checkUpKeep(bytes calldata checkData) external @@ -16,36 +13,26 @@ contract OrderKeeperClear is PausableGuardian_0_8 { returns (bool upkeepNeeded, bytes memory performData) { (uint256 start, uint256 end) = abi.decode(checkData, (uint256, uint256)); - uint256 orderIDLength = factory.getTotalOrderIDs(); - if(end < orderIDLength) { - if (start > orderIDLength) { - end = orderIDLength; - } else { - return (upkeepNeeded, performData); - } + uint256 orderIDLength = orderBook.getTotalOrderIDs(); + if (start > orderIDLength) { + return (upkeepNeeded, performData); } - IOrderBook.Order[] memory listOfMainOrders = factory - .getOrdersLimited(start, end); - bytes32[] memory clearable = new bytes32[](7); - uint iter = 0; - for (uint256 x = 0; x < listOfMainOrders.length;) { - if (factory.clearOrder(listOfMainOrders[x].orderID)) { - upkeepNeeded = true; - clearable[iter] = listOfMainOrders[x].orderID; - ++iter; - return (upkeepNeeded, performData); - } - unchecked { ++x; } + if(end > orderIDLength) { + end = orderIDLength; } - return (upkeepNeeded, performData); + return orderBook.getClearOrderList(start, end); } function performUpKeep(bytes calldata performData) external pausable { bytes32[] memory orderId = abi.decode(performData, (bytes32[])); //emit OrderExecuted(trader,orderId); for (uint i;i Date: Sat, 9 Apr 2022 18:02:16 -0700 Subject: [PATCH 54/83] full comments on interface --- contracts/orderbook/IOrderBook.sol | 121 +++++++++++++++++--- contracts/orderbook/Logic/OrderBookData.sol | 21 ++++ contracts/utils/ExponentMath.sol | 4 +- 3 files changed, 128 insertions(+), 18 deletions(-) diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index 3cca467c..73bd897b 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -13,21 +13,53 @@ interface IOrderBook { EXECUTED } + /* + Used values for different order types: + LIMIT_OPEN: + loanID + orderID + amountReceived + leverage + loanTokenAmount + collateralTokenAmount + trader + iToken + loanTokenAddress + base + orderType + status + timeTillExpiration + loanDataBytes + LIMIT_CLOSE and MARKET_STOP: + loanID + orderID + amountReceived + loanTokenAmount + collateralTokenAmount + trader + iToken + loanTokenAddress + base + orderType + status + timeTillExpiration + loanDataBytes + */ struct Order { - bytes32 loanID; - bytes32 orderID; - uint256 amountReceived; - uint256 leverage; - uint256 loanTokenAmount; - uint256 collateralTokenAmount; - address trader; - address iToken; - address loanTokenAddress; - address base; - OrderType orderType; - OrderStatus status; - uint64 timeTillExpiration; - bytes loanDataBytes; + bytes32 loanID; //ID of the loan on OOKI protocol + bytes32 orderID; //order ID + uint256 amountReceived; //amount received from the trade executing. Denominated in base for limit open and loanTokenAddress for limit close and market stop + uint256 leverage; //leverage amount + uint256 loanTokenAmount; //loan token amount denominated in loanTokenAddress + uint256 collateralTokenAmount; //collateral token amount denominated in base + address trader; //trader placing order + address iToken; //iToken being interacted with + address loanTokenAddress; //loan token + address base; //collateral token + OrderType orderType; //order type + OrderStatus status; //order status + uint64 timeTillExpiration; //Time till expiration. Useful for GTD and time-based cancellation + bytes loanDataBytes; //data passed for margin trades } /// Returns Deposits contract address @@ -35,7 +67,7 @@ interface IOrderBook { function vault() external view returns(address vault); /// Returns Protocol contract address - /// @return protocol bZxProtocol Contract + /// @return protocol ooki protocol contract function protocol() external view returns(address protocol); /// Returns minimum trade size in USDC @@ -70,41 +102,98 @@ interface IOrderBook { /// @return priceFeed Price Feed Contract Address function getFeed() external view returns (address priceFeed); + /// Return amount received through a specified swap + /// @param srcToken source token address + /// @param destToken destination token address + /// @param payload loanDataBytes passed for margin trades + /// @param amountIn amount in for the swap function getDexRate(address srcToken, address destToken, bytes calldata payload, uint256 amountIn) external returns(uint256); + /// Checks if order is able to be cleared from books due to failing to meet all requirements + /// @param orderID order ID function clearOrder(bytes32 orderID) external view returns (bool); + /// Returns list of orders that are up to be cleared. Used for Chainlink Keepers + /// @param start starting index + /// @param end ending index + /// @return hasOrders true if the payload contains any orders + /// @return payload bytes32[] encoded with the order IDs up for clearing from books function getClearOrderList(uint start, uint end) external view returns (bool hasOrders, bytes memory payload); + /// Returns an order ID available for execution. Used for Chainlink Keepers + /// @param start starting index + /// @param end ending index + /// @return ID order ID up for execution. If equal to 0 there is no order ID up for execution in the specified index range function getExecuteOrder(uint start, uint end) external returns (bytes32 ID); + /// Checks if order meets requirements for execution + /// @param orderID order ID of order being checked function prelimCheck(bytes32 orderID) external returns (bool); + /// Returns oracle rate for a swap + /// @param srcToken source token address + /// @param destToken destination token address + /// @param amount swap amount function queryRateReturn(address srcToken, address destToken, uint256 amount) external view returns(uint256); + /// Checks if dex rate is within acceptable bounds from oracle rate + /// @param srcToken source token address + /// @param destToken destination token address + /// @param payload loanDataBytes used for margin trade function priceCheck(address srcToken, address destToken, bytes calldata payload) external returns(bool); + /// Executes Order + /// @param orderID order ID function executeOrder(bytes32 orderID) external; + /// sets token allowances + /// @param spenders addresses that will be given allowance + /// @param tokens token addresses function adjustAllowance(address[] calldata spenders, address[] calldata tokens) external; + /// revokes token allowances + /// @param spenders addresses that will have allowance revoked + /// @param tokens token addresses + function revokeAllowance(address[] calldata spenders, address[] calldata tokens) external; + + /// Retrieves active orders for a trader + /// @param trader address of trader function getActiveOrders(address trader) external view returns (Order[] memory); + /// Retrieves active orders for a trader + /// @param trader address of trader + /// @param start starting index + /// @param end ending index function getActiveOrdersLimited(address trader, uint256 start, uint256 end) external view returns (Order[] memory); + /// Retrieves order corresponding to an order ID + /// @param orderID order ID function getOrderByOrderID(bytes32 orderID) external view returns (Order[] memory); + /// Retrieves active order IDs for a trader + /// @param trader address of trader function getActiveOrderIDs(address trader) external view returns (bytes32[] memory); + /// Returns total active orders count for a trader + /// @param trader address of trader function getTotalOrders(address trader) external view returns (uint256); + /// Returns total active orders count function getTotalOrderIDs() external view returns (uint256); + /// Returns total active order IDs function getOrderIDs() external view returns (bytes32[] memory); - + + /// Returns total active orders function getOrders() external view returns (Order[] memory); + /// Returns active order IDs + /// @param start starting index + /// @param end ending index function getOrderIDsLimited(uint256 start, uint256 end) external view returns (bytes32[] memory); + /// Returns active orders + /// @param start starting index + /// @param end ending index function getOrdersLimited(uint256 start, uint256 end) external view returns (Order[] memory); } diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index 44e36423..b5625401 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -10,6 +10,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { function initialize(address target) public onlyOwner { _setTarget(this.adjustAllowance.selector, target); + _setTarget(this.revokeAllowance.selector, target); _setTarget(this.getActiveOrders.selector, target); _setTarget(this.getActiveOrdersLimited.selector, target); _setTarget(this.getOrderByOrderID.selector, target); @@ -43,6 +44,26 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { } + function revokeAllowance(address[] memory spenders, address[] memory tokens) external onlyOwner { + address spender; + address token; + for (uint i; i < spenders.length;) { + spender = spenders[i]; + for (uint y; y < tokens.length;) { + token = tokens[y]; + require( + protocol.isLoanPool(spender) || + address(protocol) == spender || + vault == spender, + "OrderBook: invalid spender" + ); + IERC20(token).safeApprove(spender, 0); + unchecked { ++y; } + } + unchecked { ++i; } + } + + } function getActiveOrders(address trader) external view diff --git a/contracts/utils/ExponentMath.sol b/contracts/utils/ExponentMath.sol index bab557e8..4bb14e71 100644 --- a/contracts/utils/ExponentMath.sol +++ b/contracts/utils/ExponentMath.sol @@ -1,9 +1,9 @@ -pragma solidity ^0.8.4; +pragma solidity ^0.8.0; library ExponentMath{ - function TenExp(uint256 number, int8 pow) public pure returns (uint256){ + function tenExp(uint256 number, int8 pow) public pure returns (uint256){ if(pow < 0){ number=number/10**(uint8(pow*-1)); }else{ From 1ce6760ce8c54e9a84efbf7760ab81805669060f Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Sat, 9 Apr 2022 18:19:23 -0700 Subject: [PATCH 55/83] updated test case --- .../orderbook/Logic/OrderBookOrderPlace.sol | 2 +- testsarbitrum/limit-orders/test_Orders.py | 69 ------------------- testspolygon/limit-orders/test_Orders.py | 54 +++++++++++++++ 3 files changed, 55 insertions(+), 70 deletions(-) delete mode 100644 testsarbitrum/limit-orders/test_Orders.py create mode 100644 testspolygon/limit-orders/test_Orders.py diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index b595d2c8..2c3e472e 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -40,7 +40,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { : true, "OrderBook: case checks failed"); require(order.orderType == IOrderBook.OrderType.LIMIT_OPEN - ? protocol.isLoanPool(order.iToken) + ? protocol.loanPoolToUnderlying(order.iToken) == order.loanTokenAddress : true, "OrderBook: Not a loan pool"); require(order.orderType == IOrderBook.OrderType.LIMIT_OPEN diff --git a/testsarbitrum/limit-orders/test_Orders.py b/testsarbitrum/limit-orders/test_Orders.py deleted file mode 100644 index d8b54c65..00000000 --- a/testsarbitrum/limit-orders/test_Orders.py +++ /dev/null @@ -1,69 +0,0 @@ -from brownie import * -import pytest -from eth_abi import encode_abi -accounts = ['0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266'] - -def test_main(): - orderbook, keeper = deploy_contracts() - set_perms(orderbook) - orderbook = interface.IOrderBook(orderbook.address) - place_order_open(orderbook) - print(orderbook.prelimCheck.call(orderbook.getActiveOrders(accounts[0])[0][1])) - orderbook.executeOrder(orderbook.getActiveOrders(accounts[0])[0][1], {'from':accounts[0]}) - trades = interface.IBZx('0x37407F3178ffE07a6cF5C847F8f680FEcf319FAB').getUserLoans(accounts[0],0,10,0,False,False) - print(trades) - place_order_close(orderbook, trades[0][0], int(7.4e14)) - print(orderbook.getOrders()) - isExec, data = keeper.checkUpKeep.call(encode_abi(['uint256','uint256'],[0,orderbook.getTotalActiveOrders()])) - keeper.checkUpKeep(encode_abi(['uint256','uint256'],[0,orderbook.getTotalActiveOrders()]), {'from':accounts[0]}) - if(isExec): - keeper.performUpKeep(data) - print(orderbook.getActiveOrders(accounts[0])) - trades = interface.IBZx('0x37407F3178ffE07a6cF5C847F8f680FEcf319FAB').getUserLoans(accounts[0],0,10,0,False,False) - print(trades) - assert(False) - -def deploy_contracts(): - orderbook = OrderBookProxy.deploy('0x37407F3178ffE07a6cF5C847F8f680FEcf319FAB', {'from':accounts[0]}) - orderbookLogic = OrderBook.deploy({'from':accounts[0]}) - orderBookData = OrderBookData.deploy({'from':accounts[0]}) - orderBookOrderPlace = OrderBookOrderPlace.deploy({'from':accounts[0]}) - orderbook.replaceContract(orderbookLogic.address, {'from':orderbook.owner()}) - orderbook.replaceContract(orderBookData.address, {'from':orderbook.owner()}) - orderbook.replaceContract(orderBookOrderPlace.address, {'from':orderbook.owner()}) - d = Deposits.deploy({'from':accounts[0]}) - d.setOrderBook(orderbook.address, {'from':accounts[0]}) - Contract.from_abi('',orderbook.address,OrderBook.abi).setVaultAddress(d.address, {'from':orderbook.owner()}) - keeper = OrderKeeper.deploy(orderbook.address, {'from':accounts[0]}) - BZX = interface.IBZx('0x37407F3178ffE07a6cF5C847F8f680FEcf319FAB') - bzx = Contract.from_abi('',BZX.address,bZxProtocol.abi) - LoanOpening = LoanOpenings.deploy({'from':bzx.owner()}) - bzx.replaceContract(LoanOpening.address, {'from':bzx.owner()}) - return orderbook, keeper - -def set_perms(orderbook): - Contract.from_abi('',orderbook.address,OrderBookData.abi).adjustAllowance('0xEDa7f294844808B7C93EE524F990cA7792AC2aBd', - '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8', {'from':orderbook.owner()}) - interface.IERC20('0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8').transfer(accounts[0], 200e6, {'from':'0x489ee077994b6658eafa855c308275ead8097c4a'}) - interface.IERC20('0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8').approve(orderbook.vault.call(), 2000e6, {'from':accounts[0]}) -def place_order_open(orderbook_contract): - payload = encode_abi(['uint128','bytes[]'],[4,[b'',encode_abi(['address'], [orderbook_contract.address])]]) - orderdata = [0,0,3e14,2e18,1e6,0,(1647152473+60*60*24*30),accounts[0], - '0xEDa7f294844808B7C93EE524F990cA7792AC2aBd','0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8','0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', - 0,False,False,payload] - orderbook_contract.placeOrder(orderdata, {'from':accounts[0]}) - -def place_order_close(orderbook_contract, position, size): - orderdata = [position,0,10,0,0,size,(1647152473+60*60*24*30),accounts[0], - '0xEDa7f294844808B7C93EE524F990cA7792AC2aBd','0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8','0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', - 1,False,False,b''] - orderbook_contract.placeOrder(orderdata, {'from':accounts[0]}) - -def amend_order(): - print() - -def cancel_order(orderbook_contract, position): - orderbook_contract.cancelOrder(position, {'from':accounts[0]}) - -def get_order_specs(orderID): - return diff --git a/testspolygon/limit-orders/test_Orders.py b/testspolygon/limit-orders/test_Orders.py new file mode 100644 index 00000000..a9c95bad --- /dev/null +++ b/testspolygon/limit-orders/test_Orders.py @@ -0,0 +1,54 @@ +from brownie import * +from eth_abi import encode_abi + +def test_t(): + ORDERBOOK = interface.IOrderBook('0x9A3B9d4379Ec31aA527cB226890412Ef40A3C1c8') + print(ORDERBOOK.getDexRate.call( + "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", b'', 1e18)) + print(ORDERBOOK.queryRateReturn.call( + "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", 1e18)) + + print(ORDERBOOK.priceCheck.call( + "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", b'')) + USDC = interface.IERC20('0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174') + iUSDC = interface.IERC20('0xC3f6816C860e7d7893508C8F8568d5AF190f6d7d') + ETH = interface.IERC20('0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619') + USDC.transfer(accounts[0], 20e6, { + 'from': '0xf977814e90da44bfa03b6295a0616a897441acec'}) + USDC.approve(ORDERBOOK.vault(), 2e10, {'from': accounts[0]}) + data = encode_abi(['address'],[ORDERBOOK.address]) + data = encode_abi(['uint256','bytes[]'],[6,(encode_abi(['uint256','bytes'],[1,encode_abi(['address','address'],[USDC.address,ETH.address])]),data)]) + print(ORDERBOOK.placeOrder([0, 0, 1e6, 1e18, int(1.1e6), 0, accounts[0], + iUSDC, USDC, ETH, 0, 0, 1000000000000, data], {'from': accounts[0]}).gas_used) + print(ORDERBOOK.getOrders.call()[0]) + print(ORDERBOOK.executeOrder(ORDERBOOK.getOrders.call()[0][1],{'from':accounts[0]}).gas_used) + protocolRank = interface.IBZx(ORDERBOOK.protocol.call()) + ll = protocolRank.getUserLoans.call(accounts[0],0,10,0,False,False) + print(ll) + print(ORDERBOOK.placeOrder([ll[0][0],0,1e18,0,0,374183490999849,accounts[0], + iUSDC, USDC, ETH, 2, 0, 1000000000000, b''], {'from': accounts[0]}).gas_used) + ORDERBOOK.changeStopType(True, {'from':accounts[0]}) + print(ORDERBOOK.getActiveOrders.call(accounts[0])) + print(ORDERBOOK.getActiveOrdersLimited.call(accounts[0],0,ORDERBOOK.getTotalOrders.call(accounts[0]))) + print(ORDERBOOK.getActiveOrderIDs.call(accounts[0])) + print(ORDERBOOK.getTotalOrderIDs.call()) + print(ORDERBOOK.getOrderIDs.call()) + print(ORDERBOOK.getOrderIDsLimited.call(0,ORDERBOOK.getTotalOrderIDs.call())) + print(ORDERBOOK.getOrdersLimited.call(0,ORDERBOOK.getTotalOrderIDs.call())) + print(ORDERBOOK.clearOrder.call(ORDERBOOK.getOrderIDs.call()[0])) + keeper = deploy_keeper(ORDERBOOK) + print(keeper.checkUpKeep(encode_abi(['uint256','uint256'],[0,1]),{'from':accounts[0]}).gas_used) + (needed, data) = keeper.checkUpKeep.call(encode_abi(['uint256','uint256'],[0,1])) + if(needed): + keeper.performUpKeep(data, {'from':accounts[0]}) + print(ORDERBOOK.getOrders.call()) + ll = protocolRank.getUserLoans.call(accounts[0],0,10,0,False,False) + print(ll) + assert(False) + +def deploy_keeper(ORDERBOOK): + keeper = OrderKeeper.deploy({'from':accounts[0]}) + proxy = Proxy_0_8.deploy(keeper, {'from':accounts[0]}) + keeper = Contract.from_abi('',proxy.address,OrderKeeper.abi) + keeper.setOrderBook(ORDERBOOK,{'from':accounts[0]}) + return keeper \ No newline at end of file From ff496b139572547a91963720f2193b8874a047c2 Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Mon, 11 Apr 2022 00:30:15 -0700 Subject: [PATCH 56/83] keeper fix --- contracts/orderbook/Keepers/OrderKeeperClear.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol index 7f3f47cd..be6a1312 100644 --- a/contracts/orderbook/Keepers/OrderKeeperClear.sol +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -27,6 +27,10 @@ contract OrderKeeperClear is PausableGuardian_0_8 { bytes32[] memory orderId = abi.decode(performData, (bytes32[])); //emit OrderExecuted(trader,orderId); for (uint i;i Date: Mon, 11 Apr 2022 12:31:04 -0700 Subject: [PATCH 57/83] min order size includes leverage --- .../orderbook/Logic/OrderBookOrderPlace.sol | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 2c3e472e..9326a0ba 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -57,8 +57,25 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { (uint256 amountUsed, address usedToken) = order.loanTokenAmount > order.collateralTokenAmount ? (order.loanTokenAmount, order.loanTokenAddress) : (order.collateralTokenAmount, order.base); + address srcToken; + if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { + if (usedToken == order.base) { + amountUsed = IPriceFeeds(protocol.priceFeeds()).queryReturn( + order.base, + order.loanTokenAddress, + amountUsed + ); + amountUsed = (amountUsed * order.leverage) / 10**18; + srcToken = order.loanTokenAddress; + } else { + amountUsed += + (amountUsed * order.leverage) / + 10**18; //adjusts leverage + srcToken = order.base; + } + } require( - IPriceFeeds(protocol.priceFeeds()).queryReturn(usedToken, USDC, amountUsed) >= + IPriceFeeds(protocol.priceFeeds()).queryReturn(srcToken, USDC, amountUsed) >= MIN_AMOUNT_IN_USDC, "OrderBook: Order too small" ); From 03095c960c1e6cc4ac4ad0b22bcae1f6459d19d8 Mon Sep 17 00:00:00 2001 From: Drypto Date: Mon, 11 Apr 2022 14:51:59 -0700 Subject: [PATCH 58/83] minor fix on order sizing --- contracts/orderbook/Logic/OrderBookOrderPlace.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 9326a0ba..766caa05 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -71,8 +71,10 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { amountUsed += (amountUsed * order.leverage) / 10**18; //adjusts leverage - srcToken = order.base; + srcToken = order.loanTokenAddress; } + } else { + srcToken = order.base; } require( IPriceFeeds(protocol.priceFeeds()).queryReturn(srcToken, USDC, amountUsed) >= From 46d77cb3259077e6ae277126fb890c256b4fb9ee Mon Sep 17 00:00:00 2001 From: Drypto Date: Tue, 19 Apr 2022 09:26:01 -0700 Subject: [PATCH 59/83] changes --- .../orderbook/Events/OrderBookEvents.sol | 23 +++++++++ contracts/orderbook/IOrderBook.sol | 8 +-- contracts/orderbook/Logic/OrderBook.sol | 42 +++++++-------- contracts/orderbook/Logic/OrderBookData.sol | 14 ++--- .../orderbook/Logic/OrderBookOrderPlace.sol | 51 ++++++++----------- contracts/orderbook/OrderBookProxy.sol | 2 +- .../orderbook/Storage/OrderBookConstants.sol | 14 +++++ .../orderbook/Storage/OrderBookStorage.sol | 23 +++++---- testspolygon/limit-orders/test_Orders.py | 11 ++++ 9 files changed, 117 insertions(+), 71 deletions(-) create mode 100644 contracts/orderbook/Events/OrderBookEvents.sol create mode 100644 contracts/orderbook/Storage/OrderBookConstants.sol diff --git a/contracts/orderbook/Events/OrderBookEvents.sol b/contracts/orderbook/Events/OrderBookEvents.sol new file mode 100644 index 00000000..f0b38c40 --- /dev/null +++ b/contracts/orderbook/Events/OrderBookEvents.sol @@ -0,0 +1,23 @@ +pragma solidity ^0.8.0; +import "../IOrderBook.sol"; + +contract OrderBookEvents { + event OrderCancelled(address indexed trader, bytes32 orderID); + event OrderPlaced( + address indexed trader, + IOrderBook.OrderType indexed OrderType, + uint256 indexed execPrice, + bytes32 orderID, + address collateralTokenAddress, + address loanTokenAddress + ); + event OrderExecuted(address indexed trader, bytes32 orderID); + event OrderAmended( + address indexed trader, + IOrderBook.OrderType indexed OrderType, + uint256 indexed execPrice, + bytes32 orderID, + address collateralTokenAddress, + address loanTokenAddress + ); +} \ No newline at end of file diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index 73bd897b..91a17825 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -94,13 +94,13 @@ interface IOrderBook { /// @param stopType true = index, false = dex price function changeStopType(bool stopType) external; - /// Withdraws funds from a trade that failed - /// @param orderID order ID for trade that failed to execute - function recoverFundsFromFailedOrder(bytes32 orderID) external; + /// Set price feed contract address + /// @param newFeed new price feed contract + function setPriceFeed(address newFeed) external; /// Return price feed contract address /// @return priceFeed Price Feed Contract Address - function getFeed() external view returns (address priceFeed); + function priceFeed() external view returns (address priceFeed); /// Return amount received through a specified swap /// @param srcToken source token address diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index 077f15a3..a94a743a 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -1,15 +1,16 @@ pragma solidity ^0.8.0; -import "../Storage/OrderBookEvents.sol"; +import "../Events/OrderBookEvents.sol"; import "../Storage/OrderBookStorage.sol"; import "../../swaps/ISwapsImpl.sol"; import "../OrderVault/IDeposits.sol"; import "../../mixins/Flags.sol"; +import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { using EnumerableSet for EnumerableSet.Bytes32Set; + using SafeERC20 for IERC20; function initialize(address target) public onlyOwner { - _setTarget(this.getFeed.selector, target); _setTarget(this.getDexRate.selector, target); _setTarget(this.clearOrder.selector, target); _setTarget(this.prelimCheck.selector, target); @@ -18,10 +19,11 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { _setTarget(this.queryRateReturn.selector, target); _setTarget(this.priceCheck.selector, target); _setTarget(this.executeOrder.selector, target); + _setTarget(this.setPriceFeed.selector, target); } function _executeTradeOpen(IOrderBook.Order memory order) internal { - IDeposits(vault).withdraw(order.orderID); + IDeposits(VAULT).withdraw(order.orderID); (bool result, bytes memory data) = order.iToken.call( abi.encodeWithSelector( IToken(order.iToken).marginTrade.selector, @@ -35,10 +37,10 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { ) ); if (!result) { - IDeposits(vault).refund( - order.orderID, - (order.loanTokenAmount + order.collateralTokenAmount) - ); //unlikely to be needed + (address usedToken, uint256 amount) = order.loanTokenAmount > order.collateralTokenAmount + ? (order.loanTokenAddress, order.loanTokenAmount) + : (order.base, order.collateralTokenAmount); + IERC20(usedToken).transfer(order.trader, amount); } } @@ -48,9 +50,9 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { uint256 amount, bytes memory loanDataBytes ) internal { - address(protocol).call( + address(PROTOCOL).call( abi.encodeWithSelector( - protocol.closeWithSwap.selector, + PROTOCOL.closeWithSwap.selector, loanID, trader, amount, @@ -60,12 +62,8 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { ); } - function getFeed() public view returns (address) { - return protocol.priceFeeds(); - } - function _isActiveLoan(bytes32 ID) internal view returns (bool) { - return protocol.loans(ID).active; + return PROTOCOL.loans(ID).active; } function clearOrder(bytes32 orderID) public view returns (bool) { @@ -136,7 +134,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { address end, uint256 amount ) public view returns (uint256) { - return IPriceFeeds(getFeed()) + return IPriceFeeds(priceFeed) .queryReturn(start, end, amount); } @@ -170,7 +168,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } dSwapValue = order.collateralTokenAmount + - protocol.getSwapExpectedReturn( + PROTOCOL.getSwapExpectedReturn( order.loanTokenAddress, order.base, amountUsed, @@ -185,7 +183,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { return false; } uint256 dSwapValue; - dSwapValue = protocol.getSwapExpectedReturn( + dSwapValue = PROTOCOL.getSwapExpectedReturn( order.base, order.loanTokenAddress, order.collateralTokenAmount, @@ -231,7 +229,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { uint256 amountIn ) public returns (uint256 rate) { uint256 tradeSize = 10**IERC20Metadata(srcToken).decimals(); - rate = protocol.getSwapExpectedReturn( + rate = PROTOCOL.getSwapExpectedReturn( srcToken, destToken, amountIn, @@ -300,7 +298,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } dSwapValue = order.collateralTokenAmount + - protocol.getSwapExpectedReturn( + PROTOCOL.getSwapExpectedReturn( order.loanTokenAddress, order.base, amountUsed, @@ -320,7 +318,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } else if (order.orderType == IOrderBook.OrderType.LIMIT_CLOSE) { uint256 dSwapValue; - dSwapValue = protocol.getSwapExpectedReturn( + dSwapValue = PROTOCOL.getSwapExpectedReturn( order.base, order.loanTokenAddress, order.collateralTokenAmount, @@ -384,4 +382,8 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { return; } } + + function setPriceFeed(address newFeed) external onlyOwner { + priceFeed = newFeed; + } } diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index b5625401..1b30e835 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -1,6 +1,6 @@ pragma solidity ^0.8.0; -import "../Storage/OrderBookEvents.sol"; +import "../Events/OrderBookEvents.sol"; import "../Storage/OrderBookStorage.sol"; import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; @@ -31,9 +31,9 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { for (uint y; y < tokens.length;) { token = tokens[y]; require( - protocol.isLoanPool(spender) || - address(protocol) == spender || - vault == spender, + PROTOCOL.isLoanPool(spender) || + address(PROTOCOL) == spender || + VAULT == spender, "OrderBook: invalid spender" ); IERC20(token).safeApprove(spender, type(uint256).max); @@ -52,9 +52,9 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { for (uint y; y < tokens.length;) { token = tokens[y]; require( - protocol.isLoanPool(spender) || - address(protocol) == spender || - vault == spender, + PROTOCOL.isLoanPool(spender) || + address(PROTOCOL) == spender || + VAULT == spender, "OrderBook: invalid spender" ); IERC20(token).safeApprove(spender, 0); diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 766caa05..81d0fcbf 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -1,5 +1,5 @@ pragma solidity ^0.8.0; -import "../Storage/OrderBookEvents.sol"; +import "../Events/OrderBookEvents.sol"; import "../Storage/OrderBookStorage.sol"; import "../OrderVault/IDeposits.sol"; @@ -12,7 +12,6 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { _setTarget(this.cancelOrder.selector, target); _setTarget(this.cancelOrderProtocol.selector, target); _setTarget(this.changeStopType.selector, target); - _setTarget(this.recoverFundsFromFailedOrder.selector, target); } function _caseChecks(bytes32 ID, address collateral, address loanToken) @@ -20,27 +19,27 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { view returns (bool) { - IBZx.LoanReturnData memory data = protocol.getLoan(ID); + IBZx.LoanReturnData memory data = PROTOCOL.getLoan(ID); return data.loanToken == loanToken && data.collateralToken == collateral && - protocol.delegatedManagers(ID, address(this)); + PROTOCOL.delegatedManagers(ID, address(this)); } function _isActiveLoan(bytes32 ID) internal view returns (bool) { - return protocol.loans(ID).active; + return PROTOCOL.loans(ID).active; } function _commonChecks(IOrderBook.Order calldata order) internal { require(!(order.collateralTokenAmount>0) || !(order.loanTokenAmount >0), "OrderBook: collateral and loan token cannot be non-zero"); - require(protocol.supportedTokens(order.loanTokenAddress), "OrderBook: Unsupported loan token"); - require(protocol.supportedTokens(order.base), "OrderBook: Unsupported collateral"); + require(PROTOCOL.supportedTokens(order.loanTokenAddress), "OrderBook: Unsupported loan token"); + require(PROTOCOL.supportedTokens(order.base), "OrderBook: Unsupported collateral"); require(order.loanID != 0 ? _caseChecks(order.loanID, order.base, order.loanTokenAddress) : true, "OrderBook: case checks failed"); require(order.orderType == IOrderBook.OrderType.LIMIT_OPEN - ? protocol.loanPoolToUnderlying(order.iToken) == order.loanTokenAddress + ? PROTOCOL.loanPoolToUnderlying(order.iToken) == order.loanTokenAddress : true, "OrderBook: Not a loan pool"); require(order.orderType == IOrderBook.OrderType.LIMIT_OPEN @@ -60,7 +59,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { address srcToken; if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { if (usedToken == order.base) { - amountUsed = IPriceFeeds(protocol.priceFeeds()).queryReturn( + amountUsed = IPriceFeeds(priceFeed).queryReturn( order.base, order.loanTokenAddress, amountUsed @@ -77,20 +76,20 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { srcToken = order.base; } require( - IPriceFeeds(protocol.priceFeeds()).queryReturn(srcToken, USDC, amountUsed) >= + IPriceFeeds(priceFeed).queryReturn(srcToken, USDC, amountUsed) >= MIN_AMOUNT_IN_USDC, "OrderBook: Order too small" ); require(order.status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: invalid order state"); mainOBID++; bytes32 ID = keccak256(abi.encode(msg.sender, mainOBID)); - require(IDeposits(vault).getTokenUsed(ID) == address(0), "Orderbook: collision"); //in the very unlikely chance of collision on ID error is thrown + require(IDeposits(VAULT).getTokenUsed(ID) == address(0), "Orderbook: collision"); //in the very unlikely chance of collision on ID error is thrown _allOrders[ID] = order; _allOrders[ID].orderID = ID; _histOrders[msg.sender].add(ID); _allOrderIDs.add(ID); if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { - IDeposits(vault).deposit(ID, amountUsed, msg.sender, usedToken); + IDeposits(VAULT).deposit(ID, amountUsed, msg.sender, usedToken); } emit OrderPlaced( msg.sender, @@ -118,24 +117,24 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { (uint256 amountUsed, address usedToken) = order.loanTokenAmount > order.collateralTokenAmount ? (order.loanTokenAmount, order.loanTokenAddress) : (order.collateralTokenAmount, order.base); - uint256 storedAmount = IDeposits(vault).getDeposit( + uint256 storedAmount = IDeposits(VAULT).getDeposit( order.orderID ); require( usedToken == - IDeposits(vault).getTokenUsed(order.orderID), + IDeposits(VAULT).getTokenUsed(order.orderID), "OrderBook: invalid used token" ); uint256 amountUsedOld = _allOrders[order.orderID].loanTokenAmount + _allOrders[order.orderID].collateralTokenAmount; if (amountUsedOld > amountUsed) { - IDeposits(vault).partialWithdraw( + IDeposits(VAULT).partialWithdraw( msg.sender, order.orderID, amountUsedOld - amountUsed ); } else { - IDeposits(vault).deposit( + IDeposits(VAULT).deposit( order.orderID, amountUsed - amountUsedOld, msg.sender, @@ -160,22 +159,14 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { require(_histOrders[msg.sender].remove(orderID), "OrderBook: not owner of order"); _allOrderIDs.remove(orderID); if (_allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN) { - address usedToken = IDeposits(vault).getTokenUsed( + address usedToken = IDeposits(VAULT).getTokenUsed( orderID ); - IDeposits(vault).withdrawToTrader(msg.sender, orderID); + IDeposits(VAULT).withdrawToTrader(msg.sender, orderID); } emit OrderCancelled(msg.sender, orderID); } - function recoverFundsFromFailedOrder(bytes32 orderID) external pausable { - IOrderBook.Order memory order = _allOrders[orderID]; - require(msg.sender == order.trader, "OrderBook: Not trade owner"); - require(order.status==IOrderBook.OrderStatus.CANCELLED, "OrderBook: Order not executed"); - require(!_allOrderIDs.contains(orderID), "OrderBook: Order still in records"); - IDeposits(vault).withdrawToTrader(msg.sender, orderID); - } - function cancelOrderProtocol(bytes32 orderID) external pausable { IOrderBook.Order memory order = _allOrders[orderID]; address trader = order.trader; @@ -184,13 +175,13 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { order.loanTokenAmount; uint256 swapRate; if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { - swapRate = IPriceFeeds(protocol.priceFeeds()).queryReturn( + swapRate = IPriceFeeds(priceFeed).queryReturn( order.loanTokenAddress, order.base, amountUsed ); } else { - swapRate = IPriceFeeds(protocol.priceFeeds()).queryReturn( + swapRate = IPriceFeeds(priceFeed).queryReturn( order.base, order.loanTokenAddress, amountUsed @@ -211,8 +202,8 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { _histOrders[trader].remove(orderID); _allOrderIDs.remove(orderID); if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { - address usedToken = IDeposits(vault).getTokenUsed(orderID); - IDeposits(vault).withdrawToTrader(trader, orderID); + address usedToken = IDeposits(VAULT).getTokenUsed(orderID); + IDeposits(VAULT).withdrawToTrader(trader, orderID); } emit OrderCancelled(trader, orderID); } diff --git a/contracts/orderbook/OrderBookProxy.sol b/contracts/orderbook/OrderBookProxy.sol index a97d6bb9..8e88e973 100644 --- a/contracts/orderbook/OrderBookProxy.sol +++ b/contracts/orderbook/OrderBookProxy.sol @@ -1,6 +1,6 @@ pragma solidity ^0.8.0; import "./Storage/OrderBookStorage.sol"; -import "./Storage/OrderBookEvents.sol"; +import "./Events/OrderBookEvents.sol"; contract OrderBookProxy is OrderBookEvents, OrderBookStorage { diff --git a/contracts/orderbook/Storage/OrderBookConstants.sol b/contracts/orderbook/Storage/OrderBookConstants.sol new file mode 100644 index 00000000..e4fb4470 --- /dev/null +++ b/contracts/orderbook/Storage/OrderBookConstants.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.8.0; + +import "../../../interfaces/IBZx.sol"; + + +contract OrderBookConstants { + address public constant WRAPPED_TOKEN = + 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; + uint256 public constant MIN_AMOUNT_IN_USDC = 1e6; + address public constant USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; + address public constant VAULT = 0xFA6485ec4Aa9AF504adb4ed47b567E1875E21e85; + IBZx public constant PROTOCOL = + IBZx(0x059D60a9CEfBc70b9Ea9FFBb9a041581B1dFA6a8); +} \ No newline at end of file diff --git a/contracts/orderbook/Storage/OrderBookStorage.sol b/contracts/orderbook/Storage/OrderBookStorage.sol index 550598f1..8e2ed868 100644 --- a/contracts/orderbook/Storage/OrderBookStorage.sol +++ b/contracts/orderbook/Storage/OrderBookStorage.sol @@ -1,24 +1,29 @@ pragma solidity ^0.8.0; +import "../IOrderBook.sol"; +import "../../governance/PausableGuardian_0_8.sol"; +import "@openzeppelin-4.3.2/utils/structs/EnumerableSet.sol"; import "../../../interfaces/IPriceFeeds.sol"; import "../../../interfaces/IToken.sol"; -import "../../../interfaces/IBZx.sol"; import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; +import "./OrderBookConstants.sol"; -contract OrderBookStorage { - address public constant WRAPPED_TOKEN = - 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; - uint256 public constant MIN_AMOUNT_IN_USDC = 1e6; - address public constant USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; - address public constant vault = 0xFA6485ec4Aa9AF504adb4ed47b567E1875E21e85; - IBZx public constant protocol = - IBZx(0x059D60a9CEfBc70b9Ea9FFBb9a041581B1dFA6a8); +contract OrderBookStorage is OrderBookConstants, PausableGuardian_0_8 { + + using EnumerableSet for EnumerableSet.Bytes32Set; + mapping(bytes32 => IOrderBook.Order) internal _allOrders; + mapping(bytes32 => uint256) internal _orderExpiration; + mapping(address => EnumerableSet.Bytes32Set) internal _histOrders; + mapping(address => bool) internal _useOracle; + EnumerableSet.Bytes32Set internal _allOrderIDs; mapping(bytes4 => address) public logicTargets; uint256 public mainOBID; + address public priceFeed = address(0); + function _setTarget(bytes4 sig, address target) internal { logicTargets[sig] = target; } diff --git a/testspolygon/limit-orders/test_Orders.py b/testspolygon/limit-orders/test_Orders.py index a9c95bad..a7f4cf40 100644 --- a/testspolygon/limit-orders/test_Orders.py +++ b/testspolygon/limit-orders/test_Orders.py @@ -3,6 +3,7 @@ def test_t(): ORDERBOOK = interface.IOrderBook('0x9A3B9d4379Ec31aA527cB226890412Ef40A3C1c8') + upgrade_contracts(ORDERBOOK) print(ORDERBOOK.getDexRate.call( "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", b'', 1e18)) print(ORDERBOOK.queryRateReturn.call( @@ -46,6 +47,16 @@ def test_t(): print(ll) assert(False) +def upgrade_contracts(ORDERBOOK): + main = OrderBook.deploy({'from':accounts[0]}) + data = OrderBookData.deploy({'from':accounts[0]}) + placement = OrderBookOrderPlace.deploy({'from':accounts[0]}) + ORDERBOOK = Contract.from_abi('',ORDERBOOK.address,OrderBookProxy.abi) + ORDERBOOK.replaceContract(main,{'from':ORDERBOOK.owner()}) + ORDERBOOK.replaceContract(data,{'from':ORDERBOOK.owner()}) + ORDERBOOK.replaceContract(placement,{'from':ORDERBOOK.owner()}) + interface.IOrderBook(ORDERBOOK).setPriceFeed('0x600F8E7B10CF6DA18871Ff79e4A61B13caCEd9BC',{'from':ORDERBOOK.owner()}) + def deploy_keeper(ORDERBOOK): keeper = OrderKeeper.deploy({'from':accounts[0]}) proxy = Proxy_0_8.deploy(keeper, {'from':accounts[0]}) From b1d5b8581ff710b0898d81f20c02f1c4b48aab2b Mon Sep 17 00:00:00 2001 From: Drypto | OOKI <51417606+Drypto13@users.noreply.github.com> Date: Tue, 19 Apr 2022 09:30:20 -0700 Subject: [PATCH 60/83] Update LoanOpenings.sol --- contracts/modules/LoanOpenings/LoanOpenings.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/modules/LoanOpenings/LoanOpenings.sol b/contracts/modules/LoanOpenings/LoanOpenings.sol index c1db3278..92f651bb 100644 --- a/contracts/modules/LoanOpenings/LoanOpenings.sol +++ b/contracts/modules/LoanOpenings/LoanOpenings.sol @@ -355,7 +355,7 @@ contract LoanOpenings is State, LoanOpeningsEvents, VaultController, InterestHan sentValues, isTorqueLoan ); - if (loanDataBytes.length != 0) { + if(loanDataBytes.length != 0){ if(abi.decode(loanDataBytes, (uint128)) & DELEGATE_FLAG != 0) { (, bytes[] memory payloads) = abi.decode(loanDataBytes, (uint128, bytes[])); _setDelegatedManager(loanId, sentAddresses[1], abi.decode(payloads[1], (address)), true); From e1bf5dbc8f9978a5ea2bfcfe8d93d510324b9e26 Mon Sep 17 00:00:00 2001 From: Drypto | OOKI <51417606+Drypto13@users.noreply.github.com> Date: Tue, 19 Apr 2022 09:30:40 -0700 Subject: [PATCH 61/83] Update IDexRecords.sol --- contracts/interfaces/IDexRecords.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/interfaces/IDexRecords.sol b/contracts/interfaces/IDexRecords.sol index a066d64f..6252cad4 100644 --- a/contracts/interfaces/IDexRecords.sol +++ b/contracts/interfaces/IDexRecords.sol @@ -7,7 +7,7 @@ interface IDexRecords { returns (address); function setDexID(address dexAddress) external; - + function setDexID(uint256 dexID, address dexAddress) external; function getDexCount() external view returns(uint256); From 6c18d32bfea58881576303801378ec3da12f85df Mon Sep 17 00:00:00 2001 From: Drypto | OOKI <51417606+Drypto13@users.noreply.github.com> Date: Wed, 20 Apr 2022 08:58:06 -0700 Subject: [PATCH 62/83] Delete OrderBookEvents.sol --- .../orderbook/Storage/OrderBookEvents.sol | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 contracts/orderbook/Storage/OrderBookEvents.sol diff --git a/contracts/orderbook/Storage/OrderBookEvents.sol b/contracts/orderbook/Storage/OrderBookEvents.sol deleted file mode 100644 index 01b36225..00000000 --- a/contracts/orderbook/Storage/OrderBookEvents.sol +++ /dev/null @@ -1,32 +0,0 @@ -pragma solidity ^0.8.0; -import "../IOrderBook.sol"; -import "../../governance/PausableGuardian_0_8.sol"; -import "@openzeppelin-4.3.2/utils/structs/EnumerableSet.sol"; - -contract OrderBookEvents is PausableGuardian_0_8 { - using EnumerableSet for EnumerableSet.Bytes32Set; - mapping(bytes32 => IOrderBook.Order) internal _allOrders; - mapping(bytes32 => uint256) internal _orderExpiration; - mapping(address => EnumerableSet.Bytes32Set) internal _histOrders; - mapping(address => bool) internal _useOracle; - EnumerableSet.Bytes32Set internal _allOrderIDs; - - event OrderCancelled(address indexed trader, bytes32 orderID); - event OrderPlaced( - address indexed trader, - IOrderBook.OrderType indexed OrderType, - uint256 indexed execPrice, - bytes32 orderID, - address collateralTokenAddress, - address loanTokenAddress - ); - event OrderExecuted(address indexed trader, bytes32 orderID); - event OrderAmended( - address indexed trader, - IOrderBook.OrderType indexed OrderType, - uint256 indexed execPrice, - bytes32 orderID, - address collateralTokenAddress, - address loanTokenAddress - ); -} From 32390049d83f9c4230e2454ad00422d9512c3d48 Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Thu, 21 Apr 2022 02:34:51 -0700 Subject: [PATCH 63/83] upgraded logic and keeper deployments --- scripts/env/set-matic.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/env/set-matic.py b/scripts/env/set-matic.py index 5dc5cf37..b97cd62b 100644 --- a/scripts/env/set-matic.py +++ b/scripts/env/set-matic.py @@ -1,6 +1,9 @@ BZX = Contract.from_abi("BZX", "0x059D60a9CEfBc70b9Ea9FFBb9a041581B1dFA6a8", interface.IBZx.abi) TOKEN_REGISTRY = Contract.from_abi("TOKEN_REGISTRY", "0x4B234781Af34E9fD756C27a47675cbba19DC8765", TokenRegistry.abi) ORDERBOOK = Contract.from_abi("ORDERBOOK", "0x9A3B9d4379Ec31aA527cB226890412Ef40A3C1c8", interface.IOrderBook.abi) +ORDERBOOK_KEEPER = Contract.from_ABI("ORDERBOOK_KEEPER", "0x7E42392C40F147dB6B40DD4764787129782ef156", OrderKeeper.abi) +ORDERBOOK_KEEPER_CLEAR = Contract.from_ABI("ORDERBOOK_KEEPER_CLEAR", "0xf72696c4E5FCC511D215AEc9A28e649AaD1BdeB9", OrderKeeperClear.abi) + list = TOKEN_REGISTRY.getTokens(0, 100) for l in list: iTokenTemp = Contract.from_abi("iTokenTemp", l[0], interface.IToken.abi) From 399f95320d0e3e6b4f7f5d631e8602302732ed6f Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Fri, 22 Apr 2022 23:45:11 -0700 Subject: [PATCH 64/83] new deployment --- contracts/orderbook/IOrderBook.sol | 4 ++-- scripts/env/set-matic.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index 91a17825..922e70be 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -64,11 +64,11 @@ interface IOrderBook { /// Returns Deposits contract address /// @return vault Deposits Contract - function vault() external view returns(address vault); + function VAULT() external view returns(address vault); /// Returns Protocol contract address /// @return protocol ooki protocol contract - function protocol() external view returns(address protocol); + function PROTOCOL() external view returns(address protocol); /// Returns minimum trade size in USDC /// @return size USDC amount diff --git a/scripts/env/set-matic.py b/scripts/env/set-matic.py index b97cd62b..551eba54 100644 --- a/scripts/env/set-matic.py +++ b/scripts/env/set-matic.py @@ -1,6 +1,6 @@ BZX = Contract.from_abi("BZX", "0x059D60a9CEfBc70b9Ea9FFBb9a041581B1dFA6a8", interface.IBZx.abi) TOKEN_REGISTRY = Contract.from_abi("TOKEN_REGISTRY", "0x4B234781Af34E9fD756C27a47675cbba19DC8765", TokenRegistry.abi) -ORDERBOOK = Contract.from_abi("ORDERBOOK", "0x9A3B9d4379Ec31aA527cB226890412Ef40A3C1c8", interface.IOrderBook.abi) +ORDERBOOK = Contract.from_abi("ORDERBOOK", "0x043582611b2d62ee084d72f0e731883653f837ce", interface.IOrderBook.abi) ORDERBOOK_KEEPER = Contract.from_ABI("ORDERBOOK_KEEPER", "0x7E42392C40F147dB6B40DD4764787129782ef156", OrderKeeper.abi) ORDERBOOK_KEEPER_CLEAR = Contract.from_ABI("ORDERBOOK_KEEPER_CLEAR", "0xf72696c4E5FCC511D215AEc9A28e649AaD1BdeB9", OrderKeeperClear.abi) @@ -28,4 +28,4 @@ MULTICALL3 = Contract.from_abi("MULTICALL3", "0xcA11bde05977b3631167028862bE2a173976CA11", interface.IMulticall3.abi) -OOKI = Contract.from_abi("OOKI", "0xCd150B1F528F326f5194c012f32Eb30135C7C2c9", interface.ERC20.abi) \ No newline at end of file +OOKI = Contract.from_abi("OOKI", "0xCd150B1F528F326f5194c012f32Eb30135C7C2c9", interface.ERC20.abi) From e789bf8057e09e75cafe97ae810c66543a331507 Mon Sep 17 00:00:00 2001 From: Drypto Date: Thu, 28 Apr 2022 09:44:55 -0700 Subject: [PATCH 65/83] user pays gas costs --- contracts/governance/PausableGuardian_0_8.sol | 4 ++ contracts/interfaces/IWeth.sol | 2 +- contracts/orderbook/Logic/OrderBook.sol | 47 +++++++++++++++++-- .../orderbook/Logic/OrderBookOrderPlace.sol | 25 ---------- contracts/orderbook/OrderBookProxy.sol | 2 +- .../orderbook/Storage/OrderBookStorage.sol | 2 + 6 files changed, 52 insertions(+), 30 deletions(-) diff --git a/contracts/governance/PausableGuardian_0_8.sol b/contracts/governance/PausableGuardian_0_8.sol index 711727a5..c23fbaea 100644 --- a/contracts/governance/PausableGuardian_0_8.sol +++ b/contracts/governance/PausableGuardian_0_8.sol @@ -19,6 +19,10 @@ contract PausableGuardian_0_8 is Ownable { _; } + modifier onlyGuardian() { + require(msg.sender == getGuardian() || msg.sender == owner(), "unauthorized");_; + } + function _isPaused(bytes4 sig) public view returns (bool isPaused) { bytes32 slot = keccak256(abi.encodePacked(sig, Pausable_FunctionPause)); assembly { diff --git a/contracts/interfaces/IWeth.sol b/contracts/interfaces/IWeth.sol index c2061dbf..548cc84f 100644 --- a/contracts/interfaces/IWeth.sol +++ b/contracts/interfaces/IWeth.sol @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. */ -pragma solidity >=0.5.0 <0.6.0; +pragma solidity >=0.5.0 <0.9.0; interface IWeth { diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index a94a743a..8b903162 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -4,6 +4,7 @@ import "../Storage/OrderBookStorage.sol"; import "../../swaps/ISwapsImpl.sol"; import "../OrderVault/IDeposits.sol"; import "../../mixins/Flags.sol"; +import "../../interfaces/IWeth.sol"; import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { @@ -11,6 +12,9 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { using SafeERC20 for IERC20; function initialize(address target) public onlyOwner { + _setTarget(this.getGasPrice.selector, target); + _setTarget(this.depositGasFeeToken.selector, target); + _setTarget(this.withdrawGasFeeToken.selector, target); _setTarget(this.getDexRate.selector, target); _setTarget(this.clearOrder.selector, target); _setTarget(this.prelimCheck.selector, target); @@ -20,6 +24,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { _setTarget(this.priceCheck.selector, target); _setTarget(this.executeOrder.selector, target); _setTarget(this.setPriceFeed.selector, target); + _setTarget(this.setGasPrice.selector, target); } function _executeTradeOpen(IOrderBook.Order memory order) internal { @@ -66,6 +71,39 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { return PROTOCOL.loans(ID).active; } + function getGasPrice() public view returns (uint256) { + if (chainGasPrice == 0) { + return IPriceFeeds(priceFeed).getFastGasPrice(WRAPPED_TOKEN); + } + return chainGasPrice; + } + + function _gasToSend(uint256 gasUsed) internal view returns (uint256) { + return gasUsed*getGasPrice()*2; + } + + function depositGasFeeToken(uint256 amount) external payable { + require(msg.value==0||amount==0, "cant be both"); + if(msg.value!=0){ + IWeth(WRAPPED_TOKEN).deposit{value:msg.value}(); + IERC20(WRAPPED_TOKEN).transfer(msg.sender, msg.value); + } + bytes32 orderID = keccak256(abi.encode(msg.sender, 0)); + IDeposits(VAULT).deposit(orderID, amount, msg.sender, WRAPPED_TOKEN); + } + + function withdrawGasFeeToken(uint256 amount) external { + bytes32 orderID = keccak256(abi.encode(msg.sender, 0)); + IDeposits(VAULT).partialWithdraw(address(this), orderID, amount); + IWeth(WRAPPED_TOKEN).withdraw(amount); + msg.sender.call{value:amount}(""); + } + + function _spendGasFeeToken(uint256 amount, address trader, address receiver) internal { + bytes32 orderID = keccak256(abi.encode(trader, 0)); + IDeposits(VAULT).partialWithdraw(receiver, orderID, amount); + } + function clearOrder(bytes32 orderID) public view returns (bool) { IOrderBook.Order memory order = _allOrders[orderID]; if (order.timeTillExpiration < block.timestamp) { @@ -267,6 +305,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } function executeOrder(bytes32 orderID) external pausable { + uint256 gasStart = gasleft(); IOrderBook.Order memory order = _allOrders[orderID]; require( order.status == IOrderBook.OrderStatus.ACTIVE, @@ -314,7 +353,6 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { _allOrderIDs.remove(orderID); _histOrders[order.trader].remove(orderID); emit OrderExecuted(order.trader, orderID); - return; } else if (order.orderType == IOrderBook.OrderType.LIMIT_CLOSE) { uint256 dSwapValue; @@ -338,7 +376,6 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { _allOrderIDs.remove(orderID); _histOrders[order.trader].remove(orderID); emit OrderExecuted(order.trader, orderID); - return; } else if (order.orderType == IOrderBook.OrderType.MARKET_STOP) { bool operand; @@ -379,11 +416,15 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { _allOrderIDs.remove(orderID); _histOrders[order.trader].remove(orderID); emit OrderExecuted(order.trader, orderID); - return; } + _spendGasFeeToken(_gasToSend(gasStart-gasleft()), order.trader, msg.sender); } function setPriceFeed(address newFeed) external onlyOwner { priceFeed = newFeed; } + + function setGasPrice(uint256 price) external onlyGuardian { + chainGasPrice = price; + } } diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 81d0fcbf..33af62cf 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -47,7 +47,6 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { _isActiveLoan(order.loanID) : _isActiveLoan(order.loanID), "OrderBook: non-active loan specified"); - require(order.loanDataBytes.length < 3500, "OrderBook: loanDataBytes too complex"); require(order.trader == msg.sender, "OrderBook: invalid trader"); } @@ -56,30 +55,6 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { (uint256 amountUsed, address usedToken) = order.loanTokenAmount > order.collateralTokenAmount ? (order.loanTokenAmount, order.loanTokenAddress) : (order.collateralTokenAmount, order.base); - address srcToken; - if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { - if (usedToken == order.base) { - amountUsed = IPriceFeeds(priceFeed).queryReturn( - order.base, - order.loanTokenAddress, - amountUsed - ); - amountUsed = (amountUsed * order.leverage) / 10**18; - srcToken = order.loanTokenAddress; - } else { - amountUsed += - (amountUsed * order.leverage) / - 10**18; //adjusts leverage - srcToken = order.loanTokenAddress; - } - } else { - srcToken = order.base; - } - require( - IPriceFeeds(priceFeed).queryReturn(srcToken, USDC, amountUsed) >= - MIN_AMOUNT_IN_USDC, - "OrderBook: Order too small" - ); require(order.status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: invalid order state"); mainOBID++; bytes32 ID = keccak256(abi.encode(msg.sender, mainOBID)); diff --git a/contracts/orderbook/OrderBookProxy.sol b/contracts/orderbook/OrderBookProxy.sol index 8e88e973..99ae5273 100644 --- a/contracts/orderbook/OrderBookProxy.sol +++ b/contracts/orderbook/OrderBookProxy.sol @@ -10,7 +10,7 @@ contract OrderBookProxy is OrderBookEvents, OrderBookStorage { } address target = logicTargets[msg.sig]; - require(target != address(0), "target not active"); + require(target != address(0) || msg.value != 0, "target not active"); bytes memory data = msg.data; assembly { diff --git a/contracts/orderbook/Storage/OrderBookStorage.sol b/contracts/orderbook/Storage/OrderBookStorage.sol index 8e2ed868..444d28b6 100644 --- a/contracts/orderbook/Storage/OrderBookStorage.sol +++ b/contracts/orderbook/Storage/OrderBookStorage.sol @@ -24,6 +24,8 @@ contract OrderBookStorage is OrderBookConstants, PausableGuardian_0_8 { address public priceFeed = address(0); + uint256 public chainGasPrice; + function _setTarget(bytes4 sig, address target) internal { logicTargets[sig] = target; } From bd6ce1a96e2726a1ddb59affc6222cede077e8b8 Mon Sep 17 00:00:00 2001 From: Drypto Date: Thu, 28 Apr 2022 09:46:21 -0700 Subject: [PATCH 66/83] remove minimum --- contracts/orderbook/Storage/OrderBookConstants.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/orderbook/Storage/OrderBookConstants.sol b/contracts/orderbook/Storage/OrderBookConstants.sol index e4fb4470..3d2a6890 100644 --- a/contracts/orderbook/Storage/OrderBookConstants.sol +++ b/contracts/orderbook/Storage/OrderBookConstants.sol @@ -6,7 +6,6 @@ import "../../../interfaces/IBZx.sol"; contract OrderBookConstants { address public constant WRAPPED_TOKEN = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; - uint256 public constant MIN_AMOUNT_IN_USDC = 1e6; address public constant USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; address public constant VAULT = 0xFA6485ec4Aa9AF504adb4ed47b567E1875E21e85; IBZx public constant PROTOCOL = From 1620d5702e682dfe00ad0dbcc134ac51db4def94 Mon Sep 17 00:00:00 2001 From: Drypto Date: Thu, 28 Apr 2022 14:52:56 -0700 Subject: [PATCH 67/83] add checks and min trade size --- contracts/orderbook/Keepers/OrderKeeper.sol | 4 +-- .../orderbook/Keepers/OrderKeeperClear.sol | 4 +-- contracts/orderbook/Logic/OrderBook.sol | 4 +++ .../orderbook/Logic/OrderBookOrderPlace.sol | 28 +++++++++++++++++-- .../orderbook/Storage/OrderBookConstants.sol | 1 + 5 files changed, 34 insertions(+), 7 deletions(-) diff --git a/contracts/orderbook/Keepers/OrderKeeper.sol b/contracts/orderbook/Keepers/OrderKeeper.sol index 437aafb6..80bc0e9b 100644 --- a/contracts/orderbook/Keepers/OrderKeeper.sol +++ b/contracts/orderbook/Keepers/OrderKeeper.sol @@ -7,7 +7,7 @@ contract OrderKeeper is PausableGuardian_0_8 { address public implementation; IOrderBook public orderBook; - function checkUpKeep(bytes calldata checkData) + function checkUpkeep(bytes calldata checkData) external returns (bool upkeepNeeded, bytes memory performData) { @@ -24,7 +24,7 @@ contract OrderKeeper is PausableGuardian_0_8 { return (orderIDForExec != 0, abi.encode(orderIDForExec)); } - function performUpKeep(bytes calldata performData) external pausable { + function performUpkeep(bytes calldata performData) external pausable { bytes32 orderId = abi.decode(performData, (bytes32)); //emit OrderExecuted(trader,orderId); try orderBook.executeOrder(orderId) { diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol index be6a1312..03b0e78d 100644 --- a/contracts/orderbook/Keepers/OrderKeeperClear.sol +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -7,7 +7,7 @@ contract OrderKeeperClear is PausableGuardian_0_8 { address public implementation; IOrderBook public orderBook; - function checkUpKeep(bytes calldata checkData) + function checkUpkeep(bytes calldata checkData) external view returns (bool upkeepNeeded, bytes memory performData) @@ -23,7 +23,7 @@ contract OrderKeeperClear is PausableGuardian_0_8 { return orderBook.getClearOrderList(start, end); } - function performUpKeep(bytes calldata performData) external pausable { + function performUpkeep(bytes calldata performData) external pausable { bytes32[] memory orderId = abi.decode(performData, (bytes32[])); //emit OrderExecuted(trader,orderId); for (uint i;i(_histOrders[msg.sender].length())*_gasToSend(2500000), "too little gas left"); IDeposits(VAULT).partialWithdraw(address(this), orderID, amount); IWeth(WRAPPED_TOKEN).withdraw(amount); msg.sender.call{value:amount}(""); @@ -186,6 +187,9 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { if (order.timeTillExpiration < block.timestamp) { return false; } + if (IDeposits(VAULT).getDeposit(keccak256(abi.encode(order.trader, 0))) < _gasToSend(2500000)) { + return false; + } if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { if (order.loanID != 0 && !_isActiveLoan(order.loanID)) { return false; diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 33af62cf..40ceb07b 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -50,12 +50,25 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { require(order.trader == msg.sender, "OrderBook: invalid trader"); } + function _gasToSend(uint256 gasUsed) internal view returns (uint256) { + uint256 gasPrice = chainGasPrice == 0 ? IPriceFeeds(priceFeed).getFastGasPrice(WRAPPED_TOKEN) : chainGasPrice; + return gasUsed*gasPrice*2; + } + function placeOrder(IOrderBook.Order calldata order) external pausable { _commonChecks(order); (uint256 amountUsed, address usedToken) = order.loanTokenAmount > order.collateralTokenAmount ? (order.loanTokenAmount, order.loanTokenAddress) : (order.collateralTokenAmount, order.base); + uint256 tradeSize; + if (usedToken == order.base) { + tradeSize = IPriceFeeds(priceFeed).queryReturn(order.base, order.loanTokenAddress, amountUsed)*order.leverage/10**18; + } else { + tradeSize = amountUsed*(order.leverage+1e18)/1e18; + } + require(IPriceFeeds(priceFeed).queryReturn(order.loanTokenAddress, USDC, tradeSize) > MIN_AMOUNT_IN_USDC, "OrderBook: trade too small"); require(order.status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: invalid order state"); + require(IDeposits(VAULT).getDeposit(keccak256(abi.encode(order.trader,0)))>(_histOrders[order.trader].length()+1)*_gasToSend(2500000), "too little gas left"); mainOBID++; bytes32 ID = keccak256(abi.encode(msg.sender, mainOBID)); require(IDeposits(VAULT).getTokenUsed(ID) == address(0), "Orderbook: collision"); //in the very unlikely chance of collision on ID error is thrown @@ -88,10 +101,19 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { _allOrders[order.orderID].status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: inactive order specified" ); + (uint256 amountUsed, address usedToken) = order.loanTokenAmount > order.collateralTokenAmount + ? (order.loanTokenAmount, order.loanTokenAddress) + : (order.collateralTokenAmount, order.base); + uint256 tradeSize; + if (usedToken == order.base) { + tradeSize = IPriceFeeds(priceFeed).queryReturn(order.base, order.loanTokenAddress, amountUsed)*order.leverage/10**18; + } else { + tradeSize = amountUsed*(order.leverage+1e18)/1e18; + } + require(IPriceFeeds(priceFeed).queryReturn(order.loanTokenAddress, USDC, tradeSize) > MIN_AMOUNT_IN_USDC, "OrderBook: trade too small"); + + if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { - (uint256 amountUsed, address usedToken) = order.loanTokenAmount > order.collateralTokenAmount - ? (order.loanTokenAmount, order.loanTokenAddress) - : (order.collateralTokenAmount, order.base); uint256 storedAmount = IDeposits(VAULT).getDeposit( order.orderID ); diff --git a/contracts/orderbook/Storage/OrderBookConstants.sol b/contracts/orderbook/Storage/OrderBookConstants.sol index 3d2a6890..e4fb4470 100644 --- a/contracts/orderbook/Storage/OrderBookConstants.sol +++ b/contracts/orderbook/Storage/OrderBookConstants.sol @@ -6,6 +6,7 @@ import "../../../interfaces/IBZx.sol"; contract OrderBookConstants { address public constant WRAPPED_TOKEN = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; + uint256 public constant MIN_AMOUNT_IN_USDC = 1e6; address public constant USDC = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174; address public constant VAULT = 0xFA6485ec4Aa9AF504adb4ed47b567E1875E21e85; IBZx public constant PROTOCOL = From ea957880d6845294e2617368156abb2f1ed8f4b0 Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Fri, 6 May 2022 11:12:04 -0700 Subject: [PATCH 68/83] minor updates --- contracts/orderbook/IOrderBook.sol | 23 ++++++++++++++++- contracts/orderbook/Logic/OrderBook.sol | 11 +++++--- .../orderbook/Logic/OrderBookOrderPlace.sol | 25 ++++++++++++++++--- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index 922e70be..e8d5bc52 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -90,6 +90,10 @@ interface IOrderBook { /// @param orderID ID of order to be canceled function cancelOrderProtocol(bytes32 orderID) external; + /// Force cancels order + /// @param orderID ID of order to be canceled + function cancelOrderGuardian(bytes32 orderID) external; + /// Changes stop type between index and dex price /// @param stopType true = index, false = dex price function changeStopType(bool stopType) external; @@ -98,10 +102,26 @@ interface IOrderBook { /// @param newFeed new price feed contract function setPriceFeed(address newFeed) external; + /// Set gas price to be used for incentives (if price feed does not already contain it) + /// @param gasPrice gas price in gwei + function setGasPrice(uint256 gasPrice) external; + /// Return price feed contract address /// @return priceFeed Price Feed Contract Address function priceFeed() external view returns (address priceFeed); + /// Returns gas price used for incentive calculations + /// @return gasPrice gas price in gwei + function getGasPrice() external view returns (uint256 gasPrice); + + /// Deposit Gas Token to pay out incentives for orders to be executed + /// @param amount when depositing wrapped token, this is amount to be deposited (leave as 0 if sending native token) + function depositGasFeeToken(uint256 amount) external payable; + + /// Withdraw Gas Token (received as native token) + /// @param amount amount to be withdrawn + function withdrawGasFeeToken(uint256 amount) external; + /// Return amount received through a specified swap /// @param srcToken source token address /// @param destToken destination token address @@ -144,7 +164,8 @@ interface IOrderBook { /// Executes Order /// @param orderID order ID - function executeOrder(bytes32 orderID) external; + /// @return incentiveAmountReceived amount received in gas token from exeuction of order + function executeOrder(bytes32 orderID) external returns(uint256 incentiveAmountReceived); /// sets token allowances /// @param spenders addresses that will be given allowance diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index aa88f0e3..08d87776 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -100,9 +100,14 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { msg.sender.call{value:amount}(""); } - function _spendGasFeeToken(uint256 amount, address trader, address receiver) internal { + function _spendGasFeeToken(uint256 amount, address trader, address receiver) internal returns (uint256) { bytes32 orderID = keccak256(abi.encode(trader, 0)); + uint256 amountStored = IDeposits(VAULT).getDeposit(orderID); + if (amountStored < amount) { + amount = amountStored; + } IDeposits(VAULT).partialWithdraw(receiver, orderID, amount); + return amount; } function clearOrder(bytes32 orderID) public view returns (bool) { @@ -308,7 +313,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } } - function executeOrder(bytes32 orderID) external pausable { + function executeOrder(bytes32 orderID) external pausable returns (uint256) { uint256 gasStart = gasleft(); IOrderBook.Order memory order = _allOrders[orderID]; require( @@ -421,7 +426,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { _histOrders[order.trader].remove(orderID); emit OrderExecuted(order.trader, orderID); } - _spendGasFeeToken(_gasToSend(gasStart-gasleft()), order.trader, msg.sender); + return(_spendGasFeeToken(_gasToSend(gasStart-gasleft()), order.trader, msg.sender)); } function setPriceFeed(address newFeed) external onlyOwner { diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 40ceb07b..340a7775 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -12,6 +12,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { _setTarget(this.cancelOrder.selector, target); _setTarget(this.cancelOrderProtocol.selector, target); _setTarget(this.changeStopType.selector, target); + _setTarget(this.cancelOrderGuardian.selector, target); } function _caseChecks(bytes32 ID, address collateral, address loanToken) @@ -48,11 +49,15 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { : _isActiveLoan(order.loanID), "OrderBook: non-active loan specified"); require(order.trader == msg.sender, "OrderBook: invalid trader"); + require(order.loanDataBytes.length < 2500, "OrderBook: too much data"); } + function _getGasPrice() internal view returns (uint256 gasPrice) { + gasPrice = chainGasPrice == 0 ? IPriceFeeds(priceFeed).getFastGasPrice(WRAPPED_TOKEN) : chainGasPrice; + } + function _gasToSend(uint256 gasUsed) internal view returns (uint256) { - uint256 gasPrice = chainGasPrice == 0 ? IPriceFeeds(priceFeed).getFastGasPrice(WRAPPED_TOKEN) : chainGasPrice; - return gasUsed*gasPrice*2; + return gasUsed*_getGasPrice()*2; } function placeOrder(IOrderBook.Order calldata order) external pausable { @@ -68,7 +73,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { } require(IPriceFeeds(priceFeed).queryReturn(order.loanTokenAddress, USDC, tradeSize) > MIN_AMOUNT_IN_USDC, "OrderBook: trade too small"); require(order.status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: invalid order state"); - require(IDeposits(VAULT).getDeposit(keccak256(abi.encode(order.trader,0)))>(_histOrders[order.trader].length()+1)*_gasToSend(2500000), "too little gas left"); + require(IDeposits(VAULT).getDeposit(keccak256(abi.encode(order.trader,0)))>(_histOrders[order.trader].length()+1)*_gasToSend(4000000), "too little gas left"); mainOBID++; bytes32 ID = keccak256(abi.encode(msg.sender, mainOBID)); require(IDeposits(VAULT).getTokenUsed(ID) == address(0), "Orderbook: collision"); //in the very unlikely chance of collision on ID error is thrown @@ -164,6 +169,20 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { emit OrderCancelled(msg.sender, orderID); } + function cancelOrderGuardian(bytes32 orderID) external onlyGuardian { + _allOrders[orderID].status = IOrderBook.OrderStatus.CANCELLED; + address trader = _allOrders[orderID].trader; + _histOrders[trader].remove(orderID); + _allOrderIDs.remove(orderID); + if (_allOrders[orderID].orderType == IOrderBook.OrderType.LIMIT_OPEN) { + address usedToken = IDeposits(VAULT).getTokenUsed( + orderID + ); + IDeposits(VAULT).withdrawToTrader(trader, orderID); + } + emit OrderCancelled(trader, orderID); + } + function cancelOrderProtocol(bytes32 orderID) external pausable { IOrderBook.Order memory order = _allOrders[orderID]; address trader = order.trader; From aeee948614aec3f70578cedd6d9ab198a25c93b7 Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Sun, 8 May 2022 01:16:46 -0700 Subject: [PATCH 69/83] passed tests and fixes for issues --- contracts/orderbook/IOrderBook.sol | 2 +- contracts/orderbook/Keepers/OrderKeeper.sol | 5 ++++ .../orderbook/Keepers/OrderKeeperClear.sol | 5 ++++ contracts/orderbook/Logic/OrderBook.sol | 3 ++- .../orderbook/Logic/OrderBookOrderPlace.sol | 26 +++++++++++++++---- testspolygon/limit-orders/test_Orders.py | 19 +++++++++----- 6 files changed, 46 insertions(+), 14 deletions(-) diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index e8d5bc52..d32c86cb 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -88,7 +88,7 @@ interface IOrderBook { /// Cancels Order /// @param orderID ID of order to be canceled - function cancelOrderProtocol(bytes32 orderID) external; + function cancelOrderProtocol(bytes32 orderID) external returns (uint256); /// Force cancels order /// @param orderID ID of order to be canceled diff --git a/contracts/orderbook/Keepers/OrderKeeper.sol b/contracts/orderbook/Keepers/OrderKeeper.sol index 80bc0e9b..fa276061 100644 --- a/contracts/orderbook/Keepers/OrderKeeper.sol +++ b/contracts/orderbook/Keepers/OrderKeeper.sol @@ -5,6 +5,7 @@ import "../../governance/PausableGuardian_0_8.sol"; contract OrderKeeper is PausableGuardian_0_8 { address public implementation; + IERC20Metadata public constant WRAPPED_TOKEN = IERC20Metadata(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270); IOrderBook public orderBook; function checkUpkeep(bytes calldata checkData) @@ -35,4 +36,8 @@ contract OrderKeeper is PausableGuardian_0_8 { function setOrderBook(IOrderBook contractAddress) external onlyOwner { orderBook = contractAddress; } + + function withdrawIncentivesReceived(address receiver) external onlyOwner { + WRAPPED_TOKEN.transfer(receiver, WRAPPED_TOKEN.balanceOf(address(this))); + } } diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol index 03b0e78d..2840f9c1 100644 --- a/contracts/orderbook/Keepers/OrderKeeperClear.sol +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -5,6 +5,7 @@ import "../../governance/PausableGuardian_0_8.sol"; contract OrderKeeperClear is PausableGuardian_0_8 { address public implementation; + IERC20Metadata public constant WRAPPED_TOKEN = IERC20Metadata(0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270); IOrderBook public orderBook; function checkUpkeep(bytes calldata checkData) @@ -39,4 +40,8 @@ contract OrderKeeperClear is PausableGuardian_0_8 { function setOrderBook(IOrderBook contractAddress) external onlyOwner { orderBook = contractAddress; } + + function withdrawIncentivesReceived(address receiver) external onlyOwner { + WRAPPED_TOKEN.transfer(receiver, WRAPPED_TOKEN.balanceOf(address(this))); + } } diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index 08d87776..5e5d62c4 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -73,7 +73,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { function getGasPrice() public view returns (uint256) { if (chainGasPrice == 0) { - return IPriceFeeds(priceFeed).getFastGasPrice(WRAPPED_TOKEN); + return IPriceFeeds(priceFeed).getFastGasPrice(WRAPPED_TOKEN)/1e36; } return chainGasPrice; } @@ -87,6 +87,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { if(msg.value!=0){ IWeth(WRAPPED_TOKEN).deposit{value:msg.value}(); IERC20(WRAPPED_TOKEN).transfer(msg.sender, msg.value); + amount = msg.value; } bytes32 orderID = keccak256(abi.encode(msg.sender, 0)); IDeposits(VAULT).deposit(orderID, amount, msg.sender, WRAPPED_TOKEN); diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 340a7775..c3e42896 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -53,7 +53,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { } function _getGasPrice() internal view returns (uint256 gasPrice) { - gasPrice = chainGasPrice == 0 ? IPriceFeeds(priceFeed).getFastGasPrice(WRAPPED_TOKEN) : chainGasPrice; + gasPrice = chainGasPrice == 0 ? IPriceFeeds(priceFeed).getFastGasPrice(WRAPPED_TOKEN)/1e36 : chainGasPrice; } function _gasToSend(uint256 gasUsed) internal view returns (uint256) { @@ -66,10 +66,14 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { ? (order.loanTokenAmount, order.loanTokenAddress) : (order.collateralTokenAmount, order.base); uint256 tradeSize; - if (usedToken == order.base) { - tradeSize = IPriceFeeds(priceFeed).queryReturn(order.base, order.loanTokenAddress, amountUsed)*order.leverage/10**18; + if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { + if (usedToken == order.base) { + tradeSize = IPriceFeeds(priceFeed).queryReturn(order.base, order.loanTokenAddress, amountUsed)*order.leverage/10**18; + } else { + tradeSize = amountUsed*(order.leverage+1e18)/1e18; + } } else { - tradeSize = amountUsed*(order.leverage+1e18)/1e18; + tradeSize = amountUsed; } require(IPriceFeeds(priceFeed).queryReturn(order.loanTokenAddress, USDC, tradeSize) > MIN_AMOUNT_IN_USDC, "OrderBook: trade too small"); require(order.status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: invalid order state"); @@ -183,7 +187,18 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { emit OrderCancelled(trader, orderID); } - function cancelOrderProtocol(bytes32 orderID) external pausable { + function _spendGasFeeToken(uint256 amount, address trader, address receiver) internal returns (uint256) { + bytes32 orderID = keccak256(abi.encode(trader, 0)); + uint256 amountStored = IDeposits(VAULT).getDeposit(orderID); + if (amountStored < amount) { + amount = amountStored; + } + IDeposits(VAULT).partialWithdraw(receiver, orderID, amount); + return amount; + } + + function cancelOrderProtocol(bytes32 orderID) external pausable returns (uint256) { + uint gasStart = gasleft(); IOrderBook.Order memory order = _allOrders[orderID]; address trader = order.trader; require(order.status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: inactive order"); @@ -222,6 +237,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { IDeposits(VAULT).withdrawToTrader(trader, orderID); } emit OrderCancelled(trader, orderID); + return(_spendGasFeeToken(_gasToSend(gasStart-gasleft()), trader, msg.sender)); } function changeStopType(bool stop) external pausable { diff --git a/testspolygon/limit-orders/test_Orders.py b/testspolygon/limit-orders/test_Orders.py index a7f4cf40..f21ee533 100644 --- a/testspolygon/limit-orders/test_Orders.py +++ b/testspolygon/limit-orders/test_Orders.py @@ -2,7 +2,7 @@ from eth_abi import encode_abi def test_t(): - ORDERBOOK = interface.IOrderBook('0x9A3B9d4379Ec31aA527cB226890412Ef40A3C1c8') + ORDERBOOK = interface.IOrderBook('0x043582611B2d62Ee084D72f0E731883653f837CE') upgrade_contracts(ORDERBOOK) print(ORDERBOOK.getDexRate.call( "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619", "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", b'', 1e18)) @@ -16,17 +16,22 @@ def test_t(): ETH = interface.IERC20('0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619') USDC.transfer(accounts[0], 20e6, { 'from': '0xf977814e90da44bfa03b6295a0616a897441acec'}) - USDC.approve(ORDERBOOK.vault(), 2e10, {'from': accounts[0]}) + USDC.approve(ORDERBOOK.VAULT(), 2e10, {'from': accounts[0]}) + interface.IERC20('0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270').approve(ORDERBOOK.VAULT(), 2e18, {'from':accounts[0]}) + ORDERBOOK.depositGasFeeToken(0, {'from':accounts[0], 'amount':1e18}) + print(interface.IDeposits(ORDERBOOK.VAULT()).getDeposit(web3.keccak(encode_abi(['address','uint256'],[accounts[0].address,0])))) + print(ORDERBOOK.getGasPrice()) data = encode_abi(['address'],[ORDERBOOK.address]) data = encode_abi(['uint256','bytes[]'],[6,(encode_abi(['uint256','bytes'],[1,encode_abi(['address','address'],[USDC.address,ETH.address])]),data)]) print(ORDERBOOK.placeOrder([0, 0, 1e6, 1e18, int(1.1e6), 0, accounts[0], iUSDC, USDC, ETH, 0, 0, 1000000000000, data], {'from': accounts[0]}).gas_used) print(ORDERBOOK.getOrders.call()[0]) print(ORDERBOOK.executeOrder(ORDERBOOK.getOrders.call()[0][1],{'from':accounts[0]}).gas_used) - protocolRank = interface.IBZx(ORDERBOOK.protocol.call()) + print(interface.IDeposits(ORDERBOOK.VAULT()).getDeposit(web3.keccak(encode_abi(['address','uint256'],[accounts[0].address,0])))) + protocolRank = interface.IBZx(ORDERBOOK.PROTOCOL.call()) ll = protocolRank.getUserLoans.call(accounts[0],0,10,0,False,False) print(ll) - print(ORDERBOOK.placeOrder([ll[0][0],0,1e18,0,0,374183490999849,accounts[0], + print(ORDERBOOK.placeOrder([ll[0][0],0,1e18,0,0,ll[0][5],accounts[0], iUSDC, USDC, ETH, 2, 0, 1000000000000, b''], {'from': accounts[0]}).gas_used) ORDERBOOK.changeStopType(True, {'from':accounts[0]}) print(ORDERBOOK.getActiveOrders.call(accounts[0])) @@ -38,10 +43,10 @@ def test_t(): print(ORDERBOOK.getOrdersLimited.call(0,ORDERBOOK.getTotalOrderIDs.call())) print(ORDERBOOK.clearOrder.call(ORDERBOOK.getOrderIDs.call()[0])) keeper = deploy_keeper(ORDERBOOK) - print(keeper.checkUpKeep(encode_abi(['uint256','uint256'],[0,1]),{'from':accounts[0]}).gas_used) - (needed, data) = keeper.checkUpKeep.call(encode_abi(['uint256','uint256'],[0,1])) + print(keeper.checkUpkeep(encode_abi(['uint256','uint256'],[0,1]),{'from':accounts[0]}).gas_used) + (needed, data) = keeper.checkUpkeep.call(encode_abi(['uint256','uint256'],[0,1])) if(needed): - keeper.performUpKeep(data, {'from':accounts[0]}) + keeper.performUpkeep(data, {'from':accounts[0]}) print(ORDERBOOK.getOrders.call()) ll = protocolRank.getUserLoans.call(accounts[0],0,10,0,False,False) print(ll) From ef8e6e2f70530a42e95ce9aca3d31ea8a54c2300 Mon Sep 17 00:00:00 2001 From: AoDev <> Date: Tue, 24 May 2022 10:43:09 +0200 Subject: [PATCH 70/83] fix(set-matic): case issue in script --- scripts/env/set-matic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/env/set-matic.py b/scripts/env/set-matic.py index a2f8775d..ff05b7db 100644 --- a/scripts/env/set-matic.py +++ b/scripts/env/set-matic.py @@ -1,8 +1,8 @@ BZX = Contract.from_abi("BZX", "0x059D60a9CEfBc70b9Ea9FFBb9a041581B1dFA6a8", interface.IBZx.abi) TOKEN_REGISTRY = Contract.from_abi("TOKEN_REGISTRY", "0x4B234781Af34E9fD756C27a47675cbba19DC8765", TokenRegistry.abi) ORDERBOOK = Contract.from_abi("ORDERBOOK", "0x043582611b2d62ee084d72f0e731883653f837ce", interface.IOrderBook.abi) -ORDERBOOK_KEEPER = Contract.from_ABI("ORDERBOOK_KEEPER", "0x7E42392C40F147dB6B40DD4764787129782ef156", OrderKeeper.abi) -ORDERBOOK_KEEPER_CLEAR = Contract.from_ABI("ORDERBOOK_KEEPER_CLEAR", "0xf72696c4E5FCC511D215AEc9A28e649AaD1BdeB9", OrderKeeperClear.abi) +ORDERBOOK_KEEPER = Contract.from_abi("ORDERBOOK_KEEPER", "0x7E42392C40F147dB6B40DD4764787129782ef156", OrderKeeper.abi) +ORDERBOOK_KEEPER_CLEAR = Contract.from_abi("ORDERBOOK_KEEPER_CLEAR", "0xf72696c4E5FCC511D215AEc9A28e649AaD1BdeB9", OrderKeeperClear.abi) list = TOKEN_REGISTRY.getTokens(0, 100) for l in list: From bced548d8a19a916e7f15245e033894b6274ea9a Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Thu, 26 May 2022 23:46:24 -0700 Subject: [PATCH 71/83] interface update --- contracts/orderbook/IOrderBook.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index d32c86cb..d2ea6777 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -189,7 +189,7 @@ interface IOrderBook { /// Retrieves order corresponding to an order ID /// @param orderID order ID - function getOrderByOrderID(bytes32 orderID) external view returns (Order[] memory); + function getOrderByOrderID(bytes32 orderID) external view returns (Order memory); /// Retrieves active order IDs for a trader /// @param trader address of trader From c82d33a48e45091891386b06f23665ec06aad69a Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Wed, 22 Jun 2022 17:15:32 -0700 Subject: [PATCH 72/83] function name changes --- contracts/orderbook/IOrderBook.sol | 18 +++++----- contracts/orderbook/Keepers/OrderKeeper.sol | 2 +- .../orderbook/Keepers/OrderKeeperClear.sol | 2 +- contracts/orderbook/Logic/OrderBookData.sol | 36 +++++++++---------- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index d2ea6777..64ed23e1 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -179,13 +179,13 @@ interface IOrderBook { /// Retrieves active orders for a trader /// @param trader address of trader - function getActiveOrders(address trader) external view returns (Order[] memory); + function getUserOrders(address trader) external view returns (Order[] memory); /// Retrieves active orders for a trader /// @param trader address of trader /// @param start starting index /// @param end ending index - function getActiveOrdersLimited(address trader, uint256 start, uint256 end) external view returns (Order[] memory); + function getUserOrdersLimited(address trader, uint256 start, uint256 end) external view returns (Order[] memory); /// Retrieves order corresponding to an order ID /// @param orderID order ID @@ -193,28 +193,28 @@ interface IOrderBook { /// Retrieves active order IDs for a trader /// @param trader address of trader - function getActiveOrderIDs(address trader) external view returns (bytes32[] memory); + function getUserOrderIDs(address trader) external view returns (bytes32[] memory); /// Returns total active orders count for a trader /// @param trader address of trader - function getTotalOrders(address trader) external view returns (uint256); + function getUserOrdersCount(address trader) external view returns (uint256); /// Returns total active orders count - function getTotalOrderIDs() external view returns (uint256); + function getGlobalOrdersCount() external view returns (uint256); /// Returns total active order IDs - function getOrderIDs() external view returns (bytes32[] memory); + function getGlobalOrderIDs() external view returns (bytes32[] memory); /// Returns total active orders - function getOrders() external view returns (Order[] memory); + function getGlobalOrders() external view returns (Order[] memory); /// Returns active order IDs /// @param start starting index /// @param end ending index - function getOrderIDsLimited(uint256 start, uint256 end) external view returns (bytes32[] memory); + function getGlobalOrderIDsLimited(uint256 start, uint256 end) external view returns (bytes32[] memory); /// Returns active orders /// @param start starting index /// @param end ending index - function getOrdersLimited(uint256 start, uint256 end) external view returns (Order[] memory); + function getGlobalOrdersLimited(uint256 start, uint256 end) external view returns (Order[] memory); } diff --git a/contracts/orderbook/Keepers/OrderKeeper.sol b/contracts/orderbook/Keepers/OrderKeeper.sol index fa276061..adb1a6a5 100644 --- a/contracts/orderbook/Keepers/OrderKeeper.sol +++ b/contracts/orderbook/Keepers/OrderKeeper.sol @@ -13,7 +13,7 @@ contract OrderKeeper is PausableGuardian_0_8 { returns (bool upkeepNeeded, bytes memory performData) { (uint256 start, uint256 end) = abi.decode(checkData, (uint256, uint256)); - uint256 orderIDLength = orderBook.getTotalOrderIDs(); + uint256 orderIDLength = orderBook.getGlobalOrdersCount(); if (start > orderIDLength) { return (upkeepNeeded, performData); } diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol index 2840f9c1..17892eb0 100644 --- a/contracts/orderbook/Keepers/OrderKeeperClear.sol +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -14,7 +14,7 @@ contract OrderKeeperClear is PausableGuardian_0_8 { returns (bool upkeepNeeded, bytes memory performData) { (uint256 start, uint256 end) = abi.decode(checkData, (uint256, uint256)); - uint256 orderIDLength = orderBook.getTotalOrderIDs(); + uint256 orderIDLength = orderBook.getGlobalOrdersCount(); if (start > orderIDLength) { return (upkeepNeeded, performData); } diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index 1b30e835..e4dc69c9 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -11,16 +11,16 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { function initialize(address target) public onlyOwner { _setTarget(this.adjustAllowance.selector, target); _setTarget(this.revokeAllowance.selector, target); - _setTarget(this.getActiveOrders.selector, target); - _setTarget(this.getActiveOrdersLimited.selector, target); + _setTarget(this.getUserOrders.selector, target); + _setTarget(this.getUserOrdersLimited.selector, target); _setTarget(this.getOrderByOrderID.selector, target); - _setTarget(this.getActiveOrderIDs.selector, target); - _setTarget(this.getTotalOrders.selector, target); - _setTarget(this.getTotalOrderIDs.selector, target); - _setTarget(this.getOrderIDs.selector, target); - _setTarget(this.getOrders.selector, target); - _setTarget(this.getOrderIDsLimited.selector, target); - _setTarget(this.getOrdersLimited.selector, target); + _setTarget(this.getUserOrderIDs.selector, target); + _setTarget(this.getUserOrdersCount.selector, target); + _setTarget(this.getGlobalOrderIDs.selector, target); + _setTarget(this.getGlobalOrdersCount.selector, target); + _setTarget(this.getGlobalOrders.selector, target); + _setTarget(this.getGlobalOrderIDsLimited.selector, target); + _setTarget(this.getGlobalOrdersLimited.selector, target); } function adjustAllowance(address[] memory spenders, address[] memory tokens) external onlyOwner { @@ -64,7 +64,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { } } - function getActiveOrders(address trader) + function getUserOrders(address trader) external view returns (IOrderBook.Order[] memory fullList) @@ -79,7 +79,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { return fullList; } - function getActiveOrdersLimited(address trader, uint start, uint end) + function getUserOrdersLimited(address trader, uint start, uint end) external view returns (IOrderBook.Order[] memory fullList) @@ -101,7 +101,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { return _allOrders[orderID]; } - function getActiveOrderIDs(address trader) + function getUserOrderIDs(address trader) external view returns (bytes32[] memory) @@ -109,19 +109,19 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { return _histOrders[trader].values(); } - function getTotalOrders(address trader) external view returns (uint256) { + function getUserOrdersCount(address trader) external view returns (uint256) { return _histOrders[trader].length(); } - function getOrderIDs() external view returns (bytes32[] memory) { + function getGlobalOrderIDs() external view returns (bytes32[] memory) { return _allOrderIDs.values(); } - function getTotalOrderIDs() external view returns (uint256) { + function getGlobalOrdersCount() external view returns (uint256) { return _allOrderIDs.length(); } - function getOrderIDsLimited(uint start, uint end) external view returns (bytes32[] memory fullList) { + function getGlobalOrderIDsLimited(uint start, uint end) external view returns (bytes32[] memory fullList) { require(end<=_allOrderIDs.length(), "OrderBook: end is past max orders"); fullList = new bytes32[](end-start); for (uint256 i = start; i < end;) { @@ -131,7 +131,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { return fullList; } - function getOrders() + function getGlobalOrders() external view returns (IOrderBook.Order[] memory fullList) @@ -146,7 +146,7 @@ contract OrderBookData is OrderBookEvents, OrderBookStorage { return fullList; } - function getOrdersLimited(uint start, uint end) + function getGlobalOrdersLimited(uint start, uint end) external view returns (IOrderBook.Order[] memory fullList) From 610dc8a0a658821ef84d63e870fbf21700bfd792 Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Thu, 23 Jun 2022 21:50:28 -0700 Subject: [PATCH 73/83] avoid revert due to failure --- contracts/orderbook/Logic/OrderBook.sol | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index 5e5d62c4..72e041c0 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -167,7 +167,13 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { bytes32 tempID; for (uint256 i = start; i < end;) { tempID = _allOrderIDs.at(i); - if (prelimCheck(tempID)) { + (bool result, bytes memory returnData) = address(this).call( + abi.encodeWithSelector(this.prelimCheck.selector, tempID) + ); + if (!result) { + continue; + } + if (abi.decode(returnData, (bool))) { return tempID; } unchecked { ++i; } From 3184123c0439001585e5ce6470c63fbbba40d194 Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Tue, 28 Jun 2022 13:21:45 -0700 Subject: [PATCH 74/83] use updated getSwapExpectedReturn --- contracts/orderbook/Logic/OrderBook.sol | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index 72e041c0..f6d5165c 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -223,6 +223,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { dSwapValue = order.collateralTokenAmount + PROTOCOL.getSwapExpectedReturn( + order.trader, order.loanTokenAddress, order.base, amountUsed, @@ -238,6 +239,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } uint256 dSwapValue; dSwapValue = PROTOCOL.getSwapExpectedReturn( + order.trader, order.base, order.loanTokenAddress, order.collateralTokenAmount, @@ -284,6 +286,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { ) public returns (uint256 rate) { uint256 tradeSize = 10**IERC20Metadata(srcToken).decimals(); rate = PROTOCOL.getSwapExpectedReturn( + address(this), srcToken, destToken, amountIn, @@ -354,6 +357,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { dSwapValue = order.collateralTokenAmount + PROTOCOL.getSwapExpectedReturn( + order.trader, order.loanTokenAddress, order.base, amountUsed, @@ -373,6 +377,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { else if (order.orderType == IOrderBook.OrderType.LIMIT_CLOSE) { uint256 dSwapValue; dSwapValue = PROTOCOL.getSwapExpectedReturn( + order.trader, order.base, order.loanTokenAddress, order.collateralTokenAmount, From fa899da755c33e076e17842a12ae041b1f0a7167 Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Tue, 28 Jun 2022 16:45:50 -0700 Subject: [PATCH 75/83] minor fix --- contracts/orderbook/Logic/OrderBook.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index f6d5165c..14fd5745 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -171,6 +171,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { abi.encodeWithSelector(this.prelimCheck.selector, tempID) ); if (!result) { + unchecked { ++i; } continue; } if (abi.decode(returnData, (bool))) { From da33a86bbc0cc1c8e3a03bc10ad2e77ba2f2f2ee Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Tue, 28 Jun 2022 17:08:32 -0700 Subject: [PATCH 76/83] minor bug fixes --- contracts/orderbook/Logic/OrderBookOrderPlace.sol | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index c3e42896..754e6531 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -73,7 +73,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { tradeSize = amountUsed*(order.leverage+1e18)/1e18; } } else { - tradeSize = amountUsed; + tradeSize = IPriceFeeds(priceFeed).queryReturn(order.base, order.loanTokenAddress, amountUsed); } require(IPriceFeeds(priceFeed).queryReturn(order.loanTokenAddress, USDC, tradeSize) > MIN_AMOUNT_IN_USDC, "OrderBook: trade too small"); require(order.status==IOrderBook.OrderStatus.ACTIVE, "OrderBook: invalid order state"); @@ -114,10 +114,14 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { ? (order.loanTokenAmount, order.loanTokenAddress) : (order.collateralTokenAmount, order.base); uint256 tradeSize; - if (usedToken == order.base) { - tradeSize = IPriceFeeds(priceFeed).queryReturn(order.base, order.loanTokenAddress, amountUsed)*order.leverage/10**18; + if (order.orderType == IOrderBook.OrderType.LIMIT_OPEN) { + if (usedToken == order.base) { + tradeSize = IPriceFeeds(priceFeed).queryReturn(order.base, order.loanTokenAddress, amountUsed)*order.leverage/10**18; + } else { + tradeSize = amountUsed*(order.leverage+1e18)/1e18; + } } else { - tradeSize = amountUsed*(order.leverage+1e18)/1e18; + tradeSize = IPriceFeeds(priceFeed).queryReturn(order.base, order.loanTokenAddress, amountUsed); } require(IPriceFeeds(priceFeed).queryReturn(order.loanTokenAddress, USDC, tradeSize) > MIN_AMOUNT_IN_USDC, "OrderBook: trade too small"); From f597cbd96a1674a169ac0563c14891bc1a6b0f2b Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Tue, 28 Jun 2022 22:47:18 -0700 Subject: [PATCH 77/83] min amount received on stop orders --- contracts/orderbook/Logic/OrderBook.sol | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index 14fd5745..21499f86 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -400,7 +400,15 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { emit OrderExecuted(order.trader, orderID); } else if (order.orderType == IOrderBook.OrderType.MARKET_STOP) { + //order.leverage is repurposed to be min amount received. optional bool operand; + uint256 dexSwapReceived = PROTOCOL.getSwapExpectedReturn( + order.trader, + order.base, + order.loanTokenAddress, + order.collateralTokenAmount, + order.loanDataBytes + ); if (_useOracle[order.trader]) { operand = order.amountReceived >= @@ -410,16 +418,10 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { order.collateralTokenAmount ); //TODO: Adjust for precision } else { - operand = - order.amountReceived >= - getDexRate( - order.base, - order.loanTokenAddress, - order.loanDataBytes, - order.collateralTokenAmount - ); + operand = order.amountReceived >= dexSwapReceived; } require( + order.leverage <= dexSwapReceived && operand && priceCheck( order.base, From aee96fa9c99b541362af4ab68182a6a371ae0aee Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Wed, 29 Jun 2022 00:27:30 -0700 Subject: [PATCH 78/83] Update OrderBook.sol --- contracts/orderbook/Logic/OrderBook.sol | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index 21499f86..51f7cbdc 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -197,7 +197,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { srcToken = order.collateralTokenAmount > order.loanTokenAmount ? order.base : order.loanTokenAddress; - if (order.timeTillExpiration < block.timestamp) { + if (order.timeTillExpiration < block.timestamp || order.status != IOrderBook.OrderStatus.ACTIVE) { return false; } if (IDeposits(VAULT).getDeposit(keccak256(abi.encode(order.trader, 0))) < _gasToSend(2500000)) { @@ -249,11 +249,19 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { if (order.amountReceived <= dSwapValue) { return true; } - } else { + } else if (order.orderType == IOrderBook.OrderType.MARKET_STOP) { if (!_isActiveLoan(order.loanID)) { return false; } + //order.leverage is repurposed to be min amount received. optional bool operand; + uint256 dexSwapReceived = PROTOCOL.getSwapExpectedReturn( + order.trader, + order.base, + order.loanTokenAddress, + order.collateralTokenAmount, + order.loanDataBytes + ); if (_useOracle[order.trader]) { operand = order.amountReceived >= @@ -263,16 +271,9 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { order.collateralTokenAmount ); } else { - operand = - order.amountReceived >= - getDexRate( - order.base, - order.loanTokenAddress, - order.loanDataBytes, - order.collateralTokenAmount - ); + operand = order.amountReceived >= dexSwapReceived; } - if (operand) { + if (order.leverage <= dexSwapReceived && operand) { return true; } } @@ -416,7 +417,7 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { order.base, order.loanTokenAddress, order.collateralTokenAmount - ); //TODO: Adjust for precision + ); } else { operand = order.amountReceived >= dexSwapReceived; } From e0a02497920c85e960589e25b213721bde76d89e Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Sun, 10 Jul 2022 01:32:27 -0700 Subject: [PATCH 79/83] test cases --- testspolygon/limit-orders/test_Orders_Full.py | 222 ++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 testspolygon/limit-orders/test_Orders_Full.py diff --git a/testspolygon/limit-orders/test_Orders_Full.py b/testspolygon/limit-orders/test_Orders_Full.py new file mode 100644 index 00000000..c948e4e2 --- /dev/null +++ b/testspolygon/limit-orders/test_Orders_Full.py @@ -0,0 +1,222 @@ +import pytest +from brownie import * +from eth_abi import encode_abi + +@pytest.fixture(scope="module") +def DEPOSITS(interface, ORDERBOOK): + return interface.IDeposits(ORDERBOOK.VAULT()) + +@pytest.fixture(scope="module") +def TOKEN_REGISTRY(TokenRegistry): + return Contract.from_abi("TOKEN_REGISTRY", "0x4B234781Af34E9fD756C27a47675cbba19DC8765", TokenRegistry.abi) + +@pytest.fixture(scope="module") +def ORDERBOOK(interface): + return Contract.from_abi("ORDERBOOK", "0x043582611b2d62ee084d72f0e731883653f837ce", interface.IOrderBook.abi) + +@pytest.fixture(scope="module") +def USDC(TestToken): + return Contract.from_abi("USDC","0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",TestToken.abi) + +@pytest.fixture(scope="module") +def ETH(TestToken): + return Contract.from_abi("ETH","0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",TestToken.abi) + +@pytest.fixture(scope="module") +def WMATIC(TestToken): + return Contract.from_abi("WMATIC","0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",TestToken.abi) + +@pytest.fixture(scope="module") +def IUSDC(interface): + return Contract.from_abi("IUSDC","0xC3f6816C860e7d7893508C8F8568d5AF190f6d7d",interface.IToken.abi) + +@pytest.fixture(scope="module") +def IBZX(interface): + return interface.IBZx("0x059D60a9CEfBc70b9Ea9FFBb9a041581B1dFA6a8") + +#test case 1. place limit open order and execute as well as limit close +def test_case1(ORDERBOOK, DEPOSITS, USDC, IUSDC, ETH, WMATIC, IBZX): + ob = ORDERBOOK + deposits = DEPOSITS + + #get some USDC + USDC.transfer(accounts[0], 10000e6, {'from':"0xf977814e90da44bfa03b6295a0616a897441acec"}) + USDC.approve(deposits, 10000e6, {'from':accounts[0]}) + + #deposit gas token + WMATIC.approve(deposits, 100e18, {"from":accounts[0]}) + ob.depositGasFeeToken(0, {'from':accounts[0], 'value':100e18}) + + #place open order + setManager = encode_abi(['address'],[ob.address]) + dex_payload = encode_abi(['address','address'],[USDC.address,ETH.address]) + dex_selector = encode_abi(['uint256','bytes'],[1,dex_payload]) + loanDataBytes = encode_abi(['uint256','bytes[]'],[6,(dex_selector,setManager)]) + ob.placeOrder([0, 0, 1e6, 1e18, int(100e6), 0, accounts[0], + IUSDC, USDC, ETH, 0, 0, 1000000000000, loanDataBytes], {'from': accounts[0]}) + assert(ob.getUserOrdersCount(accounts[0]) == 1) + assert(ob.getGlobalOrdersCount() > 1) + orderID = ob.getUserOrderIDs(accounts[0])[0] + assert(ob.prelimCheck.call(orderID) == True) + + balanceBefore = WMATIC.balanceOf(accounts[0]) + #execute order + ob.executeOrder(orderID, {'from':accounts[0]}) + assert(WMATIC.balanceOf(accounts[0]) > balanceBefore) + assert(IBZX.getUserLoansCount(accounts[0], False) == 1) + + #place close order + orderToClose = IBZX.getUserLoans(accounts[0],0,10,0,False,False)[0] + ob.placeOrder([orderToClose[0], 0, orderToClose[4], 0, 0, orderToClose[5], accounts[0], IUSDC, USDC, ETH, 1, 0, 1000000000000, b''], {'from': accounts[0]}) + assert(ob.getUserOrdersCount(accounts[0]) == 1) + assert(ob.getGlobalOrdersCount() > 1) + orderID = ob.getUserOrderIDs(accounts[0])[0] + assert(ob.prelimCheck.call(orderID) == True) + + balanceBefore = WMATIC.balanceOf(accounts[0]) + #execute order + ob.executeOrder(orderID, {'from':accounts[0]}) + assert(WMATIC.balanceOf(accounts[0]) > balanceBefore) + assert(IBZX.getUserLoansCount(accounts[0], False) == 0) + + +#test case 2. place limit open order and execute as well as market stop w/o oracle +def test_case2(ORDERBOOK, DEPOSITS, USDC, IUSDC, ETH, WMATIC, IBZX): + ob = ORDERBOOK + deposits = DEPOSITS + + #get some USDC + USDC.transfer(accounts[0], 10000e6, {'from':"0xf977814e90da44bfa03b6295a0616a897441acec"}) + USDC.approve(deposits, 10000e6, {'from':accounts[0]}) + + #deposit gas token + WMATIC.approve(deposits, 100e18, {"from":accounts[0]}) + ob.depositGasFeeToken(0, {'from':accounts[0], 'value':100e18}) + + #place open order + setManager = encode_abi(['address'],[ob.address]) + dex_payload = encode_abi(['address','address'],[USDC.address,ETH.address]) + dex_selector = encode_abi(['uint256','bytes'],[1,dex_payload]) + loanDataBytes = encode_abi(['uint256','bytes[]'],[6,(dex_selector,setManager)]) + ob.placeOrder([0, 0, 1e6, 1e18, int(100e6), 0, accounts[0], + IUSDC, USDC, ETH, 0, 0, 1000000000000, loanDataBytes], {'from': accounts[0]}) + assert(ob.getUserOrdersCount(accounts[0]) == 1) + assert(ob.getGlobalOrdersCount() > 1) + orderID = ob.getUserOrderIDs(accounts[0])[0] + assert(ob.prelimCheck.call(orderID) == True) + + balanceBefore = WMATIC.balanceOf(accounts[0]) + #execute order + ob.executeOrder(orderID, {'from':accounts[0]}) + assert(WMATIC.balanceOf(accounts[0]) > balanceBefore) + assert(IBZX.getUserLoansCount(accounts[0], False) == 1) + + #place close order + orderToClose = IBZX.getUserLoans(accounts[0],0,10,0,False,False)[0] + ob.placeOrder([orderToClose[0], 0, orderToClose[4]*10, 0, 0, orderToClose[5], accounts[0], IUSDC, USDC, ETH, 2, 0, 1000000000000, b''], {'from': accounts[0]}) + assert(ob.getUserOrdersCount(accounts[0]) == 1) + assert(ob.getGlobalOrdersCount() > 1) + orderID = ob.getUserOrderIDs(accounts[0])[0] + assert(ob.prelimCheck.call(orderID) == True) + + balanceBefore = WMATIC.balanceOf(accounts[0]) + #execute order + ob.executeOrder(orderID, {'from':accounts[0]}) + assert(WMATIC.balanceOf(accounts[0]) > balanceBefore) + assert(IBZX.getUserLoansCount(accounts[0], False) == 0) + +#test case 3. place limit open order and execute as well as market stop w/ oracle +def test_case3(ORDERBOOK, DEPOSITS, USDC, IUSDC, ETH, WMATIC, IBZX): + ob = ORDERBOOK + deposits = DEPOSITS + + #get some USDC + USDC.transfer(accounts[0], 10000e6, {'from':"0xf977814e90da44bfa03b6295a0616a897441acec"}) + USDC.approve(deposits, 10000e6, {'from':accounts[0]}) + + #deposit gas token + WMATIC.approve(deposits, 100e18, {"from":accounts[0]}) + ob.depositGasFeeToken(0, {'from':accounts[0], 'value':100e18}) + + #place open order + setManager = encode_abi(['address'],[ob.address]) + dex_payload = encode_abi(['address','address'],[USDC.address,ETH.address]) + dex_selector = encode_abi(['uint256','bytes'],[1,dex_payload]) + loanDataBytes = encode_abi(['uint256','bytes[]'],[6,(dex_selector,setManager)]) + ob.placeOrder([0, 0, 1e6, 1e18, int(100e6), 0, accounts[0], + IUSDC, USDC, ETH, 0, 0, 1000000000000, loanDataBytes], {'from': accounts[0]}) + assert(ob.getUserOrdersCount(accounts[0]) == 1) + assert(ob.getGlobalOrdersCount() > 1) + orderID = ob.getUserOrderIDs(accounts[0])[0] + assert(ob.prelimCheck.call(orderID) == True) + + balanceBefore = WMATIC.balanceOf(accounts[0]) + #execute order + ob.executeOrder(orderID, {'from':accounts[0]}) + assert(WMATIC.balanceOf(accounts[0]) > balanceBefore) + assert(IBZX.getUserLoansCount(accounts[0], False) == 1) + + #change stop type + ob.changeStopType(True, {'from':accounts[0]}) + + #place close order + orderToClose = IBZX.getUserLoans(accounts[0],0,10,0,False,False)[0] + ob.placeOrder([orderToClose[0], 0, orderToClose[4]*10, 0, 0, orderToClose[5], accounts[0], IUSDC, USDC, ETH, 2, 0, 1000000000000, b''], {'from': accounts[0]}) + assert(ob.getUserOrdersCount(accounts[0]) == 1) + assert(ob.getGlobalOrdersCount() > 1) + orderID = ob.getUserOrderIDs(accounts[0])[0] + assert(ob.prelimCheck.call(orderID) == True) + + balanceBefore = WMATIC.balanceOf(accounts[0]) + #execute order + ob.executeOrder(orderID, {'from':accounts[0]}) + assert(WMATIC.balanceOf(accounts[0]) > balanceBefore) + assert(IBZX.getUserLoansCount(accounts[0], False) == 0) + +#test case 4. place limit open order and execute as well as market stop w/ oracle and min amount out +def test_case4(ORDERBOOK, DEPOSITS, USDC, IUSDC, ETH, WMATIC, IBZX): + ob = ORDERBOOK + deposits = DEPOSITS + + #get some USDC + USDC.transfer(accounts[0], 10000e6, {'from':"0xf977814e90da44bfa03b6295a0616a897441acec"}) + USDC.approve(deposits, 10000e6, {'from':accounts[0]}) + + #deposit gas token + WMATIC.approve(deposits, 100e18, {"from":accounts[0]}) + ob.depositGasFeeToken(0, {'from':accounts[0], 'value':100e18}) + + #place open order + setManager = encode_abi(['address'],[ob.address]) + dex_payload = encode_abi(['address','address'],[USDC.address,ETH.address]) + dex_selector = encode_abi(['uint256','bytes'],[1,dex_payload]) + loanDataBytes = encode_abi(['uint256','bytes[]'],[6,(dex_selector,setManager)]) + ob.placeOrder([0, 0, 1e6, 1e18, int(100e6), 0, accounts[0], + IUSDC, USDC, ETH, 0, 0, 1000000000000, loanDataBytes], {'from': accounts[0]}) + assert(ob.getUserOrdersCount(accounts[0]) == 1) + assert(ob.getGlobalOrdersCount() > 1) + orderID = ob.getUserOrderIDs(accounts[0])[0] + assert(ob.prelimCheck.call(orderID) == True) + + balanceBefore = WMATIC.balanceOf(accounts[0]) + #execute order + ob.executeOrder(orderID, {'from':accounts[0]}) + assert(WMATIC.balanceOf(accounts[0]) > balanceBefore) + assert(IBZX.getUserLoansCount(accounts[0], False) == 1) + + #change stop type + ob.changeStopType(True, {'from':accounts[0]}) + + #place close order + orderToClose = IBZX.getUserLoans(accounts[0],0,10,0,False,False)[0] + ob.placeOrder([orderToClose[0], 0, orderToClose[4]*10, orderToClose[4], 0, orderToClose[5], accounts[0], IUSDC, USDC, ETH, 2, 0, 1000000000000, b''], {'from': accounts[0]}) + assert(ob.getUserOrdersCount(accounts[0]) == 1) + assert(ob.getGlobalOrdersCount() > 1) + orderID = ob.getUserOrderIDs(accounts[0])[0] + assert(ob.prelimCheck.call(orderID) == True) + + balanceBefore = WMATIC.balanceOf(accounts[0]) + #execute order + ob.executeOrder(orderID, {'from':accounts[0]}) + assert(WMATIC.balanceOf(accounts[0]) > balanceBefore) + assert(IBZX.getUserLoansCount(accounts[0], False) == 0) \ No newline at end of file From 02174a54b836934f91b90f555029576cdab171d3 Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Sun, 10 Jul 2022 01:33:30 -0700 Subject: [PATCH 80/83] Update IOrderBook.sol --- contracts/orderbook/IOrderBook.sol | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/contracts/orderbook/IOrderBook.sol b/contracts/orderbook/IOrderBook.sol index 64ed23e1..3a0894d5 100644 --- a/contracts/orderbook/IOrderBook.sol +++ b/contracts/orderbook/IOrderBook.sol @@ -62,6 +62,14 @@ interface IOrderBook { bytes loanDataBytes; //data passed for margin trades } + /// Returns proxy owner + /// @return owner Contract owner + function owner() external view returns(address owner); + + /// Returns guardian + /// @return guardian Protocol guardian address + function getGuardian() external view returns(address guardian); + /// Returns Deposits contract address /// @return vault Deposits Contract function VAULT() external view returns(address vault); From ee955af4fe028f4d593a12221e40250b9117f16a Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Thu, 5 Jan 2023 03:12:01 -0800 Subject: [PATCH 81/83] OpenZeppelin version adjustment --- contracts/orderbook/Keepers/OrderKeeper.sol | 2 +- contracts/orderbook/Keepers/OrderKeeperClear.sol | 2 +- contracts/orderbook/Logic/OrderBook.sol | 2 +- contracts/orderbook/Logic/OrderBookData.sol | 2 +- contracts/orderbook/OrderVault/Deposits.sol | 4 ++-- contracts/orderbook/Storage/OrderBookStorage.sol | 6 +++--- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/contracts/orderbook/Keepers/OrderKeeper.sol b/contracts/orderbook/Keepers/OrderKeeper.sol index adb1a6a5..0d744e36 100644 --- a/contracts/orderbook/Keepers/OrderKeeper.sol +++ b/contracts/orderbook/Keepers/OrderKeeper.sol @@ -1,6 +1,6 @@ pragma solidity ^0.8.0; import "../IOrderBook.sol"; -import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; +import "@openzeppelin-4.7.0/token/ERC20/extensions/IERC20Metadata.sol"; import "../../governance/PausableGuardian_0_8.sol"; contract OrderKeeper is PausableGuardian_0_8 { diff --git a/contracts/orderbook/Keepers/OrderKeeperClear.sol b/contracts/orderbook/Keepers/OrderKeeperClear.sol index 17892eb0..76cf2a7b 100644 --- a/contracts/orderbook/Keepers/OrderKeeperClear.sol +++ b/contracts/orderbook/Keepers/OrderKeeperClear.sol @@ -1,6 +1,6 @@ pragma solidity ^0.8.0; import "../IOrderBook.sol"; -import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; +import "@openzeppelin-4.7.0/token/ERC20/extensions/IERC20Metadata.sol"; import "../../governance/PausableGuardian_0_8.sol"; contract OrderKeeperClear is PausableGuardian_0_8 { diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index 51f7cbdc..cfd52e08 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -5,7 +5,7 @@ import "../../swaps/ISwapsImpl.sol"; import "../OrderVault/IDeposits.sol"; import "../../mixins/Flags.sol"; import "../../interfaces/IWeth.sol"; -import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin-4.7.0/token/ERC20/utils/SafeERC20.sol"; contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { using EnumerableSet for EnumerableSet.Bytes32Set; diff --git a/contracts/orderbook/Logic/OrderBookData.sol b/contracts/orderbook/Logic/OrderBookData.sol index e4dc69c9..bb7c7453 100644 --- a/contracts/orderbook/Logic/OrderBookData.sol +++ b/contracts/orderbook/Logic/OrderBookData.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import "../Events/OrderBookEvents.sol"; import "../Storage/OrderBookStorage.sol"; -import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin-4.7.0/token/ERC20/utils/SafeERC20.sol"; contract OrderBookData is OrderBookEvents, OrderBookStorage { using EnumerableSet for EnumerableSet.Bytes32Set; diff --git a/contracts/orderbook/OrderVault/Deposits.sol b/contracts/orderbook/OrderVault/Deposits.sol index 7e2f6c19..dcff2a85 100644 --- a/contracts/orderbook/OrderVault/Deposits.sol +++ b/contracts/orderbook/OrderVault/Deposits.sol @@ -1,7 +1,7 @@ pragma solidity ^0.8.0; import "../../governance/PausableGuardian_0_8.sol"; -import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; -import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; +import "@openzeppelin-4.7.0/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin-4.7.0/token/ERC20/extensions/IERC20Metadata.sol"; contract Deposits is PausableGuardian_0_8 { struct DepositInfo { diff --git a/contracts/orderbook/Storage/OrderBookStorage.sol b/contracts/orderbook/Storage/OrderBookStorage.sol index 444d28b6..9766c7d6 100644 --- a/contracts/orderbook/Storage/OrderBookStorage.sol +++ b/contracts/orderbook/Storage/OrderBookStorage.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.0; import "../IOrderBook.sol"; import "../../governance/PausableGuardian_0_8.sol"; -import "@openzeppelin-4.3.2/utils/structs/EnumerableSet.sol"; +import "@openzeppelin-4.7.0/utils/structs/EnumerableSet.sol"; import "../../../interfaces/IPriceFeeds.sol"; import "../../../interfaces/IToken.sol"; -import "@openzeppelin-4.3.2/token/ERC20/utils/SafeERC20.sol"; -import "@openzeppelin-4.3.2/token/ERC20/extensions/IERC20Metadata.sol"; +import "@openzeppelin-4.7.0/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin-4.7.0/token/ERC20/extensions/IERC20Metadata.sol"; import "./OrderBookConstants.sol"; contract OrderBookStorage is OrderBookConstants, PausableGuardian_0_8 { From bc997569fa442980db398a237cb10f980d09ef22 Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Thu, 5 Jan 2023 03:25:12 -0800 Subject: [PATCH 82/83] gas price fix --- contracts/orderbook/Logic/OrderBook.sol | 3 --- contracts/orderbook/Logic/OrderBookOrderPlace.sol | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index cfd52e08..5cd3aa83 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -72,9 +72,6 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { } function getGasPrice() public view returns (uint256) { - if (chainGasPrice == 0) { - return IPriceFeeds(priceFeed).getFastGasPrice(WRAPPED_TOKEN)/1e36; - } return chainGasPrice; } diff --git a/contracts/orderbook/Logic/OrderBookOrderPlace.sol b/contracts/orderbook/Logic/OrderBookOrderPlace.sol index 754e6531..4156b1bd 100644 --- a/contracts/orderbook/Logic/OrderBookOrderPlace.sol +++ b/contracts/orderbook/Logic/OrderBookOrderPlace.sol @@ -53,7 +53,7 @@ contract OrderBookOrderPlace is OrderBookEvents, OrderBookStorage { } function _getGasPrice() internal view returns (uint256 gasPrice) { - gasPrice = chainGasPrice == 0 ? IPriceFeeds(priceFeed).getFastGasPrice(WRAPPED_TOKEN)/1e36 : chainGasPrice; + gasPrice = chainGasPrice; } function _gasToSend(uint256 gasUsed) internal view returns (uint256) { From ab62376188dc24d5c0b97d5ff84aa5ddd25c4d9f Mon Sep 17 00:00:00 2001 From: Drypto13 Date: Thu, 16 Mar 2023 01:31:37 -0700 Subject: [PATCH 83/83] Remove approve requirement for gas fee --- contracts/orderbook/Logic/OrderBook.sol | 9 ++++----- contracts/orderbook/OrderVault/Deposits.sol | 10 ++++++++++ contracts/orderbook/OrderVault/IDeposits.sol | 4 +++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/contracts/orderbook/Logic/OrderBook.sol b/contracts/orderbook/Logic/OrderBook.sol index 5cd3aa83..47a3d418 100644 --- a/contracts/orderbook/Logic/OrderBook.sol +++ b/contracts/orderbook/Logic/OrderBook.sol @@ -81,13 +81,12 @@ contract OrderBook is OrderBookEvents, OrderBookStorage, Flags { function depositGasFeeToken(uint256 amount) external payable { require(msg.value==0||amount==0, "cant be both"); - if(msg.value!=0){ - IWeth(WRAPPED_TOKEN).deposit{value:msg.value}(); - IERC20(WRAPPED_TOKEN).transfer(msg.sender, msg.value); - amount = msg.value; + if(msg.value==0){ + IERC20(WRAPPED_TOKEN).safeTransferFrom(msg.sender, address(this), amount); + IWeth(WRAPPED_TOKEN).withdraw(amount); } bytes32 orderID = keccak256(abi.encode(msg.sender, 0)); - IDeposits(VAULT).deposit(orderID, amount, msg.sender, WRAPPED_TOKEN); + IDeposits(VAULT).depositGasToken{value:amount}(msg.sender); } function withdrawGasFeeToken(uint256 amount) external { diff --git a/contracts/orderbook/OrderVault/Deposits.sol b/contracts/orderbook/OrderVault/Deposits.sol index dcff2a85..2aa24eb1 100644 --- a/contracts/orderbook/OrderVault/Deposits.sol +++ b/contracts/orderbook/OrderVault/Deposits.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.0; import "../../governance/PausableGuardian_0_8.sol"; import "@openzeppelin-4.7.0/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin-4.7.0/token/ERC20/extensions/IERC20Metadata.sol"; +import "../../interfaces/IWeth.sol"; contract Deposits is PausableGuardian_0_8 { struct DepositInfo { @@ -9,6 +10,7 @@ contract Deposits is PausableGuardian_0_8 { uint256 depositAmount; } mapping(bytes32 => DepositInfo) internal _depositInfo; + address public constant WRAPPED_TOKEN = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270; address public orderBook = address(0); function deposit( @@ -30,6 +32,14 @@ contract Deposits is PausableGuardian_0_8 { ); } + function depositGasToken(address trader) external pausable payable { + require(msg.sender == orderBook, "unauthorized"); + IWeth(WRAPPED_TOKEN).deposit{value:msg.value}(); + bytes32 orderID = keccak256(abi.encode(trader, 0)); + _depositInfo[orderID].depositToken = WRAPPED_TOKEN; + _depositInfo[orderID].depositAmount += msg.value; + } + function setOrderBook(address n) external onlyOwner { orderBook = n; } diff --git a/contracts/orderbook/OrderVault/IDeposits.sol b/contracts/orderbook/OrderVault/IDeposits.sol index 014c1778..4dd3ef79 100644 --- a/contracts/orderbook/OrderVault/IDeposits.sol +++ b/contracts/orderbook/OrderVault/IDeposits.sol @@ -3,11 +3,13 @@ pragma solidity ^0.8.0; interface IDeposits { function deposit( bytes32 orderID, - uint256 TokenAmount, + uint256 tokenAmount, address trader, address token ) external; + function depositGasToken(address trader) external payable; + function withdraw(bytes32 orderID) external; function withdrawToTrader(address trader, bytes32 orderID) external;