From 678fff4a9981443c473bb005a46d9ad9d4a84f56 Mon Sep 17 00:00:00 2001 From: david_tsai <108356037@nccu.edu.tw> Date: Tue, 5 Sep 2023 09:56:37 +0800 Subject: [PATCH 1/4] add WETH option design for maker --- contracts/RFQ.sol | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/contracts/RFQ.sol b/contracts/RFQ.sol index a6865147..b9bfdc3b 100644 --- a/contracts/RFQ.sol +++ b/contracts/RFQ.sol @@ -19,6 +19,7 @@ contract RFQ is IRFQ, Ownable, TokenCollector, EIP712 { uint256 private constant FLG_ALLOW_CONTRACT_SENDER = 1 << 255; uint256 private constant FLG_ALLOW_PARTIAL_FILL = 1 << 254; + uint256 private constant FLG_MAKER_RECEIVES_WETH = 1 << 253; IWETH public immutable weth; address payable public feeCollector; @@ -117,12 +118,16 @@ contract RFQ is IRFQ, Ownable, TokenCollector, EIP712 { // transfer takerToken to maker if (_rfqOffer.takerToken.isETH()) { if (msg.value != _rfqTx.takerRequestAmount) revert InvalidMsgValue(); - Address.sendValue(_rfqOffer.maker, _rfqTx.takerRequestAmount); + _collecETHAndSend(_rfqOffer.maker, _rfqTx.takerRequestAmount, _takerTokenPermit, ((_rfqOffer.flags & FLG_MAKER_RECEIVES_WETH) != 0)); } else if (_rfqOffer.takerToken == address(weth)) { if (msg.value != 0) revert InvalidMsgValue(); - _collect(_rfqOffer.takerToken, _rfqOffer.taker, address(this), _rfqTx.takerRequestAmount, _takerTokenPermit); - weth.withdraw(_rfqTx.takerRequestAmount); - Address.sendValue(_rfqOffer.maker, _rfqTx.takerRequestAmount); + _collecWETHAndSend( + _rfqOffer.taker, + _rfqOffer.maker, + _rfqTx.takerRequestAmount, + _takerTokenPermit, + ((_rfqOffer.flags & FLG_MAKER_RECEIVES_WETH) != 0) + ); } else { if (msg.value != 0) revert InvalidMsgValue(); _collect(_rfqOffer.takerToken, _rfqOffer.taker, _rfqOffer.maker, _rfqTx.takerRequestAmount, _takerTokenPermit); @@ -174,4 +179,25 @@ contract RFQ is IRFQ, Ownable, TokenCollector, EIP712 { fee ); } + + // Only used when taker token is ETH + function _collecETHAndSend(address to, uint256 amount, bytes calldata data, bool makerReceivesWETH) internal { + if (makerReceivesWETH) { + weth.deposit{ value: amount }(); + weth.transfer(to, amount); // NOTE: data not used, is this an issue? + } else { + Address.sendValue(payable(to), amount); + } + } + + // Only used when taker token is WETH + function _collecWETHAndSend(address from, address to, uint256 amount, bytes calldata data, bool makerReceivesWETH) internal { + if (makerReceivesWETH) { + _collect(address(weth), from, to, amount, data); + } else { + _collect(address(weth), from, address(this), amount, data); + weth.withdraw(amount); + Address.sendValue(payable(to), amount); + } + } } From 154aa2343fa1e3e51416048a9e77efee883e31fb Mon Sep 17 00:00:00 2001 From: david_tsai <108356037@nccu.edu.tw> Date: Tue, 5 Sep 2023 10:05:27 +0800 Subject: [PATCH 2/4] remove unused param in _collecETHAndSend --- contracts/RFQ.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/RFQ.sol b/contracts/RFQ.sol index b9bfdc3b..249fed67 100644 --- a/contracts/RFQ.sol +++ b/contracts/RFQ.sol @@ -118,7 +118,7 @@ contract RFQ is IRFQ, Ownable, TokenCollector, EIP712 { // transfer takerToken to maker if (_rfqOffer.takerToken.isETH()) { if (msg.value != _rfqTx.takerRequestAmount) revert InvalidMsgValue(); - _collecETHAndSend(_rfqOffer.maker, _rfqTx.takerRequestAmount, _takerTokenPermit, ((_rfqOffer.flags & FLG_MAKER_RECEIVES_WETH) != 0)); + _collecETHAndSend(_rfqOffer.maker, _rfqTx.takerRequestAmount, ((_rfqOffer.flags & FLG_MAKER_RECEIVES_WETH) != 0)); } else if (_rfqOffer.takerToken == address(weth)) { if (msg.value != 0) revert InvalidMsgValue(); _collecWETHAndSend( @@ -181,7 +181,7 @@ contract RFQ is IRFQ, Ownable, TokenCollector, EIP712 { } // Only used when taker token is ETH - function _collecETHAndSend(address to, uint256 amount, bytes calldata data, bool makerReceivesWETH) internal { + function _collecETHAndSend(address to, uint256 amount, bool makerReceivesWETH) internal { if (makerReceivesWETH) { weth.deposit{ value: amount }(); weth.transfer(to, amount); // NOTE: data not used, is this an issue? From adbc3b206fb0d36b58a955c14e0ce2f7095b84a5 Mon Sep 17 00:00:00 2001 From: david_tsai <108356037@nccu.edu.tw> Date: Tue, 5 Sep 2023 13:49:11 +0800 Subject: [PATCH 3/4] add test case for maker recieving WETH --- test/forkMainnet/RFQ.t.sol | 78 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/test/forkMainnet/RFQ.t.sol b/test/forkMainnet/RFQ.t.sol index 33710ca5..4e40e6e8 100644 --- a/test/forkMainnet/RFQ.t.sol +++ b/test/forkMainnet/RFQ.t.sol @@ -25,6 +25,7 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { uint256 private constant FLG_ALLOW_CONTRACT_SENDER = 1 << 255; uint256 private constant FLG_ALLOW_PARTIAL_FILL = 1 << 254; + uint256 private constant FLG_MAKER_RECEIVES_WETH = 1 << 253; event FilledRFQ( bytes32 indexed rfqOfferHash, @@ -242,6 +243,44 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { fcMakerToken.assertChange(int256(fee)); } + function testFillRFQWithRawETHAndRecieveWETH() public { + // case : taker token is ETH + RFQOffer memory rfqOffer = defaultRFQOffer; + rfqOffer.takerToken = Constant.ZERO_ADDRESS; + rfqOffer.takerTokenAmount = 1 ether; + rfqOffer.flags |= FLG_MAKER_RECEIVES_WETH; + + bytes memory makerSig = signRFQOffer(makerSignerPrivateKey, rfqOffer, address(rfq)); + + Snapshot memory takerTakerToken = BalanceSnapshot.take({ owner: rfqOffer.taker, token: rfqOffer.takerToken }); + Snapshot memory takerMakerToken = BalanceSnapshot.take({ owner: rfqOffer.taker, token: rfqOffer.makerToken }); + // maker should receive WETH + Snapshot memory makerWETHToken = BalanceSnapshot.take({ owner: rfqOffer.maker, token: address(weth) }); + Snapshot memory makerMakerToken = BalanceSnapshot.take({ owner: rfqOffer.maker, token: rfqOffer.makerToken }); + Snapshot memory recTakerToken = BalanceSnapshot.take({ owner: recipient, token: rfqOffer.takerToken }); + Snapshot memory recMakerToken = BalanceSnapshot.take({ owner: recipient, token: rfqOffer.makerToken }); + Snapshot memory fcMakerToken = BalanceSnapshot.take({ owner: feeCollector, token: rfqOffer.makerToken }); + + uint256 fee = (rfqOffer.makerTokenAmount * defaultFeeFactor) / Constant.BPS_MAX; + uint256 amountAfterFee = rfqOffer.makerTokenAmount - fee; + + RFQTx memory rfqTx = defaultRFQTx; + rfqTx.rfqOffer = rfqOffer; + rfqTx.takerRequestAmount = rfqOffer.takerTokenAmount; + + vm.prank(rfqOffer.taker, rfqOffer.taker); + rfq.fillRFQ{ value: rfqOffer.takerTokenAmount }(rfqTx, makerSig, defaultMakerPermit, defaultTakerPermit); + + takerTakerToken.assertChange(-int256(rfqOffer.takerTokenAmount)); + takerMakerToken.assertChange(int256(0)); + makerWETHToken.assertChange(int256(rfqOffer.takerTokenAmount)); + makerMakerToken.assertChange(-int256(rfqOffer.makerTokenAmount)); + recTakerToken.assertChange(int256(0)); + // recipient gets less than original makerTokenAmount because of the fee + recMakerToken.assertChange(int256(amountAfterFee)); + fcMakerToken.assertChange(int256(fee)); + } + function testFillRFQTakerGetRawETH() public { // case : maker token is WETH RFQOffer memory rfqOffer = defaultRFQOffer; @@ -316,6 +355,45 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { fcMakerToken.assertChange(int256(fee)); } + function testFillRFQWithWETHAndRecieveWETH() public { + // case : taker token is WETH + RFQOffer memory rfqOffer = defaultRFQOffer; + rfqOffer.takerToken = WETH_ADDRESS; + rfqOffer.takerTokenAmount = 1 ether; + rfqOffer.flags |= FLG_MAKER_RECEIVES_WETH; + + bytes memory makerSig = signRFQOffer(makerSignerPrivateKey, rfqOffer, address(rfq)); + + Snapshot memory takerTakerToken = BalanceSnapshot.take({ owner: rfqOffer.taker, token: rfqOffer.takerToken }); + Snapshot memory takerMakerToken = BalanceSnapshot.take({ owner: rfqOffer.taker, token: rfqOffer.makerToken }); + // maker should receive WETH + Snapshot memory makerWETHToken = BalanceSnapshot.take({ owner: rfqOffer.maker, token: address(weth) }); + Snapshot memory makerMakerToken = BalanceSnapshot.take({ owner: rfqOffer.maker, token: rfqOffer.makerToken }); + Snapshot memory recTakerToken = BalanceSnapshot.take({ owner: recipient, token: rfqOffer.takerToken }); + Snapshot memory recMakerToken = BalanceSnapshot.take({ owner: recipient, token: rfqOffer.makerToken }); + Snapshot memory fcMakerToken = BalanceSnapshot.take({ owner: feeCollector, token: rfqOffer.makerToken }); + + uint256 fee = (rfqOffer.makerTokenAmount * defaultFeeFactor) / Constant.BPS_MAX; + uint256 amountAfterFee = rfqOffer.makerTokenAmount - fee; + + RFQTx memory rfqTx = defaultRFQTx; + rfqTx.rfqOffer = rfqOffer; + rfqTx.takerRequestAmount = rfqOffer.takerTokenAmount; + + bytes memory takerPermit = getTokenlonPermit2Data(taker, takerPrivateKey, rfqOffer.takerToken, address(rfq)); + + vm.prank(rfqOffer.taker, rfqOffer.taker); + rfq.fillRFQ(rfqTx, makerSig, defaultMakerPermit, takerPermit); + + takerTakerToken.assertChange(-int256(rfqOffer.takerTokenAmount)); + takerMakerToken.assertChange(int256(0)); + makerWETHToken.assertChange(int256(rfqOffer.takerTokenAmount)); + makerMakerToken.assertChange(-int256(rfqOffer.makerTokenAmount)); + recTakerToken.assertChange(int256(0)); + recMakerToken.assertChange(int256(amountAfterFee)); + fcMakerToken.assertChange(int256(fee)); + } + function testFillWithContract() public { RFQOffer memory rfqOffer = defaultRFQOffer; rfqOffer.flags |= FLG_ALLOW_CONTRACT_SENDER; From e74eee6e0d3b83b86c8718f75d9b90907ee1bda9 Mon Sep 17 00:00:00 2001 From: david_tsai <108356037@nccu.edu.tw> Date: Tue, 5 Sep 2023 14:21:01 +0800 Subject: [PATCH 4/4] remove comments & add payable param --- contracts/RFQ.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contracts/RFQ.sol b/contracts/RFQ.sol index 249fed67..2c75520c 100644 --- a/contracts/RFQ.sol +++ b/contracts/RFQ.sol @@ -181,23 +181,23 @@ contract RFQ is IRFQ, Ownable, TokenCollector, EIP712 { } // Only used when taker token is ETH - function _collecETHAndSend(address to, uint256 amount, bool makerReceivesWETH) internal { + function _collecETHAndSend(address payable to, uint256 amount, bool makerReceivesWETH) internal { if (makerReceivesWETH) { weth.deposit{ value: amount }(); - weth.transfer(to, amount); // NOTE: data not used, is this an issue? + weth.transfer(to, amount); } else { - Address.sendValue(payable(to), amount); + Address.sendValue(to, amount); } } // Only used when taker token is WETH - function _collecWETHAndSend(address from, address to, uint256 amount, bytes calldata data, bool makerReceivesWETH) internal { + function _collecWETHAndSend(address from, address payable to, uint256 amount, bytes calldata data, bool makerReceivesWETH) internal { if (makerReceivesWETH) { _collect(address(weth), from, to, amount, data); } else { _collect(address(weth), from, address(this), amount, data); weth.withdraw(amount); - Address.sendValue(payable(to), amount); + Address.sendValue(to, amount); } } }