From 9089d075b63325dd8ad72e5dd211d2f1d0104879 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Thu, 4 Jan 2024 16:07:18 +1000 Subject: [PATCH 01/13] Basic sell unit test --- script/Counter.s.sol | 12 --- test/unit/AllowanceHolderUnitTest.t.sol | 59 ++++----------- test/unit/Utils.sol | 39 ++++++++++ test/unit/core/BasicUnitTest.t.sol | 97 +++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 58 deletions(-) delete mode 100644 script/Counter.s.sol create mode 100644 test/unit/Utils.sol create mode 100644 test/unit/core/BasicUnitTest.t.sol diff --git a/script/Counter.s.sol b/script/Counter.s.sol deleted file mode 100644 index 0e546aba3..000000000 --- a/script/Counter.s.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import "forge-std/Script.sol"; - -contract CounterScript is Script { - function setUp() public {} - - function run() public { - vm.broadcast(); - } -} diff --git a/test/unit/AllowanceHolderUnitTest.t.sol b/test/unit/AllowanceHolderUnitTest.t.sol index f4d8e5b21..c3eeb2c32 100644 --- a/test/unit/AllowanceHolderUnitTest.t.sol +++ b/test/unit/AllowanceHolderUnitTest.t.sol @@ -6,6 +6,8 @@ import {IAllowanceHolder} from "../../src/IAllowanceHolder.sol"; import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol"; +import {Utils} from "./Utils.sol"; + import {Test} from "forge-std/Test.sol"; import {VmSafe} from "forge-std/Vm.sol"; @@ -19,16 +21,12 @@ contract AllowanceHolderDummy is AllowanceHolder { } } -contract FallbackDummy { - fallback() external payable {} -} - -contract AllowanceHolderUnitTest is Test { +contract AllowanceHolderUnitTest is Utils, Test { AllowanceHolderDummy ah; - address OPERATOR = address(0x01); - address TOKEN = address(0x02); + address OPERATOR = _deterministicAddress("OPERATOR"); + address TOKEN = _deterministicAddress("TOKEN"); address OWNER = address(this); - address RECIPIENT = address(0); + address RECIPIENT = _deterministicAddress("RECIPIENT"); uint256 AMOUNT = 123456; function setUp() public { @@ -41,7 +39,7 @@ contract AllowanceHolderUnitTest is Test { } function testPermitAuthorised() public { - address token = address(new FallbackDummy()); + address token = _createNamedDummy("TOKEN"); address operator = address(this); ah.setAllowed(operator, OWNER, token, AMOUNT); @@ -54,7 +52,7 @@ contract AllowanceHolderUnitTest is Test { } function testPermitAuthorisedMultipleConsumption() public { - address token = address(new FallbackDummy()); + address token = _createNamedDummy("TOKEN"); address operator = address(this); ah.setAllowed(operator, OWNER, token, AMOUNT); @@ -78,7 +76,7 @@ contract AllowanceHolderUnitTest is Test { } function testPermitUnauthorisedAmount() public { - address token = address(new FallbackDummy()); + address token = _createNamedDummy("TOKEN"); address operator = address(this); ah.setAllowed(operator, OWNER, token, AMOUNT); @@ -90,7 +88,7 @@ contract AllowanceHolderUnitTest is Test { } function testPermitUnauthorisedToken() public { - address token = address(new FallbackDummy()); + address token = _createNamedDummy("TOKEN"); address operator = address(this); ah.setAllowed(operator, OWNER, token, AMOUNT); @@ -121,8 +119,8 @@ contract AllowanceHolderUnitTest is Test { } function testPermitExecute() public { - address token = address(new FallbackDummy()); - address target = address(new FallbackDummy()); + address token = _createNamedDummy("TOKEN"); + address target = _createNamedRejectionDummy("TARGET"); address operator = target; uint256 value = 999; @@ -130,38 +128,7 @@ contract AllowanceHolderUnitTest is Test { permits[0] = ISignatureTransfer.TokenPermissions({token: token, amount: AMOUNT}); bytes memory data = hex"deadbeef"; - vm.startStateDiffRecording(); + _mockExpectCall(address(target), abi.encodePacked(data, address(this)), abi.encode(true)); ah.execute{value: value}(operator, permits, payable(target), data); - VmSafe.AccountAccess[] memory calls = - _foundry_filterAccessKind(vm.stopAndReturnStateDiff(), VmSafe.AccountAccessKind.Call); - - // First Call is to AllowanceHolder with the `execute` calldata - // Second Call is to the Target with the `data` - // We test that the msg.sender is passed along appended to `data` - assertEq(calls[1].account, target); - assertEq(calls[1].data, abi.encodePacked(data, address(this))); - assertEq(calls[1].value, value); - } - - /// @dev Utility to filter the AccountAccess[] to just the particular kind we want - function _foundry_filterAccessKind(VmSafe.AccountAccess[] memory accesses, VmSafe.AccountAccessKind kind) - public - pure - returns (VmSafe.AccountAccess[] memory filtered) - { - filtered = new VmSafe.AccountAccess[](accesses.length); - uint256 count = 0; - - for (uint256 i = 0; i < accesses.length; i++) { - if (accesses[i].kind == kind) { - filtered[count] = accesses[i]; - count++; - } - } - - assembly { - // Resize the array - mstore(filtered, count) - } } } diff --git a/test/unit/Utils.sol b/test/unit/Utils.sol new file mode 100644 index 000000000..621022d4e --- /dev/null +++ b/test/unit/Utils.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +import {Test} from "forge-std/Test.sol"; +import {Vm, VmSafe} from "forge-std/Vm.sol"; + +contract FallbackDummy { + fallback() external payable {} +} + +contract RejectionFallbackDummy { + fallback() external payable { + require(false, "Rejected"); + } +} + +contract Utils { + Vm internal constant _vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + function _deterministicAddress(string memory name) internal returns (address a) { + a = address(bytes20(keccak256(abi.encodePacked(name)))); + _vm.label(a, name); + } + + function _createNamedDummy(string memory name) internal returns (address a) { + a = address(new FallbackDummy()); + _vm.label(a, name); + } + + function _createNamedRejectionDummy(string memory name) internal returns (address a) { + a = address(new RejectionFallbackDummy()); + _vm.label(a, name); + } + + function _mockExpectCall(address callee, bytes memory data, bytes memory returnData) internal { + _vm.mockCall(callee, data, returnData); + _vm.expectCall(callee, data); + } +} diff --git a/test/unit/core/BasicUnitTest.t.sol b/test/unit/core/BasicUnitTest.t.sol new file mode 100644 index 000000000..4472da6bc --- /dev/null +++ b/test/unit/core/BasicUnitTest.t.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +import {Basic} from "../../../src/core/Basic.sol"; +import {Permit2Payment} from "../../../src/core/Permit2Payment.sol"; + +import {IERC20} from "../../../src/IERC20.sol"; +import {Utils} from "../Utils.sol"; + +import {Test} from "forge-std/Test.sol"; +import {VmSafe} from "forge-std/Vm.sol"; + +contract BasicDummy is Basic, Permit2Payment { + constructor(address permit2, address feeRecipient, address allowanceHolder) + Permit2Payment(permit2, feeRecipient, allowanceHolder) + {} + + function sellToPool(address pool, IERC20 sellToken, uint256 bips, uint256 offset, bytes memory data) public { + super.basicSellToPool(pool, sellToken, bips, offset, data); + } +} + +contract BasicUnitTest is Utils, Test { + BasicDummy basic; + address PERMIT2 = _deterministicAddress("PERMIT2"); + address FEE_RECIPIENT = _deterministicAddress("FEE_RECIPIENT"); + address ALLOWANCE_HOLDER = _deterministicAddress("ALLOWANCE_HOLDER"); + address POOL = _createNamedRejectionDummy("POOL"); + IERC20 TOKEN = IERC20(_createNamedRejectionDummy("TOKEN")); + + function setUp() public { + basic = new BasicDummy(PERMIT2, FEE_RECIPIENT, ALLOWANCE_HOLDER); + } + + function testBasicSell() public { + uint256 bips = 10_000; + uint256 offset = 4; + uint256 amount = 99999; + bytes4 selector = bytes4(hex"12345678"); + bytes memory data = abi.encodePacked(selector, amount); + + _mockExpectCall( + address(TOKEN), abi.encodeWithSelector(IERC20.balanceOf.selector, address(basic)), abi.encode(amount) + ); + _mockExpectCall( + address(TOKEN), + abi.encodeWithSelector(IERC20.allowance.selector, address(basic), address(POOL)), + abi.encode(amount) + ); + + _mockExpectCall(address(POOL), data, abi.encode(true)); + + basic.sellToPool(POOL, TOKEN, bips, offset, data); + } + + /// @dev adjust the balange of the contract to be less than expected + function testBasicSellLowerBalanceAmount() public { + uint256 bips = 10_000; + uint256 offset = 4; + uint256 amount = 99999; + bytes4 selector = bytes4(hex"12345678"); + bytes memory data = abi.encodePacked(selector, amount); + + _mockExpectCall( + address(TOKEN), abi.encodeWithSelector(IERC20.balanceOf.selector, address(basic)), abi.encode(amount / 2) + ); + _mockExpectCall( + address(TOKEN), + abi.encodeWithSelector(IERC20.allowance.selector, address(basic), address(POOL)), + abi.encode(amount) + ); + + _mockExpectCall(address(POOL), abi.encodePacked(selector, amount / 2), abi.encode(true)); + basic.sellToPool(POOL, TOKEN, bips, offset, data); + } + + /// @dev adjust the balange of the contract to be greater than expected + function testBasicSellGreaterBalanceAmount() public { + uint256 bips = 10_000; + uint256 offset = 4; + uint256 amount = 99999; + bytes4 selector = bytes4(hex"12345678"); + bytes memory data = abi.encodePacked(selector, amount); + + _mockExpectCall( + address(TOKEN), abi.encodeWithSelector(IERC20.balanceOf.selector, address(basic)), abi.encode(amount * 2) + ); + _mockExpectCall( + address(TOKEN), + abi.encodeWithSelector(IERC20.allowance.selector, address(basic), address(POOL)), + abi.encode(amount * 2) + ); + + _mockExpectCall(address(POOL), abi.encodePacked(selector, amount * 2), abi.encode(true)); + basic.sellToPool(POOL, TOKEN, bips, offset, data); + } +} From 2655ec84c3b2ee0c368674450dfa7400407349e6 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Fri, 5 Jan 2024 08:16:02 +1000 Subject: [PATCH 02/13] additional native asset tests in Basic --- test/unit/Utils.sol | 5 ++ test/unit/core/BasicUnitTest.t.sol | 100 +++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/test/unit/Utils.sol b/test/unit/Utils.sol index 621022d4e..3e199981f 100644 --- a/test/unit/Utils.sol +++ b/test/unit/Utils.sol @@ -36,4 +36,9 @@ contract Utils { _vm.mockCall(callee, data, returnData); _vm.expectCall(callee, data); } + + function _mockExpectCall(address callee, uint256 value, bytes memory data, bytes memory returnData) internal { + _vm.mockCall(callee, value, data, returnData); + _vm.expectCall(callee, value, data); + } } diff --git a/test/unit/core/BasicUnitTest.t.sol b/test/unit/core/BasicUnitTest.t.sol index 4472da6bc..8219a5944 100644 --- a/test/unit/core/BasicUnitTest.t.sol +++ b/test/unit/core/BasicUnitTest.t.sol @@ -94,4 +94,104 @@ contract BasicUnitTest is Utils, Test { _mockExpectCall(address(POOL), abi.encodePacked(selector, amount * 2), abi.encode(true)); basic.sellToPool(POOL, TOKEN, bips, offset, data); } + + /// @dev When 0xeeee (native asset) is used we expect it to transfer as value + function testBasicSellEthValue() public { + uint256 bips = 10_000; + uint256 offset = 4; + uint256 amount = 99999; + uint256 value = amount; + bytes4 selector = bytes4(hex"12345678"); + bytes memory data = abi.encodePacked(selector, amount); + + _mockExpectCall(address(POOL), value, abi.encodePacked(selector, amount), abi.encode(true)); + + vm.deal(address(basic), value); + basic.sellToPool(POOL, IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE), bips, offset, data); + } + + /// @dev When 0xeeee (native asset) is used we expect it to transfer as value and adjust for the current balance if lower + function testBasicSellLowerEthValue() public { + uint256 bips = 10_000; + uint256 offset = 4; + uint256 amount = 99999; + uint256 value = amount / 2; + bytes4 selector = bytes4(hex"12345678"); + bytes memory data = abi.encodePacked(selector, amount); + + _mockExpectCall(address(POOL), value, abi.encodePacked(selector, value), abi.encode(true)); + + vm.deal(address(basic), value); + basic.sellToPool(POOL, IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE), bips, offset, data); + } + + /// @dev When 0xeeee (native asset) is used we expect it to transfer as value and adjust for the current balance if greater + function testBasicSellGreaterEthValue() public { + uint256 bips = 10_000; + uint256 offset = 4; + uint256 amount = 99999; + uint256 value = amount * 2; + bytes4 selector = bytes4(hex"12345678"); + bytes memory data = abi.encodePacked(selector, amount); + + _mockExpectCall(address(POOL), value, abi.encodePacked(selector, value), abi.encode(true)); + + vm.deal(address(basic), value); + basic.sellToPool(POOL, IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE), bips, offset, data); + } + + /// @dev When 0xeeee (native asset) is used we expect it to transfer as value and adjust for the current balance + function testBasicSellAdjustedEthValue() public { + uint256 bips = 5_000; // sell half + uint256 offset = 4; + uint256 amount = 99999; + uint256 value = amount * 2; + bytes4 selector = bytes4(hex"12345678"); + bytes memory data = abi.encodePacked(selector, amount); + + // 5_000 / 10_000 * value == amount + _mockExpectCall(address(POOL), amount, abi.encodePacked(selector, amount), abi.encode(true)); + + vm.deal(address(basic), value); + basic.sellToPool(POOL, IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE), bips, offset, data); + } + + /// @dev When 0xeeee (native asset) is used we expect it to support a transfer with no data + function testBasicSellTransferValue() public { + uint256 bips = 10_000; + uint256 offset = 0; + uint256 amount = 99999; + uint256 value = amount; + bytes memory data; + + _mockExpectCall(address(POOL), value, data, abi.encode(true)); + + vm.deal(address(basic), value); + basic.sellToPool(POOL, IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE), bips, offset, data); + } + + function testBasicRestrictedTarget() public { + uint256 bips = 10_000; + uint256 offset = 0; + uint256 amount = 99999; + uint256 value = amount; + bytes memory data; + + vm.expectRevert(); + basic.sellToPool(PERMIT2, IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE), bips, offset, data); + + vm.expectRevert(); + basic.sellToPool(ALLOWANCE_HOLDER, IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE), bips, offset, data); + } + + function testBasicBubblesUpRevert() public { + uint256 bips = 10_000; + uint256 offset = 0; + uint256 amount = 99999; + uint256 value = amount; + bytes memory data; + + vm.expectRevert(); + basic.sellToPool(POOL, IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE), bips, offset, data); + } } From 734425c44d816965c33789733d4decc77b78865d Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Fri, 5 Jan 2024 11:41:04 +1000 Subject: [PATCH 03/13] UniswapV2 unit tests --- test/unit/Utils.sol | 6 + test/unit/core/UniswapV2UnitTest.t.sol | 164 +++++++++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 test/unit/core/UniswapV2UnitTest.t.sol diff --git a/test/unit/Utils.sol b/test/unit/Utils.sol index 3e199981f..925e53dea 100644 --- a/test/unit/Utils.sol +++ b/test/unit/Utils.sol @@ -32,6 +32,12 @@ contract Utils { _vm.label(a, name); } + function _etchNamedRejectionDummy(string memory name, address a) internal returns (address) { + _vm.etch(a, type(RejectionFallbackDummy).runtimeCode); + _vm.label(a, name); + return a; + } + function _mockExpectCall(address callee, bytes memory data, bytes memory returnData) internal { _vm.mockCall(callee, data, returnData); _vm.expectCall(callee, data); diff --git a/test/unit/core/UniswapV2UnitTest.t.sol b/test/unit/core/UniswapV2UnitTest.t.sol new file mode 100644 index 000000000..b646c6e90 --- /dev/null +++ b/test/unit/core/UniswapV2UnitTest.t.sol @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +import {UniswapV2} from "../../../src/core/UniswapV2.sol"; + +import {Utils} from "../Utils.sol"; +import {IERC20} from "../../../src/IERC20.sol"; + +import {Test} from "forge-std/Test.sol"; +import {VmSafe} from "forge-std/Vm.sol"; + +contract UniswapV2Dummy is UniswapV2 { + function sell(address recipient, bytes memory encodedPath, uint256 bips, uint256 minBuyAmount) public { + super.sellToUniswapV2(recipient, encodedPath, bips, minBuyAmount); + } +} + +contract UniswapV2UnitTest is Utils, Test { + UniswapV2Dummy uni; + address TOKEN0 = _createNamedRejectionDummy("TOKEN0"); + address TOKEN1 = _createNamedRejectionDummy("TOKEN1"); + address TOKEN2 = _createNamedRejectionDummy("TOKEN2"); + address POOL = _etchNamedRejectionDummy("POOL", 0xabedA74b789DBa7D817889Eb0266E1F58219f13f); // created from TOKEN0/TOKEN1 combo + address POOL2 = _etchNamedRejectionDummy("POOL2", 0x62D5437A22Ab167ABbe5e2FADe8C49bE7276ab2F); // created from TOKEN1/TOKEN2 combo + address RECIPIENT = _createNamedRejectionDummy("RECIPIENT"); + + function setUp() public { + uni = new UniswapV2Dummy(); + } + + function testUniswapV2Sell() public { + uint256 bips = 10_000; + uint256 amount = 99999; + uint256 minBuyAmount = 9087; + + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.balanceOf.selector, address(uni)), abi.encode(amount)); + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.transfer.selector, POOL, amount), new bytes(0)); + + // UniswapV2Pool.getReserves + _mockExpectCall(POOL, abi.encodePacked(bytes4(0x0902f1ac)), abi.encode(uint256(9999), uint256(9999))); + // UniswapV2Pool.swap + _mockExpectCall( + POOL, + abi.encodePacked(bytes4(0x022c0d9f), abi.encode(uint256(9087), 0, RECIPIENT, new bytes(0))), + new bytes(0) + ); + + uni.sell(RECIPIENT, abi.encodePacked(TOKEN0, uint8(1), TOKEN1), bips, minBuyAmount); + } + + function testUniswapV2SellSlippageCheck() public { + uint256 bips = 10_000; + uint256 amount = 99999; + uint256 minBuyAmount = 1e18; + + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.balanceOf.selector, address(uni)), abi.encode(amount)); + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.transfer.selector, POOL, amount), new bytes(0)); + + // UniswapV2Pool.getReserves + _mockExpectCall(POOL, abi.encodePacked(bytes4(0x0902f1ac)), abi.encode(uint256(9999), uint256(9999))); + // UniswapV2Pool.swap + _mockExpectCall( + POOL, + abi.encodePacked(bytes4(0x022c0d9f), abi.encode(uint256(9087), 0, RECIPIENT, new bytes(0))), + new bytes(0) + ); + + vm.expectRevert(); + uni.sell(RECIPIENT, abi.encodePacked(TOKEN0, uint8(1), TOKEN1), bips, minBuyAmount); + } + + function testUniswapV2LowerAmount() public { + uint256 bips = 10_000; + uint256 amount = 99999; + uint256 minBuyAmount = 1; + + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.balanceOf.selector, address(uni)), abi.encode(amount / 2)); + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.transfer.selector, POOL, amount / 2), new bytes(0)); + + // UniswapV2Pool.getReserves + _mockExpectCall(POOL, abi.encodePacked(bytes4(0x0902f1ac)), abi.encode(uint256(9999), uint256(9999))); + // UniswapV2Pool.swap + _mockExpectCall( + POOL, + abi.encodePacked(bytes4(0x022c0d9f), abi.encode(uint256(8328), 0, RECIPIENT, new bytes(0))), + new bytes(0) + ); + + uni.sell(RECIPIENT, abi.encodePacked(TOKEN0, uint8(1), TOKEN1), bips, minBuyAmount); + } + + function testUniswapV2GreaterAmount() public { + uint256 bips = 10_000; + uint256 amount = 99999; + uint256 minBuyAmount = 9521; + + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.balanceOf.selector, address(uni)), abi.encode(amount * 2)); + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.transfer.selector, POOL, amount * 2), new bytes(0)); + + // UniswapV2Pool.getReserves + _mockExpectCall(POOL, abi.encodePacked(bytes4(0x0902f1ac)), abi.encode(uint256(9999), uint256(9999))); + // UniswapV2Pool.swap + _mockExpectCall( + POOL, + abi.encodePacked(bytes4(0x022c0d9f), abi.encode(uint256(9521), 0, RECIPIENT, new bytes(0))), + new bytes(0) + ); + + uni.sell(RECIPIENT, abi.encodePacked(TOKEN0, uint8(1), TOKEN1), bips, minBuyAmount); + } + + function testUniswapV2SellTokenFee() public { + uint256 bips = 10_000; + uint256 amount = 99999; + uint256 minBuyAmount = 1; + + // Sell token fee branch is selected if the hopInfo param has the first bit flipped to 1 + uint8 hopInfo = uint8(1) | 0x80; + // We emulate a token which has a 50% fee when transferring to the Uniswap pool + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.balanceOf.selector, POOL), abi.encode(amount / 2)); + + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.balanceOf.selector, address(uni)), abi.encode(amount)); + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.transfer.selector, POOL, amount), new bytes(0)); + + // UniswapV2Pool.getReserves + _mockExpectCall(POOL, abi.encodePacked(bytes4(0x0902f1ac)), abi.encode(uint256(9999), uint256(9999))); + // UniswapV2Pool.swap + _mockExpectCall( + POOL, + abi.encodePacked(bytes4(0x022c0d9f), abi.encode(uint256(7994), 0, RECIPIENT, new bytes(0))), + new bytes(0) + ); + // the pool is responsible for transferring to receipient, since the pool is a dummy, this transfer is not mocked + + uni.sell(RECIPIENT, abi.encodePacked(TOKEN0, hopInfo, TOKEN1), bips, minBuyAmount); + } + + function testUniswapV2Multihop() public { + uint256 bips = 10_000; + uint256 amount = 99999; + uint256 minBuyAmount = 4869; + + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.balanceOf.selector, address(uni)), abi.encode(amount * 2)); + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.transfer.selector, POOL, amount * 2), new bytes(0)); + + // UniswapV2Pool.getReserves + _mockExpectCall(POOL, abi.encodePacked(bytes4(0x0902f1ac)), abi.encode(uint256(9999), uint256(9999))); + // UniswapV2Pool.swap + // POOL specifies POOL2 as recipient + _mockExpectCall( + POOL, abi.encodePacked(bytes4(0x022c0d9f), abi.encode(uint256(9521), 0, POOL2, new bytes(0))), new bytes(0) + ); + _mockExpectCall(POOL2, abi.encodePacked(bytes4(0x0902f1ac)), abi.encode(uint256(9999), uint256(9999))); + // UniswapV2Pool.swap + // POOL2 specifies RECIPIENT as recipient + _mockExpectCall( + POOL2, + abi.encodePacked(bytes4(0x022c0d9f), abi.encode(uint256(0), uint256(4869), RECIPIENT, new bytes(0))), + new bytes(0) + ); + + uni.sell(RECIPIENT, abi.encodePacked(TOKEN0, uint8(1), TOKEN1, uint8(1), TOKEN2), bips, minBuyAmount); + } +} From 86eca089bb4850b2f291e44f06775ec7be64b819 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Mon, 8 Jan 2024 11:53:28 +1100 Subject: [PATCH 04/13] UniswapV3 unit tests --- foundry.toml | 2 +- src/core/UniswapV3.sol | 8 +- test/unit/core/UniswapV3UnitTest.t.sol | 191 +++++++++++++++++++++++++ 3 files changed, 198 insertions(+), 3 deletions(-) create mode 100644 test/unit/core/UniswapV3UnitTest.t.sol diff --git a/foundry.toml b/foundry.toml index d27d48a91..ade537e6d 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,7 +8,7 @@ no_match_path = "*/integration/*" fuzz_runs = 10000 # needed for marktoda/forge-gas-snapshot ffi = true -fs_permissions = [{ access = "read-write", path = ".forge-snapshots/"}] +fs_permissions = [{ access = "read-write", path = ".forge-snapshots/"},{ access = "read", path = "out"}] evm_version = "shanghai" [profile.integration] diff --git a/src/core/UniswapV3.sol b/src/core/UniswapV3.sol index 222dd8a49..2c26ea2ac 100644 --- a/src/core/UniswapV3.sol +++ b/src/core/UniswapV3.sol @@ -3,7 +3,9 @@ pragma solidity ^0.8.21; import {VIPBase} from "./VIPBase.sol"; import {Permit2PaymentAbstract} from "./Permit2Payment.sol"; -import {InvalidSender} from "./SettlerErrors.sol"; +// import {InvalidSender} from "./SettlerErrors.sol"; + +error InvalidSender(address sender); import {IERC20} from "../IERC20.sol"; import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol"; @@ -344,7 +346,9 @@ abstract contract UniswapV3 is SettlerAbstract, VIPBase { payer := calldataload(add(data.offset, 0x1f)) } // Only a valid pool contract can call this function. - if (msg.sender != address(_toPool(token0, fee, token1))) revert InvalidSender(); + if (msg.sender != address(_toPool(token0, fee, token1))) { + revert InvalidSender(address(_toPool(token0, fee, token1))); + } bytes calldata permit2Data = data[SWAP_CALLBACK_PREFIX_DATA_SIZE:]; // Pay the amount owed to the pool. diff --git a/test/unit/core/UniswapV3UnitTest.t.sol b/test/unit/core/UniswapV3UnitTest.t.sol new file mode 100644 index 000000000..6caf62c86 --- /dev/null +++ b/test/unit/core/UniswapV3UnitTest.t.sol @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +import {UniswapV3, IUniswapV3Pool} from "../../../src/core/UniswapV3.sol"; +import {Permit2Payment} from "../../../src/core/Permit2Payment.sol"; +import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol"; + +import {IAllowanceHolder} from "../../../src/IAllowanceHolder.sol"; + +import {Utils} from "../Utils.sol"; +import {IERC20} from "../../../src/IERC20.sol"; + +import {Test} from "forge-std/Test.sol"; +import {VmSafe} from "forge-std/Vm.sol"; + +contract UniswapV3Dummy is UniswapV3, Permit2Payment { + constructor(address uniFactory, bytes32 poolInit, address permit2, address feeRecipient, address allowanceHolder) + UniswapV3(uniFactory, poolInit) + Permit2Payment(permit2, feeRecipient, allowanceHolder) + {} + + function sellTokenForTokenSelf(address recipient, bytes memory encodedPath, uint256 bips, uint256 minBuyAmount) + external + returns (uint256 buyAmount) + { + super.sellTokenForTokenToUniswapV3(recipient, encodedPath, bips, minBuyAmount); + } + + function sellTokenForToken( + address recipient, + bytes memory encodedPath, + uint256 sellAmount, + uint256 minBuyAmount, + address payer, + ISignatureTransfer.PermitTransferFrom memory permit, + bytes memory sig + ) external returns (uint256 buyAmount) { + super.sellTokenForTokenToUniswapV3(recipient, encodedPath, sellAmount, minBuyAmount, payer, permit, sig); + } +} + +/// @dev We need a dummy to actually call our contract, so it needs an implementation which at the very least +/// calls the `uniswapV3SwapCallback` +contract UniswapV3PoolDummy { + bytes public RETURN_DATA; + + constructor(bytes memory returnData) { + RETURN_DATA = returnData; + } + + fallback(bytes calldata) external payable returns (bytes memory) { + (,,,, bytes memory data) = abi.decode(msg.data[4:], (address, bool, int256, uint160, bytes)); + msg.sender.call(abi.encodeWithSelector(UniswapV3.uniswapV3SwapCallback.selector, int256(1), int256(1), data)); + return RETURN_DATA; + } +} + +contract UniswapV3UnitTest is Utils, Test { + UniswapV3Dummy uni; + address UNI_FACTORY = _createNamedRejectionDummy("UNI_FACTORY"); + address PERMIT2 = _createNamedRejectionDummy("PERMIT2"); + address FEE_RECIPIENT = _createNamedRejectionDummy("FEE_RECIPIENT"); + address ALLOWANCE_HOLDER = _createNamedRejectionDummy("ALLOWANCE_HOLDER"); + + address TOKEN0 = _createNamedRejectionDummy("TOKEN0"); + address TOKEN1 = _createNamedRejectionDummy("TOKEN1"); + address TOKEN2 = _createNamedRejectionDummy("TOKEN2"); + address RECIPIENT = _createNamedRejectionDummy("RECIPIENT"); + + address POOL = _etchNamedRejectionDummy("POOL", 0x33da22E66cE9c37747B80804c14dCE4a5aBD33a5); // created from TOKEN0/TOKEN1 combo + + function setUp() public { + uni = new UniswapV3Dummy( + UNI_FACTORY, keccak256(abi.encodePacked("POOL_INIT")), PERMIT2, FEE_RECIPIENT, ALLOWANCE_HOLDER + ); + } + + function testUniswapV3SellSelfFunded() public { + uint256 bips = 10_000; + uint256 amount = 99999; + uint256 minBuyAmount = amount; + + bytes memory data = abi.encodePacked(TOKEN0, uint24(500), TOKEN1); + + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.balanceOf.selector, address(uni)), abi.encode(amount)); + _mockExpectCall( + POOL, + abi.encodeWithSelector( + IUniswapV3Pool.swap.selector, + RECIPIENT, + false, + amount, + 1461446703485210103287273052203988822378723970341, + abi.encodePacked(TOKEN1, uint24(500), TOKEN0, address(uni)) /* token1 and token0 swapped due to univ3 ordering */ + ), + abi.encode(-int256(amount), 0) + ); + + uni.sellTokenForTokenSelf(RECIPIENT, data, bips, minBuyAmount); + } + + function testUniswapV3SellSlippage() public { + uint256 bips = 10_000; + uint256 amount = 99999; + uint256 minBuyAmount = amount + 1; + + bytes memory data = abi.encodePacked(TOKEN0, uint24(500), TOKEN1); + + _mockExpectCall(TOKEN0, abi.encodeWithSelector(IERC20.balanceOf.selector, address(uni)), abi.encode(amount)); + _mockExpectCall( + POOL, + abi.encodeWithSelector( + IUniswapV3Pool.swap.selector, + RECIPIENT, + false, + amount, + 1461446703485210103287273052203988822378723970341, + abi.encodePacked(TOKEN1, uint24(500), TOKEN0, address(uni)) /* token1 and token0 swapped due to univ3 ordering */ + ), + abi.encode(-int256(amount), 0) + ); + + vm.expectRevert(); + uni.sellTokenForTokenSelf(RECIPIENT, data, bips, minBuyAmount); + } + + function testUniswapV3SellPermit2() public { + uint256 bips = 10_000; + uint256 amount = 99999; + uint256 minBuyAmount = amount; + + bytes memory data = abi.encodePacked(TOKEN0, uint24(500), TOKEN1); + // Override the UniswapV3 pool code to callback our contract + // There's probably a smarter way to do this tbh + deployCodeTo( + "UniswapV3UnitTest.t.sol:UniswapV3PoolDummy", + abi.encode(abi.encodePacked(-int256(amount), -int256(amount))), + POOL + ); + + ISignatureTransfer.TokenPermissions memory permitted = + ISignatureTransfer.TokenPermissions({token: TOKEN0, amount: amount}); + ISignatureTransfer.PermitTransferFrom memory permitTransfer = + ISignatureTransfer.PermitTransferFrom({permitted: permitted, nonce: 0, deadline: 0}); + ISignatureTransfer.SignatureTransferDetails memory transferDetails = + ISignatureTransfer.SignatureTransferDetails({to: POOL, requestedAmount: amount}); + + // permitTransferFrom(((address,uint256),uint256,uint256),(address,uint256),address,bytes) 30f28b7a + // cannot use abi.encodeWithSelector due to the selector overload and ambiguity + _mockExpectCall( + PERMIT2, + abi.encodeWithSelector(bytes4(0x30f28b7a), permitTransfer, transferDetails, address(this), hex"deadbeef"), + new bytes(0) + ); + + uni.sellTokenForToken(RECIPIENT, data, amount, minBuyAmount, address(this), permitTransfer, hex"deadbeef"); + } + + function testUniswapV3SellAllowanceHolder() public { + uint256 bips = 10_000; + uint256 amount = 99999; + uint256 minBuyAmount = amount; + + bytes memory data = abi.encodePacked(TOKEN0, uint24(500), TOKEN1); + // Override the UniswapV3 pool code to callback our contract + // There's probably a smarter way to do this tbh + deployCodeTo( + "UniswapV3UnitTest.t.sol:UniswapV3PoolDummy", + abi.encode(abi.encodePacked(-int256(amount), -int256(amount))), + POOL + ); + + ISignatureTransfer.PermitTransferFrom memory permitTransfer = ISignatureTransfer.PermitTransferFrom({ + permitted: ISignatureTransfer.TokenPermissions({token: TOKEN0, amount: amount}), + nonce: 0, + deadline: 0 + }); + + IAllowanceHolder.TransferDetails[] memory transferDetails = new IAllowanceHolder.TransferDetails[](1); + transferDetails[0] = IAllowanceHolder.TransferDetails({token: TOKEN0, recipient: POOL, amount: amount}); + + _mockExpectCall( + ALLOWANCE_HOLDER, + abi.encodeCall(IAllowanceHolder.holderTransferFrom, (address(this), transferDetails)), + abi.encode(true) + ); + + vm.prank(ALLOWANCE_HOLDER); + uni.sellTokenForToken(RECIPIENT, data, amount, minBuyAmount, address(this), permitTransfer, hex""); + } +} From edc3a482c81c8abae88542e91d0e2b5fa80ab937 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Mon, 8 Jan 2024 12:37:22 +1100 Subject: [PATCH 05/13] Initial Otc Unit tests --- test/unit/core/OtcUnitTest.t.sol | 162 +++++++++++++++++++++++++ test/unit/core/UniswapV3UnitTest.t.sol | 10 +- 2 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 test/unit/core/OtcUnitTest.t.sol diff --git a/test/unit/core/OtcUnitTest.t.sol b/test/unit/core/OtcUnitTest.t.sol new file mode 100644 index 000000000..de75efaab --- /dev/null +++ b/test/unit/core/OtcUnitTest.t.sol @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +import {OtcOrderSettlement} from "../../../src/core/OtcOrderSettlement.sol"; +import {Permit2Payment} from "../../../src/core/Permit2Payment.sol"; +import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol"; +import {IAllowanceHolder} from "../../../src/IAllowanceHolder.sol"; + +import {Utils} from "../Utils.sol"; +import {IERC20} from "../../../src/IERC20.sol"; + +import {Test} from "forge-std/Test.sol"; + +contract OtcOrderSettlementDummy is OtcOrderSettlement, Permit2Payment { + constructor(address permit2, address feeRecipient, address allowanceHolder) + Permit2Payment(permit2, feeRecipient, allowanceHolder) + {} + + function fillOtcOrderDirectCounterparties( + address recipient, + ISignatureTransfer.PermitTransferFrom memory makerPermit, + address maker, + bytes memory makerSig, + ISignatureTransfer.PermitTransferFrom memory takerPermit, + bytes memory takerSig + ) external { + super.fillOtcOrder(recipient, makerPermit, maker, makerSig, takerPermit, takerSig); + } +} + +contract OtcUnitTest is Utils, Test { + OtcOrderSettlementDummy otc; + address PERMIT2 = _createNamedRejectionDummy("PERMIT2"); + address FEE_RECIPIENT = _createNamedRejectionDummy("FEE_RECIPIENT"); + address ALLOWANCE_HOLDER = _createNamedRejectionDummy("ALLOWANCE_HOLDER"); + + address TOKEN0 = _createNamedRejectionDummy("TOKEN0"); + address TOKEN1 = _createNamedRejectionDummy("TOKEN1"); + address RECIPIENT = _createNamedRejectionDummy("RECIPIENT"); + address MAKER = _createNamedRejectionDummy("MAKER"); + + function setUp() public { + otc = new OtcOrderSettlementDummy(PERMIT2, FEE_RECIPIENT, ALLOWANCE_HOLDER); + } + + function testOtcDirectCounterparties() public { + // 🎉 + uint256 amount = 9999; + ISignatureTransfer.PermitTransferFrom memory makerPermit = ISignatureTransfer.PermitTransferFrom({ + permitted: ISignatureTransfer.TokenPermissions({token: TOKEN1, amount: amount}), + nonce: 0, + deadline: 0 + }); + ISignatureTransfer.PermitTransferFrom memory takerPermit = ISignatureTransfer.PermitTransferFrom({ + permitted: ISignatureTransfer.TokenPermissions({token: TOKEN0, amount: amount}), + nonce: 0, + deadline: 0 + }); + + _mockExpectCall( + PERMIT2, + abi.encodeWithSelector( + bytes4(0x137c29fe), + makerPermit, + ISignatureTransfer.SignatureTransferDetails({to: RECIPIENT, requestedAmount: amount}), + MAKER, + bytes32(0x315954c1f9717c9d14604de3c6ceb9fd601b3bd1d0b8ec397e8c2b81668a02e1), /* witness */ + "Consideration consideration)Consideration(address token,uint256 amount,address counterparty,bool partialFillAllowed)TokenPermissions(address token,uint256 amount)", + hex"dead" + ), + new bytes(0) + ); + + _mockExpectCall( + PERMIT2, + abi.encodeWithSelector( + bytes4(0x30f28b7a), + takerPermit, + ISignatureTransfer.SignatureTransferDetails({to: MAKER, requestedAmount: amount}), + address(this), /* taker + payer */ + hex"beef" + ), + new bytes(0) + ); + + // Broken usage of OtcOrderSettlement.OtcOrderFilled in 0.8.21 + // https://github.com/foundry-rs/foundry/issues/6206 + // vm.expectEmit(address(otc)); + // emit OtcOrderSettlement.OtcOrderFilled( + // bytes32(0xbee0e2de3e64ecfe06fe7118215a033ac40a8d6a508d60b81cd9ac6addd6e11e), + // MAKER, + // address(this), + // TOKEN1, + // TOKEN0, + // amount, + // amount + // ); + + otc.fillOtcOrderDirectCounterparties(RECIPIENT, makerPermit, MAKER, hex"dead", takerPermit, hex"beef"); + } + + function testOtcDirectCounterpartiesAllowanceHolder() public { + // 🎉 + uint256 amount = 9999; + ISignatureTransfer.PermitTransferFrom memory makerPermit = ISignatureTransfer.PermitTransferFrom({ + permitted: ISignatureTransfer.TokenPermissions({token: TOKEN1, amount: amount}), + nonce: 0, + deadline: 0 + }); + ISignatureTransfer.PermitTransferFrom memory takerPermit = ISignatureTransfer.PermitTransferFrom({ + permitted: ISignatureTransfer.TokenPermissions({token: TOKEN0, amount: amount}), + nonce: 0, + deadline: 0 + }); + + _mockExpectCall( + PERMIT2, + abi.encodeWithSelector( + bytes4(0x137c29fe), + makerPermit, + ISignatureTransfer.SignatureTransferDetails({to: RECIPIENT, requestedAmount: amount}), + MAKER, + bytes32(0x315954c1f9717c9d14604de3c6ceb9fd601b3bd1d0b8ec397e8c2b81668a02e1), /* witness */ + "Consideration consideration)Consideration(address token,uint256 amount,address counterparty,bool partialFillAllowed)TokenPermissions(address token,uint256 amount)", + hex"dead" + ), + new bytes(0) + ); + + IAllowanceHolder.TransferDetails[] memory transferDetails = new IAllowanceHolder.TransferDetails[](1); + transferDetails[0] = IAllowanceHolder.TransferDetails({token: TOKEN0, recipient: MAKER, amount: amount}); + + _mockExpectCall( + ALLOWANCE_HOLDER, + abi.encodeCall(IAllowanceHolder.holderTransferFrom, (address(this), transferDetails)), + abi.encode(true) + ); + + // Broken usage of OtcOrderSettlement.OtcOrderFilled in 0.8.21 + // https://github.com/foundry-rs/foundry/issues/6206 + // vm.expectEmit(address(otc)); + // emit OtcOrderSettlement.OtcOrderFilled( + // bytes32(0xbee0e2de3e64ecfe06fe7118215a033ac40a8d6a508d60b81cd9ac6addd6e11e), + // MAKER, + // address(this), + // TOKEN1, + // TOKEN0, + // amount, + // amount + // ); + + vm.prank(ALLOWANCE_HOLDER); + address(otc).call( + abi.encodePacked( + abi.encodeCall( + otc.fillOtcOrderDirectCounterparties, (RECIPIENT, makerPermit, MAKER, hex"dead", takerPermit, hex"") + ), + address(this) + ) // Forward on true msg.sender + ); + } +} diff --git a/test/unit/core/UniswapV3UnitTest.t.sol b/test/unit/core/UniswapV3UnitTest.t.sol index 6caf62c86..67ef9ee34 100644 --- a/test/unit/core/UniswapV3UnitTest.t.sol +++ b/test/unit/core/UniswapV3UnitTest.t.sol @@ -186,6 +186,14 @@ contract UniswapV3UnitTest is Utils, Test { ); vm.prank(ALLOWANCE_HOLDER); - uni.sellTokenForToken(RECIPIENT, data, amount, minBuyAmount, address(this), permitTransfer, hex""); + address(uni).call( + abi.encodePacked( + abi.encodeCall( + uni.sellTokenForToken, (RECIPIENT, data, amount, minBuyAmount, address(this), permitTransfer, hex"") + ), + address(this) + ) // Forward on true msg.sender + ); + // uni.sellTokenForToken(RECIPIENT, data, amount, minBuyAmount, address(this), permitTransfer, hex""); } } From 4dc7d04574caf0dba3bd97acaf8df4a5461431a9 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Mon, 8 Jan 2024 16:07:02 +1100 Subject: [PATCH 06/13] clean up warnings --- src/core/Basic.sol | 6 +- src/core/OtcOrderSettlement.sol | 4 +- src/core/Permit2Payment.sol | 198 ++++++++++++++----------- test/unit/core/BasicUnitTest.t.sol | 4 - test/unit/core/UniswapV3UnitTest.t.sol | 10 +- 5 files changed, 119 insertions(+), 103 deletions(-) diff --git a/src/core/Basic.sol b/src/core/Basic.sol index e313e6c00..76e792db9 100644 --- a/src/core/Basic.sol +++ b/src/core/Basic.sol @@ -26,12 +26,14 @@ abstract contract Basic is Permit2PaymentAbstract { revert ConfusedDeputy(); } + bool success; + bytes memory returnData; uint256 value; if (sellToken == IERC20(ETH_ADDRESS)) { value = address(this).balance.mulDiv(bips, 10_000); if (data.length == 0) { if (offset != 0) revert InvalidOffset(); - (bool success, bytes memory returnData) = payable(pool).call{value: value}(""); + (success, returnData) = payable(pool).call{value: value}(""); success.maybeRevert(returnData); return; } else { @@ -56,7 +58,7 @@ abstract contract Basic is Permit2PaymentAbstract { sellToken.safeApproveIfBelow(pool, amount); } } - (bool success, bytes memory returnData) = payable(pool).call{value: value}(data); + (success, returnData) = payable(pool).call{value: value}(data); success.maybeRevert(returnData); // forbid sending data to EOAs if (returnData.length == 0 && pool.code.length == 0) revert InvalidTarget(); diff --git a/src/core/OtcOrderSettlement.sol b/src/core/OtcOrderSettlement.sol index 7de18bca0..e8325ccff 100644 --- a/src/core/OtcOrderSettlement.sol +++ b/src/core/OtcOrderSettlement.sol @@ -101,9 +101,9 @@ abstract contract OtcOrderSettlement is SettlerAbstract { bytes32 witness = _hashConsideration(consideration); // There is no taker witness (see below) - // Maker pays recipient (optional fee) + // Maker pays _transferFrom(makerPermit, makerTransferDetails, maker, witness, CONSIDERATION_WITNESS, makerSig, false); - // Taker pays Maker (optional fee) + // Taker pays Maker // We don't need to include a witness here. Taker is `_msgSender()`, so // `recipient` and the maker's details are already authenticated. We're just // using PERMIT2 to move tokens, not to provide authentication. diff --git a/src/core/Permit2Payment.sol b/src/core/Permit2Payment.sol index 472fcf947..17f70be3d 100644 --- a/src/core/Permit2Payment.sol +++ b/src/core/Permit2Payment.sol @@ -47,12 +47,6 @@ abstract contract Permit2PaymentAbstract is ContextAbstract { function isRestrictedTarget(address) internal view virtual returns (bool); - function _permitToTransferDetails(ISignatureTransfer.PermitBatchTransferFrom memory permit, address recipient) - internal - view - virtual - returns (ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, address token, uint256 amount); - function _permitToTransferDetails(ISignatureTransfer.PermitTransferFrom memory permit, address recipient) internal pure @@ -60,8 +54,8 @@ abstract contract Permit2PaymentAbstract is ContextAbstract { returns (ISignatureTransfer.SignatureTransferDetails memory transferDetails, address token, uint256 amount); function _transferFrom( - ISignatureTransfer.PermitBatchTransferFrom memory permit, - ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, + ISignatureTransfer.PermitTransferFrom memory permit, + ISignatureTransfer.SignatureTransferDetails memory transferDetails, address from, bytes32 witness, string memory witnessTypeString, @@ -70,8 +64,8 @@ abstract contract Permit2PaymentAbstract is ContextAbstract { ) internal virtual; function _transferFrom( - ISignatureTransfer.PermitBatchTransferFrom memory permit, - ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, + ISignatureTransfer.PermitTransferFrom memory permit, + ISignatureTransfer.SignatureTransferDetails memory transferDetails, address from, bytes32 witness, string memory witnessTypeString, @@ -82,8 +76,6 @@ abstract contract Permit2PaymentAbstract is ContextAbstract { ISignatureTransfer.PermitTransferFrom memory permit, ISignatureTransfer.SignatureTransferDetails memory transferDetails, address from, - bytes32 witness, - string memory witnessTypeString, bytes memory sig, bool isForwarded ) internal virtual; @@ -92,15 +84,31 @@ abstract contract Permit2PaymentAbstract is ContextAbstract { ISignatureTransfer.PermitTransferFrom memory permit, ISignatureTransfer.SignatureTransferDetails memory transferDetails, address from, - bytes32 witness, - string memory witnessTypeString, bytes memory sig ) internal virtual; +} + +/// @dev Batch support for Permit2 payments +/// === WARNING: UNUSED === +abstract contract Permit2BatchPaymentAbstract is ContextAbstract { + string internal constant TOKEN_PERMISSIONS_TYPE = "TokenPermissions(address token,uint256 amount)"; + + error FeeTokenMismatch(address paymentToken, address feeToken); + + function isRestrictedTarget(address) internal view virtual returns (bool); + + function _permitToTransferDetails(ISignatureTransfer.PermitBatchTransferFrom memory permit, address recipient) + internal + view + virtual + returns (ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, address token, uint256 amount); function _transferFrom( ISignatureTransfer.PermitBatchTransferFrom memory permit, ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, address from, + bytes32 witness, + string memory witnessTypeString, bytes memory sig, bool isForwarded ) internal virtual; @@ -109,26 +117,28 @@ abstract contract Permit2PaymentAbstract is ContextAbstract { ISignatureTransfer.PermitBatchTransferFrom memory permit, ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, address from, + bytes32 witness, + string memory witnessTypeString, bytes memory sig ) internal virtual; function _transferFrom( - ISignatureTransfer.PermitTransferFrom memory permit, - ISignatureTransfer.SignatureTransferDetails memory transferDetails, + ISignatureTransfer.PermitBatchTransferFrom memory permit, + ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, address from, bytes memory sig, bool isForwarded ) internal virtual; function _transferFrom( - ISignatureTransfer.PermitTransferFrom memory permit, - ISignatureTransfer.SignatureTransferDetails memory transferDetails, + ISignatureTransfer.PermitBatchTransferFrom memory permit, + ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, address from, bytes memory sig ) internal virtual; } -abstract contract Permit2Payment is Permit2PaymentAbstract, AllowanceHolderContext { +abstract contract Permit2BatchPayment is Permit2BatchPaymentAbstract, AllowanceHolderContext { using UnsafeMath for uint256; using UnsafeArray for IAllowanceHolder.TransferDetails[]; using UnsafeArray for ISignatureTransfer.TokenPermissions[]; @@ -138,19 +148,6 @@ abstract contract Permit2Payment is Permit2PaymentAbstract, AllowanceHolderConte ISignatureTransfer private immutable _PERMIT2; address private immutable _FEE_RECIPIENT; - function isRestrictedTarget(address target) internal view override returns (bool) { - return target == address(_PERMIT2) || target == address(allowanceHolder); - } - - constructor(address permit2, address feeRecipient, address allowanceHolder) - AllowanceHolderContext(allowanceHolder) - { - _PERMIT2 = ISignatureTransfer(permit2); - _FEE_RECIPIENT = feeRecipient; - } - - error FeeTokenMismatch(address paymentToken, address feeToken); - function _permitToTransferDetails(ISignatureTransfer.PermitBatchTransferFrom memory permit, address recipient) internal view @@ -180,15 +177,52 @@ abstract contract Permit2Payment is Permit2PaymentAbstract, AllowanceHolderConte } } - function _permitToTransferDetails(ISignatureTransfer.PermitTransferFrom memory permit, address recipient) - internal - pure - override - returns (ISignatureTransfer.SignatureTransferDetails memory transferDetails, address token, uint256 amount) - { - transferDetails.to = recipient; - transferDetails.requestedAmount = amount = permit.permitted.amount; - token = permit.permitted.token; + function _transferFrom( + ISignatureTransfer.PermitBatchTransferFrom memory permit, + ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, + address from, + bytes32 witness, + string memory witnessTypeString, + bytes memory sig, + bool isForwarded + ) internal override { + if (isForwarded) revert ForwarderNotAllowed(); + _PERMIT2.permitWitnessTransferFrom(permit, transferDetails, from, witness, witnessTypeString, sig); + } + + function _transferFrom( + ISignatureTransfer.PermitBatchTransferFrom memory permit, + ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, + address from, + bytes32 witness, + string memory witnessTypeString, + bytes memory sig + ) internal override { + _transferFrom(permit, transferDetails, from, witness, witnessTypeString, sig, _isForwarded()); + } + + function _transferFrom( + ISignatureTransfer.PermitBatchTransferFrom memory permit, + ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, + address from, + bytes memory sig, + bool isForwarded + ) internal override { + if (isForwarded) { + if (sig.length != 0) revert InvalidSignatureLen(); + allowanceHolder.holderTransferFrom(from, _formatForAllowanceHolder(permit, transferDetails)); + } else { + _PERMIT2.permitTransferFrom(permit, transferDetails, from, sig); + } + } + + function _transferFrom( + ISignatureTransfer.PermitBatchTransferFrom memory permit, + ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, + address from, + bytes memory sig + ) internal override { + _transferFrom(permit, transferDetails, from, sig, _isForwarded()); } function _formatForAllowanceHolder( @@ -212,6 +246,40 @@ abstract contract Permit2Payment is Permit2PaymentAbstract, AllowanceHolderConte } } +} + +abstract contract Permit2Payment is Permit2PaymentAbstract, AllowanceHolderContext { + using UnsafeMath for uint256; + using UnsafeArray for IAllowanceHolder.TransferDetails[]; + using UnsafeArray for ISignatureTransfer.TokenPermissions[]; + using UnsafeArray for ISignatureTransfer.SignatureTransferDetails[]; + + /// @dev Permit2 address + ISignatureTransfer private immutable _PERMIT2; + address private immutable _FEE_RECIPIENT; + + function isRestrictedTarget(address target) internal view override returns (bool) { + return target == address(_PERMIT2) || target == address(allowanceHolder); + } + + constructor(address permit2, address feeRecipient, address allowanceHolder) + AllowanceHolderContext(allowanceHolder) + { + _PERMIT2 = ISignatureTransfer(permit2); + _FEE_RECIPIENT = feeRecipient; + } + + function _permitToTransferDetails(ISignatureTransfer.PermitTransferFrom memory permit, address recipient) + internal + pure + override + returns (ISignatureTransfer.SignatureTransferDetails memory transferDetails, address token, uint256 amount) + { + transferDetails.to = recipient; + transferDetails.requestedAmount = amount = permit.permitted.amount; + token = permit.permitted.token; + } + function _formatForAllowanceHolder( ISignatureTransfer.PermitTransferFrom memory permit, ISignatureTransfer.SignatureTransferDetails memory transferDetails @@ -223,30 +291,6 @@ abstract contract Permit2Payment is Permit2PaymentAbstract, AllowanceHolderConte newDetail.amount = transferDetails.requestedAmount; } - function _transferFrom( - ISignatureTransfer.PermitBatchTransferFrom memory permit, - ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, - address from, - bytes32 witness, - string memory witnessTypeString, - bytes memory sig, - bool isForwarded - ) internal override { - if (isForwarded) revert ForwarderNotAllowed(); - _PERMIT2.permitWitnessTransferFrom(permit, transferDetails, from, witness, witnessTypeString, sig); - } - - function _transferFrom( - ISignatureTransfer.PermitBatchTransferFrom memory permit, - ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, - address from, - bytes32 witness, - string memory witnessTypeString, - bytes memory sig - ) internal override { - _transferFrom(permit, transferDetails, from, witness, witnessTypeString, sig, _isForwarded()); - } - function _transferFrom( ISignatureTransfer.PermitTransferFrom memory permit, ISignatureTransfer.SignatureTransferDetails memory transferDetails, @@ -271,30 +315,6 @@ abstract contract Permit2Payment is Permit2PaymentAbstract, AllowanceHolderConte _transferFrom(permit, transferDetails, from, witness, witnessTypeString, sig, _isForwarded()); } - function _transferFrom( - ISignatureTransfer.PermitBatchTransferFrom memory permit, - ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, - address from, - bytes memory sig, - bool isForwarded - ) internal override { - if (isForwarded) { - if (sig.length != 0) revert InvalidSignatureLen(); - allowanceHolder.holderTransferFrom(from, _formatForAllowanceHolder(permit, transferDetails)); - } else { - _PERMIT2.permitTransferFrom(permit, transferDetails, from, sig); - } - } - - function _transferFrom( - ISignatureTransfer.PermitBatchTransferFrom memory permit, - ISignatureTransfer.SignatureTransferDetails[] memory transferDetails, - address from, - bytes memory sig - ) internal override { - _transferFrom(permit, transferDetails, from, sig, _isForwarded()); - } - function _transferFrom( ISignatureTransfer.PermitTransferFrom memory permit, ISignatureTransfer.SignatureTransferDetails memory transferDetails, diff --git a/test/unit/core/BasicUnitTest.t.sol b/test/unit/core/BasicUnitTest.t.sol index 8219a5944..31934ea17 100644 --- a/test/unit/core/BasicUnitTest.t.sol +++ b/test/unit/core/BasicUnitTest.t.sol @@ -173,8 +173,6 @@ contract BasicUnitTest is Utils, Test { function testBasicRestrictedTarget() public { uint256 bips = 10_000; uint256 offset = 0; - uint256 amount = 99999; - uint256 value = amount; bytes memory data; vm.expectRevert(); @@ -187,8 +185,6 @@ contract BasicUnitTest is Utils, Test { function testBasicBubblesUpRevert() public { uint256 bips = 10_000; uint256 offset = 0; - uint256 amount = 99999; - uint256 value = amount; bytes memory data; vm.expectRevert(); diff --git a/test/unit/core/UniswapV3UnitTest.t.sol b/test/unit/core/UniswapV3UnitTest.t.sol index 67ef9ee34..ef2d73780 100644 --- a/test/unit/core/UniswapV3UnitTest.t.sol +++ b/test/unit/core/UniswapV3UnitTest.t.sol @@ -21,9 +21,9 @@ contract UniswapV3Dummy is UniswapV3, Permit2Payment { function sellTokenForTokenSelf(address recipient, bytes memory encodedPath, uint256 bips, uint256 minBuyAmount) external - returns (uint256 buyAmount) + returns (uint256) { - super.sellTokenForTokenToUniswapV3(recipient, encodedPath, bips, minBuyAmount); + return super.sellTokenForTokenToUniswapV3(recipient, encodedPath, bips, minBuyAmount); } function sellTokenForToken( @@ -34,8 +34,8 @@ contract UniswapV3Dummy is UniswapV3, Permit2Payment { address payer, ISignatureTransfer.PermitTransferFrom memory permit, bytes memory sig - ) external returns (uint256 buyAmount) { - super.sellTokenForTokenToUniswapV3(recipient, encodedPath, sellAmount, minBuyAmount, payer, permit, sig); + ) external returns (uint256) { + return super.sellTokenForTokenToUniswapV3(recipient, encodedPath, sellAmount, minBuyAmount, payer, permit, sig); } } @@ -125,7 +125,6 @@ contract UniswapV3UnitTest is Utils, Test { } function testUniswapV3SellPermit2() public { - uint256 bips = 10_000; uint256 amount = 99999; uint256 minBuyAmount = amount; @@ -157,7 +156,6 @@ contract UniswapV3UnitTest is Utils, Test { } function testUniswapV3SellAllowanceHolder() public { - uint256 bips = 10_000; uint256 amount = 99999; uint256 minBuyAmount = amount; From 86814376f842c5288c6a45be5335a22e0516d206 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Mon, 8 Jan 2024 16:08:27 +1100 Subject: [PATCH 07/13] fix lint --- src/core/Permit2Payment.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/Permit2Payment.sol b/src/core/Permit2Payment.sol index 17f70be3d..ae5e59d43 100644 --- a/src/core/Permit2Payment.sol +++ b/src/core/Permit2Payment.sol @@ -245,7 +245,6 @@ abstract contract Permit2BatchPayment is Permit2BatchPaymentAbstract, AllowanceH newDetail.amount = oldDetail.requestedAmount; } } - } abstract contract Permit2Payment is Permit2PaymentAbstract, AllowanceHolderContext { From b5569fa500e9d76ee7e3eb6af83058f446f0cce8 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Mon, 8 Jan 2024 16:13:34 +1100 Subject: [PATCH 08/13] Update integration snapshot data --- .forge-snapshots/allowanceHolder_uniswapV3VIP_DAI-WETH.snap | 2 +- .forge-snapshots/allowanceHolder_uniswapV3VIP_USDC-WETH.snap | 2 +- .forge-snapshots/allowanceHolder_uniswapV3VIP_USDT-WETH.snap | 2 +- .../allowanceHolder_uniswapV3VIP_contract_DAI-WETH.snap | 2 +- .../allowanceHolder_uniswapV3VIP_contract_USDC-WETH.snap | 2 +- .../allowanceHolder_uniswapV3VIP_contract_USDT-WETH.snap | 2 +- .forge-snapshots/allowanceHolder_uniswapV3_DAI-WETH.snap | 2 +- .forge-snapshots/allowanceHolder_uniswapV3_USDC-WETH.snap | 2 +- .forge-snapshots/allowanceHolder_uniswapV3_USDT-WETH.snap | 2 +- .../settler_externalMoveExecute_uniswapV3_DAI-WETH.snap | 2 +- .../settler_externalMoveExecute_uniswapV3_USDC-WETH.snap | 2 +- .../settler_externalMoveExecute_uniswapV3_USDT-WETH.snap | 2 +- .forge-snapshots/settler_metaTxn_uniswapV3VIP_DAI-WETH.snap | 2 +- .forge-snapshots/settler_metaTxn_uniswapV3VIP_USDC-WETH.snap | 2 +- .forge-snapshots/settler_metaTxn_uniswapV3VIP_USDT-WETH.snap | 2 +- .forge-snapshots/settler_metaTxn_uniswapV3_DAI-WETH.snap | 2 +- .forge-snapshots/settler_metaTxn_uniswapV3_USDC-WETH.snap | 2 +- .forge-snapshots/settler_metaTxn_uniswapV3_USDT-WETH.snap | 2 +- .forge-snapshots/settler_uniswapV3VIP_DAI-WETH.snap | 2 +- .forge-snapshots/settler_uniswapV3VIP_USDC-WETH.snap | 2 +- .forge-snapshots/settler_uniswapV3VIP_USDT-WETH.snap | 2 +- .forge-snapshots/settler_uniswapV3_DAI-WETH.snap | 2 +- .forge-snapshots/settler_uniswapV3_USDC-WETH.snap | 2 +- .forge-snapshots/settler_uniswapV3_USDT-WETH.snap | 2 +- .../settler_uniswapV3_buyToken_fee_full_custody_DAI-WETH.snap | 2 +- .../settler_uniswapV3_buyToken_fee_full_custody_USDC-WETH.snap | 2 +- .../settler_uniswapV3_buyToken_fee_full_custody_USDT-WETH.snap | 2 +- .../settler_uniswapV3_buyToken_fee_single_custody_DAI-WETH.snap | 2 +- ...settler_uniswapV3_buyToken_fee_single_custody_USDC-WETH.snap | 2 +- ...settler_uniswapV3_buyToken_fee_single_custody_USDT-WETH.snap | 2 +- .forge-snapshots/settler_uniswapV3_multiplex2_DAI-WETH.snap | 2 +- .forge-snapshots/settler_uniswapV3_multiplex2_USDC-WETH.snap | 2 +- .forge-snapshots/settler_uniswapV3_multiplex2_USDT-WETH.snap | 2 +- .../settler_uniswapV3_sellToken_fee_full_custody_DAI-WETH.snap | 2 +- .../settler_uniswapV3_sellToken_fee_full_custody_USDC-WETH.snap | 2 +- .../settler_uniswapV3_sellToken_fee_full_custody_USDT-WETH.snap | 2 +- 36 files changed, 36 insertions(+), 36 deletions(-) diff --git a/.forge-snapshots/allowanceHolder_uniswapV3VIP_DAI-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3VIP_DAI-WETH.snap index fd8574b02..1687ad66c 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3VIP_DAI-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3VIP_DAI-WETH.snap @@ -1 +1 @@ -118107 \ No newline at end of file +118126 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDC-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDC-WETH.snap index b9f641f22..c8a6bc4ea 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDC-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDC-WETH.snap @@ -1 +1 @@ -130694 \ No newline at end of file +130713 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDT-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDT-WETH.snap index 3a02dafaf..c2fb64aa8 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDT-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDT-WETH.snap @@ -1 +1 @@ -120928 \ No newline at end of file +120947 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_DAI-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_DAI-WETH.snap index 6bfbd9319..3e38be048 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_DAI-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_DAI-WETH.snap @@ -1 +1 @@ -118108 \ No newline at end of file +118127 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDC-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDC-WETH.snap index 38c3f77f8..80e188f2f 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDC-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDC-WETH.snap @@ -1 +1 @@ -130695 \ No newline at end of file +130714 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDT-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDT-WETH.snap index d3ebc5848..9587312e2 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDT-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDT-WETH.snap @@ -1 +1 @@ -120929 \ No newline at end of file +120948 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3_DAI-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3_DAI-WETH.snap index ea4208bd4..275e38f5e 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3_DAI-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3_DAI-WETH.snap @@ -1 +1 @@ -144294 \ No newline at end of file +144313 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3_USDC-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3_USDC-WETH.snap index f05b776f7..1da95413b 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3_USDC-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3_USDC-WETH.snap @@ -1 +1 @@ -160937 \ No newline at end of file +160956 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3_USDT-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3_USDT-WETH.snap index d8118c414..bd1aa56dc 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3_USDT-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3_USDT-WETH.snap @@ -1 +1 @@ -150971 \ No newline at end of file +150990 \ No newline at end of file diff --git a/.forge-snapshots/settler_externalMoveExecute_uniswapV3_DAI-WETH.snap b/.forge-snapshots/settler_externalMoveExecute_uniswapV3_DAI-WETH.snap index 023f541dd..47017cba3 100644 --- a/.forge-snapshots/settler_externalMoveExecute_uniswapV3_DAI-WETH.snap +++ b/.forge-snapshots/settler_externalMoveExecute_uniswapV3_DAI-WETH.snap @@ -1 +1 @@ -128791 \ No newline at end of file +128810 \ No newline at end of file diff --git a/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDC-WETH.snap b/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDC-WETH.snap index 7d1c3526a..99416f003 100644 --- a/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDC-WETH.snap +++ b/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDC-WETH.snap @@ -1 +1 @@ -139782 \ No newline at end of file +139801 \ No newline at end of file diff --git a/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDT-WETH.snap b/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDT-WETH.snap index d5d0c044e..f25c260dc 100644 --- a/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDT-WETH.snap +++ b/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDT-WETH.snap @@ -1 +1 @@ -135783 \ No newline at end of file +135802 \ No newline at end of file diff --git a/.forge-snapshots/settler_metaTxn_uniswapV3VIP_DAI-WETH.snap b/.forge-snapshots/settler_metaTxn_uniswapV3VIP_DAI-WETH.snap index db463432e..decb4f196 100644 --- a/.forge-snapshots/settler_metaTxn_uniswapV3VIP_DAI-WETH.snap +++ b/.forge-snapshots/settler_metaTxn_uniswapV3VIP_DAI-WETH.snap @@ -1 +1 @@ -128272 \ No newline at end of file +128291 \ No newline at end of file diff --git a/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDC-WETH.snap b/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDC-WETH.snap index d71eddcba..bc9d7273c 100644 --- a/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDC-WETH.snap +++ b/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDC-WETH.snap @@ -1 +1 @@ -140838 \ No newline at end of file +140857 \ No newline at end of file diff --git a/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDT-WETH.snap b/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDT-WETH.snap index 7c1b87ffa..7b9e3aa06 100644 --- a/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDT-WETH.snap +++ b/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDT-WETH.snap @@ -1 +1 @@ -131093 \ No newline at end of file +131112 \ No newline at end of file diff --git a/.forge-snapshots/settler_metaTxn_uniswapV3_DAI-WETH.snap b/.forge-snapshots/settler_metaTxn_uniswapV3_DAI-WETH.snap index 37be1bf31..212de38d8 100644 --- a/.forge-snapshots/settler_metaTxn_uniswapV3_DAI-WETH.snap +++ b/.forge-snapshots/settler_metaTxn_uniswapV3_DAI-WETH.snap @@ -1 +1 @@ -154057 \ No newline at end of file +154076 \ No newline at end of file diff --git a/.forge-snapshots/settler_metaTxn_uniswapV3_USDC-WETH.snap b/.forge-snapshots/settler_metaTxn_uniswapV3_USDC-WETH.snap index 4dc86d1b6..c55f22c09 100644 --- a/.forge-snapshots/settler_metaTxn_uniswapV3_USDC-WETH.snap +++ b/.forge-snapshots/settler_metaTxn_uniswapV3_USDC-WETH.snap @@ -1 +1 @@ -170679 \ No newline at end of file +170699 \ No newline at end of file diff --git a/.forge-snapshots/settler_metaTxn_uniswapV3_USDT-WETH.snap b/.forge-snapshots/settler_metaTxn_uniswapV3_USDT-WETH.snap index af6427320..bb505fe45 100644 --- a/.forge-snapshots/settler_metaTxn_uniswapV3_USDT-WETH.snap +++ b/.forge-snapshots/settler_metaTxn_uniswapV3_USDT-WETH.snap @@ -1 +1 @@ -160735 \ No newline at end of file +160754 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3VIP_DAI-WETH.snap b/.forge-snapshots/settler_uniswapV3VIP_DAI-WETH.snap index f72445313..ba672e12c 100644 --- a/.forge-snapshots/settler_uniswapV3VIP_DAI-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3VIP_DAI-WETH.snap @@ -1 +1 @@ -122291 \ No newline at end of file +122310 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3VIP_USDC-WETH.snap b/.forge-snapshots/settler_uniswapV3VIP_USDC-WETH.snap index 95efee473..40137e717 100644 --- a/.forge-snapshots/settler_uniswapV3VIP_USDC-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3VIP_USDC-WETH.snap @@ -1 +1 @@ -134857 \ No newline at end of file +134876 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3VIP_USDT-WETH.snap b/.forge-snapshots/settler_uniswapV3VIP_USDT-WETH.snap index b7cf64a42..b320f895e 100644 --- a/.forge-snapshots/settler_uniswapV3VIP_USDT-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3VIP_USDT-WETH.snap @@ -1 +1 @@ -125112 \ No newline at end of file +125131 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_DAI-WETH.snap b/.forge-snapshots/settler_uniswapV3_DAI-WETH.snap index 07ffe2cdb..08457d5e2 100644 --- a/.forge-snapshots/settler_uniswapV3_DAI-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_DAI-WETH.snap @@ -1 +1 @@ -147991 \ No newline at end of file +148010 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_USDC-WETH.snap b/.forge-snapshots/settler_uniswapV3_USDC-WETH.snap index 720b69360..1702944bd 100644 --- a/.forge-snapshots/settler_uniswapV3_USDC-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_USDC-WETH.snap @@ -1 +1 @@ -164613 \ No newline at end of file +164632 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_USDT-WETH.snap b/.forge-snapshots/settler_uniswapV3_USDT-WETH.snap index 4046a6867..d5e3f7d26 100644 --- a/.forge-snapshots/settler_uniswapV3_USDT-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_USDT-WETH.snap @@ -1 +1 @@ -154668 \ No newline at end of file +154687 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_DAI-WETH.snap b/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_DAI-WETH.snap index 32c162730..0d5a81a65 100644 --- a/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_DAI-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_DAI-WETH.snap @@ -1 +1 @@ -186573 \ No newline at end of file +186592 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDC-WETH.snap b/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDC-WETH.snap index 412e925f8..fbc27b8b3 100644 --- a/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDC-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDC-WETH.snap @@ -1 +1 @@ -203195 \ No newline at end of file +203214 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDT-WETH.snap b/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDT-WETH.snap index 3d07131b6..5e6ea1df2 100644 --- a/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDT-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDT-WETH.snap @@ -1 +1 @@ -193250 \ No newline at end of file +193269 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_DAI-WETH.snap b/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_DAI-WETH.snap index 6faacab56..533f58c3f 100644 --- a/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_DAI-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_DAI-WETH.snap @@ -1 +1 @@ -160872 \ No newline at end of file +160891 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDC-WETH.snap b/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDC-WETH.snap index c2c6e17a9..4b3ab23f7 100644 --- a/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDC-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDC-WETH.snap @@ -1 +1 @@ -173438 \ No newline at end of file +173457 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDT-WETH.snap b/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDT-WETH.snap index e8a631d47..5c6ceb4a3 100644 --- a/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDT-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDT-WETH.snap @@ -1 +1 @@ -163693 \ No newline at end of file +163712 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_multiplex2_DAI-WETH.snap b/.forge-snapshots/settler_uniswapV3_multiplex2_DAI-WETH.snap index 393cea293..19fb0b3be 100644 --- a/.forge-snapshots/settler_uniswapV3_multiplex2_DAI-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_multiplex2_DAI-WETH.snap @@ -1 +1 @@ -180937 \ No newline at end of file +180975 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_multiplex2_USDC-WETH.snap b/.forge-snapshots/settler_uniswapV3_multiplex2_USDC-WETH.snap index 5dda91ad3..547fc29ac 100644 --- a/.forge-snapshots/settler_uniswapV3_multiplex2_USDC-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_multiplex2_USDC-WETH.snap @@ -1 +1 @@ -201389 \ No newline at end of file +201427 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_multiplex2_USDT-WETH.snap b/.forge-snapshots/settler_uniswapV3_multiplex2_USDT-WETH.snap index c56b4e03c..d23e11a8f 100644 --- a/.forge-snapshots/settler_uniswapV3_multiplex2_USDT-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_multiplex2_USDT-WETH.snap @@ -1 +1 @@ -189861 \ No newline at end of file +189899 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_DAI-WETH.snap b/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_DAI-WETH.snap index 7e7f36262..ef71744d2 100644 --- a/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_DAI-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_DAI-WETH.snap @@ -1 +1 @@ -160313 \ No newline at end of file +160332 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDC-WETH.snap b/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDC-WETH.snap index b8f9457a6..91abd6d62 100644 --- a/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDC-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDC-WETH.snap @@ -1 +1 @@ -180991 \ No newline at end of file +181010 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDT-WETH.snap b/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDT-WETH.snap index 05614ffbb..e47e44386 100644 --- a/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDT-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDT-WETH.snap @@ -1 +1 @@ -168772 \ No newline at end of file +168791 \ No newline at end of file From 13988638be05cda9b092a7f604cb2dd8bfce6fc3 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Mon, 8 Jan 2024 16:49:28 +1100 Subject: [PATCH 09/13] additional Otc unit tests --- README.md | 56 ++++++------ src/core/OtcOrderSettlement.sol | 19 ++-- test/unit/core/OtcUnitTest.t.sol | 150 +++++++++++++++++++++++++++++-- 3 files changed, 183 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 58f2801c7..161b37534 100644 --- a/README.md +++ b/README.md @@ -32,48 +32,48 @@ Note: The following is more akin to `gasLimit` than it is `gasUsed`, this is due | ------------------- | ---------- | --------- | ------ | ------ | | 0x V4 VIP | Uniswap V3 | USDC/WETH | 125117 | 0.00% | | 0x V4 Multiplex | Uniswap V3 | USDC/WETH | 138683 | 10.84% | -| Settler VIP (warm) | Uniswap V3 | USDC/WETH | 134857 | 7.78% | -| AllowanceHolder VIP | Uniswap V3 | USDC/WETH | 130694 | 4.46% | +| Settler VIP (warm) | Uniswap V3 | USDC/WETH | 134876 | 7.80% | +| AllowanceHolder VIP | Uniswap V3 | USDC/WETH | 130713 | 4.47% | | UniswapRouter V3 | Uniswap V3 | USDC/WETH | 121137 | -3.18% | | | | | | | | 0x V4 VIP | Uniswap V3 | DAI/WETH | 112551 | 0.00% | | 0x V4 Multiplex | Uniswap V3 | DAI/WETH | 126120 | 12.06% | -| Settler VIP (warm) | Uniswap V3 | DAI/WETH | 122291 | 8.65% | -| AllowanceHolder VIP | Uniswap V3 | DAI/WETH | 118107 | 4.94% | +| Settler VIP (warm) | Uniswap V3 | DAI/WETH | 122310 | 8.67% | +| AllowanceHolder VIP | Uniswap V3 | DAI/WETH | 118126 | 4.95% | | UniswapRouter V3 | Uniswap V3 | DAI/WETH | 108571 | -3.54% | | | | | | | | 0x V4 VIP | Uniswap V3 | USDT/WETH | 115358 | 0.00% | | 0x V4 Multiplex | Uniswap V3 | USDT/WETH | 128927 | 11.76% | -| Settler VIP (warm) | Uniswap V3 | USDT/WETH | 125112 | 8.46% | -| AllowanceHolder VIP | Uniswap V3 | USDT/WETH | 120928 | 4.83% | +| Settler VIP (warm) | Uniswap V3 | USDT/WETH | 125131 | 8.47% | +| AllowanceHolder VIP | Uniswap V3 | USDT/WETH | 120947 | 4.84% | | UniswapRouter V3 | Uniswap V3 | USDT/WETH | 111250 | -3.56% | | | | | | | | Custody | DEX | Pair | Gas | % | | -------------------- | ---------- | --------- | ------ | ------- | | 0x V4 TransformERC20 | Uniswap V3 | USDC/WETH | 246374 | 0.00% | -| Settler | Uniswap V3 | USDC/WETH | 164613 | -33.19% | -| AllowanceHolder | Uniswap V3 | USDC/WETH | 160937 | -34.68% | +| Settler | Uniswap V3 | USDC/WETH | 164632 | -33.18% | +| AllowanceHolder | Uniswap V3 | USDC/WETH | 160956 | -34.67% | | | | | | | | 0x V4 TransformERC20 | Uniswap V3 | DAI/WETH | 223372 | 0.00% | -| Settler | Uniswap V3 | DAI/WETH | 147991 | -33.75% | -| AllowanceHolder | Uniswap V3 | DAI/WETH | 144294 | -35.40% | +| Settler | Uniswap V3 | DAI/WETH | 148010 | -33.74% | +| AllowanceHolder | Uniswap V3 | DAI/WETH | 144313 | -35.39% | | | | | | | | 0x V4 TransformERC20 | Uniswap V3 | USDT/WETH | 230271 | 0.00% | -| Settler | Uniswap V3 | USDT/WETH | 154668 | -32.83% | -| AllowanceHolder | Uniswap V3 | USDT/WETH | 150971 | -34.44% | +| Settler | Uniswap V3 | USDT/WETH | 154687 | -32.82% | +| AllowanceHolder | Uniswap V3 | USDT/WETH | 150990 | -34.43% | | | | | | | | MetaTransactions | DEX | Pair | Gas | % | | ---------------- | ---------- | --------- | ------ | ------- | | 0x V4 Multiplex | Uniswap V3 | USDC/WETH | 253459 | 0.00% | -| Settler | Uniswap V3 | USDC/WETH | 170679 | -32.66% | +| Settler | Uniswap V3 | USDC/WETH | 170699 | -32.65% | | | | | | | | 0x V4 Multiplex | Uniswap V3 | DAI/WETH | 240893 | 0.00% | -| Settler | Uniswap V3 | DAI/WETH | 154057 | -36.05% | +| Settler | Uniswap V3 | DAI/WETH | 154076 | -36.04% | | | | | | | | 0x V4 Multiplex | Uniswap V3 | USDT/WETH | 243700 | 0.00% | -| Settler | Uniswap V3 | USDT/WETH | 160735 | -34.04% | +| Settler | Uniswap V3 | USDT/WETH | 160754 | -34.04% | | | | | | | | OTC | DEX | Pair | Gas | % | @@ -103,33 +103,33 @@ Note: The following is more akin to `gasLimit` than it is `gasUsed`, this is due | Buy token fee | DEX | Pair | Gas | % | | ----------------- | ---------- | --------- | ------ | ----- | -| Settler - custody | Uniswap V3 | USDC/WETH | 173438 | 0.00% | +| Settler - custody | Uniswap V3 | USDC/WETH | 173457 | 0.00% | | | | | | | -| Settler - custody | Uniswap V3 | DAI/WETH | 160872 | 0.00% | +| Settler - custody | Uniswap V3 | DAI/WETH | 160891 | 0.00% | | | | | | | -| Settler - custody | Uniswap V3 | USDT/WETH | 163693 | 0.00% | +| Settler - custody | Uniswap V3 | USDT/WETH | 163712 | 0.00% | | | | | | | | Sell token fee | DEX | Pair | Gas | % | | -------------- | ---------- | --------- | ------ | ------- | -| Settler | Uniswap V3 | USDC/WETH | 180991 | 0.00% | +| Settler | Uniswap V3 | USDC/WETH | 181010 | 0.00% | | | | | | | -| Settler | Uniswap V3 | DAI/WETH | 160313 | 0.00% | +| Settler | Uniswap V3 | DAI/WETH | 160332 | 0.00% | | | | | | | -| Settler | Uniswap V3 | USDT/WETH | 168772 | 0.00% | -| Settler | Curve | USDT/WETH | 433267 | 156.72% | +| Settler | Uniswap V3 | USDT/WETH | 168791 | 0.00% | +| Settler | Curve | USDT/WETH | 433267 | 156.69% | | | | | | | | AllowanceHolder | DEX | Pair | Gas | % | | ------------------------------------ | -------------- | --------- | ------ | ------ | -| execute | Uniswap V3 VIP | USDC/WETH | 130694 | 0.00% | -| Settler - external move then execute | Uniswap V3 | USDC/WETH | 139782 | 6.95% | +| execute | Uniswap V3 VIP | USDC/WETH | 130713 | 0.00% | +| Settler - external move then execute | Uniswap V3 | USDC/WETH | 139801 | 6.95% | | | | | | | -| execute | Uniswap V3 VIP | DAI/WETH | 118107 | 0.00% | -| Settler - external move then execute | Uniswap V3 | DAI/WETH | 128791 | 9.05% | +| execute | Uniswap V3 VIP | DAI/WETH | 118126 | 0.00% | +| Settler - external move then execute | Uniswap V3 | DAI/WETH | 128810 | 9.04% | | | | | | | -| execute | Uniswap V3 VIP | USDT/WETH | 120928 | 0.00% | -| Settler - external move then execute | Uniswap V3 | USDT/WETH | 135783 | 12.28% | +| execute | Uniswap V3 VIP | USDT/WETH | 120947 | 0.00% | +| Settler - external move then execute | Uniswap V3 | USDT/WETH | 135802 | 12.28% | | | | | | | [//]: # "END TABLES" diff --git a/src/core/OtcOrderSettlement.sol b/src/core/OtcOrderSettlement.sol index e8325ccff..9502ca193 100644 --- a/src/core/OtcOrderSettlement.sol +++ b/src/core/OtcOrderSettlement.sol @@ -19,12 +19,12 @@ abstract contract OtcOrderSettlement is SettlerAbstract { bool partialFillAllowed; } - /// @dev Emitted whenever an OTC order is filled. + /// @dev Emitted whenever an Otc order is filled. /// @param orderHash The canonical hash of the order. Formed as an EIP712 struct hash. See below. /// @param maker The maker of the order. /// @param taker The taker of the order. - /// @param makerTokenFilledAmount How much maker token was filled. - /// @param takerTokenFilledAmount How much taker token was filled. + /// @param makerTokenFilledAmount Amount of maker token filled. + /// @param takerTokenFilledAmount Amount of taker token filled. event OtcOrderFilled( bytes32 indexed orderHash, address maker, @@ -79,9 +79,8 @@ abstract contract OtcOrderSettlement is SettlerAbstract { } /// @dev Settle an OtcOrder between maker and taker transfering funds directly between - /// the counterparties. Two Permit2 signatures are consumed, with the maker Permit2 containing - /// a witness of the OtcOrder. - /// This variant also includes a fee where the taker or maker pays the fee recipient + /// the counterparties. Either two Permit2 signatures are consumed, with the maker Permit2 containing + /// a witness of the OtcOrder, or AllowanceHolder is supported for the taker payment. function fillOtcOrder( address recipient, ISignatureTransfer.PermitTransferFrom memory makerPermit, @@ -101,12 +100,12 @@ abstract contract OtcOrderSettlement is SettlerAbstract { bytes32 witness = _hashConsideration(consideration); // There is no taker witness (see below) - // Maker pays + // Maker pays recipient _transferFrom(makerPermit, makerTransferDetails, maker, witness, CONSIDERATION_WITNESS, makerSig, false); // Taker pays Maker // We don't need to include a witness here. Taker is `_msgSender()`, so // `recipient` and the maker's details are already authenticated. We're just - // using PERMIT2 to move tokens, not to provide authentication. + // using Permit2 or AllowanceHolder to move tokens, not to provide authentication. _transferFrom(takerPermit, takerTransferDetails, _msgSender(), takerSig); emit OtcOrderFilled( @@ -128,6 +127,7 @@ abstract contract OtcOrderSettlement is SettlerAbstract { /// @dev Settle an OtcOrder between maker and taker transfering funds directly between /// the counterparties. Both Maker and Taker have signed the same order, and submission /// is via a third party + /// @dev `takerWitness` is not calculated nor verified here as caller is trusted function fillOtcOrderMetaTxn( address recipient, ISignatureTransfer.PermitTransferFrom memory makerPermit, @@ -151,6 +151,7 @@ abstract contract OtcOrderSettlement is SettlerAbstract { makerConsideration.counterparty = taker; bytes32 makerWitness = _hashConsideration(makerConsideration); + // Note: takerWitness is not calculated here, but in the caller code _transferFrom(makerPermit, makerTransferDetails, maker, makerWitness, CONSIDERATION_WITNESS, makerSig, false); _transferFrom(takerPermit, takerTransferDetails, taker, takerWitness, ACTIONS_AND_SLIPPAGE_WITNESS, takerSig); @@ -169,7 +170,7 @@ abstract contract OtcOrderSettlement is SettlerAbstract { /// @dev Settle an OtcOrder between maker and Settler retaining funds in this contract. /// @dev pre-condition: msgSender has been authenticated against the requestor /// One Permit2 signature is consumed, with the maker Permit2 containing a witness of the OtcOrder. - // In this variant, Maker pays Settler and Settler pays Maker + // In this variant, Maker pays recipient and Settler pays Maker function fillOtcOrderSelfFunded( address recipient, ISignatureTransfer.PermitTransferFrom memory permit, diff --git a/test/unit/core/OtcUnitTest.t.sol b/test/unit/core/OtcUnitTest.t.sol index de75efaab..4a4ec1c39 100644 --- a/test/unit/core/OtcUnitTest.t.sol +++ b/test/unit/core/OtcUnitTest.t.sol @@ -16,6 +16,14 @@ contract OtcOrderSettlementDummy is OtcOrderSettlement, Permit2Payment { Permit2Payment(permit2, feeRecipient, allowanceHolder) {} + function considerationWitnessType() external pure returns (string memory) { + return CONSIDERATION_WITNESS; + } + + function actionsAndSlippageWitnessType() external pure returns (string memory) { + return ACTIONS_AND_SLIPPAGE_WITNESS; + } + function fillOtcOrderDirectCounterparties( address recipient, ISignatureTransfer.PermitTransferFrom memory makerPermit, @@ -26,6 +34,31 @@ contract OtcOrderSettlementDummy is OtcOrderSettlement, Permit2Payment { ) external { super.fillOtcOrder(recipient, makerPermit, maker, makerSig, takerPermit, takerSig); } + + function fillOtcOrderSelf( + address recipient, + ISignatureTransfer.PermitTransferFrom memory permit, + address maker, + bytes memory makerSig, + address takerToken, + uint256 maxTakerAmount, + address msgSender + ) external { + super.fillOtcOrderSelfFunded(recipient, permit, maker, makerSig, IERC20(takerToken), maxTakerAmount, msgSender); + } + + function fillOtcOrderMeta( + address recipient, + ISignatureTransfer.PermitTransferFrom memory makerPermit, + address maker, + bytes memory makerSig, + ISignatureTransfer.PermitTransferFrom memory takerPermit, + address taker, + bytes memory takerSig, + bytes32 takerWitness + ) external { + super.fillOtcOrderMetaTxn(recipient, makerPermit, maker, makerSig, takerPermit, taker, takerSig, takerWitness); + } } contract OtcUnitTest is Utils, Test { @@ -65,7 +98,7 @@ contract OtcUnitTest is Utils, Test { ISignatureTransfer.SignatureTransferDetails({to: RECIPIENT, requestedAmount: amount}), MAKER, bytes32(0x315954c1f9717c9d14604de3c6ceb9fd601b3bd1d0b8ec397e8c2b81668a02e1), /* witness */ - "Consideration consideration)Consideration(address token,uint256 amount,address counterparty,bool partialFillAllowed)TokenPermissions(address token,uint256 amount)", + otc.considerationWitnessType(), hex"dead" ), new bytes(0) @@ -99,8 +132,7 @@ contract OtcUnitTest is Utils, Test { otc.fillOtcOrderDirectCounterparties(RECIPIENT, makerPermit, MAKER, hex"dead", takerPermit, hex"beef"); } - function testOtcDirectCounterpartiesAllowanceHolder() public { - // 🎉 + function testOtcDirectCounterpartiesViaAllowanceHolder() public { uint256 amount = 9999; ISignatureTransfer.PermitTransferFrom memory makerPermit = ISignatureTransfer.PermitTransferFrom({ permitted: ISignatureTransfer.TokenPermissions({token: TOKEN1, amount: amount}), @@ -121,7 +153,7 @@ contract OtcUnitTest is Utils, Test { ISignatureTransfer.SignatureTransferDetails({to: RECIPIENT, requestedAmount: amount}), MAKER, bytes32(0x315954c1f9717c9d14604de3c6ceb9fd601b3bd1d0b8ec397e8c2b81668a02e1), /* witness */ - "Consideration consideration)Consideration(address token,uint256 amount,address counterparty,bool partialFillAllowed)TokenPermissions(address token,uint256 amount)", + otc.considerationWitnessType(), hex"dead" ), new bytes(0) @@ -150,7 +182,7 @@ contract OtcUnitTest is Utils, Test { // ); vm.prank(ALLOWANCE_HOLDER); - address(otc).call( + (bool success,) = address(otc).call( abi.encodePacked( abi.encodeCall( otc.fillOtcOrderDirectCounterparties, (RECIPIENT, makerPermit, MAKER, hex"dead", takerPermit, hex"") @@ -158,5 +190,113 @@ contract OtcUnitTest is Utils, Test { address(this) ) // Forward on true msg.sender ); + require(success); + } + + function testOtcSelfFunded() public { + uint256 amount = 9999; + ISignatureTransfer.PermitTransferFrom memory makerPermit = ISignatureTransfer.PermitTransferFrom({ + permitted: ISignatureTransfer.TokenPermissions({token: TOKEN1, amount: amount}), + nonce: 0, + deadline: 0 + }); + + _mockExpectCall( + PERMIT2, + abi.encodeWithSelector( + bytes4(0x137c29fe), + makerPermit, + ISignatureTransfer.SignatureTransferDetails({to: RECIPIENT, requestedAmount: amount}), + MAKER, + bytes32(0x30fd0fb242892788e98ff4323f8e366b5f1dd9a4c033f5ea6ae4252f6e887e37), /* witness */ + "Consideration consideration)Consideration(address token,uint256 amount,address counterparty,bool partialFillAllowed)TokenPermissions(address token,uint256 amount)", + hex"dead" + ), + new bytes(0) + ); + + _mockExpectCall( + address(TOKEN0), abi.encodeWithSelector(IERC20.balanceOf.selector, address(otc)), abi.encode(amount) + ); + + _mockExpectCall( + address(TOKEN0), abi.encodeWithSelector(IERC20.transfer.selector, MAKER, amount), abi.encode(true) + ); + + // Broken usage of OtcOrderSettlement.OtcOrderFilled in 0.8.21 + // https://github.com/foundry-rs/foundry/issues/6206 + // vm.expectEmit(address(otc)); + // emit OtcOrderSettlement.OtcOrderFilled( + // bytes32(0x33d473fdc5cd07e2f752b882bb4f51ccc88c742aa085ebdcbd4af689aba7ffd4), + // MAKER, + // address(this), + // TOKEN1, + // TOKEN0, + // amount, + // amount + // ); + + otc.fillOtcOrderSelf(RECIPIENT, makerPermit, MAKER, hex"dead", TOKEN0, amount, address(this)); + } + + function testOtcMetaTxn() public { + uint256 amount = 9999; + ISignatureTransfer.PermitTransferFrom memory makerPermit = ISignatureTransfer.PermitTransferFrom({ + permitted: ISignatureTransfer.TokenPermissions({token: TOKEN1, amount: amount}), + nonce: 0, + deadline: 0 + }); + ISignatureTransfer.PermitTransferFrom memory takerPermit = ISignatureTransfer.PermitTransferFrom({ + permitted: ISignatureTransfer.TokenPermissions({token: TOKEN0, amount: amount}), + nonce: 0, + deadline: 0 + }); + + // Maker payment via Permit2 + _mockExpectCall( + PERMIT2, + abi.encodeWithSelector( + bytes4(0x137c29fe), + makerPermit, + ISignatureTransfer.SignatureTransferDetails({to: RECIPIENT, requestedAmount: amount}), + MAKER, + bytes32(0x315954c1f9717c9d14604de3c6ceb9fd601b3bd1d0b8ec397e8c2b81668a02e1), /* witness */ + otc.considerationWitnessType(), + hex"dead" + ), + new bytes(0) + ); + + // Taker payment via Permit2 + _mockExpectCall( + PERMIT2, + abi.encodeWithSelector( + bytes4(0x137c29fe), + takerPermit, + ISignatureTransfer.SignatureTransferDetails({to: MAKER, requestedAmount: amount}), + address(this), /* taker */ + bytes32(0x0000000000000000000000000000000000000000000000000000000000000000), /* witness */ + otc.actionsAndSlippageWitnessType(), + hex"beef" + ), + new bytes(0) + ); + + // Broken usage of OtcOrderSettlement.OtcOrderFilled in 0.8.21 + // https://github.com/foundry-rs/foundry/issues/6206 + // vm.expectEmit(address(otc)); + // emit OtcOrderSettlement.OtcOrderFilled( + // bytes32(0xbee0e2de3e64ecfe06fe7118215a033ac40a8d6a508d60b81cd9ac6addd6e11e), + // MAKER, + // address(this), + // TOKEN1, + // TOKEN0, + // amount, + // amount + // ); + + otc.fillOtcOrderMeta( + RECIPIENT, makerPermit, MAKER, hex"dead", takerPermit, address(this), hex"beef", bytes32(0x00) + ); } } From 9dd92a2240a59cecd28c7c37f685c0666412c1bc Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Mon, 8 Jan 2024 17:17:59 +1100 Subject: [PATCH 10/13] cleanup --- src/core/UniswapV3.sol | 8 +-- test/unit/AllowanceHolderUnitTest.t.sol | 75 +++++++++++++++---------- test/unit/Utils.sol | 11 +--- test/unit/core/BasicUnitTest.t.sol | 1 - test/unit/core/UniswapV2UnitTest.t.sol | 1 - test/unit/core/UniswapV3UnitTest.t.sol | 1 - 6 files changed, 47 insertions(+), 50 deletions(-) diff --git a/src/core/UniswapV3.sol b/src/core/UniswapV3.sol index 2c26ea2ac..222dd8a49 100644 --- a/src/core/UniswapV3.sol +++ b/src/core/UniswapV3.sol @@ -3,9 +3,7 @@ pragma solidity ^0.8.21; import {VIPBase} from "./VIPBase.sol"; import {Permit2PaymentAbstract} from "./Permit2Payment.sol"; -// import {InvalidSender} from "./SettlerErrors.sol"; - -error InvalidSender(address sender); +import {InvalidSender} from "./SettlerErrors.sol"; import {IERC20} from "../IERC20.sol"; import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol"; @@ -346,9 +344,7 @@ abstract contract UniswapV3 is SettlerAbstract, VIPBase { payer := calldataload(add(data.offset, 0x1f)) } // Only a valid pool contract can call this function. - if (msg.sender != address(_toPool(token0, fee, token1))) { - revert InvalidSender(address(_toPool(token0, fee, token1))); - } + if (msg.sender != address(_toPool(token0, fee, token1))) revert InvalidSender(); bytes calldata permit2Data = data[SWAP_CALLBACK_PREFIX_DATA_SIZE:]; // Pay the amount owed to the pool. diff --git a/test/unit/AllowanceHolderUnitTest.t.sol b/test/unit/AllowanceHolderUnitTest.t.sol index c3eeb2c32..91b8f1364 100644 --- a/test/unit/AllowanceHolderUnitTest.t.sol +++ b/test/unit/AllowanceHolderUnitTest.t.sol @@ -5,11 +5,11 @@ import {AllowanceHolder} from "../../src/AllowanceHolder.sol"; import {IAllowanceHolder} from "../../src/IAllowanceHolder.sol"; import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol"; +import {IERC20} from "../../src/IERC20.sol"; import {Utils} from "./Utils.sol"; import {Test} from "forge-std/Test.sol"; -import {VmSafe} from "forge-std/Vm.sol"; contract AllowanceHolderDummy is AllowanceHolder { function getAllowed(address operator, address owner, address token) external view returns (uint256 r) { @@ -23,10 +23,10 @@ contract AllowanceHolderDummy is AllowanceHolder { contract AllowanceHolderUnitTest is Utils, Test { AllowanceHolderDummy ah; - address OPERATOR = _deterministicAddress("OPERATOR"); - address TOKEN = _deterministicAddress("TOKEN"); + address OPERATOR = _createNamedRejectionDummy("OPERATOR"); + address TOKEN = _createNamedRejectionDummy("TOKEN"); address OWNER = address(this); - address RECIPIENT = _deterministicAddress("RECIPIENT"); + address RECIPIENT = _createNamedRejectionDummy("RECIPIENT"); uint256 AMOUNT = 123456; function setUp() public { @@ -39,62 +39,76 @@ contract AllowanceHolderUnitTest is Utils, Test { } function testPermitAuthorised() public { - address token = _createNamedDummy("TOKEN"); - address operator = address(this); - - ah.setAllowed(operator, OWNER, token, AMOUNT); IAllowanceHolder.TransferDetails[] memory transferDetails = new IAllowanceHolder.TransferDetails[](1); - transferDetails[0] = IAllowanceHolder.TransferDetails(token, RECIPIENT, AMOUNT); + transferDetails[0] = IAllowanceHolder.TransferDetails({token: TOKEN, recipient: RECIPIENT, amount: AMOUNT}); + + ah.setAllowed(OPERATOR, OWNER, TOKEN, AMOUNT); + assertEq(ah.getAllowed(OPERATOR, OWNER, TOKEN), AMOUNT); - assertEq(ah.getAllowed(operator, OWNER, token), AMOUNT); + _mockExpectCall( + TOKEN, abi.encodeWithSelector(IERC20.transferFrom.selector, OWNER, RECIPIENT, AMOUNT), new bytes(0) + ); + vm.prank(OPERATOR); assertTrue(ah.holderTransferFrom(OWNER, transferDetails)); - assertEq(ah.getAllowed(operator, OWNER, token), 0); + + assertEq(ah.getAllowed(OPERATOR, OWNER, TOKEN), 0); } function testPermitAuthorisedMultipleConsumption() public { - address token = _createNamedDummy("TOKEN"); - address operator = address(this); - - ah.setAllowed(operator, OWNER, token, AMOUNT); IAllowanceHolder.TransferDetails[] memory transferDetails = new IAllowanceHolder.TransferDetails[](1); - transferDetails[0] = IAllowanceHolder.TransferDetails(token, RECIPIENT, AMOUNT / 2); - assertEq(ah.getAllowed(operator, OWNER, token), AMOUNT); + // Note: we use amount / 2 (+ / - 1) to register multiple mocks + transferDetails[0] = + IAllowanceHolder.TransferDetails({token: TOKEN, recipient: RECIPIENT, amount: (AMOUNT / 2) + 1}); + + ah.setAllowed(OPERATOR, OWNER, TOKEN, AMOUNT); + assertEq(ah.getAllowed(OPERATOR, OWNER, TOKEN), AMOUNT); + _mockExpectCall( + TOKEN, + abi.encodeWithSelector(IERC20.transferFrom.selector, OWNER, RECIPIENT, (AMOUNT / 2) + 1), + new bytes(0) + ); + vm.prank(OPERATOR); assertTrue(ah.holderTransferFrom(OWNER, transferDetails)); - assertEq(ah.getAllowed(operator, OWNER, token), AMOUNT / 2); + assertEq(ah.getAllowed(OPERATOR, OWNER, TOKEN), (AMOUNT / 2) - 1); + + _mockExpectCall( + TOKEN, + abi.encodeWithSelector(IERC20.transferFrom.selector, OWNER, RECIPIENT, (AMOUNT / 2) - 1), + new bytes(0) + ); + transferDetails[0] = + IAllowanceHolder.TransferDetails({token: TOKEN, recipient: RECIPIENT, amount: (AMOUNT / 2) - 1}); + vm.prank(OPERATOR); assertTrue(ah.holderTransferFrom(OWNER, transferDetails)); - assertEq(ah.getAllowed(operator, OWNER, token), 0); + + assertEq(ah.getAllowed(OPERATOR, OWNER, TOKEN), 0); } function testPermitUnauthorisedOperator() public { - ah.setAllowed(OPERATOR, OWNER, TOKEN, AMOUNT); IAllowanceHolder.TransferDetails[] memory transferDetails = new IAllowanceHolder.TransferDetails[](1); transferDetails[0] = IAllowanceHolder.TransferDetails({token: TOKEN, recipient: RECIPIENT, amount: AMOUNT}); + ah.setAllowed(OPERATOR, OWNER, TOKEN, AMOUNT); vm.expectRevert(); ah.holderTransferFrom(OWNER, transferDetails); } function testPermitUnauthorisedAmount() public { - address token = _createNamedDummy("TOKEN"); - address operator = address(this); - - ah.setAllowed(operator, OWNER, token, AMOUNT); IAllowanceHolder.TransferDetails[] memory transferDetails = new IAllowanceHolder.TransferDetails[](1); - transferDetails[0] = IAllowanceHolder.TransferDetails({token: token, recipient: RECIPIENT, amount: AMOUNT + 1}); + transferDetails[0] = IAllowanceHolder.TransferDetails({token: TOKEN, recipient: RECIPIENT, amount: AMOUNT + 1}); + ah.setAllowed(OPERATOR, OWNER, TOKEN, AMOUNT); vm.expectRevert(); + vm.prank(OPERATOR); ah.holderTransferFrom(OWNER, transferDetails); } function testPermitUnauthorisedToken() public { - address token = _createNamedDummy("TOKEN"); - address operator = address(this); - - ah.setAllowed(operator, OWNER, token, AMOUNT); IAllowanceHolder.TransferDetails[] memory transferDetails = new IAllowanceHolder.TransferDetails[](1); transferDetails[0] = IAllowanceHolder.TransferDetails({token: TOKEN, recipient: RECIPIENT, amount: AMOUNT}); + ah.setAllowed(OPERATOR, OWNER, address(0xdead), AMOUNT); vm.expectRevert(); ah.holderTransferFrom(OWNER, transferDetails); } @@ -119,13 +133,12 @@ contract AllowanceHolderUnitTest is Utils, Test { } function testPermitExecute() public { - address token = _createNamedDummy("TOKEN"); address target = _createNamedRejectionDummy("TARGET"); address operator = target; uint256 value = 999; ISignatureTransfer.TokenPermissions[] memory permits = new ISignatureTransfer.TokenPermissions[](1); - permits[0] = ISignatureTransfer.TokenPermissions({token: token, amount: AMOUNT}); + permits[0] = ISignatureTransfer.TokenPermissions({token: TOKEN, amount: AMOUNT}); bytes memory data = hex"deadbeef"; _mockExpectCall(address(target), abi.encodePacked(data, address(this)), abi.encode(true)); diff --git a/test/unit/Utils.sol b/test/unit/Utils.sol index 925e53dea..cf2f61e41 100644 --- a/test/unit/Utils.sol +++ b/test/unit/Utils.sol @@ -2,11 +2,7 @@ pragma solidity ^0.8.21; import {Test} from "forge-std/Test.sol"; -import {Vm, VmSafe} from "forge-std/Vm.sol"; - -contract FallbackDummy { - fallback() external payable {} -} +import {Vm} from "forge-std/Vm.sol"; contract RejectionFallbackDummy { fallback() external payable { @@ -22,11 +18,6 @@ contract Utils { _vm.label(a, name); } - function _createNamedDummy(string memory name) internal returns (address a) { - a = address(new FallbackDummy()); - _vm.label(a, name); - } - function _createNamedRejectionDummy(string memory name) internal returns (address a) { a = address(new RejectionFallbackDummy()); _vm.label(a, name); diff --git a/test/unit/core/BasicUnitTest.t.sol b/test/unit/core/BasicUnitTest.t.sol index 31934ea17..10df59eb0 100644 --- a/test/unit/core/BasicUnitTest.t.sol +++ b/test/unit/core/BasicUnitTest.t.sol @@ -8,7 +8,6 @@ import {IERC20} from "../../../src/IERC20.sol"; import {Utils} from "../Utils.sol"; import {Test} from "forge-std/Test.sol"; -import {VmSafe} from "forge-std/Vm.sol"; contract BasicDummy is Basic, Permit2Payment { constructor(address permit2, address feeRecipient, address allowanceHolder) diff --git a/test/unit/core/UniswapV2UnitTest.t.sol b/test/unit/core/UniswapV2UnitTest.t.sol index b646c6e90..6fec93e08 100644 --- a/test/unit/core/UniswapV2UnitTest.t.sol +++ b/test/unit/core/UniswapV2UnitTest.t.sol @@ -7,7 +7,6 @@ import {Utils} from "../Utils.sol"; import {IERC20} from "../../../src/IERC20.sol"; import {Test} from "forge-std/Test.sol"; -import {VmSafe} from "forge-std/Vm.sol"; contract UniswapV2Dummy is UniswapV2 { function sell(address recipient, bytes memory encodedPath, uint256 bips, uint256 minBuyAmount) public { diff --git a/test/unit/core/UniswapV3UnitTest.t.sol b/test/unit/core/UniswapV3UnitTest.t.sol index ef2d73780..31c75053c 100644 --- a/test/unit/core/UniswapV3UnitTest.t.sol +++ b/test/unit/core/UniswapV3UnitTest.t.sol @@ -11,7 +11,6 @@ import {Utils} from "../Utils.sol"; import {IERC20} from "../../../src/IERC20.sol"; import {Test} from "forge-std/Test.sol"; -import {VmSafe} from "forge-std/Vm.sol"; contract UniswapV3Dummy is UniswapV3, Permit2Payment { constructor(address uniFactory, bytes32 poolInit, address permit2, address feeRecipient, address allowanceHolder) From ad89f71f48489be419631c562a1e52159d263b44 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Tue, 9 Jan 2024 15:11:01 +1100 Subject: [PATCH 11/13] MakerPSM unit test --- test/unit/core/MakerPSMUnitTest.t.sol | 59 +++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 test/unit/core/MakerPSMUnitTest.t.sol diff --git a/test/unit/core/MakerPSMUnitTest.t.sol b/test/unit/core/MakerPSMUnitTest.t.sol new file mode 100644 index 000000000..dee665b32 --- /dev/null +++ b/test/unit/core/MakerPSMUnitTest.t.sol @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.21; + +import {MakerPSM, IPSM} from "../../../src/core/MakerPSM.sol"; + +import {IERC20, IERC20Meta} from "../../../src/IERC20.sol"; +import {Utils} from "../Utils.sol"; + +import {Test} from "forge-std/Test.sol"; + +contract MakerPSMDummy is MakerPSM { + constructor(address dai) MakerPSM(dai) {} + + function sellToPool(address recipient, uint256 bips, address psm, address gemToken) public { + super.makerPsmSellGem(recipient, bips, IPSM(psm), IERC20Meta(gemToken)); + } + + function buyFromPool(address recipient, uint256 bips, address psm, address gemToken) public { + super.makerPsmBuyGem(recipient, bips, IPSM(psm), IERC20Meta(gemToken)); + } +} + +contract MakerPSMUnitTest is Utils, Test { + MakerPSMDummy psm; + address POOL = _createNamedRejectionDummy("POOL"); + address RECIPIENT = _createNamedRejectionDummy("RECIPIENT"); + address PSM = _createNamedRejectionDummy("PSM"); + address DAI = _createNamedRejectionDummy("DAI"); + address TOKEN = _createNamedRejectionDummy("TOKEN"); + + function setUp() public { + psm = new MakerPSMDummy(DAI); + } + + function testMakerPSMSell() public { + uint256 bips = 10_000; + uint256 amount = 99999; + + _mockExpectCall(TOKEN, abi.encodeWithSelector(IERC20.balanceOf.selector, address(psm)), abi.encode(amount)); + _mockExpectCall(TOKEN, abi.encodeWithSelector(IERC20.allowance.selector, address(psm), PSM), abi.encode(amount)); + _mockExpectCall(PSM, abi.encodeWithSelector(IPSM.gemJoin.selector), abi.encode(PSM)); + _mockExpectCall(PSM, abi.encodeWithSelector(IPSM.sellGem.selector, RECIPIENT, amount), abi.encode(true)); + + psm.sellToPool(RECIPIENT, bips, PSM, TOKEN); + } + + function testMakerPSMBuy() public { + uint256 bips = 10_000; + uint256 amount = 99999; + + _mockExpectCall(DAI, abi.encodeWithSelector(IERC20.balanceOf.selector, address(psm)), abi.encode(amount)); + _mockExpectCall(DAI, abi.encodeWithSelector(IERC20.allowance.selector, address(psm), PSM), abi.encode(amount)); + _mockExpectCall(TOKEN, abi.encodeWithSelector(IERC20Meta.decimals.selector), abi.encode(18)); + _mockExpectCall(PSM, abi.encodeWithSelector(IPSM.tout.selector), abi.encode(100)); + _mockExpectCall(PSM, abi.encodeWithSelector(IPSM.buyGem.selector, RECIPIENT, 99998), abi.encode(true)); + + psm.buyFromPool(RECIPIENT, bips, PSM, TOKEN); + } +} From b1cb268f535443df3071875d853fe63230c9088d Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Thu, 18 Jan 2024 10:50:49 +1000 Subject: [PATCH 12/13] update comment --- src/core/UniswapV2.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/UniswapV2.sol b/src/core/UniswapV2.sol index 73ce8986c..9f8f95bf4 100644 --- a/src/core/UniswapV2.sol +++ b/src/core/UniswapV2.sol @@ -187,7 +187,7 @@ abstract contract UniswapV2 is VIPBase { // final swap if fromPool { - // perform swap at the fromPool sending bought tokens to settler + // perform swap at the fromPool sending bought tokens to recipient mstore(add(swapCalldata, 0x44), and(0xffffffffffffffffffffffffffffffffffffffff, recipient)) if iszero(call(gas(), fromPool, 0, swapCalldata, 0xa4, 0, 0)) { bubbleRevert(swapCalldata) } } From 351dfca44a2a97abf70346072aace40370e52ff8 Mon Sep 17 00:00:00 2001 From: Jacob Evans Date: Wed, 31 Jan 2024 07:52:31 +1000 Subject: [PATCH 13/13] shared Permit2PaymentBase and updated snapshots --- ...allowanceHolder_uniswapV3VIP_DAI-WETH.snap | 2 +- ...llowanceHolder_uniswapV3VIP_USDC-WETH.snap | 2 +- ...llowanceHolder_uniswapV3VIP_USDT-WETH.snap | 2 +- ...Holder_uniswapV3VIP_contract_DAI-WETH.snap | 2 +- ...older_uniswapV3VIP_contract_USDC-WETH.snap | 2 +- ...older_uniswapV3VIP_contract_USDT-WETH.snap | 2 +- .../allowanceHolder_uniswapV3_DAI-WETH.snap | 2 +- .../allowanceHolder_uniswapV3_USDC-WETH.snap | 2 +- .../allowanceHolder_uniswapV3_USDT-WETH.snap | 2 +- ...xternalMoveExecute_uniswapV3_DAI-WETH.snap | 2 +- ...ternalMoveExecute_uniswapV3_USDC-WETH.snap | 2 +- ...ternalMoveExecute_uniswapV3_USDT-WETH.snap | 2 +- ...settler_metaTxn_uniswapV3VIP_DAI-WETH.snap | 2 +- ...ettler_metaTxn_uniswapV3VIP_USDC-WETH.snap | 2 +- ...ettler_metaTxn_uniswapV3VIP_USDT-WETH.snap | 2 +- .../settler_metaTxn_uniswapV3_DAI-WETH.snap | 2 +- .../settler_metaTxn_uniswapV3_USDC-WETH.snap | 2 +- .../settler_metaTxn_uniswapV3_USDT-WETH.snap | 2 +- .../settler_uniswapV3VIP_DAI-WETH.snap | 2 +- .../settler_uniswapV3VIP_USDC-WETH.snap | 2 +- .../settler_uniswapV3VIP_USDT-WETH.snap | 2 +- .../settler_uniswapV3_DAI-WETH.snap | 2 +- .../settler_uniswapV3_USDC-WETH.snap | 2 +- .../settler_uniswapV3_USDT-WETH.snap | 2 +- ...V3_buyToken_fee_full_custody_DAI-WETH.snap | 2 +- ...3_buyToken_fee_full_custody_USDC-WETH.snap | 2 +- ...3_buyToken_fee_full_custody_USDT-WETH.snap | 2 +- ..._buyToken_fee_single_custody_DAI-WETH.snap | 2 +- ...buyToken_fee_single_custody_USDC-WETH.snap | 2 +- ...buyToken_fee_single_custody_USDT-WETH.snap | 2 +- ...settler_uniswapV3_multiplex2_DAI-WETH.snap | 2 +- ...ettler_uniswapV3_multiplex2_USDC-WETH.snap | 2 +- ...ettler_uniswapV3_multiplex2_USDT-WETH.snap | 2 +- ...3_sellToken_fee_full_custody_DAI-WETH.snap | 2 +- ..._sellToken_fee_full_custody_USDC-WETH.snap | 2 +- ..._sellToken_fee_full_custody_USDT-WETH.snap | 2 +- ...oEx_uniswapV3VIP_multiplex1_USDC-WETH.snap | 2 +- ...oEx_uniswapV3VIP_multiplex2_USDC-WETH.snap | 2 +- README.md | 58 +++++++++---------- src/core/Permit2Payment.sol | 40 ++++++++----- test/unit/Utils.sol | 2 +- 41 files changed, 93 insertions(+), 83 deletions(-) diff --git a/.forge-snapshots/allowanceHolder_uniswapV3VIP_DAI-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3VIP_DAI-WETH.snap index 1687ad66c..fd8574b02 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3VIP_DAI-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3VIP_DAI-WETH.snap @@ -1 +1 @@ -118126 \ No newline at end of file +118107 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDC-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDC-WETH.snap index c8a6bc4ea..b9f641f22 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDC-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDC-WETH.snap @@ -1 +1 @@ -130713 \ No newline at end of file +130694 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDT-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDT-WETH.snap index c2fb64aa8..3a02dafaf 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDT-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3VIP_USDT-WETH.snap @@ -1 +1 @@ -120947 \ No newline at end of file +120928 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_DAI-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_DAI-WETH.snap index 3e38be048..6bfbd9319 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_DAI-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_DAI-WETH.snap @@ -1 +1 @@ -118127 \ No newline at end of file +118108 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDC-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDC-WETH.snap index 80e188f2f..38c3f77f8 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDC-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDC-WETH.snap @@ -1 +1 @@ -130714 \ No newline at end of file +130695 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDT-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDT-WETH.snap index 9587312e2..d3ebc5848 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDT-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3VIP_contract_USDT-WETH.snap @@ -1 +1 @@ -120948 \ No newline at end of file +120929 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3_DAI-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3_DAI-WETH.snap index 275e38f5e..ea4208bd4 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3_DAI-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3_DAI-WETH.snap @@ -1 +1 @@ -144313 \ No newline at end of file +144294 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3_USDC-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3_USDC-WETH.snap index 1da95413b..f05b776f7 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3_USDC-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3_USDC-WETH.snap @@ -1 +1 @@ -160956 \ No newline at end of file +160937 \ No newline at end of file diff --git a/.forge-snapshots/allowanceHolder_uniswapV3_USDT-WETH.snap b/.forge-snapshots/allowanceHolder_uniswapV3_USDT-WETH.snap index bd1aa56dc..d8118c414 100644 --- a/.forge-snapshots/allowanceHolder_uniswapV3_USDT-WETH.snap +++ b/.forge-snapshots/allowanceHolder_uniswapV3_USDT-WETH.snap @@ -1 +1 @@ -150990 \ No newline at end of file +150971 \ No newline at end of file diff --git a/.forge-snapshots/settler_externalMoveExecute_uniswapV3_DAI-WETH.snap b/.forge-snapshots/settler_externalMoveExecute_uniswapV3_DAI-WETH.snap index 47017cba3..023f541dd 100644 --- a/.forge-snapshots/settler_externalMoveExecute_uniswapV3_DAI-WETH.snap +++ b/.forge-snapshots/settler_externalMoveExecute_uniswapV3_DAI-WETH.snap @@ -1 +1 @@ -128810 \ No newline at end of file +128791 \ No newline at end of file diff --git a/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDC-WETH.snap b/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDC-WETH.snap index 99416f003..7d1c3526a 100644 --- a/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDC-WETH.snap +++ b/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDC-WETH.snap @@ -1 +1 @@ -139801 \ No newline at end of file +139782 \ No newline at end of file diff --git a/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDT-WETH.snap b/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDT-WETH.snap index f25c260dc..d5d0c044e 100644 --- a/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDT-WETH.snap +++ b/.forge-snapshots/settler_externalMoveExecute_uniswapV3_USDT-WETH.snap @@ -1 +1 @@ -135802 \ No newline at end of file +135783 \ No newline at end of file diff --git a/.forge-snapshots/settler_metaTxn_uniswapV3VIP_DAI-WETH.snap b/.forge-snapshots/settler_metaTxn_uniswapV3VIP_DAI-WETH.snap index decb4f196..db463432e 100644 --- a/.forge-snapshots/settler_metaTxn_uniswapV3VIP_DAI-WETH.snap +++ b/.forge-snapshots/settler_metaTxn_uniswapV3VIP_DAI-WETH.snap @@ -1 +1 @@ -128291 \ No newline at end of file +128272 \ No newline at end of file diff --git a/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDC-WETH.snap b/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDC-WETH.snap index bc9d7273c..d71eddcba 100644 --- a/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDC-WETH.snap +++ b/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDC-WETH.snap @@ -1 +1 @@ -140857 \ No newline at end of file +140838 \ No newline at end of file diff --git a/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDT-WETH.snap b/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDT-WETH.snap index 7b9e3aa06..7c1b87ffa 100644 --- a/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDT-WETH.snap +++ b/.forge-snapshots/settler_metaTxn_uniswapV3VIP_USDT-WETH.snap @@ -1 +1 @@ -131112 \ No newline at end of file +131093 \ No newline at end of file diff --git a/.forge-snapshots/settler_metaTxn_uniswapV3_DAI-WETH.snap b/.forge-snapshots/settler_metaTxn_uniswapV3_DAI-WETH.snap index 212de38d8..37be1bf31 100644 --- a/.forge-snapshots/settler_metaTxn_uniswapV3_DAI-WETH.snap +++ b/.forge-snapshots/settler_metaTxn_uniswapV3_DAI-WETH.snap @@ -1 +1 @@ -154076 \ No newline at end of file +154057 \ No newline at end of file diff --git a/.forge-snapshots/settler_metaTxn_uniswapV3_USDC-WETH.snap b/.forge-snapshots/settler_metaTxn_uniswapV3_USDC-WETH.snap index c55f22c09..4dc86d1b6 100644 --- a/.forge-snapshots/settler_metaTxn_uniswapV3_USDC-WETH.snap +++ b/.forge-snapshots/settler_metaTxn_uniswapV3_USDC-WETH.snap @@ -1 +1 @@ -170699 \ No newline at end of file +170679 \ No newline at end of file diff --git a/.forge-snapshots/settler_metaTxn_uniswapV3_USDT-WETH.snap b/.forge-snapshots/settler_metaTxn_uniswapV3_USDT-WETH.snap index bb505fe45..a7c9b9614 100644 --- a/.forge-snapshots/settler_metaTxn_uniswapV3_USDT-WETH.snap +++ b/.forge-snapshots/settler_metaTxn_uniswapV3_USDT-WETH.snap @@ -1 +1 @@ -160754 \ No newline at end of file +160734 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3VIP_DAI-WETH.snap b/.forge-snapshots/settler_uniswapV3VIP_DAI-WETH.snap index ba672e12c..f72445313 100644 --- a/.forge-snapshots/settler_uniswapV3VIP_DAI-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3VIP_DAI-WETH.snap @@ -1 +1 @@ -122310 \ No newline at end of file +122291 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3VIP_USDC-WETH.snap b/.forge-snapshots/settler_uniswapV3VIP_USDC-WETH.snap index 40137e717..95efee473 100644 --- a/.forge-snapshots/settler_uniswapV3VIP_USDC-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3VIP_USDC-WETH.snap @@ -1 +1 @@ -134876 \ No newline at end of file +134857 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3VIP_USDT-WETH.snap b/.forge-snapshots/settler_uniswapV3VIP_USDT-WETH.snap index b320f895e..b7cf64a42 100644 --- a/.forge-snapshots/settler_uniswapV3VIP_USDT-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3VIP_USDT-WETH.snap @@ -1 +1 @@ -125131 \ No newline at end of file +125112 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_DAI-WETH.snap b/.forge-snapshots/settler_uniswapV3_DAI-WETH.snap index 08457d5e2..07ffe2cdb 100644 --- a/.forge-snapshots/settler_uniswapV3_DAI-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_DAI-WETH.snap @@ -1 +1 @@ -148010 \ No newline at end of file +147991 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_USDC-WETH.snap b/.forge-snapshots/settler_uniswapV3_USDC-WETH.snap index 1702944bd..720b69360 100644 --- a/.forge-snapshots/settler_uniswapV3_USDC-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_USDC-WETH.snap @@ -1 +1 @@ -164632 \ No newline at end of file +164613 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_USDT-WETH.snap b/.forge-snapshots/settler_uniswapV3_USDT-WETH.snap index d5e3f7d26..4046a6867 100644 --- a/.forge-snapshots/settler_uniswapV3_USDT-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_USDT-WETH.snap @@ -1 +1 @@ -154687 \ No newline at end of file +154668 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_DAI-WETH.snap b/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_DAI-WETH.snap index 0d5a81a65..32c162730 100644 --- a/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_DAI-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_DAI-WETH.snap @@ -1 +1 @@ -186592 \ No newline at end of file +186573 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDC-WETH.snap b/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDC-WETH.snap index fbc27b8b3..412e925f8 100644 --- a/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDC-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDC-WETH.snap @@ -1 +1 @@ -203214 \ No newline at end of file +203195 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDT-WETH.snap b/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDT-WETH.snap index 5e6ea1df2..3d07131b6 100644 --- a/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDT-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_buyToken_fee_full_custody_USDT-WETH.snap @@ -1 +1 @@ -193269 \ No newline at end of file +193250 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_DAI-WETH.snap b/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_DAI-WETH.snap index 533f58c3f..6faacab56 100644 --- a/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_DAI-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_DAI-WETH.snap @@ -1 +1 @@ -160891 \ No newline at end of file +160872 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDC-WETH.snap b/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDC-WETH.snap index 4b3ab23f7..c2c6e17a9 100644 --- a/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDC-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDC-WETH.snap @@ -1 +1 @@ -173457 \ No newline at end of file +173438 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDT-WETH.snap b/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDT-WETH.snap index 5c6ceb4a3..e8a631d47 100644 --- a/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDT-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_buyToken_fee_single_custody_USDT-WETH.snap @@ -1 +1 @@ -163712 \ No newline at end of file +163693 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_multiplex2_DAI-WETH.snap b/.forge-snapshots/settler_uniswapV3_multiplex2_DAI-WETH.snap index 19fb0b3be..393cea293 100644 --- a/.forge-snapshots/settler_uniswapV3_multiplex2_DAI-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_multiplex2_DAI-WETH.snap @@ -1 +1 @@ -180975 \ No newline at end of file +180937 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_multiplex2_USDC-WETH.snap b/.forge-snapshots/settler_uniswapV3_multiplex2_USDC-WETH.snap index 547fc29ac..5dda91ad3 100644 --- a/.forge-snapshots/settler_uniswapV3_multiplex2_USDC-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_multiplex2_USDC-WETH.snap @@ -1 +1 @@ -201427 \ No newline at end of file +201389 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_multiplex2_USDT-WETH.snap b/.forge-snapshots/settler_uniswapV3_multiplex2_USDT-WETH.snap index d23e11a8f..c56b4e03c 100644 --- a/.forge-snapshots/settler_uniswapV3_multiplex2_USDT-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_multiplex2_USDT-WETH.snap @@ -1 +1 @@ -189899 \ No newline at end of file +189861 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_DAI-WETH.snap b/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_DAI-WETH.snap index ef71744d2..7e7f36262 100644 --- a/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_DAI-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_DAI-WETH.snap @@ -1 +1 @@ -160332 \ No newline at end of file +160313 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDC-WETH.snap b/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDC-WETH.snap index 91abd6d62..b8f9457a6 100644 --- a/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDC-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDC-WETH.snap @@ -1 +1 @@ -181010 \ No newline at end of file +180991 \ No newline at end of file diff --git a/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDT-WETH.snap b/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDT-WETH.snap index e47e44386..05614ffbb 100644 --- a/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDT-WETH.snap +++ b/.forge-snapshots/settler_uniswapV3_sellToken_fee_full_custody_USDT-WETH.snap @@ -1 +1 @@ -168791 \ No newline at end of file +168772 \ No newline at end of file diff --git a/.forge-snapshots/zeroEx_uniswapV3VIP_multiplex1_USDC-WETH.snap b/.forge-snapshots/zeroEx_uniswapV3VIP_multiplex1_USDC-WETH.snap index 9fcfa14af..80c0300cd 100644 --- a/.forge-snapshots/zeroEx_uniswapV3VIP_multiplex1_USDC-WETH.snap +++ b/.forge-snapshots/zeroEx_uniswapV3VIP_multiplex1_USDC-WETH.snap @@ -1 +1 @@ -138683 \ No newline at end of file +138686 \ No newline at end of file diff --git a/.forge-snapshots/zeroEx_uniswapV3VIP_multiplex2_USDC-WETH.snap b/.forge-snapshots/zeroEx_uniswapV3VIP_multiplex2_USDC-WETH.snap index dee14e418..a25bd5209 100644 --- a/.forge-snapshots/zeroEx_uniswapV3VIP_multiplex2_USDC-WETH.snap +++ b/.forge-snapshots/zeroEx_uniswapV3VIP_multiplex2_USDC-WETH.snap @@ -1 +1 @@ -181465 \ No newline at end of file +181468 \ No newline at end of file diff --git a/README.md b/README.md index 161b37534..5edb84986 100644 --- a/README.md +++ b/README.md @@ -31,49 +31,49 @@ Note: The following is more akin to `gasLimit` than it is `gasUsed`, this is due | VIP | DEX | Pair | Gas | % | | ------------------- | ---------- | --------- | ------ | ------ | | 0x V4 VIP | Uniswap V3 | USDC/WETH | 125117 | 0.00% | -| 0x V4 Multiplex | Uniswap V3 | USDC/WETH | 138683 | 10.84% | -| Settler VIP (warm) | Uniswap V3 | USDC/WETH | 134876 | 7.80% | -| AllowanceHolder VIP | Uniswap V3 | USDC/WETH | 130713 | 4.47% | +| 0x V4 Multiplex | Uniswap V3 | USDC/WETH | 138686 | 10.85% | +| Settler VIP (warm) | Uniswap V3 | USDC/WETH | 134857 | 7.78% | +| AllowanceHolder VIP | Uniswap V3 | USDC/WETH | 130694 | 4.46% | | UniswapRouter V3 | Uniswap V3 | USDC/WETH | 121137 | -3.18% | | | | | | | | 0x V4 VIP | Uniswap V3 | DAI/WETH | 112551 | 0.00% | | 0x V4 Multiplex | Uniswap V3 | DAI/WETH | 126120 | 12.06% | -| Settler VIP (warm) | Uniswap V3 | DAI/WETH | 122310 | 8.67% | -| AllowanceHolder VIP | Uniswap V3 | DAI/WETH | 118126 | 4.95% | +| Settler VIP (warm) | Uniswap V3 | DAI/WETH | 122291 | 8.65% | +| AllowanceHolder VIP | Uniswap V3 | DAI/WETH | 118107 | 4.94% | | UniswapRouter V3 | Uniswap V3 | DAI/WETH | 108571 | -3.54% | | | | | | | | 0x V4 VIP | Uniswap V3 | USDT/WETH | 115358 | 0.00% | | 0x V4 Multiplex | Uniswap V3 | USDT/WETH | 128927 | 11.76% | -| Settler VIP (warm) | Uniswap V3 | USDT/WETH | 125131 | 8.47% | -| AllowanceHolder VIP | Uniswap V3 | USDT/WETH | 120947 | 4.84% | +| Settler VIP (warm) | Uniswap V3 | USDT/WETH | 125112 | 8.46% | +| AllowanceHolder VIP | Uniswap V3 | USDT/WETH | 120928 | 4.83% | | UniswapRouter V3 | Uniswap V3 | USDT/WETH | 111250 | -3.56% | | | | | | | | Custody | DEX | Pair | Gas | % | | -------------------- | ---------- | --------- | ------ | ------- | | 0x V4 TransformERC20 | Uniswap V3 | USDC/WETH | 246374 | 0.00% | -| Settler | Uniswap V3 | USDC/WETH | 164632 | -33.18% | -| AllowanceHolder | Uniswap V3 | USDC/WETH | 160956 | -34.67% | +| Settler | Uniswap V3 | USDC/WETH | 164613 | -33.19% | +| AllowanceHolder | Uniswap V3 | USDC/WETH | 160937 | -34.68% | | | | | | | | 0x V4 TransformERC20 | Uniswap V3 | DAI/WETH | 223372 | 0.00% | -| Settler | Uniswap V3 | DAI/WETH | 148010 | -33.74% | -| AllowanceHolder | Uniswap V3 | DAI/WETH | 144313 | -35.39% | +| Settler | Uniswap V3 | DAI/WETH | 147991 | -33.75% | +| AllowanceHolder | Uniswap V3 | DAI/WETH | 144294 | -35.40% | | | | | | | | 0x V4 TransformERC20 | Uniswap V3 | USDT/WETH | 230271 | 0.00% | -| Settler | Uniswap V3 | USDT/WETH | 154687 | -32.82% | -| AllowanceHolder | Uniswap V3 | USDT/WETH | 150990 | -34.43% | +| Settler | Uniswap V3 | USDT/WETH | 154668 | -32.83% | +| AllowanceHolder | Uniswap V3 | USDT/WETH | 150971 | -34.44% | | | | | | | | MetaTransactions | DEX | Pair | Gas | % | | ---------------- | ---------- | --------- | ------ | ------- | | 0x V4 Multiplex | Uniswap V3 | USDC/WETH | 253459 | 0.00% | -| Settler | Uniswap V3 | USDC/WETH | 170699 | -32.65% | +| Settler | Uniswap V3 | USDC/WETH | 170679 | -32.66% | | | | | | | | 0x V4 Multiplex | Uniswap V3 | DAI/WETH | 240893 | 0.00% | -| Settler | Uniswap V3 | DAI/WETH | 154076 | -36.04% | +| Settler | Uniswap V3 | DAI/WETH | 154057 | -36.05% | | | | | | | | 0x V4 Multiplex | Uniswap V3 | USDT/WETH | 243700 | 0.00% | -| Settler | Uniswap V3 | USDT/WETH | 160754 | -34.04% | +| Settler | Uniswap V3 | USDT/WETH | 160734 | -34.04% | | | | | | | | OTC | DEX | Pair | Gas | % | @@ -103,33 +103,33 @@ Note: The following is more akin to `gasLimit` than it is `gasUsed`, this is due | Buy token fee | DEX | Pair | Gas | % | | ----------------- | ---------- | --------- | ------ | ----- | -| Settler - custody | Uniswap V3 | USDC/WETH | 173457 | 0.00% | +| Settler - custody | Uniswap V3 | USDC/WETH | 173438 | 0.00% | | | | | | | -| Settler - custody | Uniswap V3 | DAI/WETH | 160891 | 0.00% | +| Settler - custody | Uniswap V3 | DAI/WETH | 160872 | 0.00% | | | | | | | -| Settler - custody | Uniswap V3 | USDT/WETH | 163712 | 0.00% | +| Settler - custody | Uniswap V3 | USDT/WETH | 163693 | 0.00% | | | | | | | | Sell token fee | DEX | Pair | Gas | % | | -------------- | ---------- | --------- | ------ | ------- | -| Settler | Uniswap V3 | USDC/WETH | 181010 | 0.00% | +| Settler | Uniswap V3 | USDC/WETH | 180991 | 0.00% | | | | | | | -| Settler | Uniswap V3 | DAI/WETH | 160332 | 0.00% | +| Settler | Uniswap V3 | DAI/WETH | 160313 | 0.00% | | | | | | | -| Settler | Uniswap V3 | USDT/WETH | 168791 | 0.00% | -| Settler | Curve | USDT/WETH | 433267 | 156.69% | +| Settler | Uniswap V3 | USDT/WETH | 168772 | 0.00% | +| Settler | Curve | USDT/WETH | 433267 | 156.72% | | | | | | | | AllowanceHolder | DEX | Pair | Gas | % | | ------------------------------------ | -------------- | --------- | ------ | ------ | -| execute | Uniswap V3 VIP | USDC/WETH | 130713 | 0.00% | -| Settler - external move then execute | Uniswap V3 | USDC/WETH | 139801 | 6.95% | +| execute | Uniswap V3 VIP | USDC/WETH | 130694 | 0.00% | +| Settler - external move then execute | Uniswap V3 | USDC/WETH | 139782 | 6.95% | | | | | | | -| execute | Uniswap V3 VIP | DAI/WETH | 118126 | 0.00% | -| Settler - external move then execute | Uniswap V3 | DAI/WETH | 128810 | 9.04% | +| execute | Uniswap V3 VIP | DAI/WETH | 118107 | 0.00% | +| Settler - external move then execute | Uniswap V3 | DAI/WETH | 128791 | 9.05% | | | | | | | -| execute | Uniswap V3 VIP | USDT/WETH | 120947 | 0.00% | -| Settler - external move then execute | Uniswap V3 | USDT/WETH | 135802 | 12.28% | +| execute | Uniswap V3 VIP | USDT/WETH | 120928 | 0.00% | +| Settler - external move then execute | Uniswap V3 | USDT/WETH | 135783 | 12.28% | | | | | | | [//]: # "END TABLES" diff --git a/src/core/Permit2Payment.sol b/src/core/Permit2Payment.sol index ae5e59d43..1d3288c73 100644 --- a/src/core/Permit2Payment.sol +++ b/src/core/Permit2Payment.sol @@ -138,15 +138,32 @@ abstract contract Permit2BatchPaymentAbstract is ContextAbstract { ) internal virtual; } -abstract contract Permit2BatchPayment is Permit2BatchPaymentAbstract, AllowanceHolderContext { +contract Permit2PaymentBase is AllowanceHolderContext { + /// @dev Permit2 address + ISignatureTransfer internal immutable _PERMIT2; + address internal immutable _FEE_RECIPIENT; + + constructor(address permit2, address feeRecipient, address allowanceHolder) + AllowanceHolderContext(allowanceHolder) + { + _PERMIT2 = ISignatureTransfer(permit2); + _FEE_RECIPIENT = feeRecipient; + } +} + +abstract contract Permit2BatchPayment is Permit2PaymentBase, Permit2BatchPaymentAbstract { using UnsafeMath for uint256; using UnsafeArray for IAllowanceHolder.TransferDetails[]; using UnsafeArray for ISignatureTransfer.TokenPermissions[]; using UnsafeArray for ISignatureTransfer.SignatureTransferDetails[]; - /// @dev Permit2 address - ISignatureTransfer private immutable _PERMIT2; - address private immutable _FEE_RECIPIENT; + constructor(address permit2, address feeRecipient, address allowanceHolder) + Permit2PaymentBase(permit2, feeRecipient, allowanceHolder) + {} + + function isRestrictedTarget(address target) internal view override returns (bool) { + return target == address(_PERMIT2) || target == address(allowanceHolder); + } function _permitToTransferDetails(ISignatureTransfer.PermitBatchTransferFrom memory permit, address recipient) internal @@ -247,27 +264,20 @@ abstract contract Permit2BatchPayment is Permit2BatchPaymentAbstract, AllowanceH } } -abstract contract Permit2Payment is Permit2PaymentAbstract, AllowanceHolderContext { +abstract contract Permit2Payment is Permit2PaymentBase, Permit2PaymentAbstract { using UnsafeMath for uint256; using UnsafeArray for IAllowanceHolder.TransferDetails[]; using UnsafeArray for ISignatureTransfer.TokenPermissions[]; using UnsafeArray for ISignatureTransfer.SignatureTransferDetails[]; - /// @dev Permit2 address - ISignatureTransfer private immutable _PERMIT2; - address private immutable _FEE_RECIPIENT; + constructor(address permit2, address feeRecipient, address allowanceHolder) + Permit2PaymentBase(permit2, feeRecipient, allowanceHolder) + {} function isRestrictedTarget(address target) internal view override returns (bool) { return target == address(_PERMIT2) || target == address(allowanceHolder); } - constructor(address permit2, address feeRecipient, address allowanceHolder) - AllowanceHolderContext(allowanceHolder) - { - _PERMIT2 = ISignatureTransfer(permit2); - _FEE_RECIPIENT = feeRecipient; - } - function _permitToTransferDetails(ISignatureTransfer.PermitTransferFrom memory permit, address recipient) internal pure diff --git a/test/unit/Utils.sol b/test/unit/Utils.sol index cf2f61e41..71d7652a4 100644 --- a/test/unit/Utils.sol +++ b/test/unit/Utils.sol @@ -6,7 +6,7 @@ import {Vm} from "forge-std/Vm.sol"; contract RejectionFallbackDummy { fallback() external payable { - require(false, "Rejected"); + revert("Rejected"); } }