From ce1fe1dd94a4cc68e4695902c80ee30d30d7fd5e Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Mon, 7 Jul 2025 18:00:53 -0400 Subject: [PATCH 1/4] feat: UniswapXFiller skeleton --- foundry/src/uniswap_x/IReactor.sol | 31 +++++ foundry/src/uniswap_x/IReactorCallback.sol | 16 +++ foundry/src/uniswap_x/IStructs.sol | 115 ++++++++++++++++++ foundry/src/uniswap_x/UniswapXFiller.sol | 109 +++++++++++++++++ foundry/test/TychoRouter.t.sol | 1 - foundry/test/uniswap_x/UniswapXFiller.t.sol | 126 ++++++++++++++++++++ 6 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 foundry/src/uniswap_x/IReactor.sol create mode 100644 foundry/src/uniswap_x/IReactorCallback.sol create mode 100644 foundry/src/uniswap_x/IStructs.sol create mode 100644 foundry/src/uniswap_x/UniswapXFiller.sol create mode 100644 foundry/test/uniswap_x/UniswapXFiller.t.sol diff --git a/foundry/src/uniswap_x/IReactor.sol b/foundry/src/uniswap_x/IReactor.sol new file mode 100644 index 0000000..5ca5414 --- /dev/null +++ b/foundry/src/uniswap_x/IReactor.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.26; + +import {SignedOrder} from "./IStructs.sol"; +import {IReactorCallback} from "./IReactorCallback.sol"; + +/// @notice Interface for order execution reactors +interface IReactor { + /// @notice Execute a single order + /// @param order The order definition and valid signature to execute + function execute(SignedOrder calldata order) external payable; + + /// @notice Execute a single order using the given callback data + /// @param order The order definition and valid signature to execute + function executeWithCallback( + SignedOrder calldata order, + bytes calldata callbackData + ) external payable; + + /// @notice Execute the given orders at once + /// @param orders The order definitions and valid signatures to execute + function executeBatch(SignedOrder[] calldata orders) external payable; + + /// @notice Execute the given orders at once using a callback with the given callback data + /// @param orders The order definitions and valid signatures to execute + /// @param callbackData The callbackData to pass to the callback + function executeBatchWithCallback( + SignedOrder[] calldata orders, + bytes calldata callbackData + ) external payable; +} diff --git a/foundry/src/uniswap_x/IReactorCallback.sol b/foundry/src/uniswap_x/IReactorCallback.sol new file mode 100644 index 0000000..12af565 --- /dev/null +++ b/foundry/src/uniswap_x/IReactorCallback.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.26; + +import "./IStructs.sol"; + +/// @notice Callback for executing orders through a reactor. +interface IReactorCallback { + /// @notice Called by the reactor during the execution of an order + /// @param resolvedOrders Has inputs and outputs + /// @param fillData The fillData specified for an order execution + /// @dev Must have approved each token and amount in outputs to the msg.sender + function reactorCallback( + ResolvedOrder[] memory resolvedOrders, + bytes memory fillData + ) external; +} diff --git a/foundry/src/uniswap_x/IStructs.sol b/foundry/src/uniswap_x/IStructs.sol new file mode 100644 index 0000000..3781266 --- /dev/null +++ b/foundry/src/uniswap_x/IStructs.sol @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.26; + +/// @dev external struct including a generic encoded order and swapper signature +/// The order bytes will be parsed and mapped to a ResolvedOrder in the concrete reactor contract + +struct SignedOrder { + bytes order; + bytes sig; +} + +struct OrderInfo { + // The address of the reactor that this order is targeting + // Note that this must be included in every order so the swapper + // signature commits to the specific reactor that they trust to fill their order properly + address reactor; + // The address of the user which created the order + // Note that this must be included so that order hashes are unique by swapper + address swapper; + // The nonce of the order, allowing for signature replay protection and cancellation + uint256 nonce; + // The timestamp after which this order is no longer valid + uint256 deadline; + // Custom validation contract + address additionalValidationContract; + // Encoded validation params for additionalValidationContract + bytes additionalValidationData; +} + +/// @dev tokens that need to be sent from the swapper in order to satisfy an order +struct InputToken { + address token; + uint256 amount; + // Needed for dutch decaying inputs + uint256 maxAmount; +} + +/// @dev tokens that need to be received by the recipient in order to satisfy an order +struct OutputToken { + address token; + uint256 amount; + address recipient; +} +/// @dev generic concrete order that specifies exact tokens which need to be sent and received + +struct ResolvedOrder { + OrderInfo info; + InputToken input; + OutputToken[] outputs; + bytes sig; + bytes32 hash; +} + +struct DutchOutput { + address token; + uint256 startAmount; + uint256 endAmount; + address recipient; +} + +struct DutchInput { + address token; + uint256 startAmount; + uint256 endAmount; +} + +struct ExclusiveDutchOrder { + OrderInfo info; + uint256 decayStartTime; + uint256 decayEndTime; + address exclusiveFiller; + uint256 exclusivityOverrideBps; + DutchInput input; + DutchOutput[] outputs; +} + +struct DutchOrder { + OrderInfo info; + uint256 decayStartTime; + uint256 decayEndTime; + address exclusiveFiller; + uint256 exclusivityOverrideBps; + DutchInput input; + DutchOutput[] outputs; +} + +struct CosignerData { + // The time at which the DutchOutputs start decaying + uint256 decayStartTime; + // The time at which price becomes static + uint256 decayEndTime; + // The address who has exclusive rights to the order until decayStartTime + address exclusiveFiller; + // The amount in bps that a non-exclusive filler needs to improve the outputs by to be able to fill the order + uint256 exclusivityOverrideBps; + // The tokens that the swapper will provide when settling the order + uint256 inputAmount; + // The tokens that must be received to satisfy the order + uint256[] outputAmounts; +} + +struct V2DutchOrder { + // generic order information + OrderInfo info; + // The address which must cosign the full order + address cosigner; + // The tokens that the swapper will provide when settling the order + DutchInput baseInput; + // The tokens that must be received to satisfy the order + DutchOutput[] baseOutputs; + // signed over by the cosigner + CosignerData cosignerData; + // signature from the cosigner over (orderHash || cosignerData) + bytes cosignature; +} diff --git a/foundry/src/uniswap_x/UniswapXFiller.sol b/foundry/src/uniswap_x/UniswapXFiller.sol new file mode 100644 index 0000000..bbb8af3 --- /dev/null +++ b/foundry/src/uniswap_x/UniswapXFiller.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.26; + +import "./IReactor.sol"; +import "./IReactorCallback.sol"; +import { + SafeERC20, + IERC20 +} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/access/AccessControl.sol"; +import "@openzeppelin/contracts/utils/Address.sol"; + +error UniswapXFiller__AddressZero(); + +contract UniswapXFiller is AccessControl, IReactorCallback { + using SafeERC20 for IERC20; + + // UniswapX V2DutchOrder Reactor + IReactor public constant USXEDAReactor = + IReactor(0x00000011F84B9aa48e5f8aA8B9897600006289Be); + address public immutable tychoRouter; + + // keccak256("NAME_OF_ROLE") : save gas on deployment + bytes32 public constant REACTOR_ROLE = + 0x39dd1d7269516fc1f719706a5e9b05cdcb1644978808b171257d9a8eab55dd57; + bytes32 public constant EXECUTOR_ROLE = + 0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63; + + event Withdrawal( + address indexed token, uint256 amount, address indexed receiver + ); + + constructor(address _tychoRouter) { + if (_tychoRouter == address(0)) revert UniswapXFiller__AddressZero(); + + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + _grantRole(REACTOR_ROLE, address(USXEDAReactor)); + tychoRouter = _tychoRouter; + } + + function execute(SignedOrder calldata order, bytes calldata callbackData) + external + onlyRole(EXECUTOR_ROLE) + { + USXEDAReactor.executeWithCallback(order, callbackData); + } + + function reactorCallback( + ResolvedOrder[] calldata resolvedOrders, + bytes calldata callbackData + ) external onlyRole(REACTOR_ROLE) { + // TODO + } + + /** + * @dev Allows granting roles to multiple accounts in a single call. + */ + function batchGrantRole(bytes32 role, address[] memory accounts) + external + onlyRole(DEFAULT_ADMIN_ROLE) + { + for (uint256 i = 0; i < accounts.length; i++) { + _grantRole(role, accounts[i]); + } + } + + /** + * @dev Allows withdrawing any ERC20 funds if funds get stuck in case of a bug. + */ + function withdraw(IERC20[] memory tokens, address receiver) + external + onlyRole(DEFAULT_ADMIN_ROLE) + { + if (receiver == address(0)) revert UniswapXFiller__AddressZero(); + + for (uint256 i = 0; i < tokens.length; i++) { + // slither-disable-next-line calls-loop + uint256 tokenBalance = tokens[i].balanceOf(address(this)); + if (tokenBalance > 0) { + emit Withdrawal(address(tokens[i]), tokenBalance, receiver); + tokens[i].safeTransfer(receiver, tokenBalance); + } + } + } + + /** + * @dev Allows withdrawing any NATIVE funds if funds get stuck in case of a bug. + * The contract should never hold any NATIVE tokens for security reasons. + */ + function withdrawNative(address receiver) + external + onlyRole(DEFAULT_ADMIN_ROLE) + { + if (receiver == address(0)) revert UniswapXFiller__AddressZero(); + + uint256 amount = address(this).balance; + if (amount > 0) { + emit Withdrawal(address(0), amount, receiver); + Address.sendValue(payable(receiver), amount); + } + } + + /** + * @dev Allows this contract to receive native token with empty msg.data from contracts + */ + receive() external payable { + require(msg.sender.code.length != 0); + } +} diff --git a/foundry/test/TychoRouter.t.sol b/foundry/test/TychoRouter.t.sol index fb53058..651461d 100644 --- a/foundry/test/TychoRouter.t.sol +++ b/foundry/test/TychoRouter.t.sol @@ -96,7 +96,6 @@ contract TychoRouterTest is TychoRouterTestSetup { } vm.startPrank(FUND_RESCUER); - tychoRouter.withdraw(tokens, FUND_RESCUER); // Check balances after withdrawing diff --git a/foundry/test/uniswap_x/UniswapXFiller.t.sol b/foundry/test/uniswap_x/UniswapXFiller.t.sol new file mode 100644 index 0000000..c267938 --- /dev/null +++ b/foundry/test/uniswap_x/UniswapXFiller.t.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.26; + +import "@src/executors/UniswapV4Executor.sol"; +import "forge-std/Test.sol"; +import "@src/uniswap_x/UniswapXFiller.sol"; +import "../TychoRouterTestSetup.sol"; + +contract UniswapXFillerTest is Test, TychoRouterTestSetup { + address EXECUTOR = makeAddr("executor"); + address REACTOR = address(0x00000011F84B9aa48e5f8aA8B9897600006289Be); + + UniswapXFiller filler; + address fillerAddr; + + bytes32 public constant EXECUTOR_ROLE = + 0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63; + + event CallbackVerifierSet(address indexed callbackVerifier); + event Withdrawal( + address indexed token, uint256 amount, address indexed receiver + ); + + function fillerSetup() public { + vm.startPrank(ADMIN); + filler = new UniswapXFiller(tychoRouterAddr); + fillerAddr = address(filler); + filler.grantRole(keccak256("EXECUTOR_ROLE"), EXECUTOR); + vm.stopPrank(); + } + + function testTychoAddressZero() public { + vm.expectRevert(UniswapXFiller__AddressZero.selector); + filler = new UniswapXFiller(address(0)); + } + + function testWithdrawNative() public { + fillerSetup(); + vm.startPrank(ADMIN); + // Send 100 ether to filler + assertEq(fillerAddr.balance, 0); + assertEq(ADMIN.balance, 0); + vm.deal(fillerAddr, 100 ether); + vm.expectEmit(); + emit Withdrawal(address(0), 100 ether, ADMIN); + filler.withdrawNative(ADMIN); + assertEq(fillerAddr.balance, 0); + assertEq(ADMIN.balance, 100 ether); + vm.stopPrank(); + } + + function testWithdrawNativeAddressZero() public { + fillerSetup(); + vm.deal(fillerAddr, 100 ether); + vm.startPrank(ADMIN); + vm.expectRevert(UniswapXFiller__AddressZero.selector); + filler.withdrawNative(address(0)); + vm.stopPrank(); + } + + function testWithdrawNativeMissingRole() public { + fillerSetup(); + vm.deal(fillerAddr, 100 ether); + // Not role ADMIN + vm.startPrank(BOB); + vm.expectRevert(); + filler.withdrawNative(ADMIN); + vm.stopPrank(); + } + + function testWithdrawERC20Tokens() public { + fillerSetup(); + + IERC20[] memory tokens = new IERC20[](2); + tokens[0] = IERC20(WETH_ADDR); + tokens[1] = IERC20(USDC_ADDR); + for (uint256 i = 0; i < tokens.length; i++) { + deal(address(tokens[i]), fillerAddr, 100 ether); + } + + vm.startPrank(ADMIN); + filler.withdraw(tokens, ADMIN); + + // Check balances after withdrawing + for (uint256 i = 0; i < tokens.length; i++) { + // slither-disable-next-line calls-loop + assertEq(tokens[i].balanceOf(fillerAddr), 0); + // slither-disable-next-line calls-loop + assertEq(tokens[i].balanceOf(ADMIN), 100 ether); + } + vm.stopPrank(); + } + + function testWithdrawERC20TokensAddressZero() public { + fillerSetup(); + + IERC20[] memory tokens = new IERC20[](2); + tokens[0] = IERC20(WETH_ADDR); + tokens[1] = IERC20(USDC_ADDR); + for (uint256 i = 0; i < tokens.length; i++) { + deal(address(tokens[i]), fillerAddr, 100 ether); + } + + vm.startPrank(ADMIN); + vm.expectRevert(UniswapXFiller__AddressZero.selector); + filler.withdraw(tokens, address(0)); + vm.stopPrank(); + } + + function testWithdrawERC20TokensAddressMissingRole() public { + fillerSetup(); + + IERC20[] memory tokens = new IERC20[](2); + tokens[0] = IERC20(WETH_ADDR); + tokens[1] = IERC20(USDC_ADDR); + for (uint256 i = 0; i < tokens.length; i++) { + deal(address(tokens[i]), fillerAddr, 100 ether); + } + + // Not role ADMIN + vm.startPrank(BOB); + vm.expectRevert(); + filler.withdraw(tokens, ADMIN); + vm.stopPrank(); + } +} From 00f22d62c1f978f27eb5a922bc7b36af2a0b806b Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Tue, 8 Jul 2025 09:48:25 -0400 Subject: [PATCH 2/4] feat: Take reactor address as input to UniswapXFiller - Put reactor zero address check in separate line for more easy debugging. - Also includes other small cosmetic changes. --- foundry/src/uniswap_x/IStructs.sol | 3 +-- foundry/src/uniswap_x/UniswapXFiller.sol | 12 ++++++------ foundry/test/uniswap_x/UniswapXFiller.t.sol | 15 ++++++++------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/foundry/src/uniswap_x/IStructs.sol b/foundry/src/uniswap_x/IStructs.sol index 3781266..9672fd2 100644 --- a/foundry/src/uniswap_x/IStructs.sol +++ b/foundry/src/uniswap_x/IStructs.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.26; /// @dev external struct including a generic encoded order and swapper signature /// The order bytes will be parsed and mapped to a ResolvedOrder in the concrete reactor contract - struct SignedOrder { bytes order; bytes sig; @@ -41,8 +40,8 @@ struct OutputToken { uint256 amount; address recipient; } -/// @dev generic concrete order that specifies exact tokens which need to be sent and received +/// @dev generic concrete order that specifies exact tokens which need to be sent and received struct ResolvedOrder { OrderInfo info; InputToken input; diff --git a/foundry/src/uniswap_x/UniswapXFiller.sol b/foundry/src/uniswap_x/UniswapXFiller.sol index bbb8af3..7196a0a 100644 --- a/foundry/src/uniswap_x/UniswapXFiller.sol +++ b/foundry/src/uniswap_x/UniswapXFiller.sol @@ -16,8 +16,7 @@ contract UniswapXFiller is AccessControl, IReactorCallback { using SafeERC20 for IERC20; // UniswapX V2DutchOrder Reactor - IReactor public constant USXEDAReactor = - IReactor(0x00000011F84B9aa48e5f8aA8B9897600006289Be); + IReactor public immutable USXEDAReactor; address public immutable tychoRouter; // keccak256("NAME_OF_ROLE") : save gas on deployment @@ -30,12 +29,14 @@ contract UniswapXFiller is AccessControl, IReactorCallback { address indexed token, uint256 amount, address indexed receiver ); - constructor(address _tychoRouter) { + constructor(address _tychoRouter, address _reactor) { if (_tychoRouter == address(0)) revert UniswapXFiller__AddressZero(); + if (_reactor == address(0)) revert UniswapXFiller__AddressZero(); _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _grantRole(REACTOR_ROLE, address(USXEDAReactor)); tychoRouter = _tychoRouter; + USXEDAReactor = IReactor(_reactor); } function execute(SignedOrder calldata order, bytes calldata callbackData) @@ -65,7 +66,7 @@ contract UniswapXFiller is AccessControl, IReactorCallback { } /** - * @dev Allows withdrawing any ERC20 funds if funds get stuck in case of a bug. + * @dev Allows withdrawing any ERC20 funds. */ function withdraw(IERC20[] memory tokens, address receiver) external @@ -84,8 +85,7 @@ contract UniswapXFiller is AccessControl, IReactorCallback { } /** - * @dev Allows withdrawing any NATIVE funds if funds get stuck in case of a bug. - * The contract should never hold any NATIVE tokens for security reasons. + * @dev Allows withdrawing any NATIVE funds. */ function withdrawNative(address receiver) external diff --git a/foundry/test/uniswap_x/UniswapXFiller.t.sol b/foundry/test/uniswap_x/UniswapXFiller.t.sol index c267938..1dc11df 100644 --- a/foundry/test/uniswap_x/UniswapXFiller.t.sol +++ b/foundry/test/uniswap_x/UniswapXFiller.t.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.26; -import "@src/executors/UniswapV4Executor.sol"; import "forge-std/Test.sol"; import "@src/uniswap_x/UniswapXFiller.sol"; import "../TychoRouterTestSetup.sol"; @@ -13,9 +12,6 @@ contract UniswapXFillerTest is Test, TychoRouterTestSetup { UniswapXFiller filler; address fillerAddr; - bytes32 public constant EXECUTOR_ROLE = - 0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63; - event CallbackVerifierSet(address indexed callbackVerifier); event Withdrawal( address indexed token, uint256 amount, address indexed receiver @@ -23,15 +19,20 @@ contract UniswapXFillerTest is Test, TychoRouterTestSetup { function fillerSetup() public { vm.startPrank(ADMIN); - filler = new UniswapXFiller(tychoRouterAddr); + filler = new UniswapXFiller(tychoRouterAddr, REACTOR); fillerAddr = address(filler); filler.grantRole(keccak256("EXECUTOR_ROLE"), EXECUTOR); vm.stopPrank(); } - function testTychoAddressZero() public { + function testTychoAddressZeroTychoRouter() public { vm.expectRevert(UniswapXFiller__AddressZero.selector); - filler = new UniswapXFiller(address(0)); + filler = new UniswapXFiller(address(0), REACTOR); + } + + function testTychoAddressZeroReactor() public { + vm.expectRevert(UniswapXFiller__AddressZero.selector); + filler = new UniswapXFiller(tychoRouterAddr, address(0)); } function testWithdrawNative() public { From d61469ea67f2c8d0f28e5185da4099260c2b69ea Mon Sep 17 00:00:00 2001 From: TAMARA LIPOWSKI Date: Tue, 8 Jul 2025 10:24:40 -0400 Subject: [PATCH 3/4] fix: Make slither happy Naming conventions (mixed case) for variables --- foundry/src/uniswap_x/UniswapXFiller.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/foundry/src/uniswap_x/UniswapXFiller.sol b/foundry/src/uniswap_x/UniswapXFiller.sol index 7196a0a..de52edd 100644 --- a/foundry/src/uniswap_x/UniswapXFiller.sol +++ b/foundry/src/uniswap_x/UniswapXFiller.sol @@ -16,7 +16,7 @@ contract UniswapXFiller is AccessControl, IReactorCallback { using SafeERC20 for IERC20; // UniswapX V2DutchOrder Reactor - IReactor public immutable USXEDAReactor; + IReactor public immutable reactor; address public immutable tychoRouter; // keccak256("NAME_OF_ROLE") : save gas on deployment @@ -34,16 +34,16 @@ contract UniswapXFiller is AccessControl, IReactorCallback { if (_reactor == address(0)) revert UniswapXFiller__AddressZero(); _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); - _grantRole(REACTOR_ROLE, address(USXEDAReactor)); + _grantRole(REACTOR_ROLE, address(reactor)); tychoRouter = _tychoRouter; - USXEDAReactor = IReactor(_reactor); + reactor = IReactor(_reactor); } function execute(SignedOrder calldata order, bytes calldata callbackData) external onlyRole(EXECUTOR_ROLE) { - USXEDAReactor.executeWithCallback(order, callbackData); + reactor.executeWithCallback(order, callbackData); } function reactorCallback( From 7a5cf1b89eb70df3b7c1f124c6db2c3b5c024e1c Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 8 Jul 2025 14:31:16 +0000 Subject: [PATCH 4/4] chore(release): 0.104.0 [skip ci] ## [0.104.0](https://github.com/propeller-heads/tycho-execution/compare/0.103.0...0.104.0) (2025-07-08) ### Features * Take reactor address as input to UniswapXFiller ([00f22d6](https://github.com/propeller-heads/tycho-execution/commit/00f22d62c1f978f27eb5a922bc7b36af2a0b806b)) * UniswapXFiller skeleton ([ce1fe1d](https://github.com/propeller-heads/tycho-execution/commit/ce1fe1dd94a4cc68e4695902c80ee30d30d7fd5e)) ### Bug Fixes * Make slither happy ([d61469e](https://github.com/propeller-heads/tycho-execution/commit/d61469ea67f2c8d0f28e5185da4099260c2b69ea)) --- CHANGELOG.md | 13 +++++++++++++ Cargo.lock | 2 +- Cargo.toml | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34d27c2..ab74142 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +## [0.104.0](https://github.com/propeller-heads/tycho-execution/compare/0.103.0...0.104.0) (2025-07-08) + + +### Features + +* Take reactor address as input to UniswapXFiller ([00f22d6](https://github.com/propeller-heads/tycho-execution/commit/00f22d62c1f978f27eb5a922bc7b36af2a0b806b)) +* UniswapXFiller skeleton ([ce1fe1d](https://github.com/propeller-heads/tycho-execution/commit/ce1fe1dd94a4cc68e4695902c80ee30d30d7fd5e)) + + +### Bug Fixes + +* Make slither happy ([d61469e](https://github.com/propeller-heads/tycho-execution/commit/d61469ea67f2c8d0f28e5185da4099260c2b69ea)) + ## [0.103.0](https://github.com/propeller-heads/tycho-execution/compare/0.102.0...0.103.0) (2025-07-07) diff --git a/Cargo.lock b/Cargo.lock index df435e3..05e0314 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4658,7 +4658,7 @@ dependencies = [ [[package]] name = "tycho-execution" -version = "0.103.0" +version = "0.104.0" dependencies = [ "alloy", "chrono", diff --git a/Cargo.toml b/Cargo.toml index df96172..136586e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tycho-execution" -version = "0.103.0" +version = "0.104.0" edition = "2021" description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors." repository = "https://github.com/propeller-heads/tycho-execution"