diff --git a/contracts/8/DexRouter.sol b/contracts/8/DexRouter.sol index 40d344d..47358b6 100644 --- a/contracts/8/DexRouter.sol +++ b/contracts/8/DexRouter.sol @@ -97,7 +97,42 @@ contract DexRouter is require(priorityAddresses[msg.sender] == true, "only priority"); _; } - + function _exeAdapter( + bool reverse, + address adapter, + address to, + address poolAddress, + bytes memory moreinfo, + address refundTo + ) internal { + if (reverse) { + (bool s, bytes memory res) = address(adapter).call( + abi.encodePacked( + abi.encodeWithSelector( + IAdapter.sellQuote.selector, + to, + poolAddress, + moreinfo + ), + ORIGIN_PAYER + uint(uint160(refundTo)) + ) + ); + require(s, string(res)); + } else { + (bool s, bytes memory res) = address(adapter).call( + abi.encodePacked( + abi.encodeWithSelector( + IAdapter.sellBase.selector, + to, + poolAddress, + moreinfo + ), + ORIGIN_PAYER + uint(uint160(refundTo)) + ) + ); + require(s, string(res)); + } + } //------------------------------- //------- Internal Functions ---- //------------------------------- @@ -110,62 +145,54 @@ contract DexRouter is /// @dev It includes checks for the total weight of the paths and executes the swapping through the adapters. function _exeForks( address payer, + address refundTo, address to, uint256 batchAmount, - RouterPath calldata path, + RouterPath memory path, bool noTransfer ) private { - address fromToken = _bytes32ToAddress(path.fromToken); - // fix post audit DRW-01: lack of check on Weights uint256 totalWeight; - // execute multiple Adapters for a transaction pair - uint256 pathLength = path.mixAdapters.length; - for (uint256 i = 0; i < pathLength; ) { + for (uint256 i = 0; i < path.mixAdapters.length; i++) { bytes32 rawData = bytes32(path.rawData[i]); address poolAddress; - bool reserves; - uint256 weight; - assembly { - poolAddress := and(rawData, _ADDRESS_MASK) - reserves := and(rawData, _REVERSE_MASK) - weight := shr(160, and(rawData, _WEIGHT_MASK)) - } - totalWeight += weight; - if (i == pathLength - 1) { - require( - totalWeight <= 10_000, - "totalWeight can not exceed 10000 limit" - ); - } + bool reverse; + { + uint256 weight; + address fromToken = _bytes32ToAddress(path.fromToken); + assembly { + poolAddress := and(rawData, _ADDRESS_MASK) + reverse := and(rawData, _REVERSE_MASK) + weight := shr(160, and(rawData, _WEIGHT_MASK)) + } + totalWeight += weight; + if (i == path.mixAdapters.length - 1) { + require( + totalWeight <= 10_000, + "totalWeight can not exceed 10000 limit" + ); + } - if (!noTransfer) { - uint256 _fromTokenAmount = weight == 10_000 - ? batchAmount - : (batchAmount * weight) / 10_000; - _transferInternal( - payer, - path.assetTo[i], - fromToken, - _fromTokenAmount - ); + if (!noTransfer) { + uint256 _fromTokenAmount = weight == 10_000 + ? batchAmount + : (batchAmount * weight) / 10_000; + _transferInternal( + payer, + path.assetTo[i], + fromToken, + _fromTokenAmount + ); + } } - if (reserves) { - IAdapter(path.mixAdapters[i]).sellQuote( - to, - poolAddress, - path.extraData[i] - ); - } else { - IAdapter(path.mixAdapters[i]).sellBase( - to, - poolAddress, - path.extraData[i] - ); - } - unchecked { - ++i; - } + _exeAdapter( + reverse, + path.mixAdapters[i], + to, + poolAddress, + path.extraData[i], + refundTo + ); } } /// @notice Executes a series of swaps or operations defined by a set of routing paths, potentially across different protocols or pools. @@ -179,10 +206,11 @@ contract DexRouter is function _exeHop( address payer, + address refundTo, address receiver, bool isToNative, uint256 batchAmount, - RouterPath[] calldata hops + RouterPath[] memory hops ) private { address fromToken = _bytes32ToAddress(hops[0].fromToken); bool toNext; @@ -210,7 +238,7 @@ contract DexRouter is } // 3.2 execute forks - _exeForks(payer, to, batchAmount, hops[i], noTransfer); + _exeForks(payer, refundTo, to, batchAmount, hops[i], noTransfer); noTransfer = toNext; unchecked { @@ -295,8 +323,9 @@ contract DexRouter is function _smartSwapInternal( BaseRequest memory baseRequest, uint256[] memory batchesAmount, - RouterPath[][] calldata batches, + RouterPath[][] memory batches, address payer, + address refundTo, address receiver ) private returns (uint256 returnAmount) { // 1. transfer from token in @@ -342,6 +371,7 @@ contract DexRouter is // execute hop, if the whole swap replacing by pmm fails, the funds will return to dexRouter _exeHop( payer, + refundTo, receiver, IERC20(_baseRequest.toToken).isETH(), batchesAmount[i], @@ -431,7 +461,14 @@ contract DexRouter is .payerReceiver(); require(receiver != address(0), "not address(0)"); return - _smartSwapTo(payer, receiver, baseRequest, batchesAmount, batches); + _smartSwapTo( + payer, + payer, + receiver, + baseRequest, + batchesAmount, + batches + ); } /// @notice Executes a token swap using Unxswap protocol via XBridge for a specific order ID. /// @param srcToken The source token's address to be swapped. @@ -509,6 +546,7 @@ contract DexRouter is emit SwapOrderId(orderId); return _smartSwapTo( + msg.sender, msg.sender, msg.sender, baseRequest, @@ -552,13 +590,31 @@ contract DexRouter is /// @dev This function is designed for scenarios where investments are made in batches or through complex paths to optimize returns. Adjustments are made based on the contract's current token balance to ensure precise allocation. function smartSwapByInvest( - BaseRequest calldata baseRequest, - uint256[] calldata batchesAmount, - RouterPath[][] calldata batches, - PMMLib.PMMSwapRequest[] calldata extraData, + BaseRequest memory baseRequest, + uint256[] memory batchesAmount, + RouterPath[][] memory batches, + PMMLib.PMMSwapRequest[] memory extraData, address to + ) external payable returns (uint256 returnAmount) { + return + smartSwapByInvestWithRefund( + baseRequest, + batchesAmount, + batches, + extraData, + to, + to + ); + } + function smartSwapByInvestWithRefund( + BaseRequest memory baseRequest, + uint256[] memory batchesAmount, + RouterPath[][] memory batches, + PMMLib.PMMSwapRequest[] memory extraData, + address to, + address refundTo ) - external + public payable isExpired(baseRequest.deadLine) nonReentrant @@ -566,30 +622,28 @@ contract DexRouter is { address fromToken = _bytes32ToAddress(baseRequest.fromToken); require(fromToken != _ETH, "Invalid source token"); + require(refundTo != address(0), "refundTo is address(0)"); + require(to != address(0), "to is address(0)"); + require(baseRequest.fromTokenAmount > 0, "fromTokenAmount is 0"); uint256 amount = IERC20(fromToken).balanceOf(address(this)); - BaseRequest memory newBaseRequest = BaseRequest({ - fromToken: baseRequest.fromToken, - toToken: baseRequest.toToken, - fromTokenAmount: amount, - minReturnAmount: baseRequest.minReturnAmount, - deadLine: baseRequest.deadLine - }); - uint256[] memory newBatchesAmount = new uint256[](batchesAmount.length); for (uint256 i = 0; i < batchesAmount.length; ) { - newBatchesAmount[i] = + batchesAmount[i] = (batchesAmount[i] * amount) / baseRequest.fromTokenAmount; unchecked { ++i; } } - returnAmount = _smartSwapInternal( - newBaseRequest, - newBatchesAmount, - batches, - address(this), - to - ); + baseRequest.fromTokenAmount = amount; + return + _smartSwapInternal( + baseRequest, + batchesAmount, + batches, + address(this), // payer + refundTo, // refundTo + to // receiver + ); } /// @notice Executes a Uniswap V3 swap after obtaining a permit, allowing the approval of token spending and swap execution in a single transaction. @@ -645,11 +699,12 @@ contract DexRouter is uint256 balanceBefore ) = _doCommissionFromToken( commissionInfo, + payer, address(uint160(receiver)), amount ); - (uint256 swappedAmount, ) = _uniswapV3Swap( + uint256 swappedAmount = _uniswapV3Swap( payer, payable(middleReceiver), amount, @@ -662,7 +717,6 @@ contract DexRouter is address(uint160(receiver)), balanceBefore ); - return swappedAmount - commissionAmount; } @@ -694,6 +748,7 @@ contract DexRouter is emit SwapOrderId(orderId); return _smartSwapTo( + msg.sender, msg.sender, receiver, baseRequest, @@ -704,10 +759,11 @@ contract DexRouter is function _smartSwapTo( address payer, + address refundTo, address receiver, - BaseRequest calldata baseRequest, - uint256[] calldata batchesAmount, - RouterPath[][] calldata batches + BaseRequest memory baseRequest, + uint256[] memory batchesAmount, + RouterPath[][] memory batches ) internal returns (uint256) { require(receiver != address(0), "not addr(0)"); CommissionInfo memory commissionInfo = _getCommissionInfo(); @@ -717,6 +773,7 @@ contract DexRouter is uint256 balanceBefore ) = _doCommissionFromToken( commissionInfo, + payer, receiver, baseRequest.fromTokenAmount ); @@ -726,6 +783,7 @@ contract DexRouter is batchesAmount, batches, _payer, + refundTo, middleReceiver ); @@ -734,7 +792,6 @@ contract DexRouter is receiver, balanceBefore ); - return swappedAmount - commissionAmount; } /// @notice Executes a token swap using the Unxswap protocol, sending the output directly to a specified receiver. @@ -781,7 +838,7 @@ contract DexRouter is ( address middleReceiver, uint256 balanceBefore - ) = _doCommissionFromToken(commissionInfo, receiver, amount); + ) = _doCommissionFromToken(commissionInfo, payer, receiver, amount); uint256 swappedAmount = _unxswapInternal( IERC20(address(uint160(srcToken & _ADDRESS_MASK))), @@ -797,29 +854,6 @@ contract DexRouter is receiver, balanceBefore ); - return swappedAmount - commissionAmount; } - - /// @notice Allows the contract owner to withdraw any tokens or native currency considered as "dust". - /// @param token The address of the token to withdraw, or the zero address for native currency. - /// @param to The address where the dust tokens or native currency should be sent. - /// @param amount The amount of the token or native currency to withdraw. - /// @dev This function is intended for recovering small amounts of tokens or native currency - /// left in the contract, which might not be recoverable through normal operations. - /// It can only be executed by the contract owner to ensure control over the contract's assets. - function withdrawDust( - address token, - address to, - uint256 amount - ) external onlyOwner { - if (token == _ETH) { - (bool success, bytes memory data) = payable(to).call{value: amount}( - "" - ); - require(success, string(data)); - } else { - SafeERC20.safeTransfer(IERC20(token), to, amount); - } - } } diff --git a/contracts/8/UnxswapV3Router.sol b/contracts/8/UnxswapV3Router.sol index 52ef46c..6a21381 100644 --- a/contracts/8/UnxswapV3Router.sol +++ b/contracts/8/UnxswapV3Router.sol @@ -26,6 +26,8 @@ contract UnxswapV3Router is IUniswapV3SwapCallback, CommonUtils { // concatenation of withdraw(uint),transfer() bytes32 private constant _SELECTORS2 = 0x2e1a7d4da9059cbb000000000000000000000000000000000000000000000000; + bytes32 private constant _SELECTORS3 = + 0xa9059cbb70a08231000000000000000000000000000000000000000000000000; uint160 private constant _MIN_SQRT_RATIO = 4_295_128_739 + 1; uint160 private constant _MAX_SQRT_RATIO = 1_461_446_703_485_210_103_287_273_052_203_988_822_378_723_970_342 - 1; @@ -43,7 +45,6 @@ contract UnxswapV3Router is IUniswapV3SwapCallback, CommonUtils { /// @param minReturn The minimum amount of tokens that must be received for the swap to be valid, safeguarding against excessive slippage. /// @param pools An array of pool identifiers defining the swap route within Uniswap V3. /// @return returnAmount The amount of tokens received from the swap. - /// @return srcTokenAddr The address of the source token used for the swap. /// @dev This internal function encapsulates the core logic for executing swaps on Uniswap V3. It is intended to be used by other functions in the contract that prepare and pass the necessary parameters. The function handles the swapping process, ensuring that the minimum return is met and managing the transfer of tokens. function _uniswapV3Swap( address payer, @@ -51,7 +52,7 @@ contract UnxswapV3Router is IUniswapV3SwapCallback, CommonUtils { uint256 amount, uint256 minReturn, uint256[] calldata pools - ) internal returns (uint256 returnAmount, address srcTokenAddr) { + ) internal returns (uint256 returnAmount) { assembly { function _revertWithReason(m, len) { mstore( @@ -65,7 +66,7 @@ contract UnxswapV3Router is IUniswapV3SwapCallback, CommonUtils { mstore(0x40, m) revert(0, len) } - function _makeSwap(_receiver, _payer, _pool, _amount) + function _makeSwap(_receiver, _payer, _refundTo, _pool, _amount) -> _returnAmount { if lt(_INT256_MAX, _amount) { @@ -88,9 +89,18 @@ contract UnxswapV3Router is IUniswapV3SwapCallback, CommonUtils { mstore(add(paramPtr, 0x40), _amount) mstore(add(paramPtr, 0x60), _MIN_SQRT_RATIO) mstore(add(paramPtr, 0x80), 0xa0) - mstore(add(paramPtr, 0xa0), 32) + mstore(add(paramPtr, 0xa0), 64) mstore(add(paramPtr, 0xc0), _payer) - let success := call(gas(), poolAddr, 0, freePtr, 0xe4, 0, 0) + mstore(add(paramPtr, 0xe0), _refundTo) + let success := call( + gas(), + poolAddr, + 0, + freePtr, + 0x104, + 0, + 0 + ) if iszero(success) { revert(0, 32) } @@ -104,9 +114,18 @@ contract UnxswapV3Router is IUniswapV3SwapCallback, CommonUtils { mstore(add(paramPtr, 0x40), _amount) mstore(add(paramPtr, 0x60), _MAX_SQRT_RATIO) mstore(add(paramPtr, 0x80), 0xa0) - mstore(add(paramPtr, 0xa0), 32) + mstore(add(paramPtr, 0xa0), 64) mstore(add(paramPtr, 0xc0), _payer) - let success := call(gas(), poolAddr, 0, freePtr, 0xe4, 0, 0) + mstore(add(paramPtr, 0xe0), _refundTo) + let success := call( + gas(), + poolAddr, + 0, + freePtr, + 0x104, + 0, + 0 + ) if iszero(success) { revert(0, 32) } @@ -209,13 +228,11 @@ contract UnxswapV3Router is IUniswapV3SwapCallback, CommonUtils { function _emitEvent( _firstPoolStart, _lastPoolStart, - _returnAmount, - wrapWeth, - unwrapWeth - ) -> srcToken { - srcToken := _ETH + _returnAmount + ) { + let srcToken := _ETH let toToken := _ETH - if eq(wrapWeth, false) { + if eq(callvalue(), 0) { let firstPool := calldataload(_firstPoolStart) switch eq(0, and(firstPool, _ONE_FOR_ZERO_MASK)) case true { @@ -225,7 +242,7 @@ contract UnxswapV3Router is IUniswapV3SwapCallback, CommonUtils { srcToken := _token1(firstPool) } } - if eq(unwrapWeth, false) { + if eq(and(calldataload(_lastPoolStart), _WETH_UNWRAP_MASK), 0) { let lastPool := calldataload(_lastPoolStart) switch eq(0, and(lastPool, _ONE_FOR_ZERO_MASK)) case true { @@ -250,6 +267,7 @@ contract UnxswapV3Router is IUniswapV3SwapCallback, CommonUtils { } let firstPoolStart let lastPoolStart + { let len := pools.length firstPoolStart := pools.offset // @@ -263,11 +281,13 @@ contract UnxswapV3Router is IUniswapV3SwapCallback, CommonUtils { revert(0, 4) } } - - let wrapWeth := gt(callvalue(), 0) - if wrapWeth { - _wrapWeth(amount) - payer := address() + let refundTo := payer + { + let wrapWeth := gt(callvalue(), 0) + if wrapWeth { + _wrapWeth(amount) + payer := address() + } } mstore(96, amount) // 96 is not override by _makeSwap, since it only use freePtr memory, and it is not override by unWrapWeth ethier @@ -276,46 +296,51 @@ contract UnxswapV3Router is IUniswapV3SwapCallback, CommonUtils { } lt(i, lastPoolStart) { i := add(i, 32) } { - amount := _makeSwap(address(), payer, calldataload(i), amount) - payer := address() - } - let unwrapWeth := gt( - and(calldataload(lastPoolStart), _WETH_UNWRAP_MASK), - 0 - ) // pools[lastIndex] & _WETH_UNWRAP_MASK > 0 - - // last one or only one - switch unwrapWeth - case 1 { - returnAmount := _makeSwap( + amount := _makeSwap( address(), payer, - calldataload(lastPoolStart), + refundTo, + calldataload(i), amount ) - _unWrapWeth(receiver, returnAmount) + payer := address() } - case 0 { - returnAmount := _makeSwap( - receiver, - payer, - calldataload(lastPoolStart), - amount - ) + { + let unwrapWeth := gt( + and(calldataload(lastPoolStart), _WETH_UNWRAP_MASK), + 0 + ) // pools[lastIndex] & _WETH_UNWRAP_MASK > 0 + + // last one or only one + switch unwrapWeth + case 1 { + returnAmount := _makeSwap( + address(), + payer, + refundTo, + calldataload(lastPoolStart), + amount + ) + _unWrapWeth(receiver, returnAmount) + } + case 0 { + returnAmount := _makeSwap( + receiver, + payer, + refundTo, + calldataload(lastPoolStart), + amount + ) + } } + if lt(returnAmount, minReturn) { _revertWithReason( 0x000000164d696e2072657475726e206e6f742072656163686564000000000000, 90 ) // Min return not reached } - srcTokenAddr := _emitEvent( - firstPoolStart, - lastPoolStart, - returnAmount, - wrapWeth, - unwrapWeth - ) + _emitEvent(firstPoolStart, lastPoolStart, returnAmount) } } @@ -331,6 +356,24 @@ contract UnxswapV3Router is IUniswapV3SwapCallback, CommonUtils { returndatacopy(0, 0, returndatasize()) revert(0, returndatasize()) } + function getBalanceAndTransfer(emptyPtr, token) { + mstore(emptyPtr, _SELECTORS3) + mstore(add(8, emptyPtr), address()) + if iszero( + staticcall(gas(), token, add(4, emptyPtr), 36, 0, 32) + ) { + reRevert() + } + let amount := mload(0) + if gt(amount, 0) { + let refundTo := calldataload(164) + mstore(add(4, emptyPtr), refundTo) + mstore(add(36, emptyPtr), amount) + validateERC20Transfer( + call(gas(), token, 0, emptyPtr, 0x44, 0, 0x20) + ) + } + } function validateERC20Transfer(status) { if iszero(status) { @@ -405,6 +448,7 @@ contract UnxswapV3Router is IUniswapV3SwapCallback, CommonUtils { validateERC20Transfer( call(gas(), token, 0, add(emptyPtr, 0x0c), 0x44, 0, 0x20) ) + getBalanceAndTransfer(emptyPtr, token) } default { // approveProxy.claimTokens(token, payer, msg.sender, amount); diff --git a/contracts/8/adapter/NativePmmAdapter.sol b/contracts/8/adapter/NativePmmAdapter.sol new file mode 100644 index 0000000..ef6697d --- /dev/null +++ b/contracts/8/adapter/NativePmmAdapter.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/IAdapter.sol"; +import "../interfaces/INativeRfqPool.sol"; +import "../interfaces/IERC20.sol"; +import "../libraries/SafeERC20.sol"; +import "../interfaces/IWETH.sol"; +contract NativePmmAdapter is IAdapter { + address ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + address public immutable WETH; + constructor(address _weth) { + WETH = _weth; + } + + function _NativePmmSwap( + address to, + address pool, + bytes memory moreInfo + ) internal { + INativeRfqPool.RFQTQuote memory quote = abi.decode( + moreInfo, + (INativeRfqPool.RFQTQuote) + ); + uint256 value = quote.sellerToken == address(0) + ? quote.effectiveSellerTokenAmount + : 0; + if (quote.sellerToken == address(0)) { + IWETH(WETH).withdraw(value); + } else { + SafeERC20.safeApprove( + IERC20(quote.sellerToken), + pool, + quote.effectiveSellerTokenAmount + ); + } + INativeRfqPool(pool).tradeRFQT{value: value}(quote); + if (quote.buyerToken == address(0)) { + IWETH(WETH).deposit{value: address(this).balance}(); + SafeERC20.safeTransfer( + IERC20(WETH), + to, + IERC20(WETH).balanceOf(address(this)) + ); + } else { + SafeERC20.safeTransfer( + IERC20(quote.buyerToken), + to, + IERC20(quote.buyerToken).balanceOf(address(this)) + ); + } + } + + function sellBase( + address to, + address pool, + bytes memory moreInfo + ) external override { + _NativePmmSwap(to, pool, moreInfo); + } + + function sellQuote( + address to, + address pool, + bytes memory moreInfo + ) external override { + _NativePmmSwap(to, pool, moreInfo); + } + receive() external payable {} +} diff --git a/contracts/8/adapter/UniAdapterForRebaseToken.sol b/contracts/8/adapter/UniAdapterForRebaseToken.sol new file mode 100644 index 0000000..dd95da7 --- /dev/null +++ b/contracts/8/adapter/UniAdapterForRebaseToken.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../interfaces/IAdapter.sol"; +import "../interfaces/IUni.sol"; +import "../interfaces/IERC20.sol"; +import "../libraries/SafeERC20.sol"; + +/// @title UniAdapterForRebaseToken +/// @notice Adapter for handling swaps with rebase tokens on Uni pools. +contract UniAdapterForRebaseToken is IAdapter { + // fromToken == token0 + function sellBase( + address to, + address pool, + bytes memory + ) external override { + IUni(pool).sync(); + address baseToken = IUni(pool).token0(); + (uint256 reserveIn, uint256 reserveOut, ) = IUni(pool).getReserves(); + require( + reserveIn > 0 && reserveOut > 0, + "UniAdapter: INSUFFICIENT_LIQUIDITY" + ); + + uint256 amountIn = IERC20(baseToken).balanceOf(address(this)); + SafeERC20.safeTransfer(IERC20(baseToken), pool, amountIn); + + uint256 balance0 = IERC20(baseToken).balanceOf(pool); + uint256 sellBaseAmount = balance0 - reserveIn; + + uint256 receiveQuoteAmount; + assembly { + let sellAmountWithFee := mul(sellBaseAmount, 997) + let numerator := mul(sellAmountWithFee, reserveOut) + let denominator := add(mul(reserveIn, 1000), sellAmountWithFee) + receiveQuoteAmount := div(numerator, denominator) + } + + IUni(pool).swap(0, receiveQuoteAmount, to, new bytes(0)); + } + + // fromToken == token1 + function sellQuote( + address to, + address pool, + bytes memory + ) external override { + IUni(pool).sync(); + address quoteToken = IUni(pool).token1(); + (uint256 reserveOut, uint256 reserveIn, ) = IUni(pool).getReserves(); + require( + reserveIn > 0 && reserveOut > 0, + "UniAdapter: INSUFFICIENT_LIQUIDITY" + ); + + uint256 amountIn = IERC20(quoteToken).balanceOf(address(this)); + SafeERC20.safeTransfer(IERC20(quoteToken), pool, amountIn); + + uint256 balance1 = IERC20(quoteToken).balanceOf(pool); + uint256 sellQuoteAmount = balance1 - reserveIn; + + uint256 receiveBaseAmount; + assembly { + let sellAmountWithFee := mul(sellQuoteAmount, 997) + let numerator := mul(sellAmountWithFee, reserveOut) + let denominator := add(mul(reserveIn, 1000), sellAmountWithFee) + receiveBaseAmount := div(numerator, denominator) + } + + IUni(pool).swap(receiveBaseAmount, 0, to, new bytes(0)); + } +} diff --git a/contracts/8/adapter/UniV3Adapter.sol b/contracts/8/adapter/UniV3Adapter.sol index c594dc7..5a652fc 100644 --- a/contracts/8/adapter/UniV3Adapter.sol +++ b/contracts/8/adapter/UniV3Adapter.sol @@ -16,6 +16,8 @@ import "../interfaces/IWETH.sol"; /// @dev Explain to a developer any extra details contract UniV3Adapter is IAdapter, IUniswapV3SwapCallback { address constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + uint256 internal constant ORIGIN_PAYER = + 0x3ca20afc2ccc0000000000000000000000000000000000000000000000000000; address public immutable WETH; constructor(address payable weth) { @@ -26,8 +28,14 @@ contract UniV3Adapter is IAdapter, IUniswapV3SwapCallback { address to, address pool, uint160 sqrtX96, - bytes memory data + bytes memory data, + uint256 payerOrigin ) internal { + address _payerOrigin; + if ((payerOrigin & ORIGIN_PAYER) == ORIGIN_PAYER) { + _payerOrigin = address(uint160(uint256(payerOrigin))); + } + (address fromToken, address toToken, ) = abi.decode( data, (address, address, uint24) @@ -49,6 +57,10 @@ contract UniV3Adapter is IAdapter, IUniswapV3SwapCallback { : sqrtX96, data ); + uint amount = IERC20(fromToken).balanceOf(address(this)); + if (amount > 0 && _payerOrigin != address(0)) { + SafeERC20.safeTransfer(IERC20(fromToken), _payerOrigin, amount); + } } function sellBase( @@ -60,7 +72,12 @@ contract UniV3Adapter is IAdapter, IUniswapV3SwapCallback { moreInfo, (uint160, bytes) ); - _uniV3Swap(to, pool, sqrtX96, data); + uint256 payerOrigin; + assembly { + let size := calldatasize() + payerOrigin := calldataload(sub(size, 32)) + } + _uniV3Swap(to, pool, sqrtX96, data, payerOrigin); } function sellQuote( @@ -72,7 +89,12 @@ contract UniV3Adapter is IAdapter, IUniswapV3SwapCallback { moreInfo, (uint160, bytes) ); - _uniV3Swap(to, pool, sqrtX96, data); + uint256 payerOrigin; + assembly { + let size := calldatasize() + payerOrigin := calldataload(sub(size, 32)) + } + _uniV3Swap(to, pool, sqrtX96, data, payerOrigin); } // for uniV3 callback diff --git a/contracts/8/interfaces/INativeRfqPool.sol b/contracts/8/interfaces/INativeRfqPool.sol new file mode 100644 index 0000000..53e8f94 --- /dev/null +++ b/contracts/8/interfaces/INativeRfqPool.sol @@ -0,0 +1,86 @@ +interface INativeRfqPool { + struct WidgetFee { + address signer; + address feeRecipient; + uint256 feeRate; + } + /// @notice Used for intra-chain RFQ-T trades. + struct RFQTQuote { + address pool; + address signer; + /// @notice The recipient of the buyerToken at the end of the trade. + address recipient; + /** + * @notice The account "effectively" making the trade (ultimately receiving the funds). + * This is commonly used by aggregators, where a proxy contract (the 'trader') + * receives the buyerToken, and the effective trader is the user initiating the call. + * + * This field DOES NOT influence movement of funds. However, it is used to check against + * quote replay. + */ + // address effectiveTrader; + /// @notice The token that the trader sells. + address sellerToken; + /// @notice The token that the trader buys. + address buyerToken; + /** + * @notice The amount of sellerToken sold in this trade. The exchange rate + * is going to be preserved as the buyerTokenAmount / sellerTokenAmount ratio. + * + * Most commonly, effectiveSellerTokenAmount will == sellerTokenAmount. + */ + uint256 effectiveSellerTokenAmount; + /// @notice The max amount of sellerToken sold. + uint256 sellerTokenAmount; + /// @notice The amount of buyerToken bought when sellerTokenAmount is sold. + uint256 buyerTokenAmount; + /// @notice The Unix timestamp (in seconds) when the quote expires. + /// @dev This gets checked against block.timestamp. + uint256 deadlineTimestamp; + /// @notice The nonce used by this effectiveTrader. Nonces are used to protect against replay. + uint256 nonce; + /// @notice Unique identifier for the quote. + /// @dev Generated off-chain via a distributed UUID generator. + bytes16 quoteId; + /// @dev false if this quote is for the 1st hop of a multi-hop or a single-hop, in which case msg.sender is the payer. + /// true if this quote is for 2nd or later hop of a multi-hop, in which case router is the payer. + bool multiHop; + /// @notice Signature provided by the market maker (EIP-191). + bytes signature; + WidgetFee widgetFee; + bytes widgetFeeSignature; + /// @notice not used for RFQ flow, only for external swaps + bytes externalSwapCalldata; + /// @notice not used for RFQ flow, only for external swaps for slippage check + uint amountOutMinimum; + } + + function tradeRFQT(RFQTQuote memory quote) external payable; + + event SignerUpdated(address signer, bool value); + event OwnerSet(address owner); + event TreasurySet(address treasury); + event PostTradeCallbackSet(bool value); + event PauseSet(bool value); + event RfqTrade( + address recipient, + address sellerToken, + address buyerToken, + uint256 sellerTokenAmount, + uint256 buyerTokenAmount, + bytes16 quoteId, + address signer + ); + + error InvalidNewImplementation(); + error CallerNotFactory(); + error CallerNotRouter(); + error CallerNotOwner(); + error CallerNotPendingOwner(); + error ZeroOrEmptyInput(); + error TradePaused(); + error NonceUsed(); + error InvalidSigner(); + error InvalidSignature(); + error Overflow(); +} diff --git a/contracts/8/interfaces/IUni.sol b/contracts/8/interfaces/IUni.sol index 210c820..10bdaf7 100644 --- a/contracts/8/interfaces/IUni.sol +++ b/contracts/8/interfaces/IUni.sol @@ -30,4 +30,6 @@ interface IUni { function token0() external view returns (address); function token1() external view returns (address); + + function sync() external; } diff --git a/contracts/8/libraries/CommissionLib.sol b/contracts/8/libraries/CommissionLib.sol index 63387d1..2065c51 100644 --- a/contracts/8/libraries/CommissionLib.sol +++ b/contracts/8/libraries/CommissionLib.sol @@ -108,6 +108,7 @@ abstract contract CommissionLib is CommonUtils { function _doCommissionFromToken( CommissionInfo memory commissionInfo, + address payer, address receiver, uint256 inputAmount ) internal returns (address, uint256) { @@ -161,7 +162,7 @@ abstract contract CommissionLib is CommonUtils { 0x0a5ea46600000000000000000000000000000000000000000000000000000000 ) // claimTokens mstore(add(freePtr, 0x04), token) - mstore(add(freePtr, 0x24), caller()) + mstore(add(freePtr, 0x24), payer) mstore(add(freePtr, 0x44), referer) mstore(add(freePtr, 0x64), amount) let success := call( diff --git a/contracts/8/libraries/CommonUtils.sol b/contracts/8/libraries/CommonUtils.sol index d4b42f4..835da4c 100644 --- a/contracts/8/libraries/CommonUtils.sol +++ b/contracts/8/libraries/CommonUtils.sol @@ -3,70 +3,82 @@ pragma solidity ^0.8.0; /// @title Base contract with common permit handling logics abstract contract CommonUtils { + address internal constant _ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; - address internal constant _ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + uint256 internal constant _ADDRESS_MASK = + 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff; + uint256 internal constant _REVERSE_MASK = + 0x8000000000000000000000000000000000000000000000000000000000000000; + uint256 internal constant _ORDER_ID_MASK = + 0xffffffffffffffffffffffff0000000000000000000000000000000000000000; + uint256 internal constant _WEIGHT_MASK = + 0x00000000000000000000ffff0000000000000000000000000000000000000000; + uint256 internal constant _CALL_GAS_LIMIT = 5000; + uint256 internal constant ORIGIN_PAYER = + 0x3ca20afc2ccc0000000000000000000000000000000000000000000000000000; - uint256 internal constant _ADDRESS_MASK = 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff; - uint256 internal constant _REVERSE_MASK = 0x8000000000000000000000000000000000000000000000000000000000000000; - uint256 internal constant _ORDER_ID_MASK = 0xffffffffffffffffffffffff0000000000000000000000000000000000000000; - uint256 internal constant _WEIGHT_MASK = 0x00000000000000000000ffff0000000000000000000000000000000000000000; - uint256 internal constant _CALL_GAS_LIMIT = 5000; + /// @dev WETH address is network-specific and needs to be changed before deployment. + /// It can not be moved to immutable as immutables are not supported in assembly + // ETH: C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 + // BSC: bb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c + // OEC: 8f8526dbfd6e38e3d8307702ca8469bae6c56c15 + // LOCAL: 5FbDB2315678afecb367f032d93F642f64180aa3 + // LOCAL2: 02121128f1Ed0AdA5Df3a87f42752fcE4Ad63e59 + // POLYGON: 0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270 + // AVAX: B31f66AA3C1e785363F0875A1B74E27b85FD66c7 + // FTM: 21be370D5312f44cB42ce377BC9b8a0cEF1A4C83 + // ARB: 82aF49447D8a07e3bd95BD0d56f35241523fBab1 + // OP: 4200000000000000000000000000000000000006 + // CRO: 5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23 + // CFX: 14b2D3bC65e74DAE1030EAFd8ac30c533c976A9b + // POLYZK 4F9A0e7FD2Bf6067db6994CF12E4495Df938E6e9 + address public constant _WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + // address public constant _WETH = 0x5FbDB2315678afecb367f032d93F642f64180aa3; // hardhat1 + // address public constant _WETH = 0x707531c9999AaeF9232C8FEfBA31FBa4cB78d84a; // hardhat2 - /// @dev WETH address is network-specific and needs to be changed before deployment. - /// It can not be moved to immutable as immutables are not supported in assembly - // ETH: C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 - // BSC: bb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c - // OEC: 8f8526dbfd6e38e3d8307702ca8469bae6c56c15 - // LOCAL: 5FbDB2315678afecb367f032d93F642f64180aa3 - // LOCAL2: 02121128f1Ed0AdA5Df3a87f42752fcE4Ad63e59 - // POLYGON: 0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270 - // AVAX: B31f66AA3C1e785363F0875A1B74E27b85FD66c7 - // FTM: 21be370D5312f44cB42ce377BC9b8a0cEF1A4C83 - // ARB: 82aF49447D8a07e3bd95BD0d56f35241523fBab1 - // OP: 4200000000000000000000000000000000000006 - // CRO: 5C7F8A570d578ED84E63fdFA7b1eE72dEae1AE23 - // CFX: 14b2D3bC65e74DAE1030EAFd8ac30c533c976A9b - // POLYZK 4F9A0e7FD2Bf6067db6994CF12E4495Df938E6e9 - address public constant _WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - // address public constant _WETH = 0x5FbDB2315678afecb367f032d93F642f64180aa3; // hardhat1 - // address public constant _WETH = 0x707531c9999AaeF9232C8FEfBA31FBa4cB78d84a; // hardhat2 + // ETH: 70cBb871E8f30Fc8Ce23609E9E0Ea87B6b222F58 + // ETH-DEV:02D0131E5Cc86766e234EbF1eBe33444443b98a3 + // BSC: d99cAE3FAC551f6b6Ba7B9f19bDD316951eeEE98 + // OEC: E9BBD6eC0c9Ca71d3DcCD1282EE9de4F811E50aF + // LOCAL: e7f1725E7734CE288F8367e1Bb143E90bb3F0512 + // LOCAL2: 95D7fF1684a8F2e202097F28Dc2e56F773A55D02 + // POLYGON: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f + // AVAX: 70cBb871E8f30Fc8Ce23609E9E0Ea87B6b222F58 + // FTM: E9BBD6eC0c9Ca71d3DcCD1282EE9de4F811E50aF + // ARB: E9BBD6eC0c9Ca71d3DcCD1282EE9de4F811E50aF + // OP: 100F3f74125C8c724C7C0eE81E4dd5626830dD9a + // CRO: E9BBD6eC0c9Ca71d3DcCD1282EE9de4F811E50aF + // CFX: 100F3f74125C8c724C7C0eE81E4dd5626830dD9a + // POLYZK 1b5d39419C268b76Db06DE49e38B010fbFB5e226 + address public constant _APPROVE_PROXY = + 0x70cBb871E8f30Fc8Ce23609E9E0Ea87B6b222F58; + // address public constant _APPROVE_PROXY = 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512; // hardhat1 + // address public constant _APPROVE_PROXY = 0x2538a10b7fFb1B78c890c870FC152b10be121f04; // hardhat2 + // ETH: 5703B683c7F928b721CA95Da988d73a3299d4757 + // BSC: 0B5f474ad0e3f7ef629BD10dbf9e4a8Fd60d9A48 + // OEC: d99cAE3FAC551f6b6Ba7B9f19bDD316951eeEE98 + // LOCAL: D49a0e9A4CD5979aE36840f542D2d7f02C4817Be + // LOCAL2: 11457D5b1025D162F3d9B7dBeab6E1fBca20e043 + // POLYGON: f332761c673b59B21fF6dfa8adA44d78c12dEF09 + // AVAX: 3B86917369B83a6892f553609F3c2F439C184e31 + // FTM: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f + // ARB: d99cAE3FAC551f6b6Ba7B9f19bDD316951eeEE98 + // OP: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f + // CRO: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f + // CFX: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f + // POLYZK d2F0aC2012C8433F235c8e5e97F2368197DD06C7 + address public constant _WNATIVE_RELAY = + 0x5703B683c7F928b721CA95Da988d73a3299d4757; + // address public constant _WNATIVE_RELAY = 0x0B306BF915C4d645ff596e518fAf3F9669b97016; // hardhat1 + // address public constant _WNATIVE_RELAY = 0x6A47346e722937B60Df7a1149168c0E76DD6520f; // hardhat2 - // ETH: 70cBb871E8f30Fc8Ce23609E9E0Ea87B6b222F58 - // ETH-DEV:02D0131E5Cc86766e234EbF1eBe33444443b98a3 - // BSC: d99cAE3FAC551f6b6Ba7B9f19bDD316951eeEE98 - // OEC: E9BBD6eC0c9Ca71d3DcCD1282EE9de4F811E50aF - // LOCAL: e7f1725E7734CE288F8367e1Bb143E90bb3F0512 - // LOCAL2: 95D7fF1684a8F2e202097F28Dc2e56F773A55D02 - // POLYGON: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f - // AVAX: 70cBb871E8f30Fc8Ce23609E9E0Ea87B6b222F58 - // FTM: E9BBD6eC0c9Ca71d3DcCD1282EE9de4F811E50aF - // ARB: E9BBD6eC0c9Ca71d3DcCD1282EE9de4F811E50aF - // OP: 100F3f74125C8c724C7C0eE81E4dd5626830dD9a - // CRO: E9BBD6eC0c9Ca71d3DcCD1282EE9de4F811E50aF - // CFX: 100F3f74125C8c724C7C0eE81E4dd5626830dD9a - // POLYZK 1b5d39419C268b76Db06DE49e38B010fbFB5e226 - address public constant _APPROVE_PROXY = 0x70cBb871E8f30Fc8Ce23609E9E0Ea87B6b222F58; - // address public constant _APPROVE_PROXY = 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512; // hardhat1 - // address public constant _APPROVE_PROXY = 0x2538a10b7fFb1B78c890c870FC152b10be121f04; // hardhat2 - - // ETH: 5703B683c7F928b721CA95Da988d73a3299d4757 - // BSC: 0B5f474ad0e3f7ef629BD10dbf9e4a8Fd60d9A48 - // OEC: d99cAE3FAC551f6b6Ba7B9f19bDD316951eeEE98 - // LOCAL: D49a0e9A4CD5979aE36840f542D2d7f02C4817Be - // LOCAL2: 11457D5b1025D162F3d9B7dBeab6E1fBca20e043 - // POLYGON: f332761c673b59B21fF6dfa8adA44d78c12dEF09 - // AVAX: 3B86917369B83a6892f553609F3c2F439C184e31 - // FTM: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f - // ARB: d99cAE3FAC551f6b6Ba7B9f19bDD316951eeEE98 - // OP: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f - // CRO: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f - // CFX: 40aA958dd87FC8305b97f2BA922CDdCa374bcD7f - // POLYZK d2F0aC2012C8433F235c8e5e97F2368197DD06C7 - address public constant _WNATIVE_RELAY = 0x5703B683c7F928b721CA95Da988d73a3299d4757; - // address public constant _WNATIVE_RELAY = 0x0B306BF915C4d645ff596e518fAf3F9669b97016; // hardhat1 - // address public constant _WNATIVE_RELAY = 0x6A47346e722937B60Df7a1149168c0E76DD6520f; // hardhat2 - - event OrderRecord(address fromToken, address toToken, address sender, uint256 fromAmount, uint256 returnAmount); - event SwapOrderId(uint256 id); + event OrderRecord( + address fromToken, + address toToken, + address sender, + uint256 fromAmount, + uint256 returnAmount + ); + event SwapOrderId(uint256 id); }