From 9b4afdb8b04240a85893824204e48eed37e80d29 Mon Sep 17 00:00:00 2001 From: alex0207s Date: Thu, 2 Jan 2025 23:21:36 +0800 Subject: [PATCH] add gas measurement to tests and refactor some test structures --- .gitmodules | 2 - lib/forge-std | 2 +- snapshots/AdminManagement.json | 4 + snapshots/AllowanceTarget.json | 8 + snapshots/Asset.json | 11 + snapshots/CoordinatedTaker.json | 6 + snapshots/EIP712.json | 5 + snapshots/GenericSwap.json | 9 + snapshots/LimitOrderSwap.json | 20 ++ snapshots/Ownable.json | 5 + snapshots/RFQ.json | 15 ++ snapshots/SmartOrderStrategy.json | 11 + snapshots/TokenCollector.json | 7 + test/AllowanceTarget.t.sol | 34 ++- test/{ => abstracts}/AdminManagement.t.sol | 16 +- test/abstracts/EIP712.t.sol | 33 +-- test/abstracts/Ownable.t.sol | 39 +++- test/forkMainnet/GenericSwap.t.sol | 67 +++--- .../LimitOrderSwap/CancelOrder.t.sol | 27 ++- .../LimitOrderSwap/CoordinatedTaker.t.sol | 70 ++++-- test/forkMainnet/LimitOrderSwap/Fill.t.sol | 118 ++++++++--- .../LimitOrderSwap/FullOrKill.t.sol | 10 +- .../LimitOrderSwap/GroupFill.t.sol | 27 ++- .../LimitOrderSwap/Management.t.sol | 16 +- test/forkMainnet/LimitOrderSwap/Setup.t.sol | 3 +- .../LimitOrderSwap/Validation.t.sol | 17 +- test/forkMainnet/RFQ.t.sol | 199 ++++++++++-------- .../forkMainnet/SmartOrderStrategy/AMMs.t.sol | 21 +- .../SmartOrderStrategy/IntegrationV6.t.sol | 19 +- .../SmartOrderStrategy/Setup.t.sol | 3 +- .../SmartOrderStrategy/Validation.t.sol | 23 +- test/forkMainnet/TokenCollector.t.sol | 167 ++++++++------- test/libraries/Asset.t.sol | 69 +++--- 33 files changed, 716 insertions(+), 367 deletions(-) create mode 100644 snapshots/AdminManagement.json create mode 100644 snapshots/AllowanceTarget.json create mode 100644 snapshots/Asset.json create mode 100644 snapshots/CoordinatedTaker.json create mode 100644 snapshots/EIP712.json create mode 100644 snapshots/GenericSwap.json create mode 100644 snapshots/LimitOrderSwap.json create mode 100644 snapshots/Ownable.json create mode 100644 snapshots/RFQ.json create mode 100644 snapshots/SmartOrderStrategy.json create mode 100644 snapshots/TokenCollector.json rename test/{ => abstracts}/AdminManagement.t.sol (83%) diff --git a/.gitmodules b/.gitmodules index 9c687ea0..f5a571c9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,8 +1,6 @@ [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts url = https://github.com/OpenZeppelin/openzeppelin-contracts - branch = v5.0.2 [submodule "lib/forge-std"] path = lib/forge-std url = https://github.com/foundry-rs/forge-std - branch = v1.3.0 diff --git a/lib/forge-std b/lib/forge-std index 066ff16c..1eea5bae 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 066ff16c5c03e6f931cd041fd366bc4be1fae82a +Subproject commit 1eea5bae12ae557d589f9f0f0edae2faa47cb262 diff --git a/snapshots/AdminManagement.json b/snapshots/AdminManagement.json new file mode 100644 index 00000000..7758a0a4 --- /dev/null +++ b/snapshots/AdminManagement.json @@ -0,0 +1,4 @@ +{ + "approveTokens(): testApproveTokens": "136967", + "rescueTokens(): testRescueTokens": "86851" +} diff --git a/snapshots/AllowanceTarget.json b/snapshots/AllowanceTarget.json new file mode 100644 index 00000000..bf145c19 --- /dev/null +++ b/snapshots/AllowanceTarget.json @@ -0,0 +1,8 @@ +{ + "pause(): testSpendFromUserToAfterUnpause": "27667", + "spendFromUserTo(): testSpendFromUserTo": "63533", + "spendFromUserTo(): testSpendFromUserToAfterUnpause": "63533", + "spendFromUserTo(): testSpendFromUserToWithDeflationaryToken": "91576", + "spendFromUserTo(): testSpendFromUserToWithNoReturnValueToken": "68943", + "unpause(): testSpendFromUserToAfterUnpause": "27645" +} diff --git a/snapshots/Asset.json b/snapshots/Asset.json new file mode 100644 index 00000000..b43e3e74 --- /dev/null +++ b/snapshots/Asset.json @@ -0,0 +1,11 @@ +{ + "getBalance(): testGetBalance": "6107", + "getBalance(): testGetBalance(ETH_ADDRESS)": "764", + "getBalance(): testGetBalance(ZERO_ADDRESS)": "781", + "isETH(): testIsETH(ETH_ADDRESS)": "441", + "isETH(): testIsETH2(ZERO_ADDRESS)": "458", + "transferTo(): testDoNothingIfTransferToSelf": "22431", + "transferTo(): testDoNothingIfTransferWithZeroAmount": "22419", + "transferTo(): testTransferETH": "57012", + "transferTo(): testTransferToken": "51501" +} diff --git a/snapshots/CoordinatedTaker.json b/snapshots/CoordinatedTaker.json new file mode 100644 index 00000000..04cdbe83 --- /dev/null +++ b/snapshots/CoordinatedTaker.json @@ -0,0 +1,6 @@ +{ + "approveTokens(): testApproveTokens": "54307", + "setCoordinator(): testSetCoordinator": "30043", + "submitLimitOrderFill(): testFillWithETH": "192971", + "submitLimitOrderFill(): testFillWithPermission": "256473" +} diff --git a/snapshots/EIP712.json b/snapshots/EIP712.json new file mode 100644 index 00000000..a3e532e1 --- /dev/null +++ b/snapshots/EIP712.json @@ -0,0 +1,5 @@ +{ + "EIP712_DOMAIN_SEPARATOR(): testDomainSeparatorOnChain": "276", + "EIP712_DOMAIN_SEPARATOR(): testDomainSeparatorOnDifferentChain": "626", + "getEIP712Hash(): testGetEIP712Hash": "828" +} diff --git a/snapshots/GenericSwap.json b/snapshots/GenericSwap.json new file mode 100644 index 00000000..af002a54 --- /dev/null +++ b/snapshots/GenericSwap.json @@ -0,0 +1,9 @@ +{ + "executeSwap(): testGenericSwapWithUniswap": "251909", + "executeSwap(): testLeaveOneWeiWithMultipleUsers(the first deposit)": "251909", + "executeSwap(): testLeaveOneWeiWithMultipleUsers(the second deposit)": "208263", + "executeSwap(): testSwapWithETHInput": "100382", + "executeSwap(): testSwapWithETHOutput": "130241", + "executeSwap(): testSwapWithLessOutputButWithinTolerance": "160688", + "executeSwapWithSig(): testGenericSwapRelayed": "283756" +} diff --git a/snapshots/LimitOrderSwap.json b/snapshots/LimitOrderSwap.json new file mode 100644 index 00000000..eb51510a --- /dev/null +++ b/snapshots/LimitOrderSwap.json @@ -0,0 +1,20 @@ +{ + "cancelOrder(): testCancelOrder": "52753", + "fillLimitOrder(): testFillLimitOrderWithETH": "158029", + "fillLimitOrder(): testFillWithBetterTakingAmount": "215471", + "fillLimitOrder(): testFillWithBetterTakingAmountButGetAdjusted": "215705", + "fillLimitOrder(): testFillWithETHRefund": "165308", + "fillLimitOrder(): testFillWithLargerVolumeAndSettleAsManyAsPossible": "215693", + "fillLimitOrder(): testFillWithoutMakerSigForVerifiedOrder": "215471", + "fillLimitOrder(): testFillWithoutMakerSigForVerifiedOrder(without makerSig)": "120983", + "fillLimitOrder(): testFullyFillLimitOrder": "215471", + "fillLimitOrder(): testFullyFillLimitOrderUsingAMM": "235046", + "fillLimitOrder(): testPartiallyFillLimitOrder": "215471", + "fillLimitOrderFullOrKill(): testFillWithFOK": "215449", + "fillLimitOrderGroup(): testGroupFillRingTrade": "291749", + "fillLimitOrderGroup(): testGroupFillWithPartialWETHUnwrap": "277818", + "fillLimitOrderGroup(): testGroupFillWithTakerPrefundETH": "198117", + "fillLimitOrderGroup(): testGroupFillWithWETHUnwrap": "198117", + "fillLimitOrderGroup(): testPartialFillLargeOrderWithSmallOrders": "265910", + "testGroupFillWithProfit: fillLimitOrderGroup()": "225225" +} diff --git a/snapshots/Ownable.json b/snapshots/Ownable.json new file mode 100644 index 00000000..af4731bf --- /dev/null +++ b/snapshots/Ownable.json @@ -0,0 +1,5 @@ +{ + "acceptOwnership(): testAcceptOwnership": "28390", + "nominateNewOwner(): testNominateNewOwner": "47132", + "renounceOwnership(): testRenounceOwnership": "25351" +} diff --git a/snapshots/RFQ.json b/snapshots/RFQ.json new file mode 100644 index 00000000..d0cc412c --- /dev/null +++ b/snapshots/RFQ.json @@ -0,0 +1,15 @@ +{ + "cancelRFQOffer(): testCancelRFQOffer": "49848", + "fillRFQ(): testFillRFQ": "219666", + "fillRFQ(): testFillRFQTakerGetRawETH": "238797", + "fillRFQ(): testFillRFQWithRawETH": "159563", + "fillRFQ(): testFillRFQWithRawETHAndReceiveWETH": "174630", + "fillRFQ(): testFillRFQWithTakerApproveAllowanceTarget": "187631", + "fillRFQ(): testFillRFQWithWETH": "222131", + "fillRFQ(): testFillRFQWithWETHAndReceiveWETH": "208373", + "fillRFQ(): testFillRFQWithZeroFee": "192740", + "fillRFQ(): testFillWithContract": "223381", + "fillRFQ(): testPartialFill": "219826", + "fillRFQWithSig(): testFillRFQByTakerSig": "228592", + "setFeeCollector(): testSetFeeCollector": "30099" +} diff --git a/snapshots/SmartOrderStrategy.json b/snapshots/SmartOrderStrategy.json new file mode 100644 index 00000000..cb1bcf5e --- /dev/null +++ b/snapshots/SmartOrderStrategy.json @@ -0,0 +1,11 @@ +{ + "executeStrategy(): testMultipleAMMs": "610173", + "executeStrategy(): testUniswapV2WithWETHUnwrap": "142340", + "executeStrategy(): testUniswapV3WithAmountReplace": "161811", + "executeStrategy(): testUniswapV3WithMaxAmountReplace": "161607", + "executeStrategy(): testUniswapV3WithoutAmountReplace": "155017", + "executeStrategy(): testV6LOIntegration": "180661", + "executeStrategy(): testV6RFQIntegration": "183394", + "executeStrategy(): testV6RFQIntegrationWhenMakerTokenIsETH": "149739", + "executeStrategy(): testV6RFQIntegrationWhenTakerTokenIsETH": "204956" +} diff --git a/snapshots/TokenCollector.json b/snapshots/TokenCollector.json new file mode 100644 index 00000000..ea9cd193 --- /dev/null +++ b/snapshots/TokenCollector.json @@ -0,0 +1,7 @@ +{ + "collect(): testCollectByAllowanceTarget": "66338", + "collect(): testCollectByPermit2AllowanceTransfer": "102281", + "collect(): testCollectByPermit2SignatureTransfer": "94388", + "collect(): testCollectByTokenApproval": "58597", + "collect(): testCollectByTokenPermit": "93729" +} diff --git a/test/AllowanceTarget.t.sol b/test/AllowanceTarget.t.sol index 2539528b..003edf89 100644 --- a/test/AllowanceTarget.t.sol +++ b/test/AllowanceTarget.t.sol @@ -32,7 +32,6 @@ contract AllowanceTargetTest is BalanceUtil { MockNoRevertERC20 noRevertERC20 = new MockNoRevertERC20(); IERC20[] tokens = [IERC20(mockERC20), IERC20(address(deflationaryERC20)), IERC20(address(noReturnERC20)), IERC20(address(noRevertERC20))]; - // effectively a "beforeEach" block function setUp() public { // Deploy allowanceTarget = new AllowanceTarget(allowanceTargetOwner, trusted); @@ -58,16 +57,20 @@ contract AllowanceTargetTest is BalanceUtil { function testCannotSpendFromUserInsufficientBalanceWithNoReturnValueToken() public { uint256 userBalance = noReturnERC20.balanceOf(user); + + vm.startPrank(authorized); vm.expectRevert("ERC20: transfer amount exceeds balance"); - vm.prank(authorized); allowanceTarget.spendFromUserTo(user, address(noReturnERC20), recipient, userBalance + 1); + vm.stopPrank(); } function testCannotSpendFromUserInsufficientBalanceWithReturnFalseToken() public { uint256 userBalance = noRevertERC20.balanceOf(user); + + vm.startPrank(authorized); vm.expectRevert(abi.encodeWithSelector(SafeERC20.SafeERC20FailedOperation.selector, address(noRevertERC20))); - vm.prank(authorized); allowanceTarget.spendFromUserTo(user, address(noRevertERC20), recipient, userBalance + 1); + vm.stopPrank(); } function testCannotPauseIfNotOwner() public { @@ -76,16 +79,18 @@ contract AllowanceTargetTest is BalanceUtil { } function testCannotUnpauseIfNotOwner() public { - vm.prank(allowanceTargetOwner, allowanceTargetOwner); + vm.startPrank(allowanceTargetOwner); allowanceTarget.pause(); + vm.stopPrank(); vm.expectRevert(Ownable.NotOwner.selector); allowanceTarget.unpause(); } function testCannotSpendIfPaused() public { - vm.prank(allowanceTargetOwner, allowanceTargetOwner); + vm.startPrank(allowanceTargetOwner); allowanceTarget.pause(); + vm.stopPrank(); vm.expectRevert(Pausable.EnforcedPause.selector); allowanceTarget.spendFromUserTo(user, address(mockERC20), recipient, 1234); @@ -96,8 +101,11 @@ contract AllowanceTargetTest is BalanceUtil { Snapshot memory toBalance = BalanceSnapshot.take({ owner: recipient, token: address(mockERC20) }); uint256 amount = 100; - vm.prank(authorized); + + vm.startPrank(authorized); allowanceTarget.spendFromUserTo(user, address(mockERC20), recipient, amount); + vm.stopPrank(); + vm.snapshotGasLastCall("AllowanceTarget", "spendFromUserTo(): testSpendFromUserTo"); fromBalance.assertChange(-int256(amount)); toBalance.assertChange(int256(amount)); @@ -111,11 +119,15 @@ contract AllowanceTargetTest is BalanceUtil { vm.startPrank(allowanceTargetOwner); allowanceTarget.pause(); + vm.snapshotGasLastCall("AllowanceTarget", "pause(): testSpendFromUserToAfterUnpause"); allowanceTarget.unpause(); + vm.snapshotGasLastCall("AllowanceTarget", "unpause(): testSpendFromUserToAfterUnpause"); vm.stopPrank(); - vm.prank(authorized); + vm.startPrank(authorized); allowanceTarget.spendFromUserTo(user, address(mockERC20), recipient, amount); + vm.stopPrank(); + vm.snapshotGasLastCall("AllowanceTarget", "spendFromUserTo(): testSpendFromUserToAfterUnpause"); fromBalance.assertChange(-int256(amount)); toBalance.assertChange(int256(amount)); @@ -126,8 +138,10 @@ contract AllowanceTargetTest is BalanceUtil { Snapshot memory toBalance = BalanceSnapshot.take({ owner: recipient, token: address(noReturnERC20) }); uint256 amount = 100; - vm.prank(authorized); + vm.startPrank(authorized); allowanceTarget.spendFromUserTo(user, address(noReturnERC20), recipient, amount); + vm.stopPrank(); + vm.snapshotGasLastCall("AllowanceTarget", "spendFromUserTo(): testSpendFromUserToWithNoReturnValueToken"); fromBalance.assertChange(-int256(amount)); toBalance.assertChange(int256(amount)); @@ -138,8 +152,10 @@ contract AllowanceTargetTest is BalanceUtil { Snapshot memory toBalance = BalanceSnapshot.take({ owner: recipient, token: address(deflationaryERC20) }); uint256 amount = 100; - vm.prank(authorized); + vm.startPrank(authorized); allowanceTarget.spendFromUserTo(user, address(deflationaryERC20), recipient, 100); + vm.stopPrank(); + vm.snapshotGasLastCall("AllowanceTarget", "spendFromUserTo(): testSpendFromUserToWithDeflationaryToken"); uint256 expectedReceive = 99; // MockDeflationaryERC20 will burn 1% during each transfer fromBalance.assertChange(-int256(amount)); diff --git a/test/AdminManagement.t.sol b/test/abstracts/AdminManagement.t.sol similarity index 83% rename from test/AdminManagement.t.sol rename to test/abstracts/AdminManagement.t.sol index 4b4ccdc1..4805c399 100644 --- a/test/AdminManagement.t.sol +++ b/test/abstracts/AdminManagement.t.sol @@ -31,17 +31,19 @@ contract AdminManagementTest is BalanceUtil { } function testApproveTokens() public { - for (uint256 i = 0; i < tokens.length; ++i) { - for (uint256 j = 0; j < spenders.length; ++j) { + for (uint256 i; i < tokens.length; ++i) { + for (uint256 j; j < spenders.length; ++j) { assertEq(IERC20(tokens[i]).allowance(address(contractWithAdmin), spenders[j]), 0); } } - vm.prank(owner); + vm.startPrank(owner); contractWithAdmin.approveTokens(tokens, spenders); + vm.stopPrank(); + vm.snapshotGasLastCall("AdminManagement", "approveTokens(): testApproveTokens"); - for (uint256 i = 0; i < tokens.length; ++i) { - for (uint256 j = 0; j < spenders.length; ++j) { + for (uint256 i; i < tokens.length; ++i) { + for (uint256 j; j < spenders.length; ++j) { assertEq(IERC20(tokens[i]).allowance(address(contractWithAdmin), spenders[j]), type(uint256).max); } } @@ -63,8 +65,10 @@ contract AdminManagementTest is BalanceUtil { assertEq(IERC20(tokens[1]).balanceOf(address(contractWithAdmin)), amount2); assertEq(IERC20(tokens[1]).balanceOf(rescueTarget), 0); - vm.prank(owner); + vm.startPrank(owner); contractWithAdmin.rescueTokens(tokens, rescueTarget); + vm.stopPrank(); + vm.snapshotGasLastCall("AdminManagement", "rescueTokens(): testRescueTokens"); assertEq(IERC20(tokens[0]).balanceOf(address(contractWithAdmin)), 0); assertEq(IERC20(tokens[0]).balanceOf(rescueTarget), amount1); diff --git a/test/abstracts/EIP712.t.sol b/test/abstracts/EIP712.t.sol index 423e18be..a7653642 100644 --- a/test/abstracts/EIP712.t.sol +++ b/test/abstracts/EIP712.t.sol @@ -5,48 +5,55 @@ import { Test } from "forge-std/Test.sol"; import { EIP712 } from "contracts/abstracts/EIP712.sol"; contract EIP712Test is Test { - EIP712TestContract eip712; + EIP712Harness eip712Harness; // Dummy struct hash for testing bytes32 public constant DUMMY_STRUCT_HASH = keccak256("DummyStruct(string message)"); function setUp() public { - eip712 = new EIP712TestContract(); + eip712Harness = new EIP712Harness(); } function testOriginalChainId() public { uint256 chainId = block.chainid; - assertEq(eip712.originalChainId(), chainId); + assertEq(eip712Harness.originalChainId(), chainId); } function testOriginalDomainSeparator() public { - bytes32 expectedDomainSeparator = eip712.calculateDomainSeparator(); - assertEq(eip712.originalEIP712DomainSeparator(), expectedDomainSeparator); + bytes32 expectedDomainSeparator = eip712Harness.calculateDomainSeparator(); + assertEq(eip712Harness.originalEIP712DomainSeparator(), expectedDomainSeparator); } function testGetEIP712Hash() public { bytes32 structHash = DUMMY_STRUCT_HASH; - bytes32 domainSeparator = eip712.calculateDomainSeparator(); - bytes32 expectedEIP712Hash = keccak256(abi.encodePacked(eip712.EIP191_HEADER(), domainSeparator, structHash)); + bytes32 domainSeparator = eip712Harness.calculateDomainSeparator(); + bytes32 expectedEIP712Hash = keccak256(abi.encodePacked(eip712Harness.EIP191_HEADER(), domainSeparator, structHash)); - assertEq(eip712.getEIP712HashWrap(structHash), expectedEIP712Hash); + assertEq(eip712Harness.exposedGetEIP712Hash(structHash), expectedEIP712Hash); + vm.snapshotGasLastCall("EIP712", "getEIP712Hash(): testGetEIP712Hash"); } function testDomainSeparatorOnDifferentChain() public { uint256 chainId = block.chainid + 1234; vm.chainId(chainId); - bytes32 newDomainSeparator = eip712.calculateDomainSeparator(); - assertEq(eip712.EIP712_DOMAIN_SEPARATOR(), newDomainSeparator, "Domain separator should match the expected value on a different chain"); + bytes32 newDomainSeparator = eip712Harness.calculateDomainSeparator(); + assertEq(eip712Harness.EIP712_DOMAIN_SEPARATOR(), newDomainSeparator, "Domain separator should match the expected value on a different chain"); + vm.snapshotGasLastCall("EIP712", "EIP712_DOMAIN_SEPARATOR(): testDomainSeparatorOnDifferentChain"); + } + + function testDomainSeparatorOnChain() public { + eip712Harness.EIP712_DOMAIN_SEPARATOR(); + vm.snapshotGasLastCall("EIP712", "EIP712_DOMAIN_SEPARATOR(): testDomainSeparatorOnChain"); } } -contract EIP712TestContract is EIP712 { +contract EIP712Harness is EIP712 { function calculateDomainSeparator() external view returns (bytes32) { return keccak256(abi.encode(EIP712_TYPE_HASH, keccak256(bytes(EIP712_NAME)), keccak256(bytes(EIP712_VERSION)), block.chainid, address(this))); } - function getEIP712HashWrap(bytes32 structHash) public view returns (bytes32) { - return super.getEIP712Hash(structHash); + function exposedGetEIP712Hash(bytes32 structHash) public view returns (bytes32) { + return getEIP712Hash(structHash); } } diff --git a/test/abstracts/Ownable.t.sol b/test/abstracts/Ownable.t.sol index 6c4b5c2a..02f89d5a 100644 --- a/test/abstracts/Ownable.t.sol +++ b/test/abstracts/Ownable.t.sol @@ -13,8 +13,9 @@ contract OwnableTest is Test { address otherAccount = makeAddr("otherAccount"); function setUp() public { - vm.prank(owner); + vm.startPrank(owner); ownable = new OwnableTestContract(owner); + vm.stopPrank(); } function testOwnableInitialState() public { @@ -27,64 +28,80 @@ contract OwnableTest is Test { } function testCannotAcceptOwnershipWithOtherAccount() public { - vm.prank(owner); + vm.startPrank(owner); ownable.nominateNewOwner(newOwner); + vm.stopPrank(); - vm.prank(otherAccount); + vm.startPrank(otherAccount); vm.expectRevert(Ownable.NotNominated.selector); ownable.acceptOwnership(); + vm.stopPrank(); } function testCannotRenounceOwnershipWithNominatedOwner() public { - vm.prank(owner); + vm.startPrank(owner); ownable.nominateNewOwner(newOwner); + vm.stopPrank(); - vm.prank(owner); + vm.startPrank(owner); vm.expectRevert(Ownable.NominationExists.selector); ownable.renounceOwnership(); + vm.stopPrank(); } function testCannotRenounceOwnershipWithOtherAccount() public { - vm.prank(otherAccount); + vm.startPrank(otherAccount); vm.expectRevert(Ownable.NotOwner.selector); ownable.renounceOwnership(); + vm.stopPrank(); } function testCannotNominateNewOwnerWithOtherAccount() public { - vm.prank(otherAccount); + vm.startPrank(otherAccount); vm.expectRevert(Ownable.NotOwner.selector); ownable.nominateNewOwner(newOwner); + vm.stopPrank(); } function testAcceptOwnership() public { - vm.prank(owner); + vm.startPrank(owner); ownable.nominateNewOwner(newOwner); + vm.stopPrank(); assertEq(ownable.nominatedOwner(), newOwner); - vm.prank(newOwner); vm.expectEmit(true, true, false, false); emit Ownable.OwnerChanged(owner, newOwner); + + vm.startPrank(newOwner); ownable.acceptOwnership(); + vm.stopPrank(); + vm.snapshotGasLastCall("Ownable", "acceptOwnership(): testAcceptOwnership"); assertEq(ownable.owner(), newOwner); assertEq(ownable.nominatedOwner(), address(0)); } function testRenounceOwnership() public { - vm.prank(owner); vm.expectEmit(true, true, false, false); emit Ownable.OwnerChanged(owner, address(0)); + + vm.startPrank(owner); ownable.renounceOwnership(); + vm.stopPrank(); + vm.snapshotGasLastCall("Ownable", "renounceOwnership(): testRenounceOwnership"); assertEq(ownable.owner(), address(0)); } function testNominateNewOwner() public { - vm.prank(owner); vm.expectEmit(true, false, false, false); emit Ownable.OwnerNominated(newOwner); + + vm.startPrank(owner); ownable.nominateNewOwner(newOwner); + vm.stopPrank(); + vm.snapshotGasLastCall("Ownable", "nominateNewOwner(): testNominateNewOwner"); assertEq(ownable.nominatedOwner(), newOwner); } diff --git a/test/forkMainnet/GenericSwap.t.sol b/test/forkMainnet/GenericSwap.t.sol index b71e4999..4eb489de 100644 --- a/test/forkMainnet/GenericSwap.t.sol +++ b/test/forkMainnet/GenericSwap.t.sol @@ -14,7 +14,6 @@ import { IUniswapSwapRouter02 } from "test/utils/IUniswapSwapRouter02.sol"; import { MockStrategy } from "test/mocks/MockStrategy.sol"; import { GenericSwap } from "contracts/GenericSwap.sol"; import { AllowanceTarget } from "contracts/AllowanceTarget.sol"; -import { TokenCollector } from "contracts/abstracts/TokenCollector.sol"; import { SmartOrderStrategy } from "contracts/SmartOrderStrategy.sol"; import { Constant } from "contracts/libraries/Constant.sol"; import { GenericSwapData, getGSDataHash } from "contracts/libraries/GenericSwapData.sol"; @@ -24,18 +23,6 @@ import { ISmartOrderStrategy } from "contracts/interfaces/ISmartOrderStrategy.so contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { using BalanceSnapshot for Snapshot; - event Swap( - bytes32 indexed swapHash, - address indexed maker, - address indexed taker, - address recipient, - address inputToken, - uint256 inputAmount, - address outputToken, - uint256 outputAmount, - uint256 salt - ); - address strategyAdmin = makeAddr("strategyAdmin"); address allowanceTargetOwner = makeAddr("allowanceTargetOwner"); uint256 takerPrivateKey = uint256(1); @@ -69,12 +56,13 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper smartStrategy = new SmartOrderStrategy(strategyAdmin, address(genericSwap), WETH_ADDRESS); mockStrategy = new MockStrategy(); - vm.prank(strategyAdmin); address[] memory tokenList = new address[](1); tokenList[0] = USDT_ADDRESS; address[] memory ammList = new address[](1); ammList[0] = UNISWAP_SWAP_ROUTER_02_ADDRESS; + vm.startPrank(strategyAdmin); smartStrategy.approveTokens(tokenList, ammList); + vm.stopPrank(); IUniswapV3Quoter v3Quoter = IUniswapV3Quoter(UNISWAP_V3_QUOTER_ADDRESS); bytes memory encodedPath = UniswapV3.encodePath(defaultPath, defaultV3Fees); @@ -141,7 +129,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper Snapshot memory takerMakerToken = BalanceSnapshot.take({ owner: taker, token: defaultGSData.makerToken }); vm.expectEmit(true, true, true, true); - emit Swap( + emit IGenericSwap.Swap( getGSDataHash(defaultGSData), defaultGSData.maker, taker, @@ -153,8 +141,10 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper defaultGSData.salt ); - vm.prank(taker); + vm.startPrank(taker); genericSwap.executeSwap(defaultGSData, defaultTakerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("GenericSwap", "executeSwap(): testGenericSwapWithUniswap"); takerTakerToken.assertChange(-int256(defaultGSData.takerTokenAmount)); // the makerTokenAmount in the defaultGSData is the exact quote from strategy @@ -178,7 +168,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper // 800 < 900 < 1000 mockStrategy.setOutputAmountAndRecipient(actualOutput, payable(address(genericSwap))); vm.expectEmit(true, true, true, true); - emit Swap( + emit IGenericSwap.Swap( getGSDataHash(gsData), gsData.maker, taker, @@ -189,8 +179,10 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper realChangedInGS, gsData.salt ); - vm.prank(taker); + vm.startPrank(taker); genericSwap.executeSwap(gsData, defaultTakerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("GenericSwap", "executeSwap(): testSwapWithLessOutputButWithinTolerance"); takerTakerToken.assertChange(-int256(gsData.takerTokenAmount)); takerMakerToken.assertChange(int256(realChangedInGS)); @@ -213,7 +205,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper mockStrategy.setOutputAmountAndRecipient(gsData.makerTokenAmount, payable(address(genericSwap))); vm.expectEmit(true, true, true, true); - emit Swap( + emit IGenericSwap.Swap( getGSDataHash(gsData), gsData.maker, taker, @@ -224,8 +216,10 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper realChangedInGS, gsData.salt ); - vm.prank(taker); + vm.startPrank(taker); genericSwap.executeSwap{ value: gsData.takerTokenAmount }(gsData, defaultTakerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("GenericSwap", "executeSwap(): testSwapWithETHInput"); takerTakerToken.assertChange(-int256(gsData.takerTokenAmount)); takerMakerToken.assertChange(int256(realChangedInGS)); @@ -249,7 +243,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper mockStrategy.setOutputAmountAndRecipient(gsData.makerTokenAmount, payable(address(genericSwap))); vm.expectEmit(true, true, true, true); - emit Swap( + emit IGenericSwap.Swap( getGSDataHash(gsData), gsData.maker, taker, @@ -260,8 +254,10 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper realChangedInGS, gsData.salt ); - vm.prank(taker); + vm.startPrank(taker); genericSwap.executeSwap(gsData, defaultTakerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("GenericSwap", "executeSwap(): testSwapWithETHOutput"); takerTakerToken.assertChange(-int256(gsData.takerTokenAmount)); takerMakerToken.assertChange(int256(realChangedInGS)); @@ -272,9 +268,10 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper function testCannotSwapWithExpiredOrder() public { vm.warp(defaultExpiry + 1); - vm.prank(taker); + vm.startPrank(taker); vm.expectRevert(IGenericSwap.ExpiredOrder.selector); genericSwap.executeSwap(defaultGSData, defaultTakerPermit); + vm.stopPrank(); } function testCannotSwapWithInvalidETHInput() public { @@ -288,14 +285,16 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper gsData.takerTokenAmount = 1 ether; // case2 : msg.value > takerTokenAmount - vm.prank(taker); + vm.startPrank(taker); vm.expectRevert(IGenericSwap.InvalidMsgValue.selector); genericSwap.executeSwap{ value: gsData.takerTokenAmount + 1 }(gsData, defaultTakerPermit); + vm.stopPrank(); // case3 : msg.value < takerTokenAmount - vm.prank(taker); + vm.startPrank(taker); vm.expectRevert(IGenericSwap.InvalidMsgValue.selector); genericSwap.executeSwap{ value: gsData.takerTokenAmount - 1 }(gsData, defaultTakerPermit); + vm.stopPrank(); } function testCannotSwapWithInsufficientOutput() public { @@ -304,18 +303,20 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper gsData.maker = payable(address(mockStrategy)); mockStrategy.setOutputAmountAndRecipient(gsData.minMakerTokenAmount - 1, payable(address(genericSwap))); - vm.prank(taker); + vm.startPrank(taker); vm.expectRevert(IGenericSwap.InsufficientOutput.selector); genericSwap.executeSwap(gsData, defaultTakerPermit); + vm.stopPrank(); } function testCannotSwapWithZeroRecipient() public { GenericSwapData memory gsData = defaultGSData; gsData.recipient = payable(address(0)); - vm.prank(taker); + vm.startPrank(taker); vm.expectRevert(IGenericSwap.ZeroAddress.selector); genericSwap.executeSwap(gsData, defaultTakerPermit); + vm.stopPrank(); } function testGenericSwapRelayed() public { @@ -323,7 +324,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper Snapshot memory takerMakerToken = BalanceSnapshot.take({ owner: taker, token: defaultGSData.makerToken }); vm.expectEmit(true, true, true, true); - emit Swap( + emit IGenericSwap.Swap( getGSDataHash(defaultGSData), defaultGSData.maker, taker, @@ -337,6 +338,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper bytes memory takerSig = signGenericSwap(takerPrivateKey, defaultGSData, address(genericSwap)); genericSwap.executeSwapWithSig(defaultGSData, defaultTakerPermit, taker, takerSig); + vm.snapshotGasLastCall("GenericSwap", "executeSwapWithSig(): testGenericSwapRelayed"); takerTakerToken.assertChange(-int256(defaultGSData.takerTokenAmount)); // the makerTokenAmount in the defaultGSData is the exact quote from strategy @@ -372,7 +374,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper // his makerTokenAmount has already been reduced by 2 in the setup function // leaving 1 wei in GS and SOS separately vm.expectEmit(true, true, true, true); - emit Swap( + emit IGenericSwap.Swap( getGSDataHash(defaultGSData), defaultGSData.maker, taker, @@ -384,8 +386,10 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper defaultGSData.salt ); - vm.prank(taker); + vm.startPrank(taker); genericSwap.executeSwap(defaultGSData, defaultTakerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("GenericSwap", "executeSwap(): testLeaveOneWeiWithMultipleUsers(the first deposit)"); // the second user: Alice // his makerTokenAmount is recalculate by `quoteExactInput() function base on the current state` @@ -405,7 +409,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper vm.expectEmit(true, true, true, true); - emit Swap( + emit IGenericSwap.Swap( getGSDataHash(aliceGSData), aliceGSData.maker, alice, @@ -420,6 +424,7 @@ contract GenericSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper vm.startPrank(alice); genericSwap.executeSwap(aliceGSData, alicePermit); vm.stopPrank(); + vm.snapshotGasLastCall("GenericSwap", "executeSwap(): testLeaveOneWeiWithMultipleUsers(the second deposit)"); takerTakerToken.assertChange(-int256(defaultGSData.takerTokenAmount)); takerMakerToken.assertChange(int256(defaultGSData.makerTokenAmount)); diff --git a/test/forkMainnet/LimitOrderSwap/CancelOrder.t.sol b/test/forkMainnet/LimitOrderSwap/CancelOrder.t.sol index 07db6c52..9955424f 100644 --- a/test/forkMainnet/LimitOrderSwap/CancelOrder.t.sol +++ b/test/forkMainnet/LimitOrderSwap/CancelOrder.t.sol @@ -6,46 +6,53 @@ import { ILimitOrderSwap } from "contracts/interfaces/ILimitOrderSwap.sol"; import { LimitOrderSwapTest } from "test/forkMainnet/LimitOrderSwap/Setup.t.sol"; contract CancelOrderTest is LimitOrderSwapTest { - event OrderCanceled(bytes32 orderHash, address maker); - function testCancelOrder() public { vm.expectEmit(true, true, true, true); - emit OrderCanceled(getLimitOrderHash(defaultOrder), maker); + emit ILimitOrderSwap.OrderCanceled(getLimitOrderHash(defaultOrder), maker); - vm.prank(maker, maker); + vm.startPrank(maker); limitOrderSwap.cancelOrder(defaultOrder); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "cancelOrder(): testCancelOrder"); + assertEq(limitOrderSwap.isOrderCanceled(getLimitOrderHash(defaultOrder)), true); } function testCannotCancelOrderIfNotMaker() public { + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.NotOrderMaker.selector); - vm.prank(taker, taker); limitOrderSwap.cancelOrder(defaultOrder); + vm.stopPrank(); } function testCannotCancelExpiredOrder() public { vm.warp(defaultOrder.expiry + 1); + vm.startPrank(maker); vm.expectRevert(ILimitOrderSwap.ExpiredOrder.selector); - vm.prank(maker, maker); limitOrderSwap.cancelOrder(defaultOrder); + vm.stopPrank(); } function testCannotCancelFilledOrder() public { - vm.prank(taker); + vm.startPrank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: defaultMakerSig, takerParams: defaultTakerParams }); + vm.stopPrank(); + vm.startPrank(maker); vm.expectRevert(ILimitOrderSwap.FilledOrder.selector); - vm.prank(maker, maker); limitOrderSwap.cancelOrder(defaultOrder); + vm.stopPrank(); } function testCannotCancelCanceledOrder() public { - vm.prank(maker, maker); + vm.startPrank(maker); limitOrderSwap.cancelOrder(defaultOrder); + vm.stopPrank(); + vm.startPrank(maker); vm.expectRevert(ILimitOrderSwap.CanceledOrder.selector); - vm.prank(maker, maker); limitOrderSwap.cancelOrder(defaultOrder); + vm.stopPrank(); } } diff --git a/test/forkMainnet/LimitOrderSwap/CoordinatedTaker.t.sol b/test/forkMainnet/LimitOrderSwap/CoordinatedTaker.t.sol index 636848d6..69686562 100644 --- a/test/forkMainnet/LimitOrderSwap/CoordinatedTaker.t.sol +++ b/test/forkMainnet/LimitOrderSwap/CoordinatedTaker.t.sol @@ -16,9 +16,6 @@ import { MockERC20 } from "test/mocks/MockERC20.sol"; contract CoordinatedTakerTest is LimitOrderSwapTest { using BalanceSnapshot for Snapshot; - event CoordinatorFill(address indexed user, bytes32 indexed orderHash, bytes32 indexed allowFillHash); - event SetCoordinator(address newCoordinator); - address crdTakerOwner = makeAddr("crdTakerOwner"); uint256 userPrivateKey = uint256(5); address user = vm.addr(userPrivateKey); @@ -47,8 +44,9 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { // setup coordinatedTaker approval address[] memory targetList = new address[](1); targetList[0] = address(limitOrderSwap); - vm.prank(crdTakerOwner); + vm.startPrank(crdTakerOwner); coordinatedTaker.approveTokens(tokenList, targetList); + vm.stopPrank(); deal(user, 100 ether); setTokenBalanceAndApprove(user, UNISWAP_PERMIT2_ADDRESS, tokens, 100000); @@ -95,22 +93,31 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { function testCannotSetCoordinatorByNotOwner() public { address newCoordinator = makeAddr("newCoordinator"); - vm.prank(newCoordinator); + + vm.startPrank(newCoordinator); vm.expectRevert(Ownable.NotOwner.selector); - coordinatedTaker.setCoordinator(payable(newCoordinator)); + coordinatedTaker.setCoordinator(newCoordinator); + vm.stopPrank(); } function testCannotSetCoordinatorToZero() public { - vm.prank(crdTakerOwner, crdTakerOwner); + vm.startPrank(crdTakerOwner); vm.expectRevert(ICoordinatedTaker.ZeroAddress.selector); - coordinatedTaker.setCoordinator(payable(address(0))); + coordinatedTaker.setCoordinator(Constant.ZERO_ADDRESS); + vm.stopPrank(); } function testSetCoordinator() public { address newCoordinator = makeAddr("newCoordinator"); - vm.prank(crdTakerOwner, crdTakerOwner); - coordinatedTaker.setCoordinator(payable(newCoordinator)); - emit SetCoordinator(newCoordinator); + + vm.expectEmit(false, false, false, true); + emit ICoordinatedTaker.SetCoordinator(newCoordinator); + + vm.startPrank(crdTakerOwner); + coordinatedTaker.setCoordinator(newCoordinator); + vm.stopPrank(); + vm.snapshotGasLastCall("CoordinatedTaker", "setCoordinator(): testSetCoordinator"); + assertEq(coordinatedTaker.coordinator(), newCoordinator); } @@ -129,8 +136,12 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { targetList[0] = target; assertEq(mockERC20.allowance(address(coordinatedTaker), target), 0); - vm.prank(crdTakerOwner); + + vm.startPrank(crdTakerOwner); coordinatedTaker.approveTokens(newTokens, targetList); + vm.stopPrank(); + vm.snapshotGasLastCall("CoordinatedTaker", "approveTokens(): testApproveTokens"); + assertEq(mockERC20.allowance(address(coordinatedTaker), target), type(uint256).max); } @@ -144,14 +155,14 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { uint256 fee = (defaultCrdOrder.makerTokenAmount * defaultFeeFactor) / Constant.BPS_MAX; vm.expectEmit(true, true, true, true); - emit CoordinatorFill( + emit ICoordinatedTaker.CoordinatorFill( user, getLimitOrderHash(defaultCrdOrder), getEIP712Hash(coordinatedTaker.EIP712_DOMAIN_SEPARATOR(), getAllowFillHash(defaultAllowFill)) ); vm.expectEmit(true, true, true, true); - emit LimitOrderFilled( + emit ILimitOrderSwap.LimitOrderFilled( getLimitOrderHash(defaultCrdOrder), address(coordinatedTaker), // taker defaultCrdOrder.maker, @@ -163,7 +174,7 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { user // recipient ); - vm.prank(user, user); + vm.startPrank(user); coordinatedTaker.submitLimitOrderFill({ order: defaultCrdOrder, makerSignature: defaultMakerSig, @@ -173,6 +184,8 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { userTokenPermit: defaultUserPermit, crdParams: defaultCRDParams }); + vm.stopPrank(); + vm.snapshotGasLastCall("CoordinatedTaker", "submitLimitOrderFill(): testFillWithPermission"); userTakerToken.assertChange(-int256(defaultCrdOrder.takerTokenAmount)); userMakerToken.assertChange(int256(defaultCrdOrder.makerTokenAmount - fee)); @@ -212,10 +225,14 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { uint256 fee = (order.makerTokenAmount * defaultFeeFactor) / Constant.BPS_MAX; vm.expectEmit(true, true, true, true); - emit CoordinatorFill(user, getLimitOrderHash(order), getEIP712Hash(coordinatedTaker.EIP712_DOMAIN_SEPARATOR(), getAllowFillHash(allowFill))); + emit ICoordinatedTaker.CoordinatorFill( + user, + getLimitOrderHash(order), + getEIP712Hash(coordinatedTaker.EIP712_DOMAIN_SEPARATOR(), getAllowFillHash(allowFill)) + ); vm.expectEmit(true, true, true, true); - emit LimitOrderFilled( + emit ILimitOrderSwap.LimitOrderFilled( getLimitOrderHash(order), address(coordinatedTaker), // taker order.maker, @@ -227,7 +244,7 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { user // recipient ); - vm.prank(user, user); + vm.startPrank(user); coordinatedTaker.submitLimitOrderFill{ value: order.takerTokenAmount }({ order: order, makerSignature: makerSig, @@ -237,6 +254,8 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { userTokenPermit: defaultUserPermit, crdParams: crdParams }); + vm.stopPrank(); + vm.snapshotGasLastCall("CoordinatedTaker", "submitLimitOrderFill(): testFillWithETH"); userTakerToken.assertChange(-int256(order.takerTokenAmount)); userMakerToken.assertChange(int256(order.makerTokenAmount - fee)); @@ -248,8 +267,8 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { function testCannotFillWithExpiredPermission() public { vm.warp(defaultAllowFill.expiry + 1); + vm.startPrank(user); vm.expectRevert(ICoordinatedTaker.ExpiredPermission.selector); - vm.prank(user, user); coordinatedTaker.submitLimitOrderFill({ order: defaultCrdOrder, makerSignature: defaultMakerSig, @@ -259,6 +278,7 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { userTokenPermit: defaultUserPermit, crdParams: defaultCRDParams }); + vm.stopPrank(); } function testCannotFillWithIncorrectCoordinatorSig() public { @@ -268,8 +288,8 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { ICoordinatedTaker.CoordinatorParams memory crdParams = defaultCRDParams; crdParams.sig = randomAllowFillSig; + vm.startPrank(user); vm.expectRevert(ICoordinatedTaker.InvalidSignature.selector); - vm.prank(user, user); coordinatedTaker.submitLimitOrderFill({ order: defaultCrdOrder, makerSignature: defaultMakerSig, @@ -279,10 +299,11 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { userTokenPermit: defaultUserPermit, crdParams: crdParams }); + vm.stopPrank(); } function testCannotFillWithReplayedPermission() public { - vm.prank(user, user); + vm.startPrank(user); coordinatedTaker.submitLimitOrderFill({ order: defaultCrdOrder, makerSignature: defaultMakerSig, @@ -292,9 +313,10 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { userTokenPermit: defaultUserPermit, crdParams: defaultCRDParams }); + vm.stopPrank(); + vm.startPrank(user); vm.expectRevert(ICoordinatedTaker.ReusedPermission.selector); - vm.prank(user, user); coordinatedTaker.submitLimitOrderFill({ order: defaultCrdOrder, makerSignature: defaultMakerSig, @@ -304,11 +326,12 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { userTokenPermit: allowanceTransferPermit, // should transfer from permit2 allowance directly crdParams: defaultCRDParams }); + vm.stopPrank(); } function testCannotFillWithInvalidMsgValue() public { + vm.startPrank(user); vm.expectRevert(ICoordinatedTaker.InvalidMsgValue.selector); - vm.prank(user, user); coordinatedTaker.submitLimitOrderFill{ value: 1 ether }({ order: defaultCrdOrder, makerSignature: defaultMakerSig, @@ -318,5 +341,6 @@ contract CoordinatedTakerTest is LimitOrderSwapTest { userTokenPermit: defaultUserPermit, crdParams: defaultCRDParams }); + vm.stopPrank(); } } diff --git a/test/forkMainnet/LimitOrderSwap/Fill.t.sol b/test/forkMainnet/LimitOrderSwap/Fill.t.sol index fb1fc69b..18313b1a 100644 --- a/test/forkMainnet/LimitOrderSwap/Fill.t.sol +++ b/test/forkMainnet/LimitOrderSwap/Fill.t.sol @@ -52,8 +52,10 @@ contract FillTest is LimitOrderSwapTest { recipient ); - vm.prank(taker); + vm.startPrank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: defaultMakerSig, takerParams: defaultTakerParams }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrder(): testFullyFillLimitOrder"); takerTakerToken.assertChange(-int256(defaultOrder.takerTokenAmount)); takerMakerToken.assertChange(int256(0)); @@ -101,7 +103,7 @@ contract FillTest is LimitOrderSwapTest { address(mockLimitOrderTaker) ); - vm.prank(address(mockLimitOrderTaker)); + vm.startPrank(address(mockLimitOrderTaker)); limitOrderSwap.fillLimitOrder({ order: order, makerSignature: makerSig, @@ -113,6 +115,8 @@ contract FillTest is LimitOrderSwapTest { takerTokenPermit: directApprovePermit }) }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrder(): testFullyFillLimitOrderUsingAMM"); // taker should not have token balance changes takerTakerToken.assertChange(int256(0)); @@ -132,7 +136,7 @@ contract FillTest is LimitOrderSwapTest { Snapshot memory fcMakerToken = BalanceSnapshot.take({ owner: feeCollector, token: defaultOrder.makerToken }); uint256 takingAmount = defaultOrder.takerTokenAmount / 2; - uint256 makingAmount = defaultOrder.takerTokenAmount / 2; + uint256 makingAmount = defaultOrder.makerTokenAmount / 2; uint256 fee = (makingAmount * defaultFeeFactor) / Constant.BPS_MAX; vm.expectEmit(true, true, true, true); @@ -151,8 +155,10 @@ contract FillTest is LimitOrderSwapTest { takerParams.takerTokenAmount = takingAmount; takerParams.makerTokenAmount = makingAmount; - vm.prank(taker); + vm.startPrank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: defaultMakerSig, takerParams: takerParams }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrder(): testPartiallyFillLimitOrder"); takerTakerToken.assertChange(-int256(takingAmount)); takerMakerToken.assertChange(int256(0)); @@ -194,7 +200,7 @@ contract FillTest is LimitOrderSwapTest { recipient ); - vm.prank(taker); + vm.startPrank(taker); limitOrderSwap.fillLimitOrder{ value: order.takerTokenAmount }({ order: order, makerSignature: makerSig, @@ -206,6 +212,8 @@ contract FillTest is LimitOrderSwapTest { takerTokenPermit: defaultTakerPermit }) }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrder(): testFillLimitOrderWithETH"); takerTakerToken.assertChange(-int256(order.takerTokenAmount)); takerMakerToken.assertChange(int256(0)); @@ -245,8 +253,10 @@ contract FillTest is LimitOrderSwapTest { recipient ); - vm.prank(taker); + vm.startPrank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: defaultMakerSig, takerParams: takerParams }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrder(): testFillWithBetterTakingAmount"); takerTakerToken.assertChange(-int256(actualTokenAmount)); takerMakerToken.assertChange(int256(0)); @@ -284,7 +294,7 @@ contract FillTest is LimitOrderSwapTest { recipient ); - vm.prank(taker); + vm.startPrank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: defaultMakerSig, @@ -296,6 +306,8 @@ contract FillTest is LimitOrderSwapTest { takerTokenPermit: defaultTakerPermit }) }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrder(): testFillWithLargerVolumeAndSettleAsManyAsPossible"); takerTakerToken.assertChange(-int256(defaultOrder.takerTokenAmount)); takerMakerToken.assertChange(int256(0)); @@ -308,7 +320,7 @@ contract FillTest is LimitOrderSwapTest { function testFillWithBetterTakingAmountButGetAdjusted() public { // fill with better price but the order doesn't have enough for the requested - // so the makingAmount == order's avaliable amount + // so the makingAmount == order's available amount // takingAmount should be adjusted to keep the original price that taker provided Snapshot memory takerTakerToken = BalanceSnapshot.take({ owner: taker, token: defaultOrder.takerToken }); Snapshot memory takerMakerToken = BalanceSnapshot.take({ owner: taker, token: defaultOrder.makerToken }); @@ -346,8 +358,10 @@ contract FillTest is LimitOrderSwapTest { recipient ); - vm.prank(taker); + vm.startPrank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: defaultMakerSig, takerParams: takerParams }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrder(): testFillWithBetterTakingAmountButGetAdjusted"); takerTakerToken.assertChange(-int256(settleTakingAMount)); takerMakerToken.assertChange(int256(0)); @@ -402,7 +416,7 @@ contract FillTest is LimitOrderSwapTest { recipient ); - vm.prank(taker); + vm.startPrank(taker); limitOrderSwap.fillLimitOrder{ value: traderTakingAmount }({ order: order, makerSignature: makerSig, @@ -414,6 +428,8 @@ contract FillTest is LimitOrderSwapTest { takerTokenPermit: defaultTakerPermit }) }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrder(): testFillWithETHRefund"); takerTakerToken.assertChange(-int256(order.takerTokenAmount)); takerMakerToken.assertChange(int256(0)); @@ -425,33 +441,67 @@ contract FillTest is LimitOrderSwapTest { } function testFillWithoutMakerSigForVerifiedOrder() public { + uint256 takingAmount = defaultOrder.takerTokenAmount / 10; + uint256 makingAmount = defaultOrder.makerTokenAmount / 10; + uint256 fee = (makingAmount * defaultFeeFactor) / Constant.BPS_MAX; + + vm.expectEmit(true, true, true, true); + emit LimitOrderFilled( + getLimitOrderHash(defaultOrder), + taker, + defaultOrder.maker, + defaultOrder.takerToken, + takingAmount, + defaultOrder.makerToken, + makingAmount - fee, + fee, + recipient + ); + // fill default order first with 1/10 amount - vm.prank(taker); + vm.startPrank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: defaultMakerSig, takerParams: ILimitOrderSwap.TakerParams({ - takerTokenAmount: defaultOrder.takerTokenAmount / 10, - makerTokenAmount: defaultOrder.makerTokenAmount / 10, + takerTokenAmount: takingAmount, + makerTokenAmount: makingAmount, recipient: recipient, extraAction: bytes(""), takerTokenPermit: defaultTakerPermit }) }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrder(): testFillWithoutMakerSigForVerifiedOrder"); + + vm.expectEmit(true, true, true, true); + emit LimitOrderFilled( + getLimitOrderHash(defaultOrder), + taker, + defaultOrder.maker, + defaultOrder.takerToken, + takingAmount, + defaultOrder.makerToken, + makingAmount - fee, + fee, + recipient + ); // fill default order again without makerSig - vm.prank(taker); + vm.startPrank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: bytes(""), takerParams: ILimitOrderSwap.TakerParams({ - takerTokenAmount: defaultOrder.takerTokenAmount / 10, - makerTokenAmount: defaultOrder.makerTokenAmount / 10, + takerTokenAmount: takingAmount, + makerTokenAmount: makingAmount, recipient: recipient, extraAction: bytes(""), takerTokenPermit: allowanceTransferPermit }) }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrder(): testFillWithoutMakerSigForVerifiedOrder(without makerSig)"); } function testCannotFillWithNotEnoughTakingAmount() public { @@ -460,9 +510,10 @@ contract FillTest is LimitOrderSwapTest { ILimitOrderSwap.TakerParams memory takerParams = defaultTakerParams; takerParams.takerTokenAmount = actualTokenAmount; + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.InvalidTakingAmount.selector); - vm.prank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: defaultMakerSig, takerParams: takerParams }); + vm.stopPrank(); } function testCannotFillIfStrategyNotReturnEnoughTakingAmount() public { @@ -495,9 +546,10 @@ contract FillTest is LimitOrderSwapTest { function testCannotFillExpiredOrder() public { vm.warp(defaultOrder.expiry + 1); + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.ExpiredOrder.selector); - vm.prank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: defaultMakerSig, takerParams: defaultTakerParams }); + vm.stopPrank(); } function testCannotFillByNotSpecifiedTaker() public { @@ -505,73 +557,83 @@ contract FillTest is LimitOrderSwapTest { order.taker = makeAddr("specialTaker"); bytes memory makerSig = signLimitOrder(makerPrivateKey, order, address(limitOrderSwap)); + vm.startPrank(makeAddr("randomTaker")); vm.expectRevert(ILimitOrderSwap.InvalidTaker.selector); - vm.prank(makeAddr("randomTaker")); limitOrderSwap.fillLimitOrder({ order: order, makerSignature: makerSig, takerParams: defaultTakerParams }); + vm.stopPrank(); } function testCannotFillCanceledOrder() public { - vm.prank(maker); + vm.startPrank(maker); limitOrderSwap.cancelOrder(defaultOrder); + vm.stopPrank(); + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.CanceledOrder.selector); - vm.prank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: defaultMakerSig, takerParams: defaultTakerParams }); + vm.stopPrank(); } function testCannotFillWithIncorrectMakerSig() public { uint256 randomPrivateKey = 5677; bytes memory makerSig = signLimitOrder(randomPrivateKey, defaultOrder, address(limitOrderSwap)); + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.InvalidSignature.selector); - vm.prank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: makerSig, takerParams: defaultTakerParams }); + vm.stopPrank(); } function testCannotTradeFilledOrder() public { - vm.prank(taker); + vm.startPrank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: defaultMakerSig, takerParams: defaultTakerParams }); + vm.stopPrank(); + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.FilledOrder.selector); - vm.prank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: defaultMakerSig, takerParams: defaultTakerParams }); + vm.stopPrank(); } function testCannotFillWithIncorrectMsgValue() public { // case1 : takerToken is not ETH but msg.value != 0 + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.InvalidMsgValue.selector); - vm.prank(taker); limitOrderSwap.fillLimitOrder{ value: 100 }({ order: defaultOrder, makerSignature: defaultMakerSig, takerParams: defaultTakerParams }); + vm.stopPrank(); LimitOrder memory order = defaultOrder; order.takerToken = Constant.ETH_ADDRESS; bytes memory makerSig = signLimitOrder(makerPrivateKey, order, address(limitOrderSwap)); // case2 : takerToken is ETH but msg.value > takingAmount + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.InvalidMsgValue.selector); - vm.prank(taker); limitOrderSwap.fillLimitOrder{ value: defaultTakerParams.takerTokenAmount + 1 }({ order: order, makerSignature: makerSig, takerParams: defaultTakerParams }); + vm.stopPrank(); // case3 : takerToken is ETH but msg.value < takingAmount + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.InvalidMsgValue.selector); - vm.prank(taker); limitOrderSwap.fillLimitOrder{ value: defaultTakerParams.takerTokenAmount - 1 }({ order: order, makerSignature: makerSig, takerParams: defaultTakerParams }); + vm.stopPrank(); } function testCannotFillWithZeroRecipient() public { ILimitOrderSwap.TakerParams memory takerParams = defaultTakerParams; takerParams.recipient = address(0); + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.ZeroAddress.selector); - vm.prank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: defaultMakerSig, takerParams: takerParams }); + vm.stopPrank(); } } diff --git a/test/forkMainnet/LimitOrderSwap/FullOrKill.t.sol b/test/forkMainnet/LimitOrderSwap/FullOrKill.t.sol index 4c48e10b..7b6ffd1c 100644 --- a/test/forkMainnet/LimitOrderSwap/FullOrKill.t.sol +++ b/test/forkMainnet/LimitOrderSwap/FullOrKill.t.sol @@ -37,7 +37,7 @@ contract FullOrKillTest is LimitOrderSwapTest { recipient ); - vm.prank(taker); + vm.startPrank(taker); limitOrderSwap.fillLimitOrderFullOrKill({ order: defaultOrder, makerSignature: defaultMakerSig, @@ -49,6 +49,8 @@ contract FullOrKillTest is LimitOrderSwapTest { takerTokenPermit: defaultTakerPermit }) }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrderFullOrKill(): testFillWithFOK"); takerTakerToken.assertChange(-int256(traderTakingAmount)); takerMakerToken.assertChange(int256(0)); @@ -64,8 +66,8 @@ contract FullOrKillTest is LimitOrderSwapTest { uint256 traderMakingAmount = defaultOrder.makerTokenAmount * 2; uint256 traderTakingAmount = defaultOrder.takerTokenAmount * 2; + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.NotEnoughForFill.selector); - vm.prank(taker); limitOrderSwap.fillLimitOrderFullOrKill({ order: defaultOrder, makerSignature: defaultMakerSig, @@ -77,6 +79,7 @@ contract FullOrKillTest is LimitOrderSwapTest { takerTokenPermit: defaultTakerPermit }) }); + vm.stopPrank(); } function testCannotFillFOKIfNotEnoughEvenPriceIsBetter() public { @@ -84,8 +87,8 @@ contract FullOrKillTest is LimitOrderSwapTest { uint256 traderMakingAmount = defaultOrder.makerTokenAmount * 2; uint256 traderTakingAmount = defaultOrder.takerTokenAmount * 20; + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.NotEnoughForFill.selector); - vm.prank(taker); limitOrderSwap.fillLimitOrderFullOrKill({ order: defaultOrder, makerSignature: defaultMakerSig, @@ -97,5 +100,6 @@ contract FullOrKillTest is LimitOrderSwapTest { takerTokenPermit: defaultTakerPermit }) }); + vm.stopPrank(); } } diff --git a/test/forkMainnet/LimitOrderSwap/GroupFill.t.sol b/test/forkMainnet/LimitOrderSwap/GroupFill.t.sol index 81d86c46..55d74cbd 100644 --- a/test/forkMainnet/LimitOrderSwap/GroupFill.t.sol +++ b/test/forkMainnet/LimitOrderSwap/GroupFill.t.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.26; -import { ILimitOrderSwap } from "contracts/interfaces/ILimitOrderSwap.sol"; import { IUniswapPermit2 } from "contracts/interfaces/IUniswapPermit2.sol"; import { LimitOrder, getLimitOrderHash } from "contracts/libraries/LimitOrder.sol"; import { Constant } from "contracts/libraries/Constant.sol"; @@ -83,8 +82,11 @@ contract GroupFillTest is LimitOrderSwapTest { address[] memory profitTokens = new address[](1); profitTokens[0] = DAI_ADDRESS; Snapshot memory arbProfitToken = BalanceSnapshot.take({ owner: arbitrageur, token: DAI_ADDRESS }); - vm.prank(arbitrageur, arbitrageur); + + vm.startPrank(arbitrageur); limitOrderSwap.fillLimitOrderGroup({ orders: orders, makerSignatures: makerSigs, makerTokenAmounts: makerTokenAmounts, profitTokens: profitTokens }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "testGroupFillWithProfit: fillLimitOrderGroup()"); // two makers should give/get exactly as order specified maker0TakerToken.assertChange(int256(orders[0].takerTokenAmount)); @@ -155,7 +157,11 @@ contract GroupFillTest is LimitOrderSwapTest { Snapshot memory maker2MakerToken = BalanceSnapshot.take({ owner: orders[2].maker, token: orders[2].makerToken }); address[] memory profitTokens; + + vm.startPrank(arbitrageur); limitOrderSwap.fillLimitOrderGroup({ orders: orders, makerSignatures: makerSigs, makerTokenAmounts: makerTokenAmounts, profitTokens: profitTokens }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrderGroup(): testPartialFillLargeOrderWithSmallOrders"); // small orders maker should be fully filled maker0TakerToken.assertChange(int256(orders[0].takerTokenAmount)); @@ -211,7 +217,11 @@ contract GroupFillTest is LimitOrderSwapTest { Snapshot memory maker1MakerToken = BalanceSnapshot.take({ owner: orders[1].maker, token: orders[1].makerToken }); address[] memory profitTokens; + + vm.startPrank(arbitrageur); limitOrderSwap.fillLimitOrderGroup({ orders: orders, makerSignatures: makerSigs, makerTokenAmounts: makerTokenAmounts, profitTokens: profitTokens }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrderGroup(): testGroupFillWithWETHUnwrap"); // all orders should be fully filled maker0TakerToken.assertChange(int256(orders[0].takerTokenAmount)); @@ -289,8 +299,10 @@ contract GroupFillTest is LimitOrderSwapTest { profitTokens[0] = Constant.ETH_ADDRESS; Snapshot memory arbETHProfit = BalanceSnapshot.take({ owner: arbitrageur, token: Constant.ETH_ADDRESS }); - vm.prank(arbitrageur, arbitrageur); + vm.startPrank(arbitrageur); limitOrderSwap.fillLimitOrderGroup({ orders: orders, makerSignatures: makerSigs, makerTokenAmounts: makerTokenAmounts, profitTokens: profitTokens }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrderGroup(): testGroupFillWithPartialWETHUnwrap"); // all orders should be fully filled maker0TakerToken.assertChange(int256(orders[0].takerTokenAmount)); @@ -353,13 +365,16 @@ contract GroupFillTest is LimitOrderSwapTest { uint256 takerPreFund = orders[1].takerTokenAmount - orders[0].makerTokenAmount; address[] memory profitTokens; - vm.prank(arbitrageur); + + vm.startPrank(arbitrageur); limitOrderSwap.fillLimitOrderGroup{ value: takerPreFund }({ orders: orders, makerSignatures: makerSigs, makerTokenAmounts: makerTokenAmounts, profitTokens: profitTokens }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrderGroup(): testGroupFillWithTakerPrefundETH"); // small orders maker should be fully filled maker0TakerToken.assertChange(int256(orders[0].takerTokenAmount)); @@ -428,7 +443,11 @@ contract GroupFillTest is LimitOrderSwapTest { Snapshot memory maker2MakerToken = BalanceSnapshot.take({ owner: orders[2].maker, token: orders[2].makerToken }); address[] memory profitTokens; + + vm.startPrank(arbitrageur); limitOrderSwap.fillLimitOrderGroup({ orders: orders, makerSignatures: makerSigs, makerTokenAmounts: makerTokenAmounts, profitTokens: profitTokens }); + vm.stopPrank(); + vm.snapshotGasLastCall("LimitOrderSwap", "fillLimitOrderGroup(): testGroupFillRingTrade"); // all order should be fully filled maker0TakerToken.assertChange(int256(orders[0].takerTokenAmount)); diff --git a/test/forkMainnet/LimitOrderSwap/Management.t.sol b/test/forkMainnet/LimitOrderSwap/Management.t.sol index 639f30b8..fc013fc2 100644 --- a/test/forkMainnet/LimitOrderSwap/Management.t.sol +++ b/test/forkMainnet/LimitOrderSwap/Management.t.sol @@ -8,22 +8,28 @@ import { LimitOrderSwapTest } from "test/forkMainnet/LimitOrderSwap/Setup.t.sol" contract ManagementTest is LimitOrderSwapTest { function testCannotSetFeeCollectorByNotOwner() public { address newFeeCollector = makeAddr("newFeeCollector"); - vm.prank(newFeeCollector); + + vm.startPrank(newFeeCollector); vm.expectRevert(Ownable.NotOwner.selector); limitOrderSwap.setFeeCollector(payable(newFeeCollector)); + vm.stopPrank(); } function testCannotSetFeeCollectorToZero() public { - vm.prank(limitOrderOwner, limitOrderOwner); + vm.startPrank(limitOrderOwner); vm.expectRevert(ILimitOrderSwap.ZeroAddress.selector); limitOrderSwap.setFeeCollector(payable(address(0))); + vm.stopPrank(); } function testSetFeeCollector() public { address newFeeCollector = makeAddr("newFeeCollector"); - vm.prank(limitOrderOwner, limitOrderOwner); - limitOrderSwap.setFeeCollector(payable(newFeeCollector)); + + vm.expectEmit(false, false, false, true); emit SetFeeCollector(newFeeCollector); - assertEq(limitOrderSwap.feeCollector(), newFeeCollector); + + vm.startPrank(limitOrderOwner); + limitOrderSwap.setFeeCollector(payable(newFeeCollector)); + vm.stopPrank(); } } diff --git a/test/forkMainnet/LimitOrderSwap/Setup.t.sol b/test/forkMainnet/LimitOrderSwap/Setup.t.sol index 2cd9df9e..4e9c9d29 100644 --- a/test/forkMainnet/LimitOrderSwap/Setup.t.sol +++ b/test/forkMainnet/LimitOrderSwap/Setup.t.sol @@ -93,8 +93,9 @@ contract LimitOrderSwapTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelp }); // maker should call permit2 first independently - vm.prank(maker); + vm.startPrank(maker); IUniswapPermit2(UNISWAP_PERMIT2_ADDRESS).approve(defaultOrder.makerToken, address(limitOrderSwap), type(uint160).max, uint48(block.timestamp + 1 days)); + vm.stopPrank(); defaultTakerPermit = getTokenlonPermit2Data(taker, takerPrivateKey, defaultOrder.takerToken, address(limitOrderSwap)); diff --git a/test/forkMainnet/LimitOrderSwap/Validation.t.sol b/test/forkMainnet/LimitOrderSwap/Validation.t.sol index bfa4e72d..8bc7aa64 100644 --- a/test/forkMainnet/LimitOrderSwap/Validation.t.sol +++ b/test/forkMainnet/LimitOrderSwap/Validation.t.sol @@ -12,6 +12,7 @@ contract ValidationTest is LimitOrderSwapTest { bytes memory makerSig = signLimitOrder(makerPrivateKey, order, address(limitOrderSwap)); + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.ZeroTakerTokenAmount.selector); limitOrderSwap.fillLimitOrder({ order: order, @@ -24,6 +25,7 @@ contract ValidationTest is LimitOrderSwapTest { takerTokenPermit: defaultTakerPermit }) }); + vm.stopPrank(); } function testCannotFillLimitOrderWithZeroMakerTokenAmount() public { @@ -32,6 +34,7 @@ contract ValidationTest is LimitOrderSwapTest { bytes memory makerSig = signLimitOrder(makerPrivateKey, order, address(limitOrderSwap)); + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.ZeroMakerTokenAmount.selector); limitOrderSwap.fillLimitOrder({ order: order, @@ -44,9 +47,11 @@ contract ValidationTest is LimitOrderSwapTest { takerTokenPermit: defaultTakerPermit }) }); + vm.stopPrank(); } function testCannotFillLimitOrderWithZeroTakerSpendingAmount() public { + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.ZeroTakerSpendingAmount.selector); limitOrderSwap.fillLimitOrder({ order: defaultOrder, @@ -59,12 +64,13 @@ contract ValidationTest is LimitOrderSwapTest { takerTokenPermit: defaultTakerPermit }) }); + vm.stopPrank(); } function testCannotFillLimitOrderWithZeroTakerSpendingAmountWhenRecalculation() public { // this case tests if _takerTokenAmount is zero due to re-calculation. + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.ZeroTakerSpendingAmount.selector); - vm.prank(taker); limitOrderSwap.fillLimitOrder({ order: defaultOrder, makerSignature: defaultMakerSig, @@ -76,9 +82,11 @@ contract ValidationTest is LimitOrderSwapTest { takerTokenPermit: defaultTakerPermit }) }); + vm.stopPrank(); } function testCannotFillLimitOrderWithZeroMakerSpendingAmount() public { + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.ZeroMakerSpendingAmount.selector); limitOrderSwap.fillLimitOrder({ order: defaultOrder, @@ -91,6 +99,7 @@ contract ValidationTest is LimitOrderSwapTest { takerTokenPermit: defaultTakerPermit }) }); + vm.stopPrank(); } function testCannotFillLimitOrderGroupWithInvalidParams() public { @@ -99,8 +108,10 @@ contract ValidationTest is LimitOrderSwapTest { uint256[] memory makerTokenAmounts = new uint256[](3); address[] memory profitTokens = new address[](1); + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.InvalidParams.selector); limitOrderSwap.fillLimitOrderGroup({ orders: orders, makerSignatures: makerSigs, makerTokenAmounts: makerTokenAmounts, profitTokens: profitTokens }); + vm.stopPrank(); } function testCannotFillLimitOrderGroupWithNotEnoughForFill() public { @@ -125,8 +136,10 @@ contract ValidationTest is LimitOrderSwapTest { makerSigs[0] = signLimitOrder(makerPrivateKey, orders[0], address(limitOrderSwap)); makerTokenAmounts[0] = orders[0].makerTokenAmount + 1; + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.NotEnoughForFill.selector); limitOrderSwap.fillLimitOrderGroup({ orders: orders, makerSignatures: makerSigs, makerTokenAmounts: makerTokenAmounts, profitTokens: profitTokens }); + vm.stopPrank(); } function testCannotFillLimitOrderGroupWithZeroMakerSpendingAmount() public { @@ -151,7 +164,9 @@ contract ValidationTest is LimitOrderSwapTest { makerSigs[0] = signLimitOrder(makerPrivateKey, orders[0], address(limitOrderSwap)); makerTokenAmounts[0] = 0; + vm.startPrank(taker); vm.expectRevert(ILimitOrderSwap.ZeroMakerSpendingAmount.selector); limitOrderSwap.fillLimitOrderGroup({ orders: orders, makerSignatures: makerSigs, makerTokenAmounts: makerTokenAmounts, profitTokens: profitTokens }); + vm.stopPrank(); } } diff --git a/test/forkMainnet/RFQ.t.sol b/test/forkMainnet/RFQ.t.sol index 3d1f37ec..c326cfcd 100644 --- a/test/forkMainnet/RFQ.t.sol +++ b/test/forkMainnet/RFQ.t.sol @@ -26,20 +26,6 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { uint256 private constant FLG_ALLOW_PARTIAL_FILL = 1 << 254; uint256 private constant FLG_MAKER_RECEIVES_WETH = 1 << 253; - event FilledRFQ( - bytes32 indexed rfqOfferHash, - address indexed user, - address indexed maker, - address takerToken, - uint256 takerTokenUserAmount, - address makerToken, - uint256 makerTokenUserAmount, - address recipient, - uint256 fee - ); - event SetFeeCollector(address newFeeCollector); - event CancelRFQOffer(bytes32 indexed rfqOfferHash, address indexed maker); - address rfqOwner = makeAddr("rfqOwner"); address allowanceTargetOwner = makeAddr("allowanceTargetOwner"); uint256 makerSignerPrivateKey = uint256(9021); @@ -112,24 +98,36 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { assertEq(rfq.feeCollector(), feeCollector); } + function testCannotNewRFQWithZeroAddressFeeCollector() public { + vm.expectRevert(IRFQ.ZeroAddress.selector); + new RFQ(rfqOwner, UNISWAP_PERMIT2_ADDRESS, address(allowanceTarget), IWETH(WETH_ADDRESS), payable(address(0))); + } + function testCannotSetFeeCollectorByNotOwner() public { address newFeeCollector = makeAddr("newFeeCollector"); - vm.prank(newFeeCollector); + vm.startPrank(newFeeCollector); vm.expectRevert(Ownable.NotOwner.selector); rfq.setFeeCollector(payable(newFeeCollector)); + vm.stopPrank(); } function testCannotSetFeeCollectorToZero() public { - vm.prank(rfqOwner, rfqOwner); + vm.startPrank(rfqOwner); vm.expectRevert(IRFQ.ZeroAddress.selector); rfq.setFeeCollector(payable(address(0))); + vm.stopPrank(); } function testSetFeeCollector() public { address newFeeCollector = makeAddr("newFeeCollector"); - vm.prank(rfqOwner, rfqOwner); + + vm.expectEmit(false, false, false, true); + emit IRFQ.SetFeeCollector(newFeeCollector); + + vm.startPrank(rfqOwner); rfq.setFeeCollector(payable(newFeeCollector)); - emit SetFeeCollector(newFeeCollector); + vm.stopPrank(); + vm.snapshotGasLastCall("RFQ", "setFeeCollector(): testSetFeeCollector"); assertEq(rfq.feeCollector(), newFeeCollector); } @@ -145,7 +143,7 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { uint256 fee = (defaultRFQOffer.makerTokenAmount * defaultFeeFactor) / Constant.BPS_MAX; uint256 amountAfterFee = defaultRFQOffer.makerTokenAmount - fee; vm.expectEmit(true, true, true, true); - emit FilledRFQ( + emit IRFQ.FilledRFQ( getRFQOfferHash(defaultRFQOffer), defaultRFQOffer.taker, defaultRFQOffer.maker, @@ -157,8 +155,11 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { fee ); - vm.prank(defaultRFQOffer.taker, defaultRFQOffer.taker); + vm.startPrank(defaultRFQOffer.taker, defaultRFQOffer.taker); rfq.fillRFQ(defaultRFQTx, defaultMakerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("RFQ", "fillRFQ(): testFillRFQ"); + takerTakerToken.assertChange(-int256(defaultRFQOffer.takerTokenAmount)); takerMakerToken.assertChange(int256(0)); makerTakerToken.assertChange(int256(defaultRFQOffer.takerTokenAmount)); @@ -174,8 +175,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { bytes memory takerPermit = abi.encodePacked(TokenCollector.Source.TokenlonAllowanceTarget); - vm.prank(defaultRFQOffer.taker, defaultRFQOffer.taker); + vm.startPrank(defaultRFQOffer.taker, defaultRFQOffer.taker); rfq.fillRFQ(defaultRFQTx, defaultMakerSig, defaultMakerPermit, takerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("RFQ", "fillRFQ(): testFillRFQWithTakerApproveAllowanceTarget"); } function testFillRFQWithZeroFee() public { @@ -193,7 +196,7 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { bytes memory makerSig = signRFQOffer(makerSignerPrivateKey, rfqTx.rfqOffer, address(rfq)); vm.expectEmit(true, true, true, true); - emit FilledRFQ( + emit IRFQ.FilledRFQ( getRFQOfferHash(rfqOffer), defaultRFQOffer.taker, defaultRFQOffer.maker, @@ -205,8 +208,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { 0 ); - vm.prank(defaultRFQOffer.taker, defaultRFQOffer.taker); + vm.startPrank(defaultRFQOffer.taker, defaultRFQOffer.taker); rfq.fillRFQ(rfqTx, makerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("RFQ", "fillRFQ(): testFillRFQWithZeroFee"); takerTakerToken.assertChange(-int256(defaultRFQOffer.takerTokenAmount)); takerMakerToken.assertChange(int256(0)); @@ -239,8 +244,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { rfqTx.rfqOffer = rfqOffer; rfqTx.takerRequestAmount = rfqOffer.takerTokenAmount; - vm.prank(rfqOffer.taker, rfqOffer.taker); + vm.startPrank(rfqOffer.taker, rfqOffer.taker); rfq.fillRFQ{ value: rfqOffer.takerTokenAmount }(rfqTx, makerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("RFQ", "fillRFQ(): testFillRFQWithRawETH"); takerTakerToken.assertChange(-int256(rfqOffer.takerTokenAmount)); takerMakerToken.assertChange(int256(0)); @@ -277,8 +284,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { rfqTx.rfqOffer = rfqOffer; rfqTx.takerRequestAmount = rfqOffer.takerTokenAmount; - vm.prank(rfqOffer.taker, rfqOffer.taker); + vm.startPrank(rfqOffer.taker, rfqOffer.taker); rfq.fillRFQ{ value: rfqOffer.takerTokenAmount }(rfqTx, makerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("RFQ", "fillRFQ(): testFillRFQWithRawETHAndReceiveWETH"); takerTakerToken.assertChange(-int256(rfqOffer.takerTokenAmount)); takerMakerToken.assertChange(int256(0)); @@ -313,43 +322,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { RFQTx memory rfqTx = defaultRFQTx; rfqTx.rfqOffer = rfqOffer; - vm.prank(rfqOffer.taker, rfqOffer.taker); - rfq.fillRFQ(rfqTx, makerSig, defaultMakerPermit, defaultTakerPermit); - - takerTakerToken.assertChange(-int256(rfqOffer.takerTokenAmount)); - takerMakerToken.assertChange(int256(0)); - makerTakerToken.assertChange(int256(rfqOffer.takerTokenAmount)); - makerMakerToken.assertChange(-int256(rfqOffer.makerTokenAmount)); - recTakerToken.assertChange(int256(0)); - // recipient gets less than original makerTokenAmount because of the fee - recMakerToken.assertChange(int256(amountAfterFee)); - fcMakerToken.assertChange(int256(fee)); - } - - function testFillRFQTakerGetRawETH2() public { - RFQOffer memory rfqOffer = defaultRFQOffer; - rfqOffer.makerToken = Constant.ZERO_ADDRESS; - rfqOffer.makerTokenAmount = 1 ether; - - bytes memory makerSig = signRFQOffer(makerSignerPrivateKey, rfqOffer, address(rfq)); - - Snapshot memory takerTakerToken = BalanceSnapshot.take({ owner: rfqOffer.taker, token: rfqOffer.takerToken }); - Snapshot memory takerMakerToken = BalanceSnapshot.take({ owner: rfqOffer.taker, token: rfqOffer.makerToken }); - Snapshot memory makerTakerToken = BalanceSnapshot.take({ owner: rfqOffer.maker, token: rfqOffer.takerToken }); - Snapshot memory makerMakerToken = BalanceSnapshot.take({ owner: rfqOffer.maker, token: WETH_ADDRESS }); - // recipient should receive raw ETH - Snapshot memory recTakerToken = BalanceSnapshot.take({ owner: recipient, token: rfqOffer.takerToken }); - Snapshot memory recMakerToken = BalanceSnapshot.take({ owner: recipient, token: rfqOffer.makerToken }); - Snapshot memory fcMakerToken = BalanceSnapshot.take({ owner: feeCollector, token: rfqOffer.makerToken }); - - uint256 fee = (rfqOffer.makerTokenAmount * defaultFeeFactor) / Constant.BPS_MAX; - uint256 amountAfterFee = rfqOffer.makerTokenAmount - fee; - - RFQTx memory rfqTx = defaultRFQTx; - rfqTx.rfqOffer = rfqOffer; - - vm.prank(rfqOffer.taker, rfqOffer.taker); + vm.startPrank(rfqOffer.taker, rfqOffer.taker); rfq.fillRFQ(rfqTx, makerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("RFQ", "fillRFQ(): testFillRFQTakerGetRawETH"); takerTakerToken.assertChange(-int256(rfqOffer.takerTokenAmount)); takerMakerToken.assertChange(int256(0)); @@ -387,8 +363,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { bytes memory takerPermit = getTokenlonPermit2Data(taker, takerPrivateKey, rfqOffer.takerToken, address(rfq)); - vm.prank(rfqOffer.taker, rfqOffer.taker); + vm.startPrank(rfqOffer.taker, rfqOffer.taker); rfq.fillRFQ(rfqTx, makerSig, defaultMakerPermit, takerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("RFQ", "fillRFQ(): testFillRFQWithWETH"); takerTakerToken.assertChange(-int256(rfqOffer.takerTokenAmount)); takerMakerToken.assertChange(int256(0)); @@ -426,8 +404,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { bytes memory takerPermit = getTokenlonPermit2Data(taker, takerPrivateKey, rfqOffer.takerToken, address(rfq)); - vm.prank(rfqOffer.taker, rfqOffer.taker); + vm.startPrank(rfqOffer.taker, rfqOffer.taker); rfq.fillRFQ(rfqTx, makerSig, defaultMakerPermit, takerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("RFQ", "fillRFQ(): testFillRFQWithWETHAndReceiveWETH"); takerTakerToken.assertChange(-int256(rfqOffer.takerTokenAmount)); takerMakerToken.assertChange(int256(0)); @@ -452,8 +432,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { bytes memory takerPermit = getTokenlonPermit2Data(takerWalletContract, takerPrivateKey, defaultRFQOffer.takerToken, address(rfq)); // tx.origin is an EOA, msg.sender is a contract - vm.prank(takerWalletContract, makeAddr("anyAddr")); + vm.startPrank(takerWalletContract, makeAddr("anyAddr")); rfq.fillRFQ(rfqTx, makerSig, defaultMakerPermit, takerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("RFQ", "fillRFQ(): testFillWithContract"); } function testPartialFill() public { @@ -474,8 +456,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { rfqTx.rfqOffer = rfqOffer; rfqTx.takerRequestAmount = rfqOffer.takerTokenAmount / 2; - vm.prank(rfqOffer.taker, rfqOffer.taker); + vm.startPrank(rfqOffer.taker, rfqOffer.taker); rfq.fillRFQ(rfqTx, makerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); + vm.snapshotGasLastCall("RFQ", "fillRFQ(): testPartialFill"); { uint256 makerActualAmount = rfqOffer.makerTokenAmount / 2; @@ -496,9 +480,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { RFQTx memory rfqTx = defaultRFQTx; rfqTx.takerRequestAmount = defaultRFQOffer.takerTokenAmount / 2; + vm.startPrank(defaultRFQOffer.taker, defaultRFQOffer.taker); vm.expectRevert(IRFQ.ForbidPartialFill.selector); - vm.prank(defaultRFQOffer.taker, defaultRFQOffer.taker); rfq.fillRFQ(rfqTx, defaultMakerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); } function testCannotPartialFillWithInvalidAmount() public { @@ -511,68 +496,79 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { RFQTx memory rfqTx = defaultRFQTx; rfqTx.rfqOffer = rfqOffer; rfqTx.takerRequestAmount = defaultRFQOffer.takerTokenAmount * 2; + + vm.startPrank(rfqOffer.taker, rfqOffer.taker); vm.expectRevert(IRFQ.InvalidTakerAmount.selector); - vm.prank(rfqOffer.taker, rfqOffer.taker); rfq.fillRFQ(rfqTx, makerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); // case : takerRequestAmount = 0 RFQTx memory rfqTx1 = defaultRFQTx; rfqTx1.rfqOffer = rfqOffer; rfqTx1.takerRequestAmount = 0; + + vm.startPrank(rfqOffer.taker, rfqOffer.taker); vm.expectRevert(IRFQ.InvalidTakerAmount.selector); - vm.prank(rfqOffer.taker, rfqOffer.taker); rfq.fillRFQ(rfqTx1, makerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); } function testCannotFillWithContractWhenNotAllowContractSender() public { RFQTx memory rfqTx = defaultRFQTx; address mockContract = makeAddr("mockContract"); + vm.startPrank(mockContract, defaultRFQOffer.taker); vm.expectRevert(IRFQ.ForbidContract.selector); - vm.prank(mockContract, defaultRFQOffer.taker); rfq.fillRFQ(rfqTx, defaultMakerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); } function testCannotFillExpiredRFQTx() public { vm.warp(defaultRFQOffer.expiry + 1); + vm.startPrank(defaultRFQOffer.taker, defaultRFQOffer.taker); vm.expectRevert(IRFQ.ExpiredRFQOffer.selector); - vm.prank(defaultRFQOffer.taker, defaultRFQOffer.taker); rfq.fillRFQ(defaultRFQTx, defaultMakerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); } function testCannotFillAlreadyFillRFQTx() public { - vm.prank(defaultRFQOffer.taker, defaultRFQOffer.taker); + vm.startPrank(defaultRFQOffer.taker, defaultRFQOffer.taker); rfq.fillRFQ(defaultRFQTx, defaultMakerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); + vm.startPrank(defaultRFQOffer.taker, defaultRFQOffer.taker); vm.expectRevert(IRFQ.FilledRFQOffer.selector); - vm.prank(defaultRFQOffer.taker, defaultRFQOffer.taker); rfq.fillRFQ(defaultRFQTx, defaultMakerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); } function testCannotFillRFQWithIncorrectMakerSig() public { uint256 randomPrivateKey = 5677; bytes memory randomMakerSig = signRFQOffer(randomPrivateKey, defaultRFQOffer, address(rfq)); + vm.startPrank(defaultRFQOffer.taker, defaultRFQOffer.taker); vm.expectRevert(IRFQ.InvalidSignature.selector); - vm.prank(defaultRFQOffer.taker, defaultRFQOffer.taker); rfq.fillRFQ(defaultRFQTx, randomMakerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); } function testCannotFillWithZeroRecipient() public { RFQTx memory rfqTx = defaultRFQTx; rfqTx.recipient = payable(address(0)); + vm.startPrank(defaultRFQOffer.taker, defaultRFQOffer.taker); vm.expectRevert(IRFQ.ZeroAddress.selector); - vm.prank(defaultRFQOffer.taker, defaultRFQOffer.taker); rfq.fillRFQ(rfqTx, defaultMakerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); } function testCannotFillWithIncorrectMsgValue() public { // case : takerToken is normal ERC20 - vm.prank(defaultRFQOffer.taker, defaultRFQOffer.taker); + vm.startPrank(defaultRFQOffer.taker, defaultRFQOffer.taker); vm.expectRevert(IRFQ.InvalidMsgValue.selector); rfq.fillRFQ{ value: 1 ether }(defaultRFQTx, defaultMakerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); // case : takerToken is WETH RFQOffer memory rfqOffer = defaultRFQOffer; @@ -583,9 +579,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { rfqTx.takerRequestAmount = rfqOffer.takerTokenAmount; bytes memory makerSig = signRFQOffer(makerSignerPrivateKey, rfqOffer, address(rfq)); - vm.prank(defaultRFQOffer.taker, defaultRFQOffer.taker); + vm.startPrank(defaultRFQOffer.taker, defaultRFQOffer.taker); vm.expectRevert(IRFQ.InvalidMsgValue.selector); rfq.fillRFQ{ value: 2 ether }(rfqTx, makerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); // case : takerToken is raw ETH RFQOffer memory rfqOffer1 = defaultRFQOffer; @@ -596,9 +593,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { rfqTx1.takerRequestAmount = rfqOffer1.takerTokenAmount; bytes memory makerSig1 = signRFQOffer(makerSignerPrivateKey, rfqOffer1, address(rfq)); - vm.prank(defaultRFQOffer.taker, defaultRFQOffer.taker); + vm.startPrank(defaultRFQOffer.taker, defaultRFQOffer.taker); vm.expectRevert(IRFQ.InvalidMsgValue.selector); rfq.fillRFQ{ value: 2 ether }(rfqTx1, makerSig1, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); } function testFillRFQByTakerSig() public { @@ -611,11 +609,11 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { Snapshot memory fcMakerToken = BalanceSnapshot.take({ owner: feeCollector, token: defaultRFQOffer.makerToken }); bytes memory takerSig = signRFQTx(takerPrivateKey, defaultRFQTx, address(rfq)); - uint256 fee = (defaultRFQOffer.makerTokenAmount * defaultFeeFactor) / Constant.BPS_MAX; uint256 amountAfterFee = defaultRFQOffer.makerTokenAmount - fee; + vm.expectEmit(true, true, true, true); - emit FilledRFQ( + emit IRFQ.FilledRFQ( getRFQOfferHash(defaultRFQOffer), defaultRFQOffer.taker, defaultRFQOffer.maker, @@ -627,8 +625,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { fee ); - vm.prank(txRelayer, txRelayer); + vm.startPrank(txRelayer, txRelayer); rfq.fillRFQWithSig(defaultRFQTx, defaultMakerSig, defaultMakerPermit, defaultTakerPermit, takerSig); + vm.stopPrank(); + vm.snapshotGasLastCall("RFQ", "fillRFQWithSig(): testFillRFQByTakerSig"); takerTakerToken.assertChange(-int256(defaultRFQOffer.takerTokenAmount)); takerMakerToken.assertChange(int256(0)); @@ -644,9 +644,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { uint256 randomPrivateKey = 5677; bytes memory randomSig = signRFQTx(randomPrivateKey, defaultRFQTx, address(rfq)); + vm.startPrank(txRelayer, txRelayer); vm.expectRevert(IRFQ.InvalidSignature.selector); - vm.prank(txRelayer, txRelayer); rfq.fillRFQWithSig(defaultRFQTx, defaultMakerSig, defaultMakerPermit, defaultTakerPermit, randomSig); + vm.stopPrank(); } function testCannotFillWithInvalidFeeFactor() public { @@ -654,9 +655,10 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { rfqTx.rfqOffer.feeFactor = Constant.BPS_MAX + 1; bytes memory takerSig = signRFQTx(takerPrivateKey, rfqTx, address(rfq)); + vm.startPrank(txRelayer, txRelayer); vm.expectRevert(IRFQ.InvalidFeeFactor.selector); - vm.prank(txRelayer, txRelayer); rfq.fillRFQWithSig(rfqTx, defaultMakerSig, defaultMakerPermit, defaultTakerPermit, takerSig); + vm.stopPrank(); } function testCannotFillIfMakerAmountIsZero() public { @@ -676,31 +678,48 @@ contract RFQTest is Test, Tokens, BalanceUtil, Permit2Helper, SigHelper { RFQTx memory rfqTx = RFQTx({ rfqOffer: rfqOffer, takerRequestAmount: 1, recipient: payable(recipient) }); bytes memory makerSig = signRFQOffer(makerSignerPrivateKey, rfqOffer, address(rfq)); - vm.prank(rfqOffer.taker, rfqOffer.taker); + vm.startPrank(rfqOffer.taker, rfqOffer.taker); vm.expectRevert(IRFQ.InvalidMakerAmount.selector); rfq.fillRFQ(rfqTx, makerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); } function testCancelRFQOffer() public { - vm.prank(defaultRFQOffer.maker, defaultRFQOffer.maker); - rfq.cancelRFQOffer(defaultRFQOffer); + vm.expectEmit(true, true, false, false); + emit IRFQ.CancelRFQOffer(getRFQOfferHash(defaultRFQOffer), defaultRFQOffer.maker); - emit CancelRFQOffer(getRFQOfferHash(defaultRFQOffer), defaultRFQOffer.maker); + vm.startPrank(defaultRFQOffer.maker); + rfq.cancelRFQOffer(defaultRFQOffer); + vm.stopPrank(); + vm.snapshotGasLastCall("RFQ", "cancelRFQOffer(): testCancelRFQOffer"); } function testCannotCancelRFQOfferIfNotMaker() public { + vm.startPrank(makeAddr("not offer maker")); vm.expectRevert(IRFQ.NotOfferMaker.selector); rfq.cancelRFQOffer(defaultRFQOffer); + vm.stopPrank(); } - function testCannotCancelRFQOfferIfFilledOrCancelled() public { - vm.startPrank(defaultRFQOffer.maker, defaultRFQOffer.maker); - rfq.cancelRFQOffer(defaultRFQOffer); + function testCannotCancelRFQOfferIfFilled() public { + vm.startPrank(defaultRFQOffer.taker, defaultRFQOffer.taker); + rfq.fillRFQ(defaultRFQTx, defaultMakerSig, defaultMakerPermit, defaultTakerPermit); + vm.stopPrank(); - // cannot cancel an offer twice + vm.startPrank(defaultRFQOffer.maker); vm.expectRevert(IRFQ.FilledRFQOffer.selector); rfq.cancelRFQOffer(defaultRFQOffer); + vm.stopPrank(); + } + + function testCannotCancelRFQOfferTwice() public { + vm.startPrank(defaultRFQOffer.maker); + rfq.cancelRFQOffer(defaultRFQOffer); + vm.stopPrank(); + vm.startPrank(defaultRFQOffer.maker); + vm.expectRevert(IRFQ.FilledRFQOffer.selector); + rfq.cancelRFQOffer(defaultRFQOffer); vm.stopPrank(); } } diff --git a/test/forkMainnet/SmartOrderStrategy/AMMs.t.sol b/test/forkMainnet/SmartOrderStrategy/AMMs.t.sol index d0952bfd..27671ccb 100644 --- a/test/forkMainnet/SmartOrderStrategy/AMMs.t.sol +++ b/test/forkMainnet/SmartOrderStrategy/AMMs.t.sol @@ -10,8 +10,6 @@ import { ISmartOrderStrategy } from "contracts/interfaces/ISmartOrderStrategy.so import { IUniswapSwapRouter02 } from "test/utils/IUniswapSwapRouter02.sol"; import { Constant } from "contracts/libraries/Constant.sol"; import { BalanceSnapshot, Snapshot } from "test/utils/BalanceSnapshot.sol"; -import { UniswapV2Library } from "test/utils/UniswapV2Library.sol"; -import { UniswapV3 } from "test/utils/UniswapV3.sol"; contract AMMsTest is SmartOrderStrategyTest { using SafeERC20 for IERC20; @@ -49,12 +47,13 @@ contract AMMsTest is SmartOrderStrategyTest { uint256 expectedOut = v3Quoter.quoteExactInput(encodedUniv3Path, defaultInputAmount); uint256 realChangedInGS = expectedOut - 1; - vm.startPrank(genericSwap, genericSwap); + vm.startPrank(genericSwap); IERC20(defaultInputToken).safeTransfer(address(smartOrderStrategy), defaultInputAmount); Snapshot memory sosInputToken = BalanceSnapshot.take(address(smartOrderStrategy), defaultInputToken); Snapshot memory gsOutputToken = BalanceSnapshot.take(genericSwap, defaultOutputToken); smartOrderStrategy.executeStrategy(defaultInputToken, defaultOutputToken, defaultInputAmount, data); vm.stopPrank(); + vm.snapshotGasLastCall("SmartOrderStrategy", "executeStrategy(): testUniswapV3WithoutAmountReplace"); sosInputToken.assertChange(-int256(defaultInputAmount)); gsOutputToken.assertChange(int256(realChangedInGS)); @@ -90,12 +89,15 @@ contract AMMsTest is SmartOrderStrategyTest { // get the exact quote from uniswap uint256 inputAmountAfterRatio = ((defaultInputAmount - 1) * defaultInputRatio) / Constant.BPS_MAX; uint256 expectedOut = v3Quoter.quoteExactInput(encodedUniv3Path, inputAmountAfterRatio); - vm.startPrank(genericSwap, genericSwap); + + vm.startPrank(genericSwap); IERC20(defaultInputToken).safeTransfer(address(smartOrderStrategy), defaultInputAmount); Snapshot memory sosInputToken = BalanceSnapshot.take(address(smartOrderStrategy), defaultInputToken); Snapshot memory gsOutputToken = BalanceSnapshot.take(genericSwap, defaultOutputToken); smartOrderStrategy.executeStrategy(defaultInputToken, defaultOutputToken, defaultInputAmount, data); vm.stopPrank(); + vm.snapshotGasLastCall("SmartOrderStrategy", "executeStrategy(): testUniswapV3WithAmountReplace"); + sosInputToken.assertChange(-int256(inputAmountAfterRatio)); gsOutputToken.assertChange(int256(expectedOut - 1)); } @@ -133,12 +135,13 @@ contract AMMsTest is SmartOrderStrategyTest { // get the exact quote from uniswap uint256 expectedOut = v3Quoter.quoteExactInput(encodedUniv3Path, actualInputAmount - 1); - vm.startPrank(genericSwap, genericSwap); + vm.startPrank(genericSwap); IERC20(defaultInputToken).safeTransfer(address(smartOrderStrategy), actualInputAmount); Snapshot memory sosInputToken = BalanceSnapshot.take(address(smartOrderStrategy), defaultInputToken); Snapshot memory gsOutputToken = BalanceSnapshot.take(genericSwap, defaultOutputToken); smartOrderStrategy.executeStrategy(defaultInputToken, defaultOutputToken, defaultInputAmount, data); vm.stopPrank(); + vm.snapshotGasLastCall("SmartOrderStrategy", "executeStrategy(): testUniswapV3WithMaxAmountReplace"); // the amount change will be the actual balance at the moment sosInputToken.assertChange(-int256(actualInputAmount - 1)); // leaving 1 wei in SOS @@ -175,15 +178,16 @@ contract AMMsTest is SmartOrderStrategyTest { // get the exact quote from uniswap uint256 expectedOut = v3Quoter.quoteExactInput(encodedUniv3Path, defaultInputAmount); uint256 realChangedInGS = expectedOut - 1; - // set output token as ETH address outputToken = Constant.ETH_ADDRESS; - vm.startPrank(genericSwap, genericSwap); + + vm.startPrank(genericSwap); IERC20(defaultInputToken).safeTransfer(address(smartOrderStrategy), defaultInputAmount); Snapshot memory sosInputToken = BalanceSnapshot.take(address(smartOrderStrategy), defaultInputToken); Snapshot memory gsOutputToken = BalanceSnapshot.take(genericSwap, outputToken); smartOrderStrategy.executeStrategy(defaultInputToken, outputToken, defaultInputAmount, data); vm.stopPrank(); + vm.snapshotGasLastCall("SmartOrderStrategy", "executeStrategy(): testUniswapV2WithWETHUnwrap"); sosInputToken.assertChange(-int256(defaultInputAmount)); gsOutputToken.assertChange(int256(realChangedInGS)); @@ -239,12 +243,13 @@ contract AMMsTest is SmartOrderStrategyTest { }); bytes memory data = abi.encode(operations); - vm.startPrank(genericSwap, genericSwap); + vm.startPrank(genericSwap); IERC20(defaultInputToken).safeTransfer(address(smartOrderStrategy), defaultInputAmount); Snapshot memory sosInputToken = BalanceSnapshot.take(address(smartOrderStrategy), defaultInputToken); Snapshot memory gsOutputToken = BalanceSnapshot.take(genericSwap, USDT_ADDRESS); smartOrderStrategy.executeStrategy(defaultInputToken, USDT_ADDRESS, defaultInputAmount, data); vm.stopPrank(); + vm.snapshotGasLastCall("SmartOrderStrategy", "executeStrategy(): testMultipleAMMs"); sosInputToken.assertChange(-int256(defaultInputAmount)); gsOutputToken.assertChange(int256(realChangedInGS)); diff --git a/test/forkMainnet/SmartOrderStrategy/IntegrationV6.t.sol b/test/forkMainnet/SmartOrderStrategy/IntegrationV6.t.sol index f767d273..e39cc3e0 100644 --- a/test/forkMainnet/SmartOrderStrategy/IntegrationV6.t.sol +++ b/test/forkMainnet/SmartOrderStrategy/IntegrationV6.t.sol @@ -13,9 +13,9 @@ import { ISmartOrderStrategy } from "contracts/interfaces/ISmartOrderStrategy.so import { ILimitOrderSwap } from "contracts/interfaces/ILimitOrderSwap.sol"; import { TokenCollector } from "contracts/abstracts/TokenCollector.sol"; import { Constant } from "contracts/libraries/Constant.sol"; -import { RFQOffer, getRFQOfferHash } from "contracts/libraries/RFQOffer.sol"; +import { RFQOffer } from "contracts/libraries/RFQOffer.sol"; import { RFQTx } from "contracts/libraries/RFQTx.sol"; -import { LimitOrder, getLimitOrderHash } from "contracts/libraries/LimitOrder.sol"; +import { LimitOrder } from "contracts/libraries/LimitOrder.sol"; import { BalanceSnapshot, Snapshot } from "test/utils/BalanceSnapshot.sol"; import { SigHelper } from "test/utils/SigHelper.sol"; @@ -44,8 +44,9 @@ contract IntegrationV6Test is SmartOrderStrategyTest, SigHelper { address[] memory spenders = new address[](2); spenders[0] = address(rfq); spenders[1] = address(limitOrderSwap); - vm.prank(strategyOwner); + vm.startPrank(strategyOwner); smartOrderStrategy.approveTokens(tokenList, spenders); + vm.stopPrank(); // maker approves RFQ & LO setTokenBalanceAndApprove(maker, address(rfq), tokens, 100000); @@ -84,12 +85,13 @@ contract IntegrationV6Test is SmartOrderStrategyTest, SigHelper { }); bytes memory opsData = abi.encode(operations); - vm.startPrank(genericSwap, genericSwap); + vm.startPrank(genericSwap); IERC20(rfqOffer.takerToken).safeTransfer(address(smartOrderStrategy), rfqOffer.takerTokenAmount); Snapshot memory sosInputToken = BalanceSnapshot.take(address(smartOrderStrategy), rfqOffer.takerToken); Snapshot memory gsOutputToken = BalanceSnapshot.take(genericSwap, rfqOffer.makerToken); smartOrderStrategy.executeStrategy(rfqOffer.takerToken, rfqOffer.makerToken, rfqOffer.takerTokenAmount, opsData); vm.stopPrank(); + vm.snapshotGasLastCall("SmartOrderStrategy", "executeStrategy(): testV6RFQIntegration"); sosInputToken.assertChange(-int256(rfqOffer.takerTokenAmount)); gsOutputToken.assertChange(int256(realChangedInGS)); @@ -127,12 +129,13 @@ contract IntegrationV6Test is SmartOrderStrategyTest, SigHelper { }); bytes memory opsData = abi.encode(operations); - vm.startPrank(genericSwap, genericSwap); + vm.startPrank(genericSwap); vm.deal(address(smartOrderStrategy), rfqOffer.takerTokenAmount); Snapshot memory sosInputToken = BalanceSnapshot.take(address(smartOrderStrategy), rfqOffer.takerToken); Snapshot memory gsOutputToken = BalanceSnapshot.take(genericSwap, rfqOffer.makerToken); smartOrderStrategy.executeStrategy{ value: rfqOffer.takerTokenAmount }(rfqOffer.takerToken, rfqOffer.makerToken, rfqOffer.takerTokenAmount, opsData); vm.stopPrank(); + vm.snapshotGasLastCall("SmartOrderStrategy", "executeStrategy(): testV6RFQIntegrationWhenTakerTokenIsETH"); sosInputToken.assertChange(-int256(rfqOffer.takerTokenAmount)); gsOutputToken.assertChange(int256(realChangedInGS)); @@ -168,10 +171,11 @@ contract IntegrationV6Test is SmartOrderStrategyTest, SigHelper { }); bytes memory opsData = abi.encode(operations); - vm.startPrank(genericSwap, genericSwap); + vm.startPrank(genericSwap); IERC20(rfqOffer.takerToken).safeTransfer(address(smartOrderStrategy), rfqOffer.takerTokenAmount); smartOrderStrategy.executeStrategy(rfqOffer.takerToken, rfqOffer.makerToken, rfqOffer.takerTokenAmount, opsData); vm.stopPrank(); + vm.snapshotGasLastCall("SmartOrderStrategy", "executeStrategy(): testV6RFQIntegrationWhenMakerTokenIsETH"); } function testV6LOIntegration() public { @@ -212,12 +216,13 @@ contract IntegrationV6Test is SmartOrderStrategyTest, SigHelper { }); bytes memory opsData = abi.encode(operations); - vm.startPrank(genericSwap, genericSwap); + vm.startPrank(genericSwap); IERC20(order.takerToken).safeTransfer(address(smartOrderStrategy), order.takerTokenAmount); Snapshot memory sosInputToken = BalanceSnapshot.take(address(smartOrderStrategy), order.takerToken); Snapshot memory gsOutputToken = BalanceSnapshot.take(genericSwap, order.makerToken); smartOrderStrategy.executeStrategy(order.takerToken, order.makerToken, order.takerTokenAmount, opsData); vm.stopPrank(); + vm.snapshotGasLastCall("SmartOrderStrategy", "executeStrategy(): testV6LOIntegration"); sosInputToken.assertChange(-int256(order.takerTokenAmount)); gsOutputToken.assertChange(int256(realChangedInGS)); diff --git a/test/forkMainnet/SmartOrderStrategy/Setup.t.sol b/test/forkMainnet/SmartOrderStrategy/Setup.t.sol index 17e069f1..e0423437 100644 --- a/test/forkMainnet/SmartOrderStrategy/Setup.t.sol +++ b/test/forkMainnet/SmartOrderStrategy/Setup.t.sol @@ -31,8 +31,9 @@ contract SmartOrderStrategyTest is Test, Tokens, BalanceUtil { function setUp() public virtual { // Deploy and setup SmartOrderStrategy smartOrderStrategy = new SmartOrderStrategy(strategyOwner, genericSwap, WETH_ADDRESS); - vm.prank(strategyOwner); + vm.startPrank(strategyOwner); smartOrderStrategy.approveTokens(tokenList, ammList); + vm.stopPrank(); // Make genericSwap rich to provide fund for strategy contract deal(genericSwap, 100 ether); diff --git a/test/forkMainnet/SmartOrderStrategy/Validation.t.sol b/test/forkMainnet/SmartOrderStrategy/Validation.t.sol index be810935..7451e2f0 100644 --- a/test/forkMainnet/SmartOrderStrategy/Validation.t.sol +++ b/test/forkMainnet/SmartOrderStrategy/Validation.t.sol @@ -12,9 +12,10 @@ contract ValidationTest is SmartOrderStrategyTest { } function testCannotExecuteWithZeroInputAmount() public { + vm.startPrank(genericSwap); vm.expectRevert(ISmartOrderStrategy.ZeroInput.selector); - vm.prank(genericSwap, genericSwap); smartOrderStrategy.executeStrategy(defaultInputToken, defaultOutputToken, 0, defaultOpsData); + vm.stopPrank(); } function testCannotExecuteWithZeroRatioDenominatorWhenRatioNumeratorIsNonZero() public { @@ -24,24 +25,27 @@ contract ValidationTest is SmartOrderStrategyTest { operations[0].ratioDenominator = 0; bytes memory opsData = abi.encode(operations); + vm.startPrank(genericSwap); vm.expectRevert(ISmartOrderStrategy.ZeroDenominator.selector); - vm.prank(genericSwap, genericSwap); smartOrderStrategy.executeStrategy(defaultInputToken, defaultOutputToken, defaultInputAmount, opsData); + vm.stopPrank(); } function testCannotExecuteWithFailDecodedData() public { + vm.startPrank(genericSwap); vm.expectRevert(); - vm.prank(genericSwap, genericSwap); smartOrderStrategy.executeStrategy(defaultInputToken, defaultOutputToken, defaultInputAmount, bytes("random data")); + vm.stopPrank(); } function testCannotExecuteWithEmptyOperation() public { ISmartOrderStrategy.Operation[] memory operations; bytes memory emptyOpsData = abi.encode(operations); + vm.startPrank(genericSwap); vm.expectRevert(ISmartOrderStrategy.EmptyOps.selector); - vm.prank(genericSwap, genericSwap); smartOrderStrategy.executeStrategy(defaultInputToken, defaultOutputToken, defaultInputAmount, emptyOpsData); + vm.stopPrank(); } function testCannotExecuteWithIncorrectMsgValue() public { @@ -49,19 +53,22 @@ contract ValidationTest is SmartOrderStrategyTest { address inputToken = Constant.ETH_ADDRESS; uint256 inputAmount = 1 ether; + vm.startPrank(genericSwap); vm.expectRevert(ISmartOrderStrategy.InvalidMsgValue.selector); - vm.prank(genericSwap, genericSwap); smartOrderStrategy.executeStrategy{ value: inputAmount + 1 }(inputToken, defaultOutputToken, inputAmount, defaultOpsData); + vm.stopPrank(); // case : ETH as input but msg.value is zero + vm.startPrank(genericSwap); vm.expectRevert(ISmartOrderStrategy.InvalidMsgValue.selector); - vm.prank(genericSwap, genericSwap); smartOrderStrategy.executeStrategy{ value: 0 }(inputToken, defaultOutputToken, inputAmount, defaultOpsData); + vm.stopPrank(); // case : token as input but msg.value is not zero + vm.startPrank(genericSwap); vm.expectRevert(ISmartOrderStrategy.InvalidMsgValue.selector); - vm.prank(genericSwap, genericSwap); smartOrderStrategy.executeStrategy{ value: 1 }(defaultInputToken, defaultOutputToken, defaultInputAmount, defaultOpsData); + vm.stopPrank(); } function testCannotExecuteAnOperationWillFail() public { @@ -77,7 +84,7 @@ contract ValidationTest is SmartOrderStrategyTest { }); bytes memory opsData = abi.encode(operations); - vm.startPrank(genericSwap, genericSwap); + vm.startPrank(genericSwap); vm.expectRevert(); smartOrderStrategy.executeStrategy(defaultInputToken, defaultOutputToken, defaultInputAmount, opsData); vm.stopPrank(); diff --git a/test/forkMainnet/TokenCollector.t.sol b/test/forkMainnet/TokenCollector.t.sol index 9c4dcf9c..b1df8aa0 100644 --- a/test/forkMainnet/TokenCollector.t.sol +++ b/test/forkMainnet/TokenCollector.t.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.26; import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; import { IERC20Errors } from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; +import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { AllowanceTarget } from "contracts/AllowanceTarget.sol"; import { TokenCollector } from "contracts/abstracts/TokenCollector.sol"; @@ -20,15 +21,26 @@ contract Strategy is TokenCollector { } contract TestTokenCollector is Addresses, Permit2Helper { - uint256 otherPrivateKey = uint256(123); - uint256 userPrivateKey = uint256(1); - address other = vm.addr(otherPrivateKey); + uint256 userPrivateKey = uint256(123); + uint256 otherPrivateKey = uint256(456); address user = vm.addr(userPrivateKey); + address other = vm.addr(otherPrivateKey); address allowanceTargetOwner = makeAddr("allowanceTargetOwner"); - MockERC20Permit token = new MockERC20Permit("Token", "TKN", 18); + struct TokenPermit { + address token; + address owner; + address spender; + uint256 amount; + uint256 nonce; + uint256 deadline; + } + TokenPermit DEFAULT_TOKEN_PERMIT; IUniswapPermit2.PermitSingle DEFAULT_PERMIT_SINGLE; + IUniswapPermit2.PermitTransferFrom DEFAULT_PERMIT_TRANSFER; + + MockERC20Permit token = new MockERC20Permit("Token", "TKN", 18); // pre-compute Strategy address since the whitelist of allowance target is immutable // NOTE: this assumes Strategy is deployed right next to Allowance Target @@ -40,6 +52,15 @@ contract TestTokenCollector is Addresses, Permit2Helper { function setUp() public { token.mint(user, 10000 ether); + DEFAULT_TOKEN_PERMIT = TokenPermit({ + token: address(token), + owner: user, + spender: address(strategy), + amount: 100 ether, + nonce: token.nonces(user), + deadline: block.timestamp + 1 days + }); + // get permit2 nonce and compose PermitSingle for AllowanceTransfer uint256 expiration = block.timestamp + 1 days; (, , uint48 nonce) = permit2.allowance(user, address(token), address(strategy)); @@ -49,8 +70,23 @@ contract TestTokenCollector is Addresses, Permit2Helper { sigDeadline: expiration }); - vm.label(address(this), "TestingContract"); - vm.label(address(token), "TKN"); + DEFAULT_PERMIT_TRANSFER = IUniswapPermit2.PermitTransferFrom({ + permitted: IUniswapPermit2.TokenPermissions({ token: address(token), amount: 100 ether }), + nonce: 0, + deadline: block.timestamp + 1 days + }); + } + + function _getTokenPermitHash(TokenPermit memory permit) private view returns (bytes32) { + MockERC20Permit tokenWithPermit = MockERC20Permit(permit.token); + bytes32 structHash = keccak256( + abi.encode(tokenWithPermit._PERMIT_TYPEHASH(), permit.owner, permit.spender, permit.amount, permit.nonce, permit.deadline) + ); + return keccak256(abi.encodePacked("\x19\x01", tokenWithPermit.DOMAIN_SEPARATOR(), structHash)); + } + + function _encodeTokenPermitData(TokenPermit memory permit, uint8 v, bytes32 r, bytes32 s) private pure returns (bytes memory) { + return abi.encodePacked(TokenCollector.Source.TokenPermit, abi.encode(permit.owner, permit.spender, permit.amount, permit.deadline, v, r, s)); } function testCannotCollectByInvalidSource() public { @@ -62,91 +98,61 @@ contract TestTokenCollector is Addresses, Permit2Helper { strategy.collect(address(token), user, address(this), 0, data); } - /* Token Approval */ - - function testCannotCollectByTokenApprovalWhenAllowanceIsNotEnough() public { - bytes memory data = abi.encodePacked(TokenCollector.Source.Token); + /* Tokenlon AllowanceTarget */ + function testCannotCollectByAllowanceTargetIfNoPriorApprove() public { + bytes memory data = abi.encodePacked(TokenCollector.Source.TokenlonAllowanceTarget); - vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InsufficientAllowance.selector, address(strategy), 0, 1)); + vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InsufficientAllowance.selector, address(allowanceTarget), 0, 1)); strategy.collect(address(token), user, address(this), 1, data); } - function testCollectByTokenApproval() public { + function testCollectByAllowanceTarget() public { uint256 amount = 100 ether; - vm.prank(user); - token.approve(address(strategy), amount); + vm.startPrank(user); + token.approve(address(allowanceTarget), amount); + vm.stopPrank(); - bytes memory data = abi.encodePacked(TokenCollector.Source.Token); + bytes memory data = abi.encodePacked(TokenCollector.Source.TokenlonAllowanceTarget); strategy.collect(address(token), user, address(this), amount, data); + vm.snapshotGasLastCall("TokenCollector", "collect(): testCollectByAllowanceTarget"); uint256 balance = token.balanceOf(address(this)); assertEq(balance, amount); } - function testCannotCollectByAllowanceTargetIfNoPriorApprove() public { - bytes memory data = abi.encodePacked(TokenCollector.Source.TokenlonAllowanceTarget); + /* Token */ + function testCannotCollectByTokenApprovalWhenAllowanceIsNotEnough() public { + bytes memory data = abi.encodePacked(TokenCollector.Source.Token); - vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InsufficientAllowance.selector, address(allowanceTarget), 0, 1)); - vm.startPrank(user); + vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InsufficientAllowance.selector, address(strategy), 0, 1)); strategy.collect(address(token), user, address(this), 1, data); - vm.stopPrank(); } - function testCollectByAllowanceTarget() public { + function testCollectByTokenApproval() public { uint256 amount = 100 ether; - vm.prank(user); - token.approve(address(allowanceTarget), amount); + vm.startPrank(user); + token.approve(address(strategy), amount); + vm.stopPrank(); - bytes memory data = abi.encodePacked(TokenCollector.Source.TokenlonAllowanceTarget); + bytes memory data = abi.encodePacked(TokenCollector.Source.Token); strategy.collect(address(token), user, address(this), amount, data); + vm.snapshotGasLastCall("TokenCollector", "collect(): testCollectByTokenApproval"); uint256 balance = token.balanceOf(address(this)); assertEq(balance, amount); } /* Token Permit */ - - TokenPermit DEFAULT_TOKEN_PERMIT = - TokenPermit({ - token: address(token), - owner: user, - spender: address(strategy), - amount: 100 ether, - nonce: token.nonces(user), - deadline: block.timestamp + 1 days - }); - - struct TokenPermit { - address token; - address owner; - address spender; - uint256 amount; - uint256 nonce; - uint256 deadline; - } - - function getTokenPermitHash(TokenPermit memory permit) private view returns (bytes32) { - MockERC20Permit tokenWithPermit = MockERC20Permit(permit.token); - bytes32 structHash = keccak256( - abi.encode(tokenWithPermit._PERMIT_TYPEHASH(), permit.owner, permit.spender, permit.amount, permit.nonce, permit.deadline) - ); - return keccak256(abi.encodePacked("\x19\x01", tokenWithPermit.DOMAIN_SEPARATOR(), structHash)); - } - - function encodeTokenPermitData(TokenPermit memory permit, uint8 v, bytes32 r, bytes32 s) private pure returns (bytes memory) { - return abi.encodePacked(TokenCollector.Source.TokenPermit, abi.encode(permit.owner, permit.spender, permit.amount, permit.deadline, v, r, s)); - } - function testCannotCollectByTokenPermitWhenPermitSigIsInvalid() public { TokenPermit memory permit = DEFAULT_TOKEN_PERMIT; - bytes32 permitHash = getTokenPermitHash(permit); + bytes32 permitHash = _getTokenPermitHash(permit); // Sign by not owner (uint8 v, bytes32 r, bytes32 s) = vm.sign(otherPrivateKey, permitHash); - bytes memory data = encodeTokenPermitData(permit, v, r, s); + bytes memory data = _encodeTokenPermitData(permit, v, r, s); vm.expectRevert(abi.encodeWithSelector(ERC20Permit.ERC2612InvalidSigner.selector, other, permit.owner)); strategy.collect(address(token), permit.owner, address(this), permit.amount, data); } @@ -156,10 +162,10 @@ contract TestTokenCollector is Addresses, Permit2Helper { // Spender is not strategy permit.spender = address(this); - bytes32 permitHash = getTokenPermitHash(permit); + bytes32 permitHash = _getTokenPermitHash(permit); (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, permitHash); - bytes memory data = encodeTokenPermitData(permit, v, r, s); + bytes memory data = _encodeTokenPermitData(permit, v, r, s); vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InsufficientAllowance.selector, address(strategy), 0, permit.amount)); strategy.collect(address(token), permit.owner, address(this), permit.amount, data); } @@ -169,10 +175,10 @@ contract TestTokenCollector is Addresses, Permit2Helper { // Amount is more than permitted uint256 invalidAmount = permit.amount + 100; - bytes32 permitHash = getTokenPermitHash(permit); + bytes32 permitHash = _getTokenPermitHash(permit); (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, permitHash); - bytes memory data = encodeTokenPermitData(permit, v, r, s); + bytes memory data = _encodeTokenPermitData(permit, v, r, s); vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InsufficientAllowance.selector, address(strategy), permit.amount, invalidAmount)); strategy.collect(address(token), permit.owner, address(this), invalidAmount, data); } @@ -182,11 +188,11 @@ contract TestTokenCollector is Addresses, Permit2Helper { // Nonce is invalid permit.nonce = 123; - bytes32 permitHash = getTokenPermitHash(permit); + bytes32 permitHash = _getTokenPermitHash(permit); (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, permitHash); - address recoveredAddress = 0x5d6b650146e111D930C9F97570876A12F568D2B5; + address recoveredAddress = ECDSA.recover(_getTokenPermitHash(DEFAULT_TOKEN_PERMIT), v, r, s); - bytes memory data = encodeTokenPermitData(permit, v, r, s); + bytes memory data = _encodeTokenPermitData(permit, v, r, s); vm.expectRevert(abi.encodeWithSelector(ERC20Permit.ERC2612InvalidSigner.selector, recoveredAddress, permit.owner)); strategy.collect(address(token), permit.owner, address(this), permit.amount, data); } @@ -196,10 +202,10 @@ contract TestTokenCollector is Addresses, Permit2Helper { // Deadline is expired permit.deadline = block.timestamp - 1 days; - bytes32 permitHash = getTokenPermitHash(permit); + bytes32 permitHash = _getTokenPermitHash(permit); (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, permitHash); - bytes memory data = encodeTokenPermitData(permit, v, r, s); + bytes memory data = _encodeTokenPermitData(permit, v, r, s); vm.expectRevert(abi.encodeWithSelector(ERC20Permit.ERC2612ExpiredSignature.selector, permit.deadline)); strategy.collect(address(token), permit.owner, address(this), permit.amount, data); } @@ -207,11 +213,12 @@ contract TestTokenCollector is Addresses, Permit2Helper { function testCollectByTokenPermit() public { TokenPermit memory permit = DEFAULT_TOKEN_PERMIT; - bytes32 permitHash = getTokenPermitHash(permit); + bytes32 permitHash = _getTokenPermitHash(permit); (uint8 v, bytes32 r, bytes32 s) = vm.sign(userPrivateKey, permitHash); - bytes memory data = encodeTokenPermitData(permit, v, r, s); + bytes memory data = _encodeTokenPermitData(permit, v, r, s); strategy.collect(address(token), permit.owner, address(this), permit.amount, data); + vm.snapshotGasLastCall("TokenCollector", "collect(): testCollectByTokenPermit"); uint256 balance = token.balanceOf(address(this)); assertEq(balance, permit.amount); @@ -277,8 +284,9 @@ contract TestTokenCollector is Addresses, Permit2Helper { IUniswapPermit2.PermitSingle memory permit = DEFAULT_PERMIT_SINGLE; uint256 amount = 1234; - vm.prank(user); + vm.startPrank(user); token.approve(address(permit2), type(uint256).max); + vm.stopPrank(); bytes memory permitSig = signPermitSingle(userPrivateKey, permit); bytes memory data = encodeAllowanceTransfer(user, permit, permitSig); @@ -295,27 +303,21 @@ contract TestTokenCollector is Addresses, Permit2Helper { IUniswapPermit2.PermitSingle memory permit = DEFAULT_PERMIT_SINGLE; uint256 amount = 1234; - vm.prank(user); + vm.startPrank(user); token.approve(address(permit2), type(uint256).max); + vm.stopPrank(); bytes memory permitSig = signPermitSingle(userPrivateKey, permit); bytes memory data = encodeAllowanceTransfer(user, permit, permitSig); strategy.collect(address(token), user, address(this), amount, data); + vm.snapshotGasLastCall("TokenCollector", "collect(): testCollectByPermit2AllowanceTransfer"); uint256 balance = token.balanceOf(address(this)); assertEq(balance, amount); } /* Permit2 Signature Transfer */ - - IUniswapPermit2.PermitTransferFrom DEFAULT_PERMIT_TRANSFER = - IUniswapPermit2.PermitTransferFrom({ - permitted: IUniswapPermit2.TokenPermissions({ token: address(token), amount: 100 ether }), - nonce: 0, - deadline: block.timestamp + 1 days - }); - function testCannotCollectByPermit2SignatureTransferWhenSpenderIsInvalid() public { IUniswapPermit2.PermitTransferFrom memory permit = DEFAULT_PERMIT_TRANSFER; // Spender is not strategy @@ -343,8 +345,9 @@ contract TestTokenCollector is Addresses, Permit2Helper { function testCannotCollectByPermit2SignatureTransferWhenNonceIsUsed() public { IUniswapPermit2.PermitTransferFrom memory permit = DEFAULT_PERMIT_TRANSFER; - vm.prank(user); + vm.startPrank(user); token.approve(address(permit2), permit.permitted.amount); + vm.stopPrank(); bytes memory permitSig = signPermitTransferFrom(userPrivateKey, permit, address(strategy)); bytes memory data = encodeSignatureTransfer(permit, permitSig); @@ -371,13 +374,15 @@ contract TestTokenCollector is Addresses, Permit2Helper { function testCollectByPermit2SignatureTransfer() public { IUniswapPermit2.PermitTransferFrom memory permit = DEFAULT_PERMIT_TRANSFER; - vm.prank(user); + vm.startPrank(user); token.approve(address(permit2), permit.permitted.amount); + vm.stopPrank(); bytes memory permitSig = signPermitTransferFrom(userPrivateKey, permit, address(strategy)); bytes memory data = encodeSignatureTransfer(permit, permitSig); strategy.collect(address(token), user, address(this), permit.permitted.amount, data); + vm.snapshotGasLastCall("TokenCollector", "collect(): testCollectByPermit2SignatureTransfer"); uint256 balance = token.balanceOf(address(this)); assertEq(balance, permit.permitted.amount); diff --git a/test/libraries/Asset.t.sol b/test/libraries/Asset.t.sol index be469116..fda24daf 100644 --- a/test/libraries/Asset.t.sol +++ b/test/libraries/Asset.t.sol @@ -10,6 +10,7 @@ contract AssetTest is Test { using Asset for address; MockERC20 token; + AssetHarness assetHarness; address payable recipient = payable(makeAddr("recipient")); uint256 tokenBalance = 123; @@ -17,59 +18,79 @@ contract AssetTest is Test { function setUp() public { token = new MockERC20("TOKEN", "TKN", 18); + assetHarness = new AssetHarness(); // set balance - token.mint(address(this), tokenBalance); - vm.deal(address(this), ethBalance); - } - - function transferToWrap(address asset, address payable to, uint256 amount) public { - Asset.transferTo(asset, to, amount); + token.mint(address(assetHarness), tokenBalance); + vm.deal(address(assetHarness), ethBalance); } function testIsETH() public { - assertTrue(Asset.isETH(Constant.ETH_ADDRESS)); - assertTrue(Asset.isETH(address(0))); + assertTrue(assetHarness.exposedIsETH(Constant.ETH_ADDRESS)); + vm.snapshotGasLastCall("Asset", "isETH(): testIsETH(ETH_ADDRESS)"); + assertTrue(assetHarness.exposedIsETH(Constant.ZERO_ADDRESS)); + vm.snapshotGasLastCall("Asset", "isETH(): testIsETH2(ZERO_ADDRESS)"); } function testGetBalance() public { - assertEq(Asset.getBalance(address(token), address(this)), tokenBalance); - assertEq(Asset.getBalance(Constant.ETH_ADDRESS, address(this)), ethBalance); - assertEq(Asset.getBalance(address(0), address(this)), ethBalance); + assertEq(assetHarness.exposedGetBalance(address(token), address(assetHarness)), tokenBalance); + vm.snapshotGasLastCall("Asset", "getBalance(): testGetBalance"); + assertEq(assetHarness.exposedGetBalance(Constant.ETH_ADDRESS, address(assetHarness)), ethBalance); + vm.snapshotGasLastCall("Asset", "getBalance(): testGetBalance(ETH_ADDRESS)"); + assertEq(assetHarness.exposedGetBalance(Constant.ZERO_ADDRESS, address(assetHarness)), ethBalance); + vm.snapshotGasLastCall("Asset", "getBalance(): testGetBalance(ZERO_ADDRESS)"); } function testDoNothingIfTransferWithZeroAmount() public { - Asset.transferTo(address(token), recipient, 0); + assetHarness.exposedTransferTo(address(token), recipient, 0); + vm.snapshotGasLastCall("Asset", "transferTo(): testDoNothingIfTransferWithZeroAmount"); } function testDoNothingIfTransferToSelf() public { - Asset.transferTo(address(token), payable(address(token)), 0); + assetHarness.exposedTransferTo(address(token), payable(address(token)), 0); + vm.snapshotGasLastCall("Asset", "transferTo(): testDoNothingIfTransferToSelf"); } - function testTransferETHWithInsufficientBalance() public { - vm.expectRevert(Asset.InsufficientBalance.selector); - this.transferToWrap(Constant.ETH_ADDRESS, recipient, address(this).balance + 1); + function testCannotTransferETHWithInsufficientBalance() public { + vm.expectRevert(); + assetHarness.exposedTransferTo(Constant.ETH_ADDRESS, recipient, address(assetHarness).balance + 1); } - function testTransferETHToContractCannotReceiveETH() public { + function testCannotTransferETHToContractCannotReceiveETH() public { vm.expectRevert(); // mockERC20 cannot receive any ETH - this.transferToWrap(Constant.ETH_ADDRESS, payable(address(token)), 1); + assetHarness.exposedTransferTo(Constant.ETH_ADDRESS, payable(address(token)), 1); } function testTransferETH() public { - uint256 amount = address(this).balance; - Asset.transferTo(Constant.ETH_ADDRESS, payable(recipient), amount); + uint256 amount = address(assetHarness).balance; + assetHarness.exposedTransferTo(Constant.ETH_ADDRESS, recipient, amount); + vm.snapshotGasLastCall("Asset", "transferTo(): testTransferETH"); assertEq(address(recipient).balance, amount); - assertEq(address(this).balance, 0); + assertEq(address(assetHarness).balance, 0); } function testTransferToken() public { - uint256 amount = token.balanceOf(address(this)); - Asset.transferTo(address(token), payable(recipient), amount); + uint256 amount = token.balanceOf(address(assetHarness)); + assetHarness.exposedTransferTo(address(token), recipient, amount); + vm.snapshotGasLastCall("Asset", "transferTo(): testTransferToken"); assertEq(token.balanceOf(recipient), amount); - assertEq(token.balanceOf(address(this)), 0); + assertEq(token.balanceOf(address(assetHarness)), 0); + } +} + +contract AssetHarness { + function exposedIsETH(address addr) external pure returns (bool) { + return Asset.isETH(addr); + } + + function exposedGetBalance(address asset, address owner) external view returns (uint256) { + return Asset.getBalance(asset, owner); + } + + function exposedTransferTo(address asset, address payable to, uint256 amount) external { + Asset.transferTo(asset, to, amount); } }