From 056582ca2f5d792d60027574c313be2ca8ac649c Mon Sep 17 00:00:00 2001 From: royvardhan Date: Thu, 23 Jan 2025 21:04:05 +0530 Subject: [PATCH 1/3] feat: add tests for withdraw, fee and make it DRY --- foundry/script/Counter.s.sol | 19 --- foundry/script/Deploy.sol | 8 ++ foundry/src/Counter.sol | 14 -- foundry/test/Constants.sol | 13 +- foundry/test/Counter.t.sol | 24 ---- foundry/test/TychoRouter.t.sol | 132 ++++++++++++------ ...tTemplate.sol => TychoRouterTestSetup.sol} | 21 +-- foundry/test/mock/MockERC20.sol | 18 +++ 8 files changed, 139 insertions(+), 110 deletions(-) delete mode 100644 foundry/script/Counter.s.sol create mode 100644 foundry/script/Deploy.sol delete mode 100644 foundry/src/Counter.sol delete mode 100644 foundry/test/Counter.t.sol rename foundry/test/{TestTemplate.sol => TychoRouterTestSetup.sol} (52%) create mode 100644 foundry/test/mock/MockERC20.sol diff --git a/foundry/script/Counter.s.sol b/foundry/script/Counter.s.sol deleted file mode 100644 index cdc1fe9..0000000 --- a/foundry/script/Counter.s.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterScript is Script { - Counter public counter; - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - counter = new Counter(); - - vm.stopBroadcast(); - } -} diff --git a/foundry/script/Deploy.sol b/foundry/script/Deploy.sol new file mode 100644 index 0000000..9c4a381 --- /dev/null +++ b/foundry/script/Deploy.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; + +contract DeployScript is Script { +// TODO: implement deploy script +} diff --git a/foundry/src/Counter.sol b/foundry/src/Counter.sol deleted file mode 100644 index 574cf8f..0000000 --- a/foundry/src/Counter.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.28; - -contract Counter { - uint256 public number; - - function setNumber(uint256 newNumber) public { - number = newNumber; - } - - function increment() public { - number++; - } -} diff --git a/foundry/test/Constants.sol b/foundry/test/Constants.sol index 43e09af..ec30775 100644 --- a/foundry/test/Constants.sol +++ b/foundry/test/Constants.sol @@ -1,10 +1,13 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; +pragma solidity ^0.8.28; -contract Constants { - address ADMIN = address(12395012351212343412541234); //admin=us - address BOB = address(123); //bob=someone!=us +import "forge-std/Test.sol"; +contract Constants is Test { + address ADMIN = makeAddr("admin"); //admin=us + address BOB = makeAddr("bob"); //bob=someone!=us + address FUND_RESCUER = makeAddr("fundRescuer"); + address FEE_SETTER = makeAddr("feeSetter"); // dummy contracts - address DUMMY = address(0x1234); + address DUMMY = makeAddr("dummy"); } diff --git a/foundry/test/Counter.t.sol b/foundry/test/Counter.t.sol deleted file mode 100644 index 3840a3f..0000000 --- a/foundry/test/Counter.t.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.28; - -import {Test, console} from "forge-std/Test.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -} diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index d261c60..cab9a79 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -2,9 +2,9 @@ pragma solidity ^0.8.28; import {TychoRouter} from "@src/TychoRouter.sol"; -import "./TestTemplate.sol"; +import "./TychoRouterTestSetup.sol"; -contract TychoRouterTest is TychoRouterTestTemplate { +contract TychoRouterTest is TychoRouterTestSetup { bytes32 public constant EXECUTOR_SETTER_ROLE = 0x6a1dd52dcad5bd732e45b6af4e7344fa284e2d7d4b23b5b09cb55d36b0685c87; bytes32 public constant FEE_SETTER_ROLE = @@ -16,15 +16,11 @@ contract TychoRouterTest is TychoRouterTestTemplate { event ExecutorSet(address indexed executor); event CallbackVerifierSet(address indexed callbackVerifier); - - function setupTychoRouter() public { - deployTychoRouter(); - } + event Withdrawal( + address indexed token, uint256 amount, address indexed receiver + ); function testSetValidExecutor() public { - setupTychoRouter(); - deployDummyContract(); - vm.startPrank(executorSetter); vm.expectEmit(); // Define the event we expect to be emitted at the next step @@ -37,9 +33,6 @@ contract TychoRouterTest is TychoRouterTestTemplate { } function testRemoveExecutor() public { - setupTychoRouter(); - deployDummyContract(); - vm.startPrank(executorSetter); tychoRouter.setSwapExecutor(DUMMY); tychoRouter.removeSwapExecutor(DUMMY); @@ -48,9 +41,6 @@ contract TychoRouterTest is TychoRouterTestTemplate { } function testRemoveUnSetExecutor() public { - setupTychoRouter(); - deployDummyContract(); - vm.startPrank(executorSetter); tychoRouter.removeSwapExecutor(BOB); vm.stopPrank(); @@ -58,24 +48,16 @@ contract TychoRouterTest is TychoRouterTestTemplate { } function testRemoveExecutorMissingSetterRole() public { - setupTychoRouter(); - deployDummyContract(); vm.expectRevert(); tychoRouter.removeSwapExecutor(BOB); } function testSetExecutorMissingSetterRole() public { - setupTychoRouter(); - deployDummyContract(); - vm.expectRevert(); tychoRouter.setSwapExecutor(DUMMY); } function testSetExecutorNonContract() public { - setupTychoRouter(); - deployDummyContract(); - vm.startPrank(executorSetter); vm.expectRevert( abi.encodeWithSelector(TychoRouter__NonContractExecutor.selector) @@ -85,9 +67,6 @@ contract TychoRouterTest is TychoRouterTestTemplate { } function testSetValidVerifier() public { - setupTychoRouter(); - deployDummyContract(); - vm.startPrank(executorSetter); vm.expectEmit(); // Define the event we expect to be emitted at the next step @@ -100,9 +79,6 @@ contract TychoRouterTest is TychoRouterTestTemplate { } function testRemoveVerifier() public { - setupTychoRouter(); - deployDummyContract(); - vm.startPrank(executorSetter); tychoRouter.setCallbackVerifier(DUMMY); tychoRouter.removeCallbackVerifier(DUMMY); @@ -111,9 +87,6 @@ contract TychoRouterTest is TychoRouterTestTemplate { } function testRemoveUnSetVerifier() public { - setupTychoRouter(); - deployDummyContract(); - vm.startPrank(executorSetter); tychoRouter.removeCallbackVerifier(BOB); vm.stopPrank(); @@ -121,24 +94,16 @@ contract TychoRouterTest is TychoRouterTestTemplate { } function testRemoveVerifierMissingSetterRole() public { - setupTychoRouter(); - deployDummyContract(); vm.expectRevert(); tychoRouter.removeCallbackVerifier(BOB); } function testSetVerifierMissingSetterRole() public { - setupTychoRouter(); - deployDummyContract(); - vm.expectRevert(); tychoRouter.setCallbackVerifier(DUMMY); } function testSetVerifierNonContract() public { - setupTychoRouter(); - deployDummyContract(); - vm.startPrank(executorSetter); vm.expectRevert( abi.encodeWithSelector(TychoRouter__NonContractVerifier.selector) @@ -146,4 +111,91 @@ contract TychoRouterTest is TychoRouterTestTemplate { tychoRouter.setCallbackVerifier(BOB); vm.stopPrank(); } + + function testWithdrawNative() public { + vm.startPrank(FUND_RESCUER); + // Send 100 ether to tychoRouter + assertEq(address(tychoRouter).balance, 0); + assertEq(FUND_RESCUER.balance, 0); + vm.deal(address(tychoRouter), 100 ether); + vm.expectEmit(); + emit Withdrawal(address(0), 100 ether, FUND_RESCUER); + tychoRouter.withdrawNative(FUND_RESCUER); + assertEq(address(tychoRouter).balance, 0); + assertEq(FUND_RESCUER.balance, 100 ether); + vm.stopPrank(); + } + + function testWithdrawNativeOtherCases() public { + vm.deal(address(tychoRouter), 100 ether); + vm.startPrank(FUND_RESCUER); + vm.expectRevert(TychoRouter__AddressZero.selector); + tychoRouter.withdrawNative(address(0)); + vm.stopPrank(); + + // Not role FUND_RESCUER + vm.startPrank(BOB); + vm.expectRevert(); + tychoRouter.withdrawNative(FUND_RESCUER); + vm.stopPrank(); + } + + function testWithdrawERC20Tokens() public { + vm.startPrank(BOB); + // Check balances before minting + for (uint256 i = 0; i < tokens.length; i++) { + // slither-disable-next-line calls-loop + assertEq(tokens[i].balanceOf(address(tychoRouter)), 0); + } + // Mint tokens to tychoRouter + for (uint256 i = 0; i < tokens.length; i++) { + // slither-disable-next-line calls-loop + tokens[i].mint(address(tychoRouter), 100 ether); + } + // Check balances after minting + for (uint256 i = 0; i < tokens.length; i++) { + // slither-disable-next-line calls-loop + assertEq(tokens[i].balanceOf(address(tychoRouter)), 100 ether); + } + vm.stopPrank(); + + vm.startPrank(FUND_RESCUER); + IERC20[] memory tokensArray = new IERC20[](3); + tokensArray[0] = IERC20(address(tokens[0])); + tokensArray[1] = IERC20(address(tokens[1])); + tokensArray[2] = IERC20(address(tokens[2])); + tychoRouter.withdraw(tokensArray, FUND_RESCUER); + + // Check balances after withdrawing + for (uint256 i = 0; i < tokens.length; i++) { + // slither-disable-next-line calls-loop + assertEq(tokens[i].balanceOf(address(tychoRouter)), 0); + // slither-disable-next-line calls-loop + assertEq(tokens[i].balanceOf(FUND_RESCUER), 100 ether); + } + vm.stopPrank(); + + vm.startPrank(FUND_RESCUER); + vm.expectRevert(); + tychoRouter.withdraw(tokensArray, address(0)); + vm.stopPrank(); + + vm.startPrank(BOB); + vm.expectRevert(); + tychoRouter.withdraw(tokensArray, FUND_RESCUER); + vm.stopPrank(); + } + + function testFeeSetterRole() public { + vm.startPrank(FEE_SETTER); + assertEq(tychoRouter.fee(), 0); + tychoRouter.setFee(100); + assertEq(tychoRouter.fee(), 100); + vm.stopPrank(); + + vm.startPrank(BOB); + vm.expectRevert(); + tychoRouter.setFee(200); + vm.stopPrank(); + } } diff --git a/foundry/test/TestTemplate.sol b/foundry/test/TychoRouterTestSetup.sol similarity index 52% rename from foundry/test/TestTemplate.sol rename to foundry/test/TychoRouterTestSetup.sol index 7927ec4..870ebba 100644 --- a/foundry/test/TestTemplate.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -1,25 +1,30 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.13; -import "forge-std/Test.sol"; import "@src/TychoRouter.sol"; import "./Constants.sol"; +import "./mock/MockERC20.sol"; -contract TychoRouterTestTemplate is Test, Constants { +contract TychoRouterTestSetup is Test, Constants { TychoRouter tychoRouter; - address tychoRouterAddress; address executorSetter; + address permit2Address = address(0x000000000022D473030F116dDEE9F6B43aC78BA3); + MockERC20[] tokens; - function deployTychoRouter() internal { + function setUp() public { vm.startPrank(ADMIN); - - address permit2Address = - address(0x000000000022D473030F116dDEE9F6B43aC78BA3); tychoRouter = new TychoRouter(permit2Address); - tychoRouterAddress = address(tychoRouter); tychoRouter.grantRole(keccak256("EXECUTOR_SETTER_ROLE"), BOB); + tychoRouter.grantRole(keccak256("FUND_RESCUER_ROLE"), FUND_RESCUER); + tychoRouter.grantRole(keccak256("FEE_SETTER_ROLE"), FEE_SETTER); executorSetter = BOB; + deployDummyContract(); + vm.stopPrank(); + vm.startPrank(BOB); + tokens.push(new MockERC20("Token A", "A")); + tokens.push(new MockERC20("Token B", "B")); + tokens.push(new MockERC20("Token C", "C")); vm.stopPrank(); } diff --git a/foundry/test/mock/MockERC20.sol b/foundry/test/mock/MockERC20.sol new file mode 100644 index 0000000..013c439 --- /dev/null +++ b/foundry/test/mock/MockERC20.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.28; + +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract MockERC20 is ERC20 { + constructor(string memory name_, string memory symbol_) + ERC20(name_, symbol_) + {} + + function mint(address to, uint256 amount) external { + _mint(to, amount); + } + + function decimals() public view virtual override returns (uint8) { + return 18; + } +} From 9c99b738841cb0dacf37bf95aec8cedebc69c5d3 Mon Sep 17 00:00:00 2001 From: royvardhan Date: Thu, 23 Jan 2025 21:54:31 +0530 Subject: [PATCH 2/3] fix: pr comments --- foundry/script/Deploy.sol | 8 ----- foundry/test/Constants.sol | 1 + foundry/test/TychoRouter.t.sol | 44 ++++++++++++++++----------- foundry/test/TychoRouterTestSetup.sol | 12 ++++++++ 4 files changed, 39 insertions(+), 26 deletions(-) delete mode 100644 foundry/script/Deploy.sol diff --git a/foundry/script/Deploy.sol b/foundry/script/Deploy.sol deleted file mode 100644 index 9c4a381..0000000 --- a/foundry/script/Deploy.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; - -contract DeployScript is Script { -// TODO: implement deploy script -} diff --git a/foundry/test/Constants.sol b/foundry/test/Constants.sol index ec30775..521dff7 100644 --- a/foundry/test/Constants.sol +++ b/foundry/test/Constants.sol @@ -10,4 +10,5 @@ contract Constants is Test { address FEE_SETTER = makeAddr("feeSetter"); // dummy contracts address DUMMY = makeAddr("dummy"); + address FEE_RECEIVER = makeAddr("feeReceiver"); } diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index cab9a79..fb9ad4b 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -126,7 +126,7 @@ contract TychoRouterTest is TychoRouterTestSetup { vm.stopPrank(); } - function testWithdrawNativeOtherCases() public { + function testWithdrawNativeFailures() public { vm.deal(address(tychoRouter), 100 ether); vm.startPrank(FUND_RESCUER); vm.expectRevert(TychoRouter__AddressZero.selector); @@ -142,21 +142,7 @@ contract TychoRouterTest is TychoRouterTestSetup { function testWithdrawERC20Tokens() public { vm.startPrank(BOB); - // Check balances before minting - for (uint256 i = 0; i < tokens.length; i++) { - // slither-disable-next-line calls-loop - assertEq(tokens[i].balanceOf(address(tychoRouter)), 0); - } - // Mint tokens to tychoRouter - for (uint256 i = 0; i < tokens.length; i++) { - // slither-disable-next-line calls-loop - tokens[i].mint(address(tychoRouter), 100 ether); - } - // Check balances after minting - for (uint256 i = 0; i < tokens.length; i++) { - // slither-disable-next-line calls-loop - assertEq(tokens[i].balanceOf(address(tychoRouter)), 100 ether); - } + mintTokens(100 ether, address(tychoRouter)); vm.stopPrank(); vm.startPrank(FUND_RESCUER); @@ -174,19 +160,28 @@ contract TychoRouterTest is TychoRouterTestSetup { assertEq(tokens[i].balanceOf(FUND_RESCUER), 100 ether); } vm.stopPrank(); + } + + function testWithdrawERC20TokensFailures() public { + mintTokens(100 ether, address(tychoRouter)); + IERC20[] memory tokensArray = new IERC20[](3); + tokensArray[0] = IERC20(address(tokens[0])); + tokensArray[1] = IERC20(address(tokens[1])); + tokensArray[2] = IERC20(address(tokens[2])); vm.startPrank(FUND_RESCUER); - vm.expectRevert(); + vm.expectRevert(TychoRouter__AddressZero.selector); tychoRouter.withdraw(tokensArray, address(0)); vm.stopPrank(); + // Not role FUND_RESCUER vm.startPrank(BOB); vm.expectRevert(); tychoRouter.withdraw(tokensArray, FUND_RESCUER); vm.stopPrank(); } - function testFeeSetterRole() public { + function testFeeSetting() public { vm.startPrank(FEE_SETTER); assertEq(tychoRouter.fee(), 0); tychoRouter.setFee(100); @@ -198,4 +193,17 @@ contract TychoRouterTest is TychoRouterTestSetup { tychoRouter.setFee(200); vm.stopPrank(); } + + function testFeeReceiverSetting() public { + vm.startPrank(FEE_SETTER); + assertEq(tychoRouter.feeReceiver(), address(0)); + tychoRouter.setFeeReceiver(FEE_RECEIVER); + assertEq(tychoRouter.feeReceiver(), FEE_RECEIVER); + vm.stopPrank(); + + vm.startPrank(BOB); + vm.expectRevert(); + tychoRouter.setFeeReceiver(FEE_RECEIVER); + vm.stopPrank(); + } } diff --git a/foundry/test/TychoRouterTestSetup.sol b/foundry/test/TychoRouterTestSetup.sol index 870ebba..9765d76 100644 --- a/foundry/test/TychoRouterTestSetup.sol +++ b/foundry/test/TychoRouterTestSetup.sol @@ -35,4 +35,16 @@ contract TychoRouterTestSetup is Test, Constants { bytes memory minimalBytecode = hex"01"; // Single-byte bytecode vm.etch(DUMMY, minimalBytecode); // Deploy minimal bytecode } + + /** + * @dev Mints tokens to the given address + * @param amount The amount of tokens to mint + * @param to The address to mint tokens to + */ + function mintTokens(uint256 amount, address to) internal { + for (uint256 i = 0; i < tokens.length; i++) { + // slither-disable-next-line calls-loop + tokens[i].mint(to, amount); + } + } } From 7e49741e93da2c89cd49207487e51923afe1b425 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 23 Jan 2025 17:08:52 +0000 Subject: [PATCH 3/3] chore(release): 0.12.0 [skip ci] ## [0.12.0](https://github.com/propeller-heads/tycho-execution/compare/0.11.0...0.12.0) (2025-01-23) ### Features * add tests for withdraw, fee and make it DRY ([056582c](https://github.com/propeller-heads/tycho-execution/commit/056582ca2f5d792d60027574c313be2ca8ac649c)) ### Bug Fixes * pr comments ([9c99b73](https://github.com/propeller-heads/tycho-execution/commit/9c99b738841cb0dacf37bf95aec8cedebc69c5d3)) --- CHANGELOG.md | 12 ++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b88e37..1743670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## [0.12.0](https://github.com/propeller-heads/tycho-execution/compare/0.11.0...0.12.0) (2025-01-23) + + +### Features + +* add tests for withdraw, fee and make it DRY ([056582c](https://github.com/propeller-heads/tycho-execution/commit/056582ca2f5d792d60027574c313be2ca8ac649c)) + + +### Bug Fixes + +* pr comments ([9c99b73](https://github.com/propeller-heads/tycho-execution/commit/9c99b738841cb0dacf37bf95aec8cedebc69c5d3)) + ## [0.11.0](https://github.com/propeller-heads/tycho-execution/compare/0.10.0...0.11.0) (2025-01-23) diff --git a/Cargo.lock b/Cargo.lock index aadd9f5..bd1fc62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3126,7 +3126,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.11.0" +version = "0.12.0" dependencies = [ "alloy", "alloy-primitives", diff --git a/Cargo.toml b/Cargo.toml index 4ecc35d..3dcfddd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.11.0" +version = "0.12.0" edition = "2021" [dependencies]