Merge branch 'main' into router/dc/ENG-4454-remove-executor-script
This commit is contained in:
37
.github/workflows/tests-and-lints-template.yaml
vendored
37
.github/workflows/tests-and-lints-template.yaml
vendored
@@ -59,9 +59,12 @@ jobs:
|
|||||||
git config --global url."https://x-access-token:${{ steps.generate-token.outputs.token }}@github.com".insteadOf ssh://github.com
|
git config --global url."https://x-access-token:${{ steps.generate-token.outputs.token }}@github.com".insteadOf ssh://github.com
|
||||||
|
|
||||||
- name: Setup toolchain
|
- name: Setup toolchain
|
||||||
uses: dtolnay/rust-toolchain@888c2e1ea69ab0d4330cbf0af1ecc7b68f368cc1
|
id: toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.toolchain }}
|
toolchain: nightly
|
||||||
|
components: rustfmt, clippy
|
||||||
|
override: true
|
||||||
|
|
||||||
- name: Setup Rust Cache
|
- name: Setup Rust Cache
|
||||||
uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6
|
uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6
|
||||||
@@ -100,28 +103,20 @@ jobs:
|
|||||||
echo "https://${{ steps.generate-token.outputs.token }}@github.com" > ~/.git-credentials
|
echo "https://${{ steps.generate-token.outputs.token }}@github.com" > ~/.git-credentials
|
||||||
git config --global url."https://x-access-token:${{ steps.generate-token.outputs.token }}@github.com".insteadOf ssh://github.com
|
git config --global url."https://x-access-token:${{ steps.generate-token.outputs.token }}@github.com".insteadOf ssh://github.com
|
||||||
|
|
||||||
- name: Setup clippy toolchain - stable
|
- name: Setup toolchain
|
||||||
uses: dtolnay/rust-toolchain@888c2e1ea69ab0d4330cbf0af1ecc7b68f368cc1
|
id: toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
toolchain: nightly
|
||||||
components: clippy
|
components: rustfmt, clippy
|
||||||
|
override: true
|
||||||
- name: Setup Rust Cache
|
- name: Setup Rust Cache
|
||||||
uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6
|
uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
cache-on-failure: true
|
cache-on-failure: true
|
||||||
|
|
||||||
- run: cargo clippy --workspace --lib --all-targets --all-features -- -D clippy::dbg-macro
|
- name: Clippy
|
||||||
env:
|
run: cargo clippy --workspace --all-targets --all-features
|
||||||
RUSTFLAGS: -Dwarnings
|
|
||||||
|
|
||||||
- run: cargo check --no-default-features
|
- name: Rustfmt
|
||||||
env:
|
run: cargo fmt --all --check
|
||||||
RUSTFLAGS: -Dwarnings
|
|
||||||
|
|
||||||
- name: Setup rustfmt toolchain - nightly
|
|
||||||
uses: dtolnay/rust-toolchain@a02741459ec5e501b9843ed30b535ca0a0376ae4
|
|
||||||
with:
|
|
||||||
components: rustfmt
|
|
||||||
|
|
||||||
- run: cargo +nightly fmt --all --check
|
|
||||||
|
|||||||
20
CHANGELOG.md
20
CHANGELOG.md
@@ -1,3 +1,23 @@
|
|||||||
|
## [0.90.0](https://github.com/propeller-heads/tycho-execution/compare/0.89.0...0.90.0) (2025-05-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Explicitly handle the TransferType.NONE case ([65bd0d0](https://github.com/propeller-heads/tycho-execution/commit/65bd0d07499b79c261efdb00debb19487d6af543))
|
||||||
|
* Verify the amount out was received correctly for arbitrage swaps ([70230bf](https://github.com/propeller-heads/tycho-execution/commit/70230bf05f4bdfdd54f62799b72bd998d351983e))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Revert if the TransferType is not valid ([b0c254a](https://github.com/propeller-heads/tycho-execution/commit/b0c254add44e56d10f203eae30301c861fd9d8ff))
|
||||||
|
|
||||||
|
## [0.89.0](https://github.com/propeller-heads/tycho-execution/compare/0.88.0...0.89.0) (2025-05-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Remove special handling of the Univ4 callback ([f14c8ee](https://github.com/propeller-heads/tycho-execution/commit/f14c8ee29ba67ca4cd8cfe8b00e8b907a0352f9a))
|
||||||
|
|
||||||
## [0.88.0](https://github.com/propeller-heads/tycho-execution/compare/0.87.0...0.88.0) (2025-05-06)
|
## [0.88.0](https://github.com/propeller-heads/tycho-execution/compare/0.87.0...0.88.0) (2025-05-06)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -4469,7 +4469,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tycho-execution"
|
name = "tycho-execution"
|
||||||
version = "0.88.0"
|
version = "0.90.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alloy",
|
"alloy",
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tycho-execution"
|
name = "tycho-execution"
|
||||||
version = "0.88.0"
|
version = "0.90.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors."
|
description = "Provides tools for encoding and executing swaps against Tycho router and protocol executors."
|
||||||
repository = "https://github.com/propeller-heads/tycho-execution"
|
repository = "https://github.com/propeller-heads/tycho-execution"
|
||||||
|
|||||||
@@ -127,7 +127,9 @@ contract Dispatcher {
|
|||||||
tstore(_CURRENTLY_SWAPPING_EXECUTOR_SLOT, 0)
|
tstore(_CURRENTLY_SWAPPING_EXECUTOR_SLOT, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is necessary because the delegatecall will prepend extra bytes we don't want like the length and prefix
|
// The final callback result should not be ABI encoded. That is why we are decoding here.
|
||||||
|
// ABI encoding is very gas expensive and we want to avoid it if possible.
|
||||||
|
// The result from `handleCallback` is always ABI encoded.
|
||||||
bytes memory decodedResult = abi.decode(result, (bytes));
|
bytes memory decodedResult = abi.decode(result, (bytes));
|
||||||
return decodedResult;
|
return decodedResult;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -436,16 +436,15 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
Address.sendValue(payable(receiver), amountOut);
|
Address.sendValue(payable(receiver), amountOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokenIn != tokenOut) {
|
_verifyAmountOutWasReceived(
|
||||||
uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
|
tokenIn,
|
||||||
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
|
tokenOut,
|
||||||
if (userAmount != amountOut) {
|
initialBalanceTokenOut,
|
||||||
revert TychoRouter__AmountOutNotFullyReceived(
|
amountOut,
|
||||||
userAmount, amountOut
|
receiver,
|
||||||
|
amountIn
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @notice Internal implementation of the core swap logic shared between singleSwap() and singleSwapPermit2().
|
* @notice Internal implementation of the core swap logic shared between singleSwap() and singleSwapPermit2().
|
||||||
@@ -493,16 +492,15 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
Address.sendValue(payable(receiver), amountOut);
|
Address.sendValue(payable(receiver), amountOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokenIn != tokenOut) {
|
_verifyAmountOutWasReceived(
|
||||||
uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
|
tokenIn,
|
||||||
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
|
tokenOut,
|
||||||
if (userAmount != amountOut) {
|
initialBalanceTokenOut,
|
||||||
revert TychoRouter__AmountOutNotFullyReceived(
|
amountOut,
|
||||||
userAmount, amountOut
|
receiver,
|
||||||
|
amountIn
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @notice Internal implementation of the core swap logic shared between sequentialSwap() and sequentialSwapPermit2().
|
* @notice Internal implementation of the core swap logic shared between sequentialSwap() and sequentialSwapPermit2().
|
||||||
@@ -546,17 +544,15 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
_unwrapETH(amountOut);
|
_unwrapETH(amountOut);
|
||||||
Address.sendValue(payable(receiver), amountOut);
|
Address.sendValue(payable(receiver), amountOut);
|
||||||
}
|
}
|
||||||
|
_verifyAmountOutWasReceived(
|
||||||
if (tokenIn != tokenOut) {
|
tokenIn,
|
||||||
uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
|
tokenOut,
|
||||||
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
|
initialBalanceTokenOut,
|
||||||
if (userAmount != amountOut) {
|
amountOut,
|
||||||
revert TychoRouter__AmountOutNotFullyReceived(
|
receiver,
|
||||||
userAmount, amountOut
|
amountIn
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Executes sequential swaps as defined by the provided swap graph.
|
* @dev Executes sequential swaps as defined by the provided swap graph.
|
||||||
@@ -657,13 +653,8 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
/**
|
/**
|
||||||
* @dev We use the fallback function to allow flexibility on callback.
|
* @dev We use the fallback function to allow flexibility on callback.
|
||||||
*/
|
*/
|
||||||
fallback() external {
|
fallback(bytes calldata data) external returns (bytes memory) {
|
||||||
bytes memory result = _callHandleCallbackOnExecutor(msg.data);
|
return _callHandleCallbackOnExecutor(data);
|
||||||
// slither-disable-next-line assembly
|
|
||||||
assembly ("memory-safe") {
|
|
||||||
// Propagate the result
|
|
||||||
return(add(result, 32), mload(result))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -778,18 +769,6 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
require(msg.sender.code.length != 0);
|
require(msg.sender.code.length != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @dev Called by UniswapV4 pool manager after achieving unlock state.
|
|
||||||
*/
|
|
||||||
function unlockCallback(bytes calldata data)
|
|
||||||
external
|
|
||||||
returns (bytes memory)
|
|
||||||
{
|
|
||||||
if (data.length < 24) revert TychoRouter__InvalidDataLength();
|
|
||||||
bytes memory result = _callHandleCallbackOnExecutor(data);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Gets balance of a token for a given address. Supports both native ETH and ERC20 tokens.
|
* @dev Gets balance of a token for a given address. Supports both native ETH and ERC20 tokens.
|
||||||
*/
|
*/
|
||||||
@@ -801,4 +780,27 @@ contract TychoRouter is AccessControl, Dispatcher, Pausable, ReentrancyGuard {
|
|||||||
return
|
return
|
||||||
token == address(0) ? owner.balance : IERC20(token).balanceOf(owner);
|
token == address(0) ? owner.balance : IERC20(token).balanceOf(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Verifies that the expected amount of output tokens was received by the receiver.
|
||||||
|
* It also handles the case of arbitrage swaps where the input and output tokens are the same.
|
||||||
|
*/
|
||||||
|
function _verifyAmountOutWasReceived(
|
||||||
|
address tokenIn,
|
||||||
|
address tokenOut,
|
||||||
|
uint256 initialBalanceTokenOut,
|
||||||
|
uint256 amountOut,
|
||||||
|
address receiver,
|
||||||
|
uint256 amountIn
|
||||||
|
) internal view {
|
||||||
|
uint256 currentBalanceTokenOut = _balanceOf(tokenOut, receiver);
|
||||||
|
if (tokenIn == tokenOut) {
|
||||||
|
// If it is an arbitrage, we need to remove the amountIn from the initial balance to get a correct userAmount
|
||||||
|
initialBalanceTokenOut -= amountIn;
|
||||||
|
}
|
||||||
|
uint256 userAmount = currentBalanceTokenOut - initialBalanceTokenOut;
|
||||||
|
if (userAmount != amountOut) {
|
||||||
|
revert TychoRouter__AmountOutNotFullyReceived(userAmount, amountOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,6 +149,11 @@ contract CurveExecutor is IExecutor, TokenTransfer {
|
|||||||
receiver = address(bytes20(data[65:85]));
|
receiver = address(bytes20(data[65:85]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Even though this contract is mostly called through delegatecall, we still want to be able to receive ETH.
|
||||||
|
* This is needed when using the executor directly and it makes testing easier.
|
||||||
|
* There are some curve pools that take ETH directly.
|
||||||
|
*/
|
||||||
receive() external payable {
|
receive() external payable {
|
||||||
require(msg.sender.code.length != 0);
|
require(msg.sender.code.length != 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,12 @@ contract MaverickV2Executor is IExecutor, TokenTransfer {
|
|||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
address public immutable factory;
|
address public immutable factory;
|
||||||
address private immutable self;
|
|
||||||
|
|
||||||
constructor(address _factory, address _permit2) TokenTransfer(_permit2) {
|
constructor(address _factory, address _permit2) TokenTransfer(_permit2) {
|
||||||
if (_factory == address(0)) {
|
if (_factory == address(0)) {
|
||||||
revert MaverickV2Executor__InvalidFactory();
|
revert MaverickV2Executor__InvalidFactory();
|
||||||
}
|
}
|
||||||
factory = _factory;
|
factory = _factory;
|
||||||
self = address(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// slither-disable-next-line locked-ether
|
// slither-disable-next-line locked-ether
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|||||||
import "@permit2/src/interfaces/IAllowanceTransfer.sol";
|
import "@permit2/src/interfaces/IAllowanceTransfer.sol";
|
||||||
|
|
||||||
error TokenTransfer__AddressZero();
|
error TokenTransfer__AddressZero();
|
||||||
|
error TokenTransfer__InvalidTransferType();
|
||||||
|
|
||||||
contract TokenTransfer {
|
contract TokenTransfer {
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
@@ -45,7 +46,9 @@ contract TokenTransfer {
|
|||||||
uint256 amount,
|
uint256 amount,
|
||||||
TransferType transferType
|
TransferType transferType
|
||||||
) internal {
|
) internal {
|
||||||
if (transferType == TransferType.TRANSFER_TO_PROTOCOL) {
|
if (transferType == TransferType.NONE) {
|
||||||
|
return;
|
||||||
|
} else if (transferType == TransferType.TRANSFER_TO_PROTOCOL) {
|
||||||
if (tokenIn == address(0)) {
|
if (tokenIn == address(0)) {
|
||||||
payable(receiver).transfer(amount);
|
payable(receiver).transfer(amount);
|
||||||
} else {
|
} else {
|
||||||
@@ -65,6 +68,8 @@ contract TokenTransfer {
|
|||||||
permit2.transferFrom(
|
permit2.transferFrom(
|
||||||
sender, address(this), uint160(amount), tokenIn
|
sender, address(this), uint160(amount), tokenIn
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
revert TokenTransfer__InvalidTransferType();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,8 +184,11 @@ contract UniswapV4Executor is
|
|||||||
external
|
external
|
||||||
returns (bytes memory)
|
returns (bytes memory)
|
||||||
{
|
{
|
||||||
verifyCallback(data);
|
bytes calldata stripped = data[68:];
|
||||||
return _unlockCallback(data);
|
verifyCallback(stripped);
|
||||||
|
// Our general callback logic returns a not ABI encoded result.
|
||||||
|
// However, the pool manager expects the result to be ABI encoded. That is why we need to encode it here again.
|
||||||
|
return abi.encode(_unlockCallback(stripped));
|
||||||
}
|
}
|
||||||
|
|
||||||
function verifyCallback(bytes calldata) public view poolManagerOnly {}
|
function verifyCallback(bytes calldata) public view poolManagerOnly {}
|
||||||
|
|||||||
Reference in New Issue
Block a user