Merge branch 'main' into router/hr/ENG-4280-gas-test

This commit is contained in:
Harsh Vardhan Roy
2025-03-01 00:11:34 +05:30
committed by GitHub
13 changed files with 60 additions and 124 deletions

View File

@@ -1,3 +1,15 @@
## [0.56.0](https://github.com/propeller-heads/tycho-execution/compare/0.55.0...0.56.0) (2025-02-28)
### Features
* update base executor addresses ([bc47c12](https://github.com/propeller-heads/tycho-execution/commit/bc47c12a1a8cae0b9464b4899b52369e5036c9f7))
### Bug Fixes
* make USV2 factory configurable in Executor ([33973a6](https://github.com/propeller-heads/tycho-execution/commit/33973a65b8486c2c78e68c6a35374bd35775a7e5))
## [0.55.0](https://github.com/propeller-heads/tycho-execution/compare/0.54.0...0.55.0) (2025-02-27) ## [0.55.0](https://github.com/propeller-heads/tycho-execution/compare/0.54.0...0.55.0) (2025-02-27)

2
Cargo.lock generated
View File

@@ -4340,7 +4340,7 @@ dependencies = [
[[package]] [[package]]
name = "tycho-execution" name = "tycho-execution"
version = "0.55.0" version = "0.56.0"
dependencies = [ dependencies = [
"alloy", "alloy",
"alloy-primitives", "alloy-primitives",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "tycho-execution" name = "tycho-execution"
version = "0.55.0" version = "0.56.0"
edition = "2021" edition = "2021"
[[bin]] [[bin]]

109
README.md
View File

@@ -6,110 +6,9 @@ Tycho Execution makes it easy to trade on different DEXs by handling the complex
custom code for each DEX, you get a simple, ready-to-use tool that generates the necessary data to execute trades. It's custom code for each DEX, you get a simple, ready-to-use tool that generates the necessary data to execute trades. It's
designed to be safe, straightforward, and quick to set up, so anyone can start trading without extra effort. designed to be safe, straightforward, and quick to set up, so anyone can start trading without extra effort.
## Quickstart For complete documentation, see Tycho docs [here](https://docs.propellerheads.xyz/tycho/for-solvers/execution).
To get started, have a look at our [Quickstart example](examples/quickstart/README.md). ## Examples
## Bin Usage Guide To get started on encoding, have a look at our [Encoding example](examples/encoding-example/README.md).
For a complete example please refer to the [Tycho Quickstart guide](https://docs.propellerheads.xyz/tycho).
### Installation
First, build and install the binary:
```bash
# Build the project
cargo build --release
# Install the binary to your system
cargo install --path .
```
After installation, the `tycho-encode` command will be available to use from any directory in your terminal.
### Commands
The command lets you choose the encoding strategy to be used. The available strategies are:
#### Tycho Router
`tycho-router`: Encodes a transaction using the Tycho Router encoding strategy.
Example:
```bash
echo '<solution_payload>' | tycho-encode tycho-router
```
#### Tycho Router With Permit2
`tycho-router-with-permit2`: Encodes a transaction using the Tycho Router encoding strategy. Requires a private key for
signing
Permit2.
Example:
```bash
echo '<solution_payload>' | tycho-encode tycho-router --swapper-pk 0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234
```
#### Direct execution
`direct-execution`: Encodes a transaction using the direct execution encoding strategy. Does not require a private key.
Example:
```bash
echo '<solution_payload>' | tycho-encode direct-execution
```
### Encoding Transactions
The commands accept the following options:
- `--config_path`: Path to the executor addresses configuration file (defaults
to `src/encoding/config/executor_addresses.json`)
- `--swapper-pk`: Private key for signing approvals (required when direct_execution is false)
#### Example
Here's a complete example that encodes a swap from WETH to DAI using Uniswap V2 and the Tycho Router strategy:
```bash
echo '{"sender":"0x1234567890123456789012345678901234567890","receiver":"0x1234567890123456789012345678901234567890","given_token":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","given_amount":"1000000000000000000","checked_token":"0x6B175474E89094C44Da98b954EedeAC495271d0F","exact_out":false,"slippage":0.01,"expected_amount":"1000000000000000000","checked_amount":"990000000000000000","router_address":"0xaa820C29648D5EA543d712cC928377Bd7206a0E7","swaps":[{"component":{"id":"0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640","protocol_system":"uniswap_v2","protocol_type_name":"UniswapV2Pool","chain":"ethereum","tokens":["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"],"contract_ids":["0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"],"static_attributes":{"factory":"0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f"},"change":"Update","creation_tx":"0x0000000000000000000000000000000000000000000000000000000000000000","created_at":"2024-02-28T12:00:00"},"token_in":"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2","token_out":"0x6B175474E89094C44Da98b954EedeAC495271d0F","split":0.0}],"direct_execution":true}' | tycho-encode tycho-router-permit2 --swapper-pk 0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef1234
```
#### JSON Payload Structure: Solution struct
The `Solution` struct is composed of the following fields:
- `sender`: The address initiating the transaction
- `receiver`: The address receiving the output tokens
- `given_token`: The address of the input token (e.g., WETH)
- `given_amount`: The amount of input tokens (in wei)
- `checked_token`: The address of the output token (e.g., DAI)
- `exact_out`: Boolean indicating if this is an exact output swap
- `slippage`: The maximum allowed slippage (e.g., 0.01 for 1%)
- `expected_amount`: The expected output amount
- `checked_amount`: The minimum acceptable output amount (accounting for slippage)
- `swaps`: Array of swap steps, each containing:
- `component`: Details about the DEX/protocol being used
- `token_in`: Input token address for this step
- `token_out`: Output token address for this step
- `split`: Proportion of tokens to route through this step (1.0 = 100%)
- `router_address`: The address of the protocol's router contract
- `direct_execution`: Boolean indicating if the transaction should be executed directly
## Contract Analysis
We use [Slither](https://github.com/crytic/slither) to detect any potential vulnerabilities in our contracts.
To run locally, simply install Slither in your conda env and run it inside the foundry directory.
```bash
conda create --name tycho-execution python=3.10
conda activate tycho-execution
pip install slither-analyzer
cd foundry
slither .
```

View File

@@ -12,7 +12,10 @@
"vm:balancer_v2": "0x543778987b293C7E8Cf0722BB2e935ba6f4068D4" "vm:balancer_v2": "0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"
}, },
"base": { "base": {
"uniswap_v2": "0x2DaE954eCF457276F90B68Cee68981C0aA07f2ef" "uniswap_v2": "0xa2D6c55676D0d2A1D090e3157aCC9a41bB53BE3B",
"uniswap_v3": "0x374fFd7224422dF5dE83B43C7Fa8379F9CAA826a",
"uniswap_v4": "0xf1531642018C9Ab6a1B07666755d975eae16a01b",
"vm:balancer_v2": "0x6920209CaAF45872006eC507f2f18595af030418"
}, },
"tenderly_base": { "tenderly_base": {
"uniswap_v3": "0x7c7E06d7317e620a185078e236879D2a87fC8d22" "uniswap_v3": "0x7c7E06d7317e620a185078e236879D2a87fC8d22"

View File

@@ -1,6 +1,6 @@
# QuickStart # Encoding example
This quickstart guide enables you to: This guide enables you to:
1. Create a Solution object 1. Create a Solution object
2. Encode the solution to interact with the Ethereum blockchain 2. Encode the solution to interact with the Ethereum blockchain
@@ -8,5 +8,5 @@ This quickstart guide enables you to:
## How to run ## How to run
```bash ```bash
cargo run --release --example quickstart cargo run --release --example encoding-example
``` ```

View File

@@ -3,12 +3,21 @@ const {ethers} = require("hardhat");
const hre = require("hardhat"); const hre = require("hardhat");
// Comment out the executors you don't want to deploy // Comment out the executors you don't want to deploy
const executors_to_deploy = [ const executors_to_deploy = {
{exchange: "UniswapV2Executor", args: []}, "ethereum":[
{exchange: "UniswapV2Executor", args: ["0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"]},
{exchange: "UniswapV3Executor", args: ["0x1F98431c8aD98523631AE4a59f267346ea31F984"]}, {exchange: "UniswapV3Executor", args: ["0x1F98431c8aD98523631AE4a59f267346ea31F984"]},
{exchange: "UniswapV4Executor", args: ["0x000000000004444c5dc75cB358380D2e3dE08A90"]}, {exchange: "UniswapV4Executor", args: ["0x000000000004444c5dc75cB358380D2e3dE08A90"]},
{exchange: "BalancerV2Executor", args: []}, {exchange: "BalancerV2Executor", args: []},
] ],
"base":[
{exchange: "UniswapV2Executor", args: ["0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6"]},
{exchange: "UniswapV3Executor", args: ["0x33128a8fC17869897dcE68Ed026d694621f6FDfD"]},
{exchange: "UniswapV4Executor", args: ["0x498581ff718922c3f8e6a244956af099b2652b2b"]},
{exchange: "BalancerV2Executor", args: []},
],
}
async function main() { async function main() {
const network = hre.network.name; const network = hre.network.name;
@@ -18,7 +27,7 @@ async function main() {
console.log(`Deploying with account: ${deployer.address}`); console.log(`Deploying with account: ${deployer.address}`);
console.log(`Account balance: ${ethers.utils.formatEther(await deployer.getBalance())} ETH`); console.log(`Account balance: ${ethers.utils.formatEther(await deployer.getBalance())} ETH`);
for (const executor of executors_to_deploy) { for (const executor of executors_to_deploy[network]) {
const {exchange, args} = executor; const {exchange, args} = executor;
const Executor = await ethers.getContractFactory(exchange); const Executor = await ethers.getContractFactory(exchange);
const deployedExecutor = await Executor.deploy(...args); const deployedExecutor = await Executor.deploy(...args);

View File

@@ -7,6 +7,7 @@ import "@uniswap-v2/contracts/interfaces/IUniswapV2Pair.sol";
error UniswapV2Executor__InvalidDataLength(); error UniswapV2Executor__InvalidDataLength();
error UniswapV2Executor__InvalidTarget(); error UniswapV2Executor__InvalidTarget();
error UniswapV2Executor__InvalidFactory();
contract UniswapV2Executor is IExecutor { contract UniswapV2Executor is IExecutor {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
@@ -14,8 +15,16 @@ contract UniswapV2Executor is IExecutor {
bytes32 internal constant POOL_INIT_CODE_HASH = bytes32 internal constant POOL_INIT_CODE_HASH =
0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f; 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f;
address private constant FACTORY = address public immutable factory;
0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f; address private immutable self;
constructor(address _factory) {
if (_factory == address(0)) {
revert UniswapV2Executor__InvalidFactory();
}
factory = _factory;
self = address(this);
}
// slither-disable-next-line locked-ether // slither-disable-next-line locked-ether
function swap(uint256 givenAmount, bytes calldata data) function swap(uint256 givenAmount, bytes calldata data)
@@ -94,7 +103,7 @@ contract UniswapV2Executor is IExecutor {
uint256( uint256(
keccak256( keccak256(
abi.encodePacked( abi.encodePacked(
hex"ff", FACTORY, salt, POOL_INIT_CODE_HASH hex"ff", factory, salt, POOL_INIT_CODE_HASH
) )
) )
) )

View File

@@ -47,7 +47,8 @@ contract Constants is Test, BaseConstants {
address USDC_WBTC_POOL = 0x004375Dff511095CC5A197A54140a24eFEF3A416; address USDC_WBTC_POOL = 0x004375Dff511095CC5A197A54140a24eFEF3A416;
// Uniswap v3 // Uniswap v3
address USV3_FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984; address USV3_FACTORY_ETHEREUM = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
address USV2_FACTORY_ETHEREUM = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
address DAI_WETH_USV3 = 0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8; address DAI_WETH_USV3 = 0xC2e9F25Be6257c210d7Adf0D4Cd6E3E881ba25f8;
// Uniswap universal router // Uniswap universal router

View File

@@ -44,7 +44,8 @@ contract TychoRouterTestSetup is Test, Constants {
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
vm.startPrank(ADMIN); vm.startPrank(ADMIN);
address factoryV3 = USV3_FACTORY; address factoryV3 = USV3_FACTORY_ETHEREUM;
address factoryV2 = USV2_FACTORY_ETHEREUM;
address poolManagerAddress = 0x000000000004444c5dc75cB358380D2e3dE08A90; address poolManagerAddress = 0x000000000004444c5dc75cB358380D2e3dE08A90;
IPoolManager poolManager = IPoolManager(poolManagerAddress); IPoolManager poolManager = IPoolManager(poolManagerAddress);
tychoRouter = new TychoRouterExposed(PERMIT2_ADDRESS, WETH_ADDR); tychoRouter = new TychoRouterExposed(PERMIT2_ADDRESS, WETH_ADDR);
@@ -59,7 +60,7 @@ contract TychoRouterTestSetup is Test, Constants {
deployDummyContract(); deployDummyContract();
vm.stopPrank(); vm.stopPrank();
usv2Executor = new UniswapV2Executor(); usv2Executor = new UniswapV2Executor(factoryV2);
usv3Executor = new UniswapV3Executor(factoryV3); usv3Executor = new UniswapV3Executor(factoryV3);
usv4Executor = new UniswapV4Executor(poolManager); usv4Executor = new UniswapV4Executor(poolManager);
vm.startPrank(EXECUTOR_SETTER); vm.startPrank(EXECUTOR_SETTER);

View File

@@ -6,6 +6,8 @@ import {Test} from "../../lib/forge-std/src/Test.sol";
import {Constants} from "../Constants.sol"; import {Constants} from "../Constants.sol";
contract UniswapV2ExecutorExposed is UniswapV2Executor { contract UniswapV2ExecutorExposed is UniswapV2Executor {
constructor(address _factory) UniswapV2Executor(_factory) {}
function decodeParams(bytes calldata data) function decodeParams(bytes calldata data)
external external
pure pure
@@ -42,7 +44,7 @@ contract FakeUniswapV2Pool {
} }
} }
contract UniswapV2ExecutorTest is UniswapV2ExecutorExposed, Test, Constants { contract UniswapV2ExecutorTest is Test, Constants {
using SafeERC20 for IERC20; using SafeERC20 for IERC20;
UniswapV2ExecutorExposed uniswapV2Exposed; UniswapV2ExecutorExposed uniswapV2Exposed;
@@ -52,7 +54,7 @@ contract UniswapV2ExecutorTest is UniswapV2ExecutorExposed, Test, Constants {
function setUp() public { function setUp() public {
uint256 forkBlock = 17323404; uint256 forkBlock = 17323404;
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
uniswapV2Exposed = new UniswapV2ExecutorExposed(); uniswapV2Exposed = new UniswapV2ExecutorExposed(USV2_FACTORY_ETHEREUM);
} }
function testDecodeParams() public view { function testDecodeParams() public view {

View File

@@ -44,7 +44,7 @@ contract UniswapV3ExecutorTest is Test, Constants {
uint256 forkBlock = 17323404; uint256 forkBlock = 17323404;
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock); vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
uniswapV3Exposed = new UniswapV3ExecutorExposed(USV3_FACTORY); uniswapV3Exposed = new UniswapV3ExecutorExposed(USV3_FACTORY_ETHEREUM);
} }
function testDecodeParams() public view { function testDecodeParams() public view {